mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
initial commit
This commit is contained in:
commit
c2da007f40
1610 changed files with 398047 additions and 0 deletions
39
catchain/CMakeLists.txt
Normal file
39
catchain/CMakeLists.txt
Normal file
|
@ -0,0 +1,39 @@
|
|||
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
|
||||
|
||||
if (NOT OPENSSL_FOUND)
|
||||
find_package(OpenSSL REQUIRED)
|
||||
endif()
|
||||
|
||||
set(CATCHAIN_SOURCE
|
||||
catchain-received-block.cpp
|
||||
#catchain-receiver-fork.cpp
|
||||
catchain-receiver-source.cpp
|
||||
catchain-receiver.cpp
|
||||
catchain-block.cpp
|
||||
catchain.cpp
|
||||
|
||||
catchain-block.hpp
|
||||
catchain-received-block.h
|
||||
catchain-received-block.hpp
|
||||
#catchain-receiver-fork.h
|
||||
#catchain-receiver-fork.hpp
|
||||
catchain-receiver-interface.h
|
||||
catchain-receiver-source.h
|
||||
catchain-receiver-source.hpp
|
||||
catchain-receiver.h
|
||||
catchain-receiver.hpp
|
||||
catchain-types.h
|
||||
catchain.h
|
||||
catchain.hpp
|
||||
)
|
||||
|
||||
add_library(catchain STATIC ${CATCHAIN_SOURCE})
|
||||
|
||||
target_include_directories(overlay PUBLIC
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
|
||||
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
|
||||
${OPENSSL_INCLUDE_DIR}
|
||||
)
|
||||
target_link_libraries(catchain PRIVATE tdutils tdactor adnl tl_api dht tdfec
|
||||
overlay)
|
||||
|
59
catchain/catchain-block.cpp
Normal file
59
catchain/catchain-block.cpp
Normal file
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "catchain-block.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
std::unique_ptr<CatChainBlock> CatChainBlock::create(td::uint32 src, td::uint32 fork, PublicKeyHash src_hash,
|
||||
CatChainBlockHeight height, CatChainBlockHash hash,
|
||||
td::SharedSlice payload, CatChainBlock *prev,
|
||||
std::vector<CatChainBlock *> deps,
|
||||
std::vector<CatChainBlockHeight> vt) {
|
||||
return std::make_unique<CatChainBlockImpl>(src, fork, src_hash, height, hash, std::move(payload), prev,
|
||||
std::move(deps), std::move(vt));
|
||||
}
|
||||
|
||||
CatChainBlockImpl::CatChainBlockImpl(td::uint32 src, td::uint32 fork, PublicKeyHash src_hash,
|
||||
CatChainBlockHeight height, CatChainBlockHash hash, td::SharedSlice payload,
|
||||
CatChainBlock *prev, std::vector<CatChainBlock *> deps,
|
||||
std::vector<CatChainBlockHeight> vt)
|
||||
: src_(src)
|
||||
, fork_(fork)
|
||||
, src_hash_(src_hash)
|
||||
, height_(height)
|
||||
, hash_(hash)
|
||||
, payload_(std::move(payload))
|
||||
, prev_(prev)
|
||||
, deps_(std::move(deps))
|
||||
, vt_(std::move(vt)) {
|
||||
}
|
||||
|
||||
bool CatChainBlockImpl::is_descendant_of(CatChainBlock *block) {
|
||||
auto fork = block->fork();
|
||||
if (fork >= vt_.size()) {
|
||||
return false;
|
||||
}
|
||||
return block->height() <= vt_[fork];
|
||||
}
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
113
catchain/catchain-block.hpp
Normal file
113
catchain/catchain-block.hpp
Normal file
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "catchain.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainBlockImpl : public CatChainBlock {
|
||||
private:
|
||||
std::unique_ptr<CatChainBlock::Extra> extra_;
|
||||
td::uint32 src_;
|
||||
td::uint32 fork_;
|
||||
PublicKeyHash src_hash_;
|
||||
CatChainBlockHeight height_;
|
||||
CatChainBlockHash hash_;
|
||||
td::SharedSlice payload_;
|
||||
CatChainBlock *prev_;
|
||||
std::vector<CatChainBlock *> deps_;
|
||||
std::vector<CatChainBlockHeight> vt_;
|
||||
bool preprocess_sent_ = false;
|
||||
bool is_processed_ = false;
|
||||
|
||||
public:
|
||||
td::SharedSlice &payload() override {
|
||||
return payload_;
|
||||
}
|
||||
const td::SharedSlice &payload() const override {
|
||||
return payload_;
|
||||
}
|
||||
|
||||
CatChainBlock::Extra *extra() const override {
|
||||
return extra_.get();
|
||||
}
|
||||
std::unique_ptr<Extra> move_extra() override {
|
||||
return std::move(extra_);
|
||||
}
|
||||
void set_extra(std::unique_ptr<Extra> extra) override {
|
||||
extra_ = std::move(extra);
|
||||
}
|
||||
|
||||
td::uint32 source() const override {
|
||||
return src_;
|
||||
}
|
||||
td::uint32 fork() const override {
|
||||
return fork_;
|
||||
}
|
||||
PublicKeyHash source_hash() const override {
|
||||
return src_hash_;
|
||||
}
|
||||
CatChainBlockHash hash() const override {
|
||||
return hash_;
|
||||
}
|
||||
CatChainBlockHeight height() const override {
|
||||
return height_;
|
||||
}
|
||||
|
||||
CatChainBlock *prev() override {
|
||||
return prev_;
|
||||
}
|
||||
const CatChainBlock *prev() const override {
|
||||
return prev_;
|
||||
}
|
||||
|
||||
const std::vector<CatChainBlock *> &deps() const override {
|
||||
return deps_;
|
||||
}
|
||||
const std::vector<CatChainBlockHeight> &vt() const override {
|
||||
return vt_;
|
||||
}
|
||||
|
||||
bool preprocess_is_sent() const override {
|
||||
return preprocess_sent_;
|
||||
}
|
||||
void preprocess_sent() override {
|
||||
preprocess_sent_ = true;
|
||||
}
|
||||
|
||||
bool is_processed() const override {
|
||||
return is_processed_;
|
||||
}
|
||||
void set_processed() override {
|
||||
is_processed_ = true;
|
||||
}
|
||||
|
||||
bool is_descendant_of(CatChainBlock *block) override;
|
||||
|
||||
CatChainBlockImpl(td::uint32 src, td::uint32 fork, PublicKeyHash src_hash, CatChainBlockHeight height,
|
||||
CatChainBlockHash hash, td::SharedSlice payload, CatChainBlock *prev,
|
||||
std::vector<CatChainBlock *> deps, std::vector<CatChainBlockHeight> vt);
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
523
catchain/catchain-received-block.cpp
Normal file
523
catchain/catchain-received-block.cpp
Normal file
|
@ -0,0 +1,523 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include <set>
|
||||
|
||||
#include "catchain-received-block.hpp"
|
||||
#include "catchain-receiver-source.h"
|
||||
|
||||
#include "auto/tl/ton_api.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
void CatChainReceivedBlockImpl::initialize(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) {
|
||||
if (state_ != bs_none) {
|
||||
return;
|
||||
}
|
||||
|
||||
payload_ = std::move(payload);
|
||||
CHECK(payload_.size() > 0);
|
||||
|
||||
prev_ = dynamic_cast<CatChainReceivedBlockImpl *>(chain_->create_block(std::move(block->data_->prev_)));
|
||||
CHECK(prev_ != nullptr);
|
||||
for (auto &X : block->data_->deps_) {
|
||||
auto B = dynamic_cast<CatChainReceivedBlockImpl *>(chain_->create_block(std::move(X)));
|
||||
CHECK(B != nullptr);
|
||||
block_deps_.push_back(B);
|
||||
}
|
||||
signature_ = td::SharedSlice{block->signature_.as_slice()};
|
||||
|
||||
state_ = bs_initialized;
|
||||
VLOG(CATCHAIN_DEBUG) << this << ": initialized payload_size=" << payload_.size();
|
||||
|
||||
if (prev_->is_ill()) {
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
for (auto &X : block_deps_) {
|
||||
if (X->is_ill()) {
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
td::uint32 pending_deps = 0;
|
||||
{
|
||||
if (!prev_->delivered()) {
|
||||
pending_deps++;
|
||||
} else {
|
||||
update_deps(prev_);
|
||||
}
|
||||
if (!prev_->delivered()) {
|
||||
prev_->add_rev_dep(this);
|
||||
}
|
||||
}
|
||||
for (auto &X : block_deps_) {
|
||||
if (!X->delivered()) {
|
||||
pending_deps++;
|
||||
} else {
|
||||
update_deps(X);
|
||||
}
|
||||
if (!X->delivered()) {
|
||||
X->add_rev_dep(this);
|
||||
}
|
||||
}
|
||||
pending_deps_ = pending_deps;
|
||||
if (pending_deps_ == 0 && in_db_) {
|
||||
schedule();
|
||||
}
|
||||
|
||||
chain_->get_source(source_id_)->block_received(height_);
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::run() {
|
||||
if (is_ill()) {
|
||||
return;
|
||||
}
|
||||
if (state_ == bs_none) {
|
||||
return;
|
||||
}
|
||||
if (state_ == bs_delivered) {
|
||||
return;
|
||||
}
|
||||
CHECK(state_ == bs_initialized);
|
||||
CHECK(!pending_deps_);
|
||||
CHECK(in_db_);
|
||||
|
||||
initialize_fork();
|
||||
pre_deliver();
|
||||
deliver();
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::initialize_fork() {
|
||||
CHECK(state_ == bs_initialized);
|
||||
CHECK(!fork_id_);
|
||||
CatChainReceiverSource *S = chain_->get_source(source_id_);
|
||||
if (height_ == 1) {
|
||||
fork_id_ = S->add_fork();
|
||||
} else {
|
||||
if (!prev_->next_) {
|
||||
prev_->next_ = this;
|
||||
fork_id_ = prev_->fork_id_;
|
||||
} else {
|
||||
fork_id_ = S->add_fork();
|
||||
}
|
||||
}
|
||||
|
||||
if (deps_.size() < fork_id_ + 1) {
|
||||
deps_.resize(fork_id_ + 1, 0);
|
||||
}
|
||||
CHECK(deps_[fork_id_] < height_);
|
||||
deps_[fork_id_] = height_;
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::pre_deliver(ton_api::catchain_block_data_fork &b) {
|
||||
{
|
||||
td::Status S;
|
||||
S = chain_->validate_block_sync(b.left_);
|
||||
if (S.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: left is invalid: " << S.move_as_error();
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
S = chain_->validate_block_sync(b.right_);
|
||||
if (S.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: right is invalid: " << S.move_as_error();
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// block is incorrect, since blocks are
|
||||
if (b.left_->height_ != b.right_->height_ || b.left_->src_ != b.right_->src_ ||
|
||||
b.left_->data_hash_ == b.right_->data_hash_) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: not a fork";
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
|
||||
auto S = chain_->get_source(b.left_->src_);
|
||||
S->on_found_fork_proof(
|
||||
create_serialize_tl_object<ton_api::catchain_block_data_fork>(std::move(b.left_), std::move(b.right_)));
|
||||
S->blame(fork_id_, height_);
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::pre_deliver(ton_api::catchain_block_data_badBlock &b) {
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::pre_deliver(ton_api::catchain_block_data_nop &b) {
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::pre_deliver() {
|
||||
if (is_ill()) {
|
||||
return;
|
||||
}
|
||||
CHECK(state_ == bs_initialized);
|
||||
CHECK(pending_deps_ == 0);
|
||||
CHECK(in_db_);
|
||||
|
||||
auto M = chain_->get_source(source_id_);
|
||||
|
||||
auto d = prev_ ? &prev_->deps_ : nullptr;
|
||||
|
||||
for (auto &X : block_deps_) {
|
||||
auto S = chain_->get_source(X->get_source_id());
|
||||
auto &f = S->get_forks();
|
||||
if (d) {
|
||||
auto &dd = *d;
|
||||
if (X->get_fork_id() < dd.size() && X->get_height() <= dd[X->get_fork_id()]) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": has direct dep from source " << X->get_source_id() << " and prev block "
|
||||
<< " has newer indirect dep";
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (S->blamed() && d) {
|
||||
auto &dd = *d;
|
||||
for (auto x : f) {
|
||||
if (x != X->get_fork_id() && dd.size() > x && dd[x] > 0) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": has direct dep from source " << X->get_source_id() << " and prev block "
|
||||
<< " has indirect dep to another fork of this source " << x << " " << X->get_fork_id()
|
||||
<< " " << dd[x] << " " << f;
|
||||
M->blame(fork_id_, height_);
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
auto v = S->get_blamed_heights();
|
||||
|
||||
for (size_t i = 0; i < v.size() && i < dd.size(); i++) {
|
||||
if (v[i] > 0 && dd[i] >= v[i]) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": has direct dep from source " << X->get_source_id() << " and prev block "
|
||||
<< " has indirect dep to block f" << i << "@" << v[i]
|
||||
<< " which is known to blame this source";
|
||||
M->blame(fork_id_, height_);
|
||||
set_ill();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto X = fetch_tl_object<ton_api::catchain_block_inner_Data>(payload_.as_slice(), true);
|
||||
if (X.is_error()) {
|
||||
is_custom_ = true;
|
||||
} else {
|
||||
ton_api::downcast_call(*X.move_as_ok().get(), [Self = this](auto &obj) { Self->pre_deliver(obj); });
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::deliver() {
|
||||
if (is_ill()) {
|
||||
return;
|
||||
}
|
||||
CHECK(state_ == bs_initialized);
|
||||
CHECK(pending_deps_ == 0);
|
||||
CHECK(in_db_);
|
||||
|
||||
chain_->deliver_block(this);
|
||||
|
||||
state_ = bs_delivered;
|
||||
VLOG(CATCHAIN_DEBUG) << this << ": delivered";
|
||||
|
||||
for (auto &B : rev_deps_) {
|
||||
B->dep_delivered(this);
|
||||
}
|
||||
rev_deps_.clear();
|
||||
|
||||
chain_->get_source(source_id_)->block_delivered(height_);
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::set_ill() {
|
||||
if (state_ == bs_ill) {
|
||||
return;
|
||||
}
|
||||
VLOG(CATCHAIN_WARNING) << this << ": got ill";
|
||||
auto M = chain_->get_source(source_id_);
|
||||
M->blame();
|
||||
state_ = bs_ill;
|
||||
for (auto &B : rev_deps_) {
|
||||
B->dep_ill(this);
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::dep_ill(CatChainReceivedBlockImpl *block) {
|
||||
set_ill();
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::update_deps(CatChainReceivedBlockImpl *block) {
|
||||
auto &d = block->deps_;
|
||||
if (d.size() > deps_.size()) {
|
||||
deps_.resize(d.size(), 0);
|
||||
}
|
||||
for (size_t i = 0; i < d.size(); i++) {
|
||||
if (deps_[i] < d[i]) {
|
||||
deps_[i] = d[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::dep_delivered(CatChainReceivedBlockImpl *block) {
|
||||
if (is_ill()) {
|
||||
return;
|
||||
}
|
||||
CHECK(!block->is_ill());
|
||||
update_deps(block);
|
||||
pending_deps_--;
|
||||
if (pending_deps_ == 0 && in_db_) {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::written() {
|
||||
if (!in_db_) {
|
||||
in_db_ = true;
|
||||
if (pending_deps_ == 0) {
|
||||
schedule();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::add_rev_dep(CatChainReceivedBlockImpl *block) {
|
||||
rev_deps_.push_back(block);
|
||||
}
|
||||
|
||||
CatChainReceivedBlock *CatChainReceivedBlockImpl::get_prev() const {
|
||||
return prev_;
|
||||
}
|
||||
|
||||
CatChainBlockHash CatChainReceivedBlockImpl::get_prev_hash() const {
|
||||
CHECK(prev_);
|
||||
return prev_->get_hash();
|
||||
}
|
||||
|
||||
std::vector<CatChainBlockHash> CatChainReceivedBlockImpl::get_dep_hashes() const {
|
||||
std::vector<CatChainBlockHash> v;
|
||||
v.resize(block_deps_.size());
|
||||
for (size_t i = 0; i < v.size(); i++) {
|
||||
v[i] = block_deps_[i]->get_hash();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::schedule() {
|
||||
chain_->run_block(this);
|
||||
}
|
||||
|
||||
void CatChainReceivedBlockImpl::find_pending_deps(std::vector<CatChainBlockHash> &vec, td::uint32 max_size) const {
|
||||
if (height_ == 0 || is_ill() || delivered() || vec.size() == max_size) {
|
||||
return;
|
||||
}
|
||||
if (!initialized()) {
|
||||
vec.push_back(get_hash());
|
||||
return;
|
||||
}
|
||||
if (prev_) {
|
||||
prev_->find_pending_deps(vec, max_size);
|
||||
}
|
||||
for (auto &X : block_deps_) {
|
||||
X->find_pending_deps(vec, max_size);
|
||||
}
|
||||
}
|
||||
|
||||
tl_object_ptr<ton_api::catchain_block_id> CatChainReceivedBlock::block_id(CatChainReceiver *chain,
|
||||
tl_object_ptr<ton_api::catchain_block> &block,
|
||||
td::Slice payload) {
|
||||
return create_tl_object<ton_api::catchain_block_id>(block->incarnation_, chain->get_source_hash(block->src_).tl(),
|
||||
block->height_, sha256_bits256(payload));
|
||||
}
|
||||
tl_object_ptr<ton_api::catchain_block_id> CatChainReceivedBlock::block_id(
|
||||
CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block_dep> &block) {
|
||||
return create_tl_object<ton_api::catchain_block_id>(
|
||||
chain->get_incarnation(), chain->get_source_hash(block->src_).tl(), block->height_, block->data_hash_);
|
||||
}
|
||||
|
||||
CatChainBlockHash CatChainReceivedBlock::block_hash(CatChainReceiver *chain,
|
||||
tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) {
|
||||
return get_tl_object_sha_bits256(block_id(chain, block, payload));
|
||||
}
|
||||
|
||||
CatChainBlockHash CatChainReceivedBlock::block_hash(CatChainReceiver *chain,
|
||||
tl_object_ptr<ton_api::catchain_block_dep> &block) {
|
||||
return get_tl_object_sha_bits256(block_id(chain, block));
|
||||
}
|
||||
|
||||
td::Status CatChainReceivedBlock::pre_validate_block(CatChainReceiver *chain,
|
||||
tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) {
|
||||
CHECK(block->incarnation_ == chain->get_incarnation());
|
||||
if (block->height_ <= 0) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, std::string("bad height ") + std::to_string(block->height_));
|
||||
}
|
||||
if (block->src_ < 0 || static_cast<td::uint32>(block->src_) >= chain->get_sources_cnt()) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, std::string("bad src ") + std::to_string(block->src_));
|
||||
}
|
||||
|
||||
if (block->data_->prev_->src_ < 0) {
|
||||
return td::Status::Error(ErrorCode::protoviolation,
|
||||
std::string("bad prev block src ") + std::to_string(block->data_->prev_->src_));
|
||||
}
|
||||
if (block->data_->deps_.size() > chain->opts().max_deps) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, std::string("too many deps"));
|
||||
}
|
||||
auto prev_src = static_cast<td::uint32>(block->data_->prev_->src_);
|
||||
|
||||
if (block->height_ > 1) {
|
||||
if (prev_src != static_cast<td::uint32>(block->src_)) {
|
||||
return td::Status::Error(ErrorCode::protoviolation,
|
||||
std::string("bad prev block src ") + std::to_string(block->data_->prev_->src_));
|
||||
}
|
||||
} else {
|
||||
if (prev_src != chain->get_sources_cnt()) {
|
||||
return td::Status::Error(ErrorCode::protoviolation,
|
||||
std::string("bad prev(first) block src ") + std::to_string(block->data_->prev_->src_));
|
||||
}
|
||||
}
|
||||
if (block->data_->prev_->height_ + 1 != block->height_) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, std::string("bad prev block height ") +
|
||||
std::to_string(block->data_->prev_->height_) + " (our " +
|
||||
std::to_string(block->height_) + ")");
|
||||
}
|
||||
|
||||
std::set<td::uint32> used;
|
||||
used.insert(block->src_);
|
||||
for (auto &X : block->data_->deps_) {
|
||||
if (used.find(X->src_) != used.end()) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "two deps from same source");
|
||||
}
|
||||
used.insert(X->src_);
|
||||
}
|
||||
|
||||
TRY_STATUS(chain->validate_block_sync(block->data_->prev_));
|
||||
for (auto &X : block->data_->deps_) {
|
||||
TRY_STATUS(chain->validate_block_sync(X));
|
||||
}
|
||||
|
||||
if (payload.size() == 0) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, "empty payload");
|
||||
}
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
td::Status CatChainReceivedBlock::pre_validate_block(CatChainReceiver *chain,
|
||||
tl_object_ptr<ton_api::catchain_block_dep> &block) {
|
||||
if (block->height_ < 0) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, std::string("bad height ") + std::to_string(block->height_));
|
||||
}
|
||||
if (block->height_ > 0) {
|
||||
if (block->src_ < 0 || static_cast<td::uint32>(block->src_) >= chain->get_sources_cnt()) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, std::string("bad src ") + std::to_string(block->src_));
|
||||
}
|
||||
} else {
|
||||
if (block->src_ < 0 || static_cast<td::uint32>(block->src_) != chain->get_sources_cnt()) {
|
||||
return td::Status::Error(ErrorCode::protoviolation,
|
||||
std::string("bad src (first block) ") + std::to_string(block->src_));
|
||||
}
|
||||
if (block->data_hash_ != chain->get_incarnation() || block->signature_.size() != 0) {
|
||||
return td::Status::Error(ErrorCode::protoviolation, std::string("bad first block"));
|
||||
}
|
||||
}
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
tl_object_ptr<ton_api::catchain_block> CatChainReceivedBlockImpl::export_tl() const {
|
||||
CHECK(initialized());
|
||||
CHECK(height_ > 0);
|
||||
std::vector<tl_object_ptr<ton_api::catchain_block_dep>> deps;
|
||||
|
||||
for (auto &B : block_deps_) {
|
||||
deps.push_back(B->export_tl_dep());
|
||||
}
|
||||
|
||||
return create_tl_object<ton_api::catchain_block>(
|
||||
chain_->get_incarnation(), source_id_, height_,
|
||||
create_tl_object<ton_api::catchain_block_data>(prev_->export_tl_dep(), std::move(deps)),
|
||||
signature_.clone_as_buffer_slice());
|
||||
}
|
||||
|
||||
tl_object_ptr<ton_api::catchain_block_dep> CatChainReceivedBlockImpl::export_tl_dep() const {
|
||||
return create_tl_object<ton_api::catchain_block_dep>(source_id_, height_, data_hash_,
|
||||
signature_.clone_as_buffer_slice());
|
||||
}
|
||||
|
||||
CatChainReceivedBlockImpl::CatChainReceivedBlockImpl(td::uint32 source_id, CatChainSessionId hash,
|
||||
CatChainReceiver *chain) {
|
||||
chain_ = chain;
|
||||
state_ = bs_delivered;
|
||||
fork_id_ = 0;
|
||||
source_id_ = source_id;
|
||||
data_ = nullptr;
|
||||
prev_ = nullptr;
|
||||
height_ = 0;
|
||||
|
||||
data_hash_ = hash;
|
||||
hash_ = get_tl_object_sha_bits256(create_tl_object<ton_api::catchain_block_id>(
|
||||
chain->get_incarnation(), chain->get_incarnation(), height_, data_hash_));
|
||||
}
|
||||
|
||||
CatChainReceivedBlockImpl::CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block> block,
|
||||
td::SharedSlice payload, CatChainReceiver *chain) {
|
||||
chain_ = chain;
|
||||
data_hash_ = sha256_bits256(payload.as_slice());
|
||||
hash_ = get_tl_object_sha_bits256(create_tl_object<ton_api::catchain_block_id>(
|
||||
block->incarnation_, chain->get_source_hash(block->src_).tl(), block->height_, data_hash_));
|
||||
height_ = block->height_;
|
||||
source_id_ = block->src_;
|
||||
|
||||
auto S = chain_->get_source(source_id_);
|
||||
S->on_new_block(this);
|
||||
|
||||
initialize(std::move(block), std::move(payload));
|
||||
}
|
||||
|
||||
CatChainReceivedBlockImpl::CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block_dep> block,
|
||||
CatChainReceiver *chain) {
|
||||
chain_ = chain;
|
||||
data_hash_ = block->data_hash_;
|
||||
source_id_ = block->src_;
|
||||
signature_ = td::SharedSlice{block->signature_.as_slice()};
|
||||
hash_ = get_tl_object_sha_bits256(create_tl_object<ton_api::catchain_block_id>(
|
||||
chain_->get_incarnation(), chain_->get_source_hash(source_id_).tl(), block->height_, data_hash_));
|
||||
height_ = block->height_;
|
||||
|
||||
auto S = chain_->get_source(source_id_);
|
||||
S->on_new_block(this);
|
||||
}
|
||||
|
||||
std::unique_ptr<CatChainReceivedBlock> CatChainReceivedBlock::create(tl_object_ptr<ton_api::catchain_block> block,
|
||||
td::SharedSlice payload, CatChainReceiver *chain) {
|
||||
return std::make_unique<CatChainReceivedBlockImpl>(std::move(block), std::move(payload), chain);
|
||||
}
|
||||
|
||||
std::unique_ptr<CatChainReceivedBlock> CatChainReceivedBlock::create(tl_object_ptr<ton_api::catchain_block_dep> block,
|
||||
CatChainReceiver *chain) {
|
||||
return std::make_unique<CatChainReceivedBlockImpl>(std::move(block), chain);
|
||||
}
|
||||
|
||||
std::unique_ptr<CatChainReceivedBlock> CatChainReceivedBlock::create_root(td::uint32 source_id,
|
||||
CatChainSessionId data_hash,
|
||||
CatChainReceiver *chain) {
|
||||
return std::make_unique<CatChainReceivedBlockImpl>(source_id, data_hash, chain);
|
||||
}
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
114
catchain/catchain-received-block.h
Normal file
114
catchain/catchain-received-block.h
Normal file
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/buffer.h"
|
||||
#include "td/utils/SharedSlice.h"
|
||||
#include "auto/tl/ton_api.h"
|
||||
|
||||
#include "catchain/catchain-receiver.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainReceiver;
|
||||
class CatChainReceiverSource;
|
||||
class CatChainReceiverFork;
|
||||
|
||||
class CatChainReceivedBlock {
|
||||
public:
|
||||
// getters
|
||||
virtual const td::SharedSlice &get_payload() const = 0;
|
||||
virtual CatChainBlockHash get_hash() const = 0;
|
||||
virtual const td::SharedSlice &get_signature() const = 0;
|
||||
|
||||
virtual CatChainBlockHeight get_height() const = 0;
|
||||
virtual CatChainReceivedBlock *get_prev() const = 0;
|
||||
virtual CatChainBlockHash get_prev_hash() const = 0;
|
||||
|
||||
virtual const std::vector<CatChainBlockHeight> &get_deps() const = 0;
|
||||
virtual std::vector<CatChainBlockHash> get_dep_hashes() const = 0;
|
||||
|
||||
virtual CatChainReceiver *get_chain() const = 0;
|
||||
|
||||
virtual td::uint32 get_fork_id() const = 0;
|
||||
virtual td::uint32 get_source_id() const = 0;
|
||||
|
||||
virtual tl_object_ptr<ton_api::catchain_block> export_tl() const = 0;
|
||||
virtual tl_object_ptr<ton_api::catchain_block_dep> export_tl_dep() const = 0;
|
||||
|
||||
virtual void find_pending_deps(std::vector<CatChainBlockHash> &vec, td::uint32 max_size) const = 0;
|
||||
|
||||
public:
|
||||
// state
|
||||
virtual bool initialized() const = 0;
|
||||
virtual bool delivered() const = 0;
|
||||
virtual bool is_ill() const = 0;
|
||||
virtual bool is_custom() const = 0;
|
||||
virtual bool in_db() const = 0;
|
||||
|
||||
public:
|
||||
//change state
|
||||
virtual void initialize(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) = 0;
|
||||
virtual void set_ill() = 0;
|
||||
virtual void written() = 0;
|
||||
virtual void run() = 0;
|
||||
|
||||
public:
|
||||
static std::unique_ptr<CatChainReceivedBlock> create(tl_object_ptr<ton_api::catchain_block> block,
|
||||
td::SharedSlice payload, CatChainReceiver *chain);
|
||||
static std::unique_ptr<CatChainReceivedBlock> create(tl_object_ptr<ton_api::catchain_block_dep> block,
|
||||
CatChainReceiver *chain);
|
||||
static std::unique_ptr<CatChainReceivedBlock> create_root(td::uint32 source_id, CatChainBlockPayloadHash data_hash,
|
||||
CatChainReceiver *chain);
|
||||
|
||||
static tl_object_ptr<ton_api::catchain_block_id> block_id(CatChainReceiver *chain,
|
||||
tl_object_ptr<ton_api::catchain_block> &block,
|
||||
td::Slice payload);
|
||||
static tl_object_ptr<ton_api::catchain_block_id> block_id(CatChainReceiver *chain,
|
||||
tl_object_ptr<ton_api::catchain_block_dep> &block);
|
||||
static CatChainBlockHash block_hash(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block> &block,
|
||||
td::Slice payload);
|
||||
static CatChainBlockHash block_hash(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block_dep> &block);
|
||||
static td::Status pre_validate_block(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block> &block,
|
||||
td::Slice payload);
|
||||
static td::Status pre_validate_block(CatChainReceiver *chain, tl_object_ptr<ton_api::catchain_block_dep> &block);
|
||||
|
||||
virtual ~CatChainReceivedBlock() = default;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlock &block) {
|
||||
sb << "[block " << block.get_chain()->get_incarnation() << " " << block.get_source_id() << " " << block.get_fork_id()
|
||||
<< " " << block.get_hash() << "]";
|
||||
return sb;
|
||||
}
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlock *block) {
|
||||
sb << *block;
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
||||
|
179
catchain/catchain-received-block.hpp
Normal file
179
catchain/catchain-received-block.hpp
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "catchain/catchain-received-block.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainReceiver;
|
||||
class CatChainReceiverSource;
|
||||
class CatChainReceiverFork;
|
||||
|
||||
class CatChainReceivedBlockImpl : public CatChainReceivedBlock {
|
||||
public:
|
||||
const td::SharedSlice &get_payload() const override {
|
||||
return payload_;
|
||||
}
|
||||
CatChainBlockHash get_hash() const override {
|
||||
return hash_;
|
||||
}
|
||||
const td::SharedSlice &get_signature() const override {
|
||||
return signature_;
|
||||
}
|
||||
|
||||
CatChainBlockHeight get_height() const override {
|
||||
return height_;
|
||||
}
|
||||
CatChainReceivedBlock *get_prev() const override;
|
||||
CatChainBlockHash get_prev_hash() const override;
|
||||
|
||||
const std::vector<CatChainBlockHeight> &get_deps() const override {
|
||||
return deps_;
|
||||
}
|
||||
|
||||
std::vector<CatChainBlockHash> get_dep_hashes() const override;
|
||||
|
||||
CatChainReceiver *get_chain() const override {
|
||||
return chain_;
|
||||
}
|
||||
|
||||
td::uint32 get_fork_id() const override {
|
||||
return fork_id_;
|
||||
}
|
||||
|
||||
td::uint32 get_source_id() const override {
|
||||
return source_id_;
|
||||
}
|
||||
|
||||
tl_object_ptr<ton_api::catchain_block> export_tl() const override;
|
||||
tl_object_ptr<ton_api::catchain_block_dep> export_tl_dep() const override;
|
||||
|
||||
void find_pending_deps(std::vector<CatChainBlockHash> &vec, td::uint32 max_size) const override;
|
||||
|
||||
public:
|
||||
bool initialized() const override {
|
||||
return state_ >= bs_initialized;
|
||||
}
|
||||
bool delivered() const override {
|
||||
return state_ >= bs_delivered;
|
||||
}
|
||||
bool is_ill() const override {
|
||||
return state_ == bs_ill;
|
||||
}
|
||||
bool is_custom() const override {
|
||||
return is_custom_;
|
||||
}
|
||||
bool in_db() const override {
|
||||
return in_db_;
|
||||
}
|
||||
|
||||
public:
|
||||
void initialize(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) override;
|
||||
|
||||
void run() override;
|
||||
void pre_deliver(ton_api::catchain_block_data_fork &b);
|
||||
void pre_deliver(ton_api::catchain_block_data_badBlock &b);
|
||||
void pre_deliver(ton_api::catchain_block_data_nop &b);
|
||||
template <class T>
|
||||
void pre_deliver(T &b) {
|
||||
// do nothing, it is custom block
|
||||
is_custom_ = true;
|
||||
}
|
||||
void pre_deliver();
|
||||
void deliver();
|
||||
|
||||
void dep_delivered(CatChainReceivedBlockImpl *block);
|
||||
void dep_ill(CatChainReceivedBlockImpl *block);
|
||||
|
||||
void set_ill() override;
|
||||
void schedule();
|
||||
|
||||
void written() override;
|
||||
|
||||
public:
|
||||
CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload,
|
||||
CatChainReceiver *chain);
|
||||
CatChainReceivedBlockImpl(tl_object_ptr<ton_api::catchain_block_dep> block, CatChainReceiver *chain);
|
||||
|
||||
CatChainReceivedBlockImpl(td::uint32 source_id, CatChainSessionId hash, CatChainReceiver *chain);
|
||||
|
||||
private:
|
||||
enum State {
|
||||
bs_none,
|
||||
bs_ill,
|
||||
bs_initialized,
|
||||
bs_delivered,
|
||||
} state_ = bs_none;
|
||||
|
||||
void update_deps(CatChainReceivedBlockImpl *block);
|
||||
|
||||
void add_rev_dep(CatChainReceivedBlockImpl *block);
|
||||
void add_child_dep(CatChainReceivedBlockImpl *block);
|
||||
|
||||
void initialize_fork();
|
||||
void on_ready_to_deliver();
|
||||
|
||||
td::uint32 fork_id_{0};
|
||||
td::uint32 source_id_;
|
||||
CatChainReceiver *chain_;
|
||||
|
||||
tl_object_ptr<ton_api::catchain_block_inner_Data> data_;
|
||||
td::SharedSlice payload_;
|
||||
|
||||
CatChainBlockHash hash_;
|
||||
CatChainBlockPayloadHash data_hash_;
|
||||
|
||||
CatChainReceivedBlockImpl *prev_;
|
||||
CatChainBlockHeight height_;
|
||||
|
||||
CatChainReceivedBlockImpl *next_ = nullptr;
|
||||
|
||||
std::vector<CatChainReceivedBlockImpl *> block_deps_;
|
||||
std::vector<CatChainBlockHeight> deps_;
|
||||
|
||||
td::SharedSlice signature_;
|
||||
|
||||
std::vector<CatChainReceivedBlockImpl *> rev_deps_;
|
||||
|
||||
td::uint32 pending_deps_ = 0;
|
||||
|
||||
bool is_custom_ = false;
|
||||
bool in_db_ = false;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlockImpl &block) {
|
||||
sb << "[block " << block.get_chain()->get_incarnation() << " " << block.get_source_id() << " " << block.get_fork_id()
|
||||
<< " " << block.get_hash() << "]";
|
||||
return sb;
|
||||
}
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceivedBlockImpl *block) {
|
||||
sb << *block;
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
70
catchain/catchain-receiver-interface.h
Normal file
70
catchain/catchain-receiver-interface.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/actor/actor.h"
|
||||
#include "adnl/adnl.h"
|
||||
#include "overlay/overlays.h"
|
||||
#include "catchain-types.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainReceiverInterface : public td::actor::Actor {
|
||||
public:
|
||||
class Callback {
|
||||
public:
|
||||
virtual void new_block(td::uint32 src_id, td::uint32 fork_id, CatChainBlockHash hash, CatChainBlockHeight height,
|
||||
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps,
|
||||
std::vector<CatChainBlockHeight> vt, td::SharedSlice data) = 0;
|
||||
virtual void blame(td::uint32 src_id) = 0;
|
||||
virtual void on_custom_message(PublicKeyHash src, td::BufferSlice data) = 0;
|
||||
virtual void on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) = 0;
|
||||
virtual void on_broadcast(PublicKeyHash src, td::BufferSlice data) = 0;
|
||||
virtual void start() = 0;
|
||||
virtual ~Callback() = default;
|
||||
};
|
||||
virtual void add_block(td::BufferSlice payload, std::vector<CatChainBlockHash> deps) = 0;
|
||||
virtual void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height,
|
||||
std::vector<CatChainBlockHash> deps) = 0;
|
||||
virtual void blame_node(td::uint32 idx) = 0;
|
||||
virtual void send_fec_broadcast(td::BufferSlice data) = 0;
|
||||
virtual void send_custom_query_data(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
|
||||
td::Timestamp timeout, td::BufferSlice query) = 0;
|
||||
virtual void send_custom_query_data_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
|
||||
td::Timestamp timeout, td::BufferSlice query, td::uint64 max_answer_size,
|
||||
td::actor::ActorId<adnl::AdnlSenderInterface> via) = 0;
|
||||
virtual void send_custom_message_data(PublicKeyHash dst, td::BufferSlice query) = 0;
|
||||
|
||||
virtual void destroy() = 0;
|
||||
|
||||
static td::actor::ActorOwn<CatChainReceiverInterface> create(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root);
|
||||
|
||||
virtual ~CatChainReceiverInterface() = default;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
179
catchain/catchain-receiver-source.cpp
Normal file
179
catchain/catchain-receiver-source.cpp
Normal file
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "catchain-receiver-source.hpp"
|
||||
#include "common/errorlog.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
td::uint32 CatChainReceiverSourceImpl::add_fork() {
|
||||
if (fork_ids_.size() > 0) {
|
||||
blame();
|
||||
}
|
||||
auto F = chain_->add_fork();
|
||||
CHECK(F > 0);
|
||||
|
||||
fork_ids_.push_back(F);
|
||||
|
||||
VLOG(CATCHAIN_INFO) << this << ": adding new fork " << F << " of source " << id_;
|
||||
|
||||
if (fork_ids_.size() > 1) {
|
||||
CHECK(blamed());
|
||||
}
|
||||
|
||||
return F;
|
||||
}
|
||||
|
||||
CatChainReceiverSourceImpl::CatChainReceiverSourceImpl(CatChainReceiver *chain, PublicKey source,
|
||||
adnl::AdnlNodeIdShort adnl_id, td::uint32 id)
|
||||
: chain_(chain), id_(id), adnl_id_(adnl_id) {
|
||||
src_ = source.compute_short_id();
|
||||
|
||||
encryptor_ = source.create_encryptor_async().move_as_ok();
|
||||
encryptor_sync_ = source.create_encryptor().move_as_ok();
|
||||
full_id_ = std::move(source);
|
||||
}
|
||||
|
||||
td::Result<std::unique_ptr<CatChainReceiverSource>> CatChainReceiverSource::create(CatChainReceiver *chain,
|
||||
PublicKey source,
|
||||
adnl::AdnlNodeIdShort adnl_id,
|
||||
td::uint32 id) {
|
||||
return std::make_unique<CatChainReceiverSourceImpl>(chain, std::move(source), adnl_id, id);
|
||||
}
|
||||
|
||||
void CatChainReceiverSourceImpl::blame(td::uint32 fork, CatChainBlockHeight height) {
|
||||
blame();
|
||||
if (blamed_heights_.size() > 0) {
|
||||
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() {
|
||||
if (!blamed_) {
|
||||
LOG(ERROR) << this << ": CATCHAIN: blaming source " << id_;
|
||||
blocks_.clear();
|
||||
chain_->on_blame(id_);
|
||||
}
|
||||
blamed_ = true;
|
||||
}
|
||||
|
||||
CatChainReceivedBlock *CatChainReceiverSourceImpl::get_block(CatChainBlockHeight height) const {
|
||||
auto it = blocks_.find(height);
|
||||
if (it != blocks_.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverSourceImpl::block_received(CatChainBlockHeight height) {
|
||||
if (blamed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (received_height_ + 1 == height) {
|
||||
received_height_ = height;
|
||||
}
|
||||
while (true) {
|
||||
auto it = blocks_.find(received_height_ + 1);
|
||||
if (it == blocks_.end()) {
|
||||
return;
|
||||
}
|
||||
if (!it->second->initialized()) {
|
||||
return;
|
||||
}
|
||||
received_height_++;
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverSourceImpl::block_delivered(CatChainBlockHeight height) {
|
||||
if (blamed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (delivered_height_ + 1 == height) {
|
||||
delivered_height_ = height;
|
||||
}
|
||||
while (true) {
|
||||
auto it = blocks_.find(delivered_height_ + 1);
|
||||
if (it == blocks_.end()) {
|
||||
return;
|
||||
}
|
||||
if (!it->second->delivered()) {
|
||||
return;
|
||||
}
|
||||
delivered_height_++;
|
||||
}
|
||||
}
|
||||
|
||||
td::Status CatChainReceiverSourceImpl::validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) {
|
||||
auto S = std::move(dep->signature_);
|
||||
auto str = serialize_tl_object(dep, true);
|
||||
dep->signature_ = std::move(S);
|
||||
|
||||
auto R = encryptor_sync_->check_signature(str.as_slice(), dep->signature_.as_slice());
|
||||
if (R.is_error()) {
|
||||
return R.move_as_error();
|
||||
}
|
||||
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
void CatChainReceiverSourceImpl::on_new_block(CatChainReceivedBlock *block) {
|
||||
if (fork_is_found()) {
|
||||
return;
|
||||
}
|
||||
|
||||
CHECK(block->get_source_id() == id_);
|
||||
auto it = blocks_.find(block->get_height());
|
||||
if (it != blocks_.end()) {
|
||||
CHECK(block->get_hash() != it->second->get_hash());
|
||||
VLOG(CATCHAIN_WARNING) << this << ": found fork on height " << block->get_height();
|
||||
if (!fork_is_found()) {
|
||||
on_found_fork_proof(create_serialize_tl_object<ton_api::catchain_block_data_fork>(block->export_tl_dep(),
|
||||
it->second->export_tl_dep())
|
||||
.as_slice());
|
||||
chain_->add_prepared_event(fork_proof());
|
||||
}
|
||||
blame();
|
||||
return;
|
||||
}
|
||||
blocks_[block->get_height()] = block;
|
||||
}
|
||||
|
||||
void CatChainReceiverSourceImpl::on_found_fork_proof(td::Slice proof) {
|
||||
if (!fork_is_found()) {
|
||||
fetch_tl_object<ton_api::catchain_block_data_fork>(proof, true).ensure();
|
||||
fork_proof_ = td::SharedSlice{proof};
|
||||
errorlog::ErrorLog::log(PSTRING() << "catchain " << chain_->get_incarnation() << " source " << id_
|
||||
<< " found fork. hash=" << sha256_bits256(fork_proof_.as_slice()).to_hex());
|
||||
errorlog::ErrorLog::log_file(fork_proof_.clone_as_buffer_slice());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
85
catchain/catchain-receiver-source.h
Normal file
85
catchain/catchain-receiver-source.h
Normal file
|
@ -0,0 +1,85 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "catchain-receiver.h"
|
||||
|
||||
#include "keys/encryptor.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainReceiver;
|
||||
class CatChainReceivedBlock;
|
||||
|
||||
class CatChainReceiverSource {
|
||||
public:
|
||||
virtual td::uint32 get_id() const = 0;
|
||||
virtual PublicKeyHash get_hash() const = 0;
|
||||
virtual PublicKey get_full_id() const = 0;
|
||||
virtual adnl::AdnlNodeIdShort get_adnl_id() const = 0;
|
||||
|
||||
virtual td::uint32 add_fork() = 0;
|
||||
|
||||
virtual bool blamed() const = 0;
|
||||
virtual void blame(td::uint32 fork, CatChainBlockHeight height) = 0;
|
||||
virtual void blame() = 0;
|
||||
|
||||
virtual const std::vector<td::uint32> &get_forks() const = 0;
|
||||
virtual const std::vector<CatChainBlockHeight> &get_blamed_heights() const = 0;
|
||||
virtual Encryptor *get_encryptor_sync() const = 0;
|
||||
virtual td::uint32 get_forks_cnt() const = 0;
|
||||
|
||||
virtual CatChainBlockHeight delivered_height() const = 0;
|
||||
virtual CatChainBlockHeight received_height() const = 0;
|
||||
virtual CatChainReceivedBlock *get_block(CatChainBlockHeight height) const = 0;
|
||||
virtual void block_received(CatChainBlockHeight height) = 0;
|
||||
virtual void block_delivered(CatChainBlockHeight height) = 0;
|
||||
|
||||
virtual td::Status validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) = 0;
|
||||
virtual void on_new_block(CatChainReceivedBlock *block) = 0;
|
||||
virtual void on_found_fork_proof(td::Slice fork) = 0;
|
||||
virtual td::BufferSlice fork_proof() const = 0;
|
||||
virtual bool fork_is_found() const = 0;
|
||||
|
||||
static td::Result<std::unique_ptr<CatChainReceiverSource>> create(CatChainReceiver *chain, PublicKey pub_key,
|
||||
adnl::AdnlNodeIdShort adnl_id, td::uint32 id);
|
||||
|
||||
virtual CatChainReceiver *get_chain() const = 0;
|
||||
|
||||
virtual ~CatChainReceiverSource() = default;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSource &source) {
|
||||
sb << "[source " << source.get_chain()->get_incarnation() << " " << source.get_id() << "]";
|
||||
return sb;
|
||||
}
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSource *source) {
|
||||
sb << *source;
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
137
catchain/catchain-receiver-source.hpp
Normal file
137
catchain/catchain-receiver-source.hpp
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "catchain-receiver-source.h"
|
||||
#include "catchain-receiver.h"
|
||||
#include "catchain-received-block.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainReceiverSourceImpl : public CatChainReceiverSource {
|
||||
public:
|
||||
td::uint32 get_id() const override {
|
||||
return id_;
|
||||
}
|
||||
PublicKeyHash get_hash() const override {
|
||||
return src_;
|
||||
}
|
||||
PublicKey get_full_id() const override {
|
||||
return full_id_;
|
||||
}
|
||||
adnl::AdnlNodeIdShort get_adnl_id() const override {
|
||||
return adnl_id_;
|
||||
}
|
||||
|
||||
td::uint32 add_fork() override;
|
||||
|
||||
bool blamed() const override {
|
||||
return blamed_;
|
||||
}
|
||||
void blame(td::uint32 fork, CatChainBlockHeight height) override;
|
||||
void blame() override;
|
||||
void block_received(CatChainBlockHeight height) override;
|
||||
void block_delivered(CatChainBlockHeight height) override;
|
||||
|
||||
const std::vector<td::uint32> &get_forks() const override {
|
||||
return fork_ids_;
|
||||
}
|
||||
|
||||
const std::vector<CatChainBlockHeight> &get_blamed_heights() const override {
|
||||
return blamed_heights_;
|
||||
}
|
||||
|
||||
td::actor::ActorId<EncryptorAsync> get_encryptor() const {
|
||||
return encryptor_.get();
|
||||
}
|
||||
Encryptor *get_encryptor_sync() const override {
|
||||
return encryptor_sync_.get();
|
||||
}
|
||||
|
||||
td::uint32 get_forks_cnt() const override {
|
||||
return static_cast<td::uint32>(fork_ids_.size());
|
||||
}
|
||||
|
||||
CatChainBlockHeight delivered_height() const override {
|
||||
return delivered_height_;
|
||||
}
|
||||
CatChainBlockHeight received_height() const override {
|
||||
return received_height_;
|
||||
}
|
||||
CatChainReceivedBlock *get_block(CatChainBlockHeight height) const override;
|
||||
|
||||
td::Status validate_dep_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) override;
|
||||
void on_new_block(CatChainReceivedBlock *block) override;
|
||||
void on_found_fork_proof(td::Slice proof) override;
|
||||
bool fork_is_found() const override {
|
||||
return !fork_proof_.empty();
|
||||
}
|
||||
td::BufferSlice fork_proof() const override {
|
||||
if (!fork_proof_.empty()) {
|
||||
return fork_proof_.clone_as_buffer_slice();
|
||||
} else {
|
||||
return td::BufferSlice();
|
||||
}
|
||||
}
|
||||
|
||||
CatChainReceiver *get_chain() const override {
|
||||
return chain_;
|
||||
}
|
||||
|
||||
CatChainReceiverSourceImpl(CatChainReceiver *chain, PublicKey source, adnl::AdnlNodeIdShort adnl_id, td::uint32 id);
|
||||
|
||||
private:
|
||||
CatChainReceiver *chain_;
|
||||
td::uint32 id_;
|
||||
PublicKeyHash src_;
|
||||
bool blamed_ = false;
|
||||
PublicKey full_id_;
|
||||
adnl::AdnlNodeIdShort adnl_id_;
|
||||
|
||||
std::vector<td::uint32> fork_ids_;
|
||||
td::actor::ActorOwn<EncryptorAsync> encryptor_;
|
||||
std::unique_ptr<Encryptor> encryptor_sync_;
|
||||
std::vector<CatChainBlockHeight> blamed_heights_;
|
||||
std::map<CatChainBlockHeight, CatChainReceivedBlock *> blocks_;
|
||||
td::SharedSlice fork_proof_;
|
||||
|
||||
CatChainBlockHeight delivered_height_ = 0;
|
||||
CatChainBlockHeight received_height_ = 0;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSourceImpl &source) {
|
||||
sb << "[source " << source.get_chain()->get_incarnation() << " " << source.get_id() << "]";
|
||||
return sb;
|
||||
}
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverSourceImpl *source) {
|
||||
sb << *source;
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
988
catchain/catchain-receiver.cpp
Normal file
988
catchain/catchain-receiver.cpp
Normal file
|
@ -0,0 +1,988 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include <set>
|
||||
#include "td/actor/PromiseFuture.h"
|
||||
#include "td/utils/Random.h"
|
||||
#include "td/db/RocksDb.h"
|
||||
#include "td/utils/port/path.h"
|
||||
#include "td/utils/overloaded.h"
|
||||
#include "common/delay.h"
|
||||
|
||||
#include "catchain-receiver.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
PublicKeyHash CatChainReceiverImpl::get_source_hash(td::uint32 source_id) const {
|
||||
CHECK(source_id < sources_.size());
|
||||
return sources_[source_id]->get_hash();
|
||||
}
|
||||
|
||||
td::uint32 CatChainReceiverImpl::add_fork() {
|
||||
return ++total_forks_;
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::deliver_block(CatChainReceivedBlock *block) {
|
||||
VLOG(CATCHAIN_INFO) << this << ": delivering block " << block->get_hash() << " src=" << block->get_source_id()
|
||||
<< " fork=" << block->get_fork_id() << " height=" << block->get_height()
|
||||
<< " custom=" << block->is_custom();
|
||||
callback_->new_block(block->get_source_id(), block->get_fork_id(), block->get_hash(), block->get_height(),
|
||||
block->get_height() == 1 ? CatChainBlockHash::zero() : block->get_prev_hash(),
|
||||
block->get_dep_hashes(), block->get_deps(),
|
||||
block->is_custom() ? block->get_payload().clone() : td::SharedSlice());
|
||||
|
||||
std::vector<adnl::AdnlNodeIdShort> v;
|
||||
|
||||
for (auto it : neighbours_) {
|
||||
auto S = get_source(it);
|
||||
v.push_back(S->get_adnl_id());
|
||||
}
|
||||
|
||||
auto update = create_tl_object<ton_api::catchain_blockUpdate>(block->export_tl());
|
||||
auto D = serialize_tl_object(update, true, block->get_payload().as_slice());
|
||||
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_multiple_messages, std::move(v),
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(D));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::receive_block(adnl::AdnlNodeIdShort src, tl_object_ptr<ton_api::catchain_block> block,
|
||||
td::BufferSlice payload) {
|
||||
auto id = CatChainReceivedBlock::block_hash(this, block, payload);
|
||||
auto B = get_block(id);
|
||||
if (B && B->initialized()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (block->incarnation_ != incarnation_) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": dropping broken block from " << src << ": bad incarnation "
|
||||
<< block->incarnation_;
|
||||
return;
|
||||
}
|
||||
|
||||
auto S = validate_block_sync(block, payload.as_slice());
|
||||
|
||||
if (S.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": received broken block from " << src << ": " << S.move_as_error();
|
||||
return;
|
||||
}
|
||||
|
||||
auto raw_data = serialize_tl_object(block, true, payload.as_slice());
|
||||
create_block(std::move(block), td::SharedSlice{payload.as_slice()});
|
||||
|
||||
if (!opts_.debug_disable_db) {
|
||||
db_.set(
|
||||
id, std::move(raw_data), [](td::Unit) {}, 1.0);
|
||||
}
|
||||
block_written_to_db(id);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::receive_block_answer(adnl::AdnlNodeIdShort src, td::BufferSlice data) {
|
||||
auto F = fetch_tl_prefix<ton_api::catchain_BlockResult>(data, true);
|
||||
if (F.is_error()) {
|
||||
VLOG(CATCHAIN_INFO) << this << ": received bad block result: " << F.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto f = F.move_as_ok();
|
||||
ton_api::downcast_call(
|
||||
*f.get(),
|
||||
td::overloaded(
|
||||
[&](ton_api::catchain_blockNotFound &r) { VLOG(CATCHAIN_INFO) << this << ": catchain block not found"; },
|
||||
[&](ton_api::catchain_blockResult &r) { receive_block(src, std::move(r.block_), std::move(data)); }));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::receive_message_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data) {
|
||||
if (!read_db_) {
|
||||
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;
|
||||
}*/
|
||||
auto R = fetch_tl_prefix<ton_api::catchain_blockUpdate>(data, true);
|
||||
if (R.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": dropping broken block from " << src << ": " << R.move_as_error();
|
||||
return;
|
||||
}
|
||||
|
||||
auto U = R.move_as_ok();
|
||||
receive_block(src, std::move(U->block_), std::move(data));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::receive_broadcast_from_overlay(PublicKeyHash src, td::BufferSlice data) {
|
||||
if (!read_db_) {
|
||||
return;
|
||||
}
|
||||
callback_->on_broadcast(src, std::move(data));
|
||||
}
|
||||
|
||||
/*void CatChainReceiverImpl::send_block(PublicKeyHash src, tl_object_ptr<ton_api::catchain_block> block,
|
||||
td::BufferSlice payload) {
|
||||
CHECK(read_db_);
|
||||
CHECK(src == local_id_);
|
||||
|
||||
validate_block_sync(block, payload.as_slice()).ensure();
|
||||
auto B = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
|
||||
CHECK(B != nullptr);
|
||||
|
||||
run_scheduler();
|
||||
CHECK(B->delivered());
|
||||
}*/
|
||||
|
||||
CatChainReceivedBlock *CatChainReceiverImpl::create_block(tl_object_ptr<ton_api::catchain_block> block,
|
||||
td::SharedSlice payload) {
|
||||
if (block->height_ == 0) {
|
||||
return root_block_;
|
||||
}
|
||||
auto hash = CatChainReceivedBlock::block_hash(this, block, payload.as_slice());
|
||||
|
||||
auto it = blocks_.find(hash);
|
||||
if (it != blocks_.end()) {
|
||||
if (!it->second->initialized()) {
|
||||
it->second->initialize(std::move(block), std::move(payload));
|
||||
}
|
||||
return it->second.get();
|
||||
} else {
|
||||
blocks_.emplace(hash, CatChainReceivedBlock::create(std::move(block), std::move(payload), this));
|
||||
it = blocks_.find(hash);
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
CatChainReceivedBlock *CatChainReceiverImpl::create_block(tl_object_ptr<ton_api::catchain_block_dep> block) {
|
||||
if (block->height_ == 0) {
|
||||
return root_block_;
|
||||
}
|
||||
auto hash = CatChainReceivedBlock::block_hash(this, block);
|
||||
auto it = blocks_.find(hash);
|
||||
if (it != blocks_.end()) {
|
||||
return it->second.get();
|
||||
} else {
|
||||
blocks_.emplace(hash, CatChainReceivedBlock::create(std::move(block), this));
|
||||
it = blocks_.find(hash);
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
td::Status CatChainReceiverImpl::validate_block_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) {
|
||||
TRY_STATUS_PREFIX(CatChainReceivedBlock::pre_validate_block(this, dep), "failed to validate block: ");
|
||||
|
||||
if (dep->height_ > 0) {
|
||||
auto id = CatChainReceivedBlock::block_id(this, dep);
|
||||
auto B = serialize_tl_object(id, true);
|
||||
auto block = get_block(get_tl_object_sha_bits256(id));
|
||||
if (block) {
|
||||
return td::Status::OK();
|
||||
}
|
||||
|
||||
auto S = get_source_by_hash(PublicKeyHash{id->src_});
|
||||
CHECK(S != nullptr);
|
||||
auto E = S->get_encryptor_sync();
|
||||
CHECK(E != nullptr);
|
||||
return E->check_signature(B.as_slice(), dep->signature_.as_slice());
|
||||
} else {
|
||||
return td::Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
td::Status CatChainReceiverImpl::validate_block_sync(tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) {
|
||||
//LOG(INFO) << ton_api::to_string(block);
|
||||
TRY_STATUS_PREFIX(CatChainReceivedBlock::pre_validate_block(this, block, payload), "failed to validate block: ");
|
||||
|
||||
if (block->height_ > 0) {
|
||||
auto id = CatChainReceivedBlock::block_id(this, block, payload);
|
||||
auto B = serialize_tl_object(id, true);
|
||||
|
||||
auto S = get_source_by_hash(PublicKeyHash{id->src_});
|
||||
CHECK(S != nullptr);
|
||||
auto E = S->get_encryptor_sync();
|
||||
CHECK(E != nullptr);
|
||||
return E->check_signature(B.as_slice(), block->signature_.as_slice());
|
||||
} else {
|
||||
return td::Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::run_scheduler() {
|
||||
while (!to_run_.empty()) {
|
||||
auto B = to_run_.front();
|
||||
to_run_.pop_front();
|
||||
|
||||
B->run();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::run_block(CatChainReceivedBlock *block) {
|
||||
to_run_.push_back(block);
|
||||
}
|
||||
|
||||
CatChainReceivedBlock *CatChainReceiverImpl::get_block(CatChainBlockHash hash) const {
|
||||
auto it = blocks_.find(hash);
|
||||
if (it == blocks_.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::add_block_cont_3(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
|
||||
last_sent_block_ = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
|
||||
last_sent_block_->written();
|
||||
|
||||
run_scheduler();
|
||||
if (!intentional_fork_) {
|
||||
CHECK(last_sent_block_->delivered());
|
||||
}
|
||||
|
||||
active_send_ = false;
|
||||
if (pending_blocks_.size() > 0) {
|
||||
auto B = std::move(pending_blocks_.front());
|
||||
pending_blocks_.pop_front();
|
||||
add_block(std::move(B->payload_), std::move(B->deps_));
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::add_block_cont_2(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
|
||||
if (opts_.debug_disable_db) {
|
||||
add_block_cont_3(std::move(block), std::move(payload));
|
||||
return;
|
||||
}
|
||||
|
||||
auto id = CatChainReceivedBlock::block_hash(this, block, payload);
|
||||
|
||||
td::BufferSlice raw_data{32};
|
||||
raw_data.as_slice().copy_from(as_slice(id));
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block = std::move(block),
|
||||
payload = std::move(payload)](td::Result<td::Unit> R) mutable {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::add_block_cont_3, std::move(block), std::move(payload));
|
||||
});
|
||||
|
||||
db_.set(CatChainBlockHash::zero(), std::move(raw_data), std::move(P), 0);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::add_block_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
|
||||
validate_block_sync(block, payload.as_slice()).ensure();
|
||||
if (opts_.debug_disable_db) {
|
||||
add_block_cont_2(std::move(block), std::move(payload));
|
||||
return;
|
||||
}
|
||||
auto id = CatChainReceivedBlock::block_hash(this, block, payload.as_slice());
|
||||
|
||||
auto raw_data = serialize_tl_object(block, true, payload.as_slice());
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block = std::move(block),
|
||||
payload = std::move(payload)](td::Result<td::Unit> R) mutable {
|
||||
R.ensure();
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::add_block_cont_2, std::move(block), std::move(payload));
|
||||
});
|
||||
|
||||
db_.set(id, std::move(raw_data), std::move(P), 0);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::add_block(td::BufferSlice payload, std::vector<CatChainBlockHash> deps) {
|
||||
if (active_send_) {
|
||||
auto B = std::make_unique<PendingBlock>(std::move(payload), std::move(deps));
|
||||
pending_blocks_.push_back(std::move(B));
|
||||
return;
|
||||
}
|
||||
active_send_ = true;
|
||||
|
||||
auto S = get_source_by_hash(local_id_);
|
||||
CHECK(S != nullptr);
|
||||
CHECK(S->get_id() == local_idx_);
|
||||
if (!intentional_fork_) {
|
||||
CHECK(!S->blamed());
|
||||
}
|
||||
|
||||
auto prev = last_sent_block_->export_tl_dep();
|
||||
|
||||
std::vector<tl_object_ptr<ton_api::catchain_block_dep>> deps_arr;
|
||||
deps_arr.resize(deps.size());
|
||||
for (size_t i = 0; i < deps.size(); i++) {
|
||||
auto B = get_block(deps[i]);
|
||||
LOG_CHECK(B != nullptr) << this << ": cannot find block with hash " << deps[i];
|
||||
if (!intentional_fork_) {
|
||||
CHECK(B->get_source_id() != local_idx_);
|
||||
}
|
||||
deps_arr[i] = B->export_tl_dep();
|
||||
}
|
||||
|
||||
auto height = prev->height_ + 1;
|
||||
auto block_data = create_tl_object<ton_api::catchain_block_data>(std::move(prev), std::move(deps_arr));
|
||||
auto block = create_tl_object<ton_api::catchain_block>(incarnation_, local_idx_, height, std::move(block_data),
|
||||
td::BufferSlice());
|
||||
|
||||
auto id = CatChainReceivedBlock::block_id(this, block, payload);
|
||||
auto id_s = serialize_tl_object(id, true);
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), print_id = print_id(), block = std::move(block),
|
||||
payload = std::move(payload)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
LOG(FATAL) << print_id << ": failed to sign: " << R.move_as_error();
|
||||
return;
|
||||
}
|
||||
block->signature_ = R.move_as_ok();
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::add_block_cont, std::move(block), std::move(payload));
|
||||
});
|
||||
|
||||
td::actor::send_closure_later(keyring_, &keyring::Keyring::sign_message, local_id_, std::move(id_s), std::move(P));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::debug_add_fork_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload) {
|
||||
validate_block_sync(block, payload.as_slice()).ensure();
|
||||
auto B = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
|
||||
B->written();
|
||||
|
||||
run_scheduler();
|
||||
CHECK(B->delivered());
|
||||
|
||||
active_send_ = false;
|
||||
if (pending_blocks_.size() > 0) {
|
||||
auto B = std::move(pending_blocks_.front());
|
||||
pending_blocks_.pop_front();
|
||||
add_block(std::move(B->payload_), std::move(B->deps_));
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height,
|
||||
std::vector<CatChainBlockHash> deps) {
|
||||
intentional_fork_ = true;
|
||||
auto S = get_source_by_hash(local_id_);
|
||||
CHECK(S != nullptr);
|
||||
CHECK(S->get_id() == local_idx_);
|
||||
|
||||
if (height > S->received_height() + 1) {
|
||||
height = S->received_height() + 1;
|
||||
}
|
||||
|
||||
CHECK(height > 0);
|
||||
CatChainReceivedBlock *prev;
|
||||
if (height == 1) {
|
||||
prev = root_block_;
|
||||
} else {
|
||||
prev = sources_[local_idx_]->get_block(height - 1);
|
||||
CHECK(prev);
|
||||
}
|
||||
|
||||
std::vector<tl_object_ptr<ton_api::catchain_block_dep>> deps_arr;
|
||||
deps_arr.resize(deps.size());
|
||||
for (size_t i = 0; i < deps.size(); i++) {
|
||||
auto B = get_block(deps[i]);
|
||||
LOG_CHECK(B != nullptr) << this << ": cannot find block with hash " << deps[i];
|
||||
CHECK(B->get_source_id() != local_idx_);
|
||||
deps_arr[i] = B->export_tl_dep();
|
||||
}
|
||||
|
||||
auto block_data = create_tl_object<ton_api::catchain_block_data>(prev->export_tl_dep(), std::move(deps_arr));
|
||||
auto block = create_tl_object<ton_api::catchain_block>(incarnation_, local_idx_, height, std::move(block_data),
|
||||
td::BufferSlice());
|
||||
|
||||
auto id = CatChainReceivedBlock::block_id(this, block, payload);
|
||||
auto id_s = serialize_tl_object(id, true);
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), print_id = print_id(), block = std::move(block),
|
||||
payload = std::move(payload)](td::Result<td::BufferSlice> R) mutable {
|
||||
if (R.is_error()) {
|
||||
LOG(FATAL) << print_id << ": failed to sign: " << R.move_as_error();
|
||||
return;
|
||||
}
|
||||
block->signature_ = R.move_as_ok();
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::debug_add_fork_cont, std::move(block), std::move(payload));
|
||||
});
|
||||
|
||||
td::actor::send_closure_later(keyring_, &keyring::Keyring::sign_message, local_id_, std::move(id_s), std::move(P));
|
||||
}
|
||||
|
||||
CatChainReceiverImpl::CatChainReceiverImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root)
|
||||
: callback_(std::move(callback))
|
||||
, opts_(std::move(opts))
|
||||
, keyring_(keyring)
|
||||
, adnl_(adnl)
|
||||
, overlay_manager_(overlay_manager)
|
||||
, local_id_(local_id)
|
||||
, db_root_(db_root) {
|
||||
std::vector<td::Bits256> short_ids;
|
||||
local_idx_ = static_cast<td::uint32>(ids.size());
|
||||
for (auto &id : ids) {
|
||||
td::uint32 seq = static_cast<td::uint32>(sources_.size());
|
||||
auto R = CatChainReceiverSource::create(this, id.pub_key, id.adnl_id, seq);
|
||||
auto S = R.move_as_ok();
|
||||
auto h = id.pub_key.compute_short_id();
|
||||
short_ids.push_back(h.bits256_value());
|
||||
sources_hashes_[h] = seq;
|
||||
sources_adnl_addrs_[id.adnl_id] = seq;
|
||||
sources_.push_back(std::move(S));
|
||||
|
||||
if (h == local_id_) {
|
||||
CHECK(local_idx_ == static_cast<td::uint32>(ids.size()));
|
||||
local_idx_ = seq;
|
||||
}
|
||||
}
|
||||
CHECK(local_idx_ != static_cast<td::uint32>(ids.size()));
|
||||
|
||||
//std::sort(short_ids.begin(), short_ids.end());
|
||||
auto F = create_tl_object<ton_api::catchain_firstblock>(unique_hash, std::move(short_ids));
|
||||
|
||||
overlay_full_id_ = overlay::OverlayIdFull{serialize_tl_object(F, true)};
|
||||
overlay_id_ = overlay_full_id_.compute_short_id();
|
||||
incarnation_ = overlay_id_.bits256_value();
|
||||
|
||||
auto R = CatChainReceivedBlock::create_root(get_sources_cnt(), incarnation_, this);
|
||||
root_block_ = R.get();
|
||||
blocks_[root_block_->get_hash()] = std::move(R);
|
||||
last_sent_block_ = root_block_;
|
||||
|
||||
choose_neighbours();
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::start_up() {
|
||||
std::vector<adnl::AdnlNodeIdShort> ids;
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
ids.push_back(get_source(i)->get_adnl_id());
|
||||
}
|
||||
std::map<PublicKeyHash, td::uint32> root_keys;
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
root_keys.emplace(get_source(i)->get_hash(), 16 << 20);
|
||||
}
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::create_private_overlay,
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_full_id_.clone(), std::move(ids),
|
||||
make_callback(), overlay::OverlayPrivacyRules{0, std::move(root_keys)});
|
||||
|
||||
CHECK(root_block_);
|
||||
|
||||
if (!opts_.debug_disable_db) {
|
||||
std::shared_ptr<td::KeyValue> kv = std::make_shared<td::RocksDb>(
|
||||
td::RocksDb::open(db_root_ + "/catchainreceiver-" + td::base64url_encode(as_slice(incarnation_))).move_as_ok());
|
||||
db_ = DbType{std::move(kv)};
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<DbType::GetResult> R) {
|
||||
R.ensure();
|
||||
auto g = R.move_as_ok();
|
||||
if (g.status == td::KeyValue::GetStatus::NotFound) {
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_db);
|
||||
} else {
|
||||
auto B = std::move(g.value);
|
||||
CHECK(B.size() == 32);
|
||||
CatChainBlockHash x;
|
||||
as_slice(x).copy_from(B.as_slice());
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_db_from, x);
|
||||
}
|
||||
});
|
||||
|
||||
db_.get(CatChainBlockHash::zero(), std::move(P));
|
||||
} else {
|
||||
read_db();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::tear_down() {
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::delete_overlay, get_source(local_idx_)->get_adnl_id(),
|
||||
overlay_id_);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::read_db_from(CatChainBlockHash id) {
|
||||
pending_in_db_ = 1;
|
||||
db_root_block_ = id;
|
||||
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id](td::Result<DbType::GetResult> R) {
|
||||
R.ensure();
|
||||
auto g = R.move_as_ok();
|
||||
CHECK(g.status == td::KeyValue::GetStatus::Ok);
|
||||
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_block_from_db, id, std::move(g.value));
|
||||
});
|
||||
|
||||
db_.get(id, std::move(P));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::read_block_from_db(CatChainBlockHash id, td::BufferSlice data) {
|
||||
pending_in_db_--;
|
||||
|
||||
auto F = fetch_tl_prefix<ton_api::catchain_block>(data, true);
|
||||
F.ensure();
|
||||
|
||||
auto block = F.move_as_ok();
|
||||
auto payload = std::move(data);
|
||||
|
||||
auto block_id = CatChainReceivedBlock::block_hash(this, block, payload);
|
||||
CHECK(block_id == id);
|
||||
|
||||
auto B = get_block(id);
|
||||
if (B && B->initialized()) {
|
||||
CHECK(B->in_db());
|
||||
if (!pending_in_db_) {
|
||||
read_db();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto source = get_source(block->src_);
|
||||
CHECK(source != nullptr);
|
||||
|
||||
CHECK(block->incarnation_ == incarnation_);
|
||||
|
||||
validate_block_sync(block, payload).ensure();
|
||||
|
||||
B = create_block(std::move(block), td::SharedSlice{payload.as_slice()});
|
||||
B->written();
|
||||
CHECK(B);
|
||||
|
||||
auto deps = B->get_dep_hashes();
|
||||
deps.push_back(B->get_prev_hash());
|
||||
for (auto &dep : deps) {
|
||||
auto dep_block = get_block(dep);
|
||||
if (!dep_block || !dep_block->initialized()) {
|
||||
pending_in_db_++;
|
||||
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), dep](td::Result<DbType::GetResult> R) {
|
||||
R.ensure();
|
||||
auto g = R.move_as_ok();
|
||||
CHECK(g.status == td::KeyValue::GetStatus::Ok);
|
||||
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::read_block_from_db, dep, std::move(g.value));
|
||||
});
|
||||
|
||||
db_.get(dep, std::move(P));
|
||||
}
|
||||
}
|
||||
|
||||
if (!pending_in_db_) {
|
||||
read_db();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::read_db() {
|
||||
if (!db_root_block_.is_zero()) {
|
||||
run_scheduler();
|
||||
last_sent_block_ = get_block(db_root_block_);
|
||||
CHECK(last_sent_block_);
|
||||
CHECK(last_sent_block_->delivered());
|
||||
}
|
||||
|
||||
read_db_ = true;
|
||||
|
||||
next_rotate_ = td::Timestamp::in(60 + td::Random::fast(0, 60));
|
||||
next_sync_ = td::Timestamp::in(0.01 * td::Random::fast(0, 60));
|
||||
alarm_timestamp().relax(next_rotate_);
|
||||
alarm_timestamp().relax(next_sync_);
|
||||
|
||||
callback_->start();
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<CatChainReceiverInterface> CatChainReceiverInterface::create(
|
||||
std::unique_ptr<Callback> callback, CatChainOptions opts, td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root) {
|
||||
auto A = td::actor::create_actor<CatChainReceiverImpl>("catchainreceiver", std::move(callback), std::move(opts),
|
||||
keyring, adnl, overlay_manager, std::move(ids), local_id,
|
||||
unique_hash, db_root);
|
||||
return std::move(A);
|
||||
}
|
||||
|
||||
CatChainReceiverSource *CatChainReceiverImpl::get_source_by_hash(PublicKeyHash source_hash) const {
|
||||
auto it = sources_hashes_.find(source_hash);
|
||||
if (it == sources_hashes_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return get_source(it->second);
|
||||
}
|
||||
|
||||
CatChainReceiverSource *CatChainReceiverImpl::get_source_by_adnl_id(adnl::AdnlNodeIdShort source_hash) const {
|
||||
auto it = sources_adnl_addrs_.find(source_hash);
|
||||
if (it == sources_adnl_addrs_.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return get_source(it->second);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::receive_query_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
if (!read_db_) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::notready, "db not read"));
|
||||
return;
|
||||
}
|
||||
td::PerfWarningTimer t{"catchain query process", 0.001};
|
||||
auto F = fetch_tl_object<ton_api::Function>(data.clone(), true);
|
||||
if (F.is_error()) {
|
||||
callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), std::move(data), std::move(promise));
|
||||
//LOG(WARNING) << this << ": unknown query from " << src;
|
||||
return;
|
||||
}
|
||||
auto f = F.move_as_ok();
|
||||
ton_api::downcast_call(*f.get(), [&](auto &obj) { this->process_query(src, obj, std::move(promise)); });
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlock &query,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
auto it = blocks_.find(query.block_);
|
||||
if (it == blocks_.end() || it->second->get_height() == 0 || !it->second->initialized()) {
|
||||
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_blockNotFound>(), true));
|
||||
} else {
|
||||
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_blockResult>(it->second->export_tl()),
|
||||
true, it->second->get_payload().as_slice()));
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlocks &query,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
if (query.blocks_.size() > 100) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "too many blocks"));
|
||||
return;
|
||||
}
|
||||
td::int32 cnt = 0;
|
||||
for (auto &b : query.blocks_) {
|
||||
auto it = blocks_.find(b);
|
||||
if (it != blocks_.end() && it->second->get_height() > 0) {
|
||||
auto block = create_tl_object<ton_api::catchain_blockUpdate>(it->second->export_tl());
|
||||
CHECK(it->second->get_payload().size() > 0);
|
||||
auto B = serialize_tl_object(block, true, it->second->get_payload().clone());
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(B));
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(cnt), true));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory &query,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
auto h = query.height_;
|
||||
if (h <= 0) {
|
||||
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "not-positive height"));
|
||||
return;
|
||||
}
|
||||
if (h > 100) {
|
||||
h = 100;
|
||||
}
|
||||
std::set<CatChainBlockHash> s{query.stop_if_.begin(), query.stop_if_.end()};
|
||||
|
||||
auto B = get_block(query.block_);
|
||||
if (B == nullptr) {
|
||||
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(0), true));
|
||||
return;
|
||||
}
|
||||
if (static_cast<CatChainBlockHeight>(h) > B->get_height()) {
|
||||
h = B->get_height();
|
||||
}
|
||||
td::uint32 cnt = 0;
|
||||
while (h-- > 0) {
|
||||
if (s.find(B->get_hash()) != s.end()) {
|
||||
break;
|
||||
}
|
||||
auto block = create_tl_object<ton_api::catchain_blockUpdate>(B->export_tl());
|
||||
CHECK(B->get_payload().size() > 0);
|
||||
auto BB = serialize_tl_object(block, true, B->get_payload().as_slice());
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(BB));
|
||||
B = B->get_prev();
|
||||
cnt++;
|
||||
}
|
||||
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_sent>(cnt), true));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getDifference &query,
|
||||
td::Promise<td::BufferSlice> promise) {
|
||||
auto &vt = query.rt_;
|
||||
if (vt.size() != get_sources_cnt()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": incorrect query from " << src;
|
||||
promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad vt size"));
|
||||
return;
|
||||
}
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
if (vt[i] >= 0) {
|
||||
auto S = get_source(i);
|
||||
if (S->fork_is_found()) {
|
||||
auto obj = fetch_tl_object<ton_api::catchain_block_data_fork>(S->fork_proof(), true);
|
||||
obj.ensure();
|
||||
auto f = obj.move_as_ok();
|
||||
promise.set_value(
|
||||
create_serialize_tl_object<ton_api::catchain_differenceFork>(std::move(f->left_), std::move(f->right_)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<td::int32> my_vt(get_sources_cnt());
|
||||
td::uint64 total = 0;
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
if (vt[i] >= 0) {
|
||||
auto x = static_cast<CatChainBlockHeight>(vt[i]);
|
||||
auto S = get_source(i);
|
||||
if (S->delivered_height() > x) {
|
||||
total += S->delivered_height() - x;
|
||||
}
|
||||
my_vt[i] = S->delivered_height();
|
||||
} else {
|
||||
my_vt[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
const td::uint32 max_send = 100;
|
||||
|
||||
td::int32 l = 0;
|
||||
td::int32 r = max_send + 1;
|
||||
while (r - l > 1) {
|
||||
td::int32 x = (r + l) / 2;
|
||||
td::uint64 sum = 0;
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
if (vt[i] >= 0 && my_vt[i] > vt[i]) {
|
||||
sum += (my_vt[i] - vt[i] > x) ? x : (my_vt[i] - vt[i]);
|
||||
}
|
||||
}
|
||||
if (sum > max_send) {
|
||||
r = x;
|
||||
} else {
|
||||
l = x;
|
||||
}
|
||||
}
|
||||
CHECK(r > 0);
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
if (vt[i] >= 0 && my_vt[i] > vt[i]) {
|
||||
auto S = get_source(i);
|
||||
auto t = (my_vt[i] - vt[i] > r) ? r : (my_vt[i] - vt[i]);
|
||||
CHECK(t > 0);
|
||||
while (t-- > 0) {
|
||||
auto M = S->get_block(++vt[i]);
|
||||
CHECK(M != nullptr);
|
||||
auto block = create_tl_object<ton_api::catchain_blockUpdate>(M->export_tl());
|
||||
CHECK(M->get_payload().size() > 0);
|
||||
auto BB = serialize_tl_object(block, true, M->get_payload().as_slice());
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, src,
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(BB));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
promise.set_value(serialize_tl_object(create_tl_object<ton_api::catchain_difference>(std::move(vt)), true));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::got_fork_proof(td::BufferSlice data) {
|
||||
auto F = fetch_tl_object<ton_api::catchain_differenceFork>(std::move(data), true);
|
||||
if (F.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": received bad fork proof: " << F.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto f = F.move_as_ok();
|
||||
{
|
||||
td::Status S;
|
||||
S = validate_block_sync(f->left_);
|
||||
if (S.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: left is invalid: " << S.move_as_error();
|
||||
return;
|
||||
}
|
||||
S = validate_block_sync(f->right_);
|
||||
if (S.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: right is invalid: " << S.move_as_error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// block is incorrect, since blocks are
|
||||
if (f->left_->height_ != f->right_->height_ || f->left_->src_ != f->right_->src_ ||
|
||||
f->left_->data_hash_ == f->right_->data_hash_) {
|
||||
VLOG(CATCHAIN_WARNING) << this << ": incorrect fork blame: not a fork";
|
||||
return;
|
||||
}
|
||||
|
||||
auto S = get_source(f->left_->src_);
|
||||
S->on_found_fork_proof(
|
||||
create_serialize_tl_object<ton_api::catchain_block_data_fork>(std::move(f->left_), std::move(f->right_)));
|
||||
S->blame();
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::synchronize_with(CatChainReceiverSource *S) {
|
||||
CHECK(!S->blamed());
|
||||
std::vector<td::int32> rt(get_sources_cnt());
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
auto SS = get_source(i);
|
||||
if (SS->blamed()) {
|
||||
rt[i] = -1;
|
||||
} else {
|
||||
rt[i] = S->delivered_height();
|
||||
}
|
||||
}
|
||||
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[SelfId = actor_id(this), src = S->get_hash(), print_id = print_id()](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
VLOG(CATCHAIN_INFO) << print_id << ": timedout syncronize query to " << src;
|
||||
return;
|
||||
}
|
||||
auto data = R.move_as_ok();
|
||||
auto X = fetch_tl_object<ton_api::catchain_Difference>(data.clone(), true);
|
||||
if (X.is_error()) {
|
||||
VLOG(CATCHAIN_WARNING) << print_id << ": received incorrect answer to syncronize query from " << src << ": "
|
||||
<< X.move_as_error();
|
||||
return;
|
||||
}
|
||||
auto A = X.move_as_ok();
|
||||
|
||||
if (A->get_id() == ton_api::catchain_differenceFork::ID) {
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::got_fork_proof, std::move(data));
|
||||
}
|
||||
// use answer ?
|
||||
return;
|
||||
});
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query, S->get_adnl_id(),
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, "sync", std::move(P),
|
||||
td::Timestamp::in(5.0),
|
||||
serialize_tl_object(create_tl_object<ton_api::catchain_getDifference>(std::move(rt)), true));
|
||||
|
||||
if (S->delivered_height() < S->received_height()) {
|
||||
auto B = S->get_block(S->delivered_height() + 1);
|
||||
CHECK(B->initialized());
|
||||
|
||||
std::vector<CatChainBlockHash> vec;
|
||||
B->find_pending_deps(vec, 16);
|
||||
|
||||
for (auto &hash : vec) {
|
||||
auto P = td::PromiseCreator::lambda(
|
||||
[SelfId = actor_id(this), print_id = print_id(), src = S->get_adnl_id()](td::Result<td::BufferSlice> R) {
|
||||
if (R.is_error()) {
|
||||
VLOG(CATCHAIN_INFO) << print_id << ": timedout syncronize query to " << src;
|
||||
} else {
|
||||
td::actor::send_closure(SelfId, &CatChainReceiverImpl::receive_block_answer, src, R.move_as_ok());
|
||||
}
|
||||
});
|
||||
auto query = serialize_tl_object(create_tl_object<ton_api::catchain_getBlock>(hash), true);
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query, S->get_adnl_id(),
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, "sync blocks", std::move(P),
|
||||
td::Timestamp::in(2.0), std::move(query));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::choose_neighbours() {
|
||||
std::vector<td::uint32> n;
|
||||
n.resize(get_max_neighbours());
|
||||
|
||||
td::uint32 size = 0;
|
||||
for (td::uint32 i = 0; i < get_sources_cnt(); i++) {
|
||||
if (i == local_idx_) {
|
||||
continue;
|
||||
}
|
||||
auto S = get_source(i);
|
||||
if (!S->blamed()) {
|
||||
size++;
|
||||
if (size <= n.size()) {
|
||||
n[size - 1] = i;
|
||||
} else {
|
||||
td::uint32 id = td::Random::fast(0, size - 1);
|
||||
if (id < n.size()) {
|
||||
n[id] = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (size < n.size()) {
|
||||
n.resize(size);
|
||||
}
|
||||
neighbours_ = std::move(n);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::alarm() {
|
||||
alarm_timestamp() = td::Timestamp::never();
|
||||
if (next_sync_ && next_sync_.is_in_past()) {
|
||||
next_sync_ = td::Timestamp::in(td::Random::fast(2.0, 3.0));
|
||||
for (auto i = 0; i < 3; i++) {
|
||||
auto S = get_source(td::Random::fast(0, get_sources_cnt() - 1));
|
||||
CHECK(S != nullptr);
|
||||
if (!S->blamed()) {
|
||||
synchronize_with(S);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (next_rotate_ && next_rotate_.is_in_past()) {
|
||||
next_rotate_ = td::Timestamp::in(td::Random::fast(60.0, 120.0));
|
||||
choose_neighbours();
|
||||
}
|
||||
alarm_timestamp().relax(next_rotate_);
|
||||
alarm_timestamp().relax(next_sync_);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::send_fec_broadcast(td::BufferSlice data) {
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_broadcast_fec_ex,
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, local_id_, 0, std::move(data));
|
||||
}
|
||||
void CatChainReceiverImpl::send_custom_query_data(PublicKeyHash dst, std::string name,
|
||||
td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
|
||||
td::BufferSlice query) {
|
||||
auto S = get_source_by_hash(dst);
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query, S->get_adnl_id(),
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(name), std::move(promise),
|
||||
timeout, std::move(query));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::send_custom_query_data_via(PublicKeyHash dst, std::string name,
|
||||
td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
|
||||
td::BufferSlice query, td::uint64 max_answer_size,
|
||||
td::actor::ActorId<adnl::AdnlSenderInterface> via) {
|
||||
auto S = get_source_by_hash(dst);
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_query_via, S->get_adnl_id(),
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(name), std::move(promise),
|
||||
timeout, std::move(query), max_answer_size, via);
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::send_custom_message_data(PublicKeyHash dst, td::BufferSlice data) {
|
||||
auto S = get_source_by_hash(dst);
|
||||
td::actor::send_closure(overlay_manager_, &overlay::Overlays::send_message, S->get_adnl_id(),
|
||||
get_source(local_idx_)->get_adnl_id(), overlay_id_, std::move(data));
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::block_written_to_db(CatChainBlockHash hash) {
|
||||
auto block = get_block(hash);
|
||||
CHECK(block);
|
||||
|
||||
block->written();
|
||||
run_scheduler();
|
||||
}
|
||||
|
||||
static void destroy_db(std::string name, td::uint32 attempt) {
|
||||
auto S = td::RocksDb::destroy(name);
|
||||
if (S.is_ok()) {
|
||||
return;
|
||||
}
|
||||
if (S.is_error() && attempt >= 10) {
|
||||
LOG(ERROR) << "failed to destroy catchain " << name << ": " << S;
|
||||
} else {
|
||||
LOG(DEBUG) << "failed to destroy catchain " << name << ": " << S;
|
||||
delay_action([name, attempt]() { destroy_db(name, attempt); }, td::Timestamp::in(1.0));
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainReceiverImpl::destroy() {
|
||||
auto name = db_root_ + "/catchainreceiver-" + td::base64url_encode(as_slice(incarnation_));
|
||||
delay_action([name]() { destroy_db(name, 0); }, td::Timestamp::in(1.0));
|
||||
stop();
|
||||
}
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
89
catchain/catchain-receiver.h
Normal file
89
catchain/catchain-receiver.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "adnl/adnl.h"
|
||||
#include "adnl/utils.hpp"
|
||||
#include "auto/tl/ton_api.h"
|
||||
|
||||
#include "catchain-types.h"
|
||||
#include "catchain-receiver-interface.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
constexpr int VERBOSITY_NAME(CATCHAIN_WARNING) = verbosity_WARNING;
|
||||
constexpr int VERBOSITY_NAME(CATCHAIN_NOTICE) = verbosity_DEBUG;
|
||||
constexpr int VERBOSITY_NAME(CATCHAIN_INFO) = verbosity_DEBUG;
|
||||
constexpr int VERBOSITY_NAME(CATCHAIN_DEBUG) = verbosity_DEBUG;
|
||||
constexpr int VERBOSITY_NAME(CATCHAIN_EXTRA_DEBUG) = verbosity_DEBUG + 1;
|
||||
|
||||
class CatChainReceivedBlock;
|
||||
class CatChainReceiverSource;
|
||||
|
||||
class CatChainReceiver : public CatChainReceiverInterface {
|
||||
public:
|
||||
struct PrintId {
|
||||
CatChainSessionId instance_;
|
||||
PublicKeyHash local_id_;
|
||||
};
|
||||
td::uint32 get_max_neighbours() const {
|
||||
return 5;
|
||||
}
|
||||
virtual PrintId print_id() const = 0;
|
||||
virtual CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block> block,
|
||||
td::SharedSlice payload) = 0;
|
||||
virtual CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block_dep> block) = 0;
|
||||
virtual CatChainReceiverSource *get_source(td::uint32 source_id) const = 0;
|
||||
virtual PublicKeyHash get_source_hash(td::uint32 source_id) const = 0;
|
||||
virtual td::uint32 get_forks_cnt() const = 0;
|
||||
virtual td::uint32 get_sources_cnt() const = 0;
|
||||
virtual CatChainSessionId get_incarnation() const = 0;
|
||||
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_blame(td::uint32 source_id) = 0;
|
||||
|
||||
virtual const CatChainOptions &opts() const = 0;
|
||||
|
||||
virtual td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) = 0;
|
||||
virtual td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) = 0;
|
||||
|
||||
virtual ~CatChainReceiver() = default;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiver::PrintId &print_id) {
|
||||
sb << "[catchainreceiver " << print_id.instance_ << "@" << print_id.local_id_ << "]";
|
||||
return sb;
|
||||
}
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiver *catchain) {
|
||||
sb << catchain->print_id();
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
240
catchain/catchain-receiver.hpp
Normal file
240
catchain/catchain-receiver.hpp
Normal file
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <list>
|
||||
#include <queue>
|
||||
#include <map>
|
||||
|
||||
#include "catchain-types.h"
|
||||
#include "catchain-receiver.h"
|
||||
#include "catchain-receiver-source.h"
|
||||
#include "catchain-received-block.h"
|
||||
|
||||
#include "td/db/KeyValueAsync.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainReceiverImpl : public CatChainReceiver {
|
||||
public:
|
||||
PrintId print_id() const override {
|
||||
return PrintId{incarnation_, local_id_};
|
||||
}
|
||||
|
||||
void add_prepared_event(td::BufferSlice data) override {
|
||||
add_block(std::move(data), std::vector<CatChainBlockHash>());
|
||||
}
|
||||
CatChainSessionId get_incarnation() const override {
|
||||
return incarnation_;
|
||||
}
|
||||
void run_block(CatChainReceivedBlock *block) override;
|
||||
|
||||
td::uint32 get_forks_cnt() const override {
|
||||
return total_forks_;
|
||||
}
|
||||
td::uint32 get_sources_cnt() const override {
|
||||
return static_cast<td::uint32>(sources_.size());
|
||||
}
|
||||
|
||||
CatChainReceiverSource *get_source(td::uint32 source_id) const override {
|
||||
if (source_id >= get_sources_cnt()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return sources_[source_id].get();
|
||||
}
|
||||
PublicKeyHash get_source_hash(td::uint32 source_id) const override;
|
||||
CatChainReceiverSource *get_source_by_hash(PublicKeyHash source_hash) const;
|
||||
CatChainReceiverSource *get_source_by_adnl_id(adnl::AdnlNodeIdShort source_hash) const;
|
||||
|
||||
td::uint32 add_fork() override;
|
||||
|
||||
void deliver_block(CatChainReceivedBlock *block) override;
|
||||
|
||||
void receive_message_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data);
|
||||
void receive_query_from_overlay(adnl::AdnlNodeIdShort src, td::BufferSlice data,
|
||||
td::Promise<td::BufferSlice> promise);
|
||||
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlock &query,
|
||||
td::Promise<td::BufferSlice> promise);
|
||||
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlocks &query,
|
||||
td::Promise<td::BufferSlice> promise);
|
||||
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getBlockHistory &query,
|
||||
td::Promise<td::BufferSlice> promise);
|
||||
void process_query(adnl::AdnlNodeIdShort src, ton_api::catchain_getDifference &query,
|
||||
td::Promise<td::BufferSlice> promise);
|
||||
template <class T>
|
||||
void process_query(adnl::AdnlNodeIdShort src, T &query, td::Promise<td::BufferSlice> promise) {
|
||||
//LOG(WARNING) << this << ": unknown query from " << src;
|
||||
callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), serialize_tl_object(&query, true),
|
||||
std::move(promise));
|
||||
}
|
||||
void receive_broadcast_from_overlay(PublicKeyHash src, td::BufferSlice data);
|
||||
|
||||
void receive_block(adnl::AdnlNodeIdShort src, tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
|
||||
void receive_block_answer(adnl::AdnlNodeIdShort src, td::BufferSlice);
|
||||
//void send_block(PublicKeyHash src, tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
|
||||
|
||||
CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block> block, td::SharedSlice payload) override;
|
||||
CatChainReceivedBlock *create_block(tl_object_ptr<ton_api::catchain_block_dep> block) override;
|
||||
|
||||
td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block_dep> &dep) override;
|
||||
td::Status validate_block_sync(tl_object_ptr<ton_api::catchain_block> &block, td::Slice payload) override;
|
||||
|
||||
void send_fec_broadcast(td::BufferSlice data) override;
|
||||
void send_custom_query_data(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
|
||||
td::Timestamp timeout, td::BufferSlice query) override;
|
||||
void send_custom_query_data_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
|
||||
td::Timestamp timeout, td::BufferSlice query, td::uint64 max_answer_size,
|
||||
td::actor::ActorId<adnl::AdnlSenderInterface> via) override;
|
||||
void send_custom_message_data(PublicKeyHash dst, td::BufferSlice query) override;
|
||||
|
||||
void run_scheduler();
|
||||
void add_block(td::BufferSlice data, std::vector<CatChainBlockHash> deps) override;
|
||||
void add_block_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
|
||||
void add_block_cont_2(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
|
||||
void add_block_cont_3(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
|
||||
void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height,
|
||||
std::vector<CatChainBlockHash> deps) override;
|
||||
void debug_add_fork_cont(tl_object_ptr<ton_api::catchain_block> block, td::BufferSlice payload);
|
||||
void on_blame(td::uint32 src) override {
|
||||
callback_->blame(src);
|
||||
}
|
||||
void blame_node(td::uint32 idx) override {
|
||||
}
|
||||
const CatChainOptions &opts() const override {
|
||||
return opts_;
|
||||
}
|
||||
|
||||
void got_fork_proof(td::BufferSlice data);
|
||||
void synchronize_with(CatChainReceiverSource *source);
|
||||
void alarm() override;
|
||||
void start_up() override;
|
||||
void tear_down() override;
|
||||
void read_db();
|
||||
void read_db_from(CatChainBlockHash id);
|
||||
void read_block_from_db(CatChainBlockHash id, td::BufferSlice data);
|
||||
|
||||
void block_written_to_db(CatChainBlockHash hash);
|
||||
|
||||
void destroy() override;
|
||||
|
||||
CatChainReceivedBlock *get_block(CatChainBlockHash hash) const;
|
||||
|
||||
CatChainReceiverImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays>, std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainBlockHash unique_hash, std::string db_root);
|
||||
|
||||
private:
|
||||
std::unique_ptr<overlay::Overlays::Callback> make_callback() {
|
||||
class Callback : public overlay::Overlays::Callback {
|
||||
public:
|
||||
void receive_message(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id,
|
||||
td::BufferSlice data) override {
|
||||
td::actor::send_closure(id_, &CatChainReceiverImpl::receive_message_from_overlay, src, std::move(data));
|
||||
}
|
||||
void receive_query(adnl::AdnlNodeIdShort src, overlay::OverlayIdShort overlay_id, td::BufferSlice data,
|
||||
td::Promise<td::BufferSlice> promise) override {
|
||||
td::actor::send_closure(id_, &CatChainReceiverImpl::receive_query_from_overlay, src, std::move(data),
|
||||
std::move(promise));
|
||||
}
|
||||
|
||||
void receive_broadcast(PublicKeyHash src, overlay::OverlayIdShort overlay_id, td::BufferSlice data) override {
|
||||
td::actor::send_closure(id_, &CatChainReceiverImpl::receive_broadcast_from_overlay, src, std::move(data));
|
||||
}
|
||||
Callback(td::actor::ActorId<CatChainReceiverImpl> id) : id_(std::move(id)) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::actor::ActorId<CatChainReceiverImpl> id_;
|
||||
};
|
||||
|
||||
return std::make_unique<Callback>(actor_id(this));
|
||||
}
|
||||
|
||||
struct PendingBlock {
|
||||
td::BufferSlice payload_;
|
||||
std::vector<CatChainBlockHash> deps_;
|
||||
|
||||
PendingBlock(td::BufferSlice &&payload, std::vector<CatChainBlockHash> &&deps)
|
||||
: payload_(std::move(payload)), deps_(std::move(deps)) {
|
||||
}
|
||||
};
|
||||
|
||||
std::list<std::unique_ptr<PendingBlock>> pending_blocks_;
|
||||
bool active_send_ = false;
|
||||
bool read_db_ = false;
|
||||
td::uint32 pending_in_db_ = 0;
|
||||
CatChainBlockHash db_root_block_ = CatChainBlockHash::zero();
|
||||
|
||||
void choose_neighbours();
|
||||
|
||||
std::vector<std::unique_ptr<CatChainReceiverSource>> sources_;
|
||||
std::map<PublicKeyHash, td::uint32> sources_hashes_;
|
||||
std::map<adnl::AdnlNodeIdShort, td::uint32> sources_adnl_addrs_;
|
||||
td::uint32 total_forks_ = 0;
|
||||
std::map<CatChainBlockHash, std::unique_ptr<CatChainReceivedBlock>> blocks_;
|
||||
CatChainReceivedBlock *root_block_;
|
||||
CatChainReceivedBlock *last_sent_block_;
|
||||
|
||||
CatChainSessionId incarnation_;
|
||||
|
||||
std::unique_ptr<Callback> callback_;
|
||||
CatChainOptions opts_;
|
||||
|
||||
std::vector<td::uint32> neighbours_;
|
||||
|
||||
//std::queue<tl_object_ptr<ton_api::catchain_block_inner_Data>> events_;
|
||||
//std::queue<td::BufferSlice> raw_events_;
|
||||
|
||||
td::actor::ActorId<keyring::Keyring> keyring_;
|
||||
td::actor::ActorId<adnl::Adnl> adnl_;
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager_;
|
||||
overlay::OverlayIdShort overlay_id_;
|
||||
overlay::OverlayIdFull overlay_full_id_;
|
||||
PublicKeyHash local_id_;
|
||||
td::uint32 local_idx_;
|
||||
|
||||
td::Timestamp next_sync_;
|
||||
td::Timestamp next_rotate_;
|
||||
|
||||
std::string db_root_;
|
||||
|
||||
using DbType = td::KeyValueAsync<CatChainBlockHash, td::BufferSlice>;
|
||||
DbType db_;
|
||||
|
||||
bool intentional_fork_ = false;
|
||||
|
||||
std::list<CatChainReceivedBlock *> to_run_;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainReceiverImpl *catchain) {
|
||||
sb << catchain->print_id();
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
47
catchain/catchain-types.h
Normal file
47
catchain/catchain-types.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "td/utils/int_types.h"
|
||||
#include "adnl/adnl-node-id.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
using CatChainBlockHash = td::Bits256;
|
||||
using CatChainBlockPayloadHash = td::Bits256;
|
||||
using CatChainBlockHeight = td::uint32;
|
||||
using CatChainSessionId = td::Bits256;
|
||||
|
||||
struct CatChainNode {
|
||||
adnl::AdnlNodeIdShort adnl_id;
|
||||
PublicKey pub_key;
|
||||
};
|
||||
|
||||
struct CatChainOptions {
|
||||
td::Clocks::Duration idle_timeout = 16.0;
|
||||
td::uint32 max_deps = 4;
|
||||
|
||||
bool debug_disable_db = false;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
317
catchain/catchain.cpp
Normal file
317
catchain/catchain.cpp
Normal file
|
@ -0,0 +1,317 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#include "catchain-types.h"
|
||||
#include "catchain.hpp"
|
||||
#include "catchain-receiver.h"
|
||||
|
||||
#include "adnl/utils.hpp"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
void CatChainImpl::send_process() {
|
||||
CHECK(receiver_started_);
|
||||
|
||||
std::vector<CatChainBlock *> v;
|
||||
std::vector<CatChainBlockHash> w;
|
||||
while (top_blocks_.size() > 0 && v.size() < opts_.max_deps) {
|
||||
auto B = *top_blocks_.get_random();
|
||||
CHECK(B != nullptr);
|
||||
top_blocks_.remove(B->hash());
|
||||
if (B->source() == sources_.size() || !blamed_sources_[B->source()]) {
|
||||
w.push_back(B->hash());
|
||||
v.push_back(B);
|
||||
set_processed(B);
|
||||
}
|
||||
}
|
||||
|
||||
process_deps_ = std::move(w);
|
||||
VLOG(CATCHAIN_INFO) << this << ": creating block. deps=" << process_deps_;
|
||||
callback_->process_blocks(std::move(v));
|
||||
VLOG(CATCHAIN_INFO) << this << ": sent creating block";
|
||||
}
|
||||
|
||||
void CatChainImpl::send_preprocess(CatChainBlock *block) {
|
||||
if (block->preprocess_is_sent()) {
|
||||
return;
|
||||
}
|
||||
auto prev = block->prev();
|
||||
if (prev) {
|
||||
send_preprocess(prev);
|
||||
}
|
||||
|
||||
auto deps = block->deps();
|
||||
for (auto X : deps) {
|
||||
send_preprocess(X);
|
||||
}
|
||||
|
||||
block->preprocess_sent();
|
||||
VLOG(CATCHAIN_INFO) << this << ": preprocessing block " << block->hash() << " src=" << block->source();
|
||||
callback_->preprocess_block(block);
|
||||
VLOG(CATCHAIN_INFO) << this << ": sent preprocessing block " << block->hash() << " src=" << block->source();
|
||||
}
|
||||
|
||||
void CatChainImpl::set_processed(CatChainBlock *block) {
|
||||
if (block->is_processed()) {
|
||||
return;
|
||||
}
|
||||
auto prev = block->prev();
|
||||
if (prev) {
|
||||
set_processed(prev);
|
||||
}
|
||||
|
||||
auto deps = block->deps();
|
||||
for (auto X : deps) {
|
||||
set_processed(X);
|
||||
}
|
||||
|
||||
block->set_processed();
|
||||
}
|
||||
|
||||
void CatChainImpl::processed_block(td::BufferSlice payload) {
|
||||
CHECK(receiver_started_);
|
||||
VLOG(CATCHAIN_INFO) << this << ": created block. deps=" << process_deps_ << " payload_size=" << payload.size();
|
||||
td::actor::send_closure(receiver_, &CatChainReceiverInterface::add_block, std::move(payload),
|
||||
std::move(process_deps_));
|
||||
CHECK(active_process_);
|
||||
if (top_blocks_.size() > 0 || force_process_) {
|
||||
force_process_ = false;
|
||||
send_process();
|
||||
} else {
|
||||
active_process_ = false;
|
||||
VLOG(CATCHAIN_INFO) << this << ": finished processing";
|
||||
callback_->finished_processing();
|
||||
VLOG(CATCHAIN_INFO) << this << ": sent finished processing";
|
||||
alarm_timestamp() = td::Timestamp::in(opts_.idle_timeout);
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainImpl::need_new_block(td::Timestamp t) {
|
||||
if (!receiver_started_) {
|
||||
return;
|
||||
}
|
||||
if (!force_process_) {
|
||||
VLOG(CATCHAIN_INFO) << this << ": forcing creation of new block";
|
||||
}
|
||||
force_process_ = true;
|
||||
if (!active_process_) {
|
||||
alarm_timestamp().relax(t);
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainImpl::on_new_block(td::uint32 src_id, td::uint32 fork, CatChainBlockHash hash, CatChainBlockHeight height,
|
||||
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps,
|
||||
std::vector<CatChainBlockHeight> vt, td::SharedSlice data) {
|
||||
VLOG(CATCHAIN_DEBUG) << this << ": new block " << hash;
|
||||
if (top_blocks_.size() == 0 && !active_process_ && receiver_started_) {
|
||||
alarm_timestamp().relax(td::Timestamp::in(opts_.idle_timeout));
|
||||
}
|
||||
|
||||
CatChainBlock *p = nullptr;
|
||||
if (!prev.is_zero()) {
|
||||
p = get_block(prev);
|
||||
CHECK(p != nullptr);
|
||||
if (top_blocks_.exists(prev)) {
|
||||
top_blocks_.remove(prev);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<CatChainBlock *> v;
|
||||
v.resize(deps.size());
|
||||
for (size_t i = 0; i < deps.size(); i++) {
|
||||
if (!blamed_sources_[src_id] && top_blocks_.exists(deps[i])) {
|
||||
top_blocks_.remove(deps[i]);
|
||||
}
|
||||
v[i] = get_block(deps[i]);
|
||||
CHECK(v[i] != nullptr);
|
||||
}
|
||||
|
||||
CHECK(src_id < sources_.size());
|
||||
auto src_hash = sources_[src_id];
|
||||
blocks_[hash] =
|
||||
CatChainBlock::create(src_id, fork, src_hash, height, hash, std::move(data), p, std::move(v), std::move(vt));
|
||||
|
||||
auto B = get_block(hash);
|
||||
CHECK(B != nullptr);
|
||||
|
||||
if (!blamed_sources_[src_id]) {
|
||||
send_preprocess(B);
|
||||
top_source_blocks_[src_id] = B;
|
||||
|
||||
if (src_id != local_idx_) {
|
||||
top_blocks_.insert(B->hash(), B);
|
||||
}
|
||||
|
||||
if (top_blocks_.size() == 0 && !active_process_ && receiver_started_) {
|
||||
alarm_timestamp().relax(td::Timestamp::in(opts_.idle_timeout));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainImpl::on_blame(td::uint32 src_id) {
|
||||
if (blamed_sources_[src_id]) {
|
||||
return;
|
||||
}
|
||||
blamed_sources_[src_id] = true;
|
||||
top_source_blocks_[src_id] = nullptr;
|
||||
|
||||
// recompute top blocks
|
||||
top_blocks_.reset();
|
||||
auto size = static_cast<td::uint32>(sources_.size());
|
||||
for (td::uint32 i = 0; i < size; i++) {
|
||||
if (!blamed_sources_[i] && top_source_blocks_[i] && i != local_idx_) {
|
||||
auto B = top_source_blocks_[i];
|
||||
bool f = true;
|
||||
if (B->is_processed()) {
|
||||
continue;
|
||||
}
|
||||
for (td::uint32 j = 0; j < size; j++) {
|
||||
if (i != j && !blamed_sources_[j] && top_source_blocks_[j]) {
|
||||
if (top_source_blocks_[j]->is_descendant_of(B)) {
|
||||
f = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (f) {
|
||||
top_blocks_.insert(B->hash(), B);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainImpl::on_custom_message(PublicKeyHash src, td::BufferSlice data) {
|
||||
callback_->process_message(src, std::move(data));
|
||||
}
|
||||
|
||||
void CatChainImpl::on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) {
|
||||
callback_->process_query(src, std::move(data), std::move(promise));
|
||||
}
|
||||
|
||||
void CatChainImpl::on_broadcast(PublicKeyHash src, td::BufferSlice data) {
|
||||
VLOG(CATCHAIN_INFO) << this << ": processing broadcast";
|
||||
callback_->process_broadcast(src, std::move(data));
|
||||
VLOG(CATCHAIN_INFO) << this << ": sent processing broadcast";
|
||||
}
|
||||
|
||||
void CatChainImpl::on_receiver_started() {
|
||||
receiver_started_ = true;
|
||||
callback_->started();
|
||||
CHECK(!active_process_);
|
||||
active_process_ = true;
|
||||
send_process();
|
||||
}
|
||||
|
||||
CatChainImpl::CatChainImpl(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager, std::vector<CatChainNode> ids,
|
||||
PublicKeyHash local_id, CatChainSessionId unique_hash, std::string db_root)
|
||||
: opts_(std::move(opts)), db_root_(db_root) {
|
||||
callback_ = std::move(callback);
|
||||
sources_.resize(ids.size());
|
||||
unique_hash_ = unique_hash;
|
||||
for (size_t i = 0; i < ids.size(); i++) {
|
||||
sources_[i] = ids[i].pub_key.compute_short_id();
|
||||
if (sources_[i] == local_id) {
|
||||
local_idx_ = static_cast<td::uint32>(i);
|
||||
}
|
||||
}
|
||||
blamed_sources_.resize(ids.size(), false);
|
||||
top_source_blocks_.resize(ids.size(), nullptr);
|
||||
|
||||
args_ = std::make_unique<Args>(keyring, adnl, overlay_manager, std::move(ids), local_id, unique_hash);
|
||||
}
|
||||
|
||||
void CatChainImpl::alarm() {
|
||||
alarm_timestamp() = td::Timestamp::never();
|
||||
if (!active_process_) {
|
||||
active_process_ = true;
|
||||
send_process();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainImpl::start_up() {
|
||||
class ChainCb : public CatChainReceiverInterface::Callback {
|
||||
public:
|
||||
void new_block(td::uint32 src_id, td::uint32 fork_id, CatChainBlockHash hash, CatChainBlockHeight height,
|
||||
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps, std::vector<CatChainBlockHeight> vt,
|
||||
td::SharedSlice data) override {
|
||||
td::actor::send_closure(id_, &CatChainImpl::on_new_block, src_id, fork_id, hash, height, prev, std::move(deps),
|
||||
std::move(vt), std::move(data));
|
||||
}
|
||||
void blame(td::uint32 src_id) override {
|
||||
td::actor::send_closure(id_, &CatChainImpl::on_blame, src_id);
|
||||
}
|
||||
void on_custom_message(PublicKeyHash src, td::BufferSlice data) override {
|
||||
td::actor::send_closure(id_, &CatChainImpl::on_custom_message, src, std::move(data));
|
||||
}
|
||||
void on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) override {
|
||||
td::actor::send_closure(id_, &CatChainImpl::on_custom_query, src, std::move(data), std::move(promise));
|
||||
}
|
||||
void on_broadcast(PublicKeyHash src, td::BufferSlice data) override {
|
||||
td::actor::send_closure(id_, &CatChainImpl::on_broadcast, src, std::move(data));
|
||||
}
|
||||
void start() override {
|
||||
td::actor::send_closure(id_, &CatChainImpl::on_receiver_started);
|
||||
}
|
||||
ChainCb(td::actor::ActorId<CatChainImpl> id) : id_(id) {
|
||||
}
|
||||
|
||||
private:
|
||||
td::actor::ActorId<CatChainImpl> id_;
|
||||
};
|
||||
|
||||
auto cb = std::make_unique<ChainCb>(actor_id(this));
|
||||
|
||||
receiver_ =
|
||||
CatChainReceiverInterface::create(std::move(cb), opts_, args_->keyring, args_->adnl, args_->overlay_manager,
|
||||
std::move(args_->ids), args_->local_id, args_->unique_hash, db_root_);
|
||||
args_ = nullptr;
|
||||
//alarm_timestamp() = td::Timestamp::in(opts_.idle_timeout);
|
||||
}
|
||||
|
||||
td::actor::ActorOwn<CatChain> CatChain::create(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root) {
|
||||
return td::actor::create_actor<CatChainImpl>("catchain", std::move(callback), std::move(opts), keyring, adnl,
|
||||
overlay_manager, std::move(ids), local_id, unique_hash, db_root);
|
||||
}
|
||||
|
||||
CatChainBlock *CatChainImpl::get_block(CatChainBlockHash hash) const {
|
||||
auto it = blocks_.find(hash);
|
||||
if (it == blocks_.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
void CatChainImpl::destroy() {
|
||||
td::actor::send_closure(receiver_, &CatChainReceiverInterface::destroy);
|
||||
receiver_.release();
|
||||
stop();
|
||||
}
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
125
catchain/catchain.h
Normal file
125
catchain/catchain.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "adnl/adnl.h"
|
||||
#include "adnl/utils.hpp"
|
||||
#include "overlay/overlays.h"
|
||||
#include "catchain-types.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainBlock {
|
||||
public:
|
||||
class Extra {
|
||||
public:
|
||||
virtual ~Extra() = default;
|
||||
};
|
||||
virtual td::SharedSlice &payload() = 0;
|
||||
virtual const td::SharedSlice &payload() const = 0;
|
||||
virtual Extra *extra() const = 0;
|
||||
virtual std::unique_ptr<Extra> move_extra() = 0;
|
||||
virtual void set_extra(std::unique_ptr<Extra> extra) = 0;
|
||||
|
||||
virtual td::uint32 source() const = 0;
|
||||
virtual td::uint32 fork() const = 0;
|
||||
virtual PublicKeyHash source_hash() const = 0;
|
||||
virtual CatChainBlockHash hash() const = 0;
|
||||
virtual CatChainBlockHeight height() const = 0;
|
||||
|
||||
virtual CatChainBlock *prev() = 0;
|
||||
virtual const CatChainBlock *prev() const = 0;
|
||||
virtual const std::vector<CatChainBlock *> &deps() const = 0;
|
||||
virtual const std::vector<CatChainBlockHeight> &vt() const = 0;
|
||||
|
||||
virtual bool preprocess_is_sent() const = 0;
|
||||
virtual void preprocess_sent() = 0;
|
||||
|
||||
virtual bool is_processed() const = 0;
|
||||
virtual void set_processed() = 0;
|
||||
|
||||
virtual bool is_descendant_of(CatChainBlock *block) = 0;
|
||||
|
||||
static std::unique_ptr<CatChainBlock> create(td::uint32 src, td::uint32 fork_id, PublicKeyHash src_hash,
|
||||
CatChainBlockHeight height, CatChainBlockHash hash,
|
||||
td::SharedSlice payload, CatChainBlock *prev,
|
||||
std::vector<CatChainBlock *> deps, std::vector<CatChainBlockHeight> vt);
|
||||
|
||||
virtual ~CatChainBlock() = default;
|
||||
};
|
||||
|
||||
class CatChain : public td::actor::Actor {
|
||||
public:
|
||||
class Callback {
|
||||
public:
|
||||
virtual void process_blocks(std::vector<CatChainBlock *> blocks) = 0;
|
||||
virtual void finished_processing() = 0;
|
||||
virtual void preprocess_block(CatChainBlock *block) = 0;
|
||||
virtual void process_broadcast(PublicKeyHash src, td::BufferSlice data) = 0;
|
||||
virtual void process_message(PublicKeyHash src, td::BufferSlice data) = 0;
|
||||
virtual void process_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise) = 0;
|
||||
virtual void started() = 0;
|
||||
virtual ~Callback() = default;
|
||||
};
|
||||
struct PrintId {
|
||||
CatChainSessionId instance_;
|
||||
PublicKeyHash local_id_;
|
||||
};
|
||||
virtual PrintId print_id() const = 0;
|
||||
virtual void processed_block(td::BufferSlice payload) = 0;
|
||||
virtual void need_new_block(td::Timestamp t) = 0;
|
||||
virtual void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height) = 0;
|
||||
|
||||
virtual void send_broadcast(td::BufferSlice data) = 0;
|
||||
virtual void send_message(PublicKeyHash dst, td::BufferSlice data) = 0;
|
||||
virtual void send_query(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
|
||||
td::Timestamp timeout, td::BufferSlice query) = 0;
|
||||
virtual void send_query_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise,
|
||||
td::Timestamp timeout, td::BufferSlice query, td::uint64 max_answer_size,
|
||||
td::actor::ActorId<adnl::AdnlSenderInterface> via) = 0;
|
||||
virtual void destroy() = 0;
|
||||
|
||||
static td::actor::ActorOwn<CatChain> create(std::unique_ptr<Callback> callback, CatChainOptions opts,
|
||||
td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash, std::string db_root);
|
||||
virtual ~CatChain() = default;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChain::PrintId &print_id) {
|
||||
sb << "[catchain " << print_id.instance_ << "@" << print_id.local_id_ << "]";
|
||||
return sb;
|
||||
}
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChain *catchain) {
|
||||
sb << catchain->print_id();
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
138
catchain/catchain.hpp
Normal file
138
catchain/catchain.hpp
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
This file is part of TON Blockchain Library.
|
||||
|
||||
TON Blockchain Library is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
TON Blockchain Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License
|
||||
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2017-2019 Telegram Systems LLP
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "catchain.h"
|
||||
#include "catchain-types.h"
|
||||
#include "catchain-receiver-interface.h"
|
||||
#include "td/utils/DecTree.h"
|
||||
|
||||
namespace ton {
|
||||
|
||||
namespace catchain {
|
||||
|
||||
class CatChainImpl : public CatChain {
|
||||
private:
|
||||
std::unique_ptr<CatChain::Callback> callback_;
|
||||
CatChainOptions opts_;
|
||||
td::DecTree<CatChainBlockHash, CatChainBlock *> top_blocks_;
|
||||
std::map<CatChainBlockHash, std::unique_ptr<CatChainBlock>> blocks_;
|
||||
std::vector<CatChainBlock *> top_source_blocks_;
|
||||
|
||||
std::vector<PublicKeyHash> sources_;
|
||||
std::vector<bool> blamed_sources_;
|
||||
|
||||
std::vector<CatChainBlockHash> process_deps_;
|
||||
|
||||
CatChainSessionId unique_hash_;
|
||||
td::uint32 local_idx_;
|
||||
bool active_process_ = false;
|
||||
bool force_process_ = false;
|
||||
td::actor::ActorOwn<CatChainReceiverInterface> receiver_;
|
||||
|
||||
bool receiver_started_ = false;
|
||||
|
||||
std::string db_root_;
|
||||
|
||||
void send_process();
|
||||
void send_preprocess(CatChainBlock *block);
|
||||
void set_processed(CatChainBlock *block);
|
||||
|
||||
struct Args {
|
||||
td::actor::ActorId<keyring::Keyring> keyring;
|
||||
td::actor::ActorId<adnl::Adnl> adnl;
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager;
|
||||
std::vector<CatChainNode> ids;
|
||||
PublicKeyHash local_id;
|
||||
CatChainSessionId unique_hash;
|
||||
|
||||
Args(td::actor::ActorId<keyring::Keyring> keyring, td::actor::ActorId<adnl::Adnl> adnl,
|
||||
td::actor::ActorId<overlay::Overlays> overlay_manager, std::vector<CatChainNode> ids, PublicKeyHash local_id,
|
||||
CatChainSessionId unique_hash)
|
||||
: keyring(keyring)
|
||||
, adnl(adnl)
|
||||
, overlay_manager(overlay_manager)
|
||||
, ids(std::move(ids))
|
||||
, local_id(local_id)
|
||||
, unique_hash(unique_hash) {
|
||||
}
|
||||
};
|
||||
std::unique_ptr<Args> args_;
|
||||
|
||||
public:
|
||||
PrintId print_id() const override {
|
||||
return PrintId{unique_hash_, sources_[local_idx_]};
|
||||
}
|
||||
CatChainBlock *get_block(CatChainBlockHash hash) const;
|
||||
void on_new_block(td::uint32 src_id, td::uint32 fork, CatChainBlockHash hash, CatChainBlockHeight height,
|
||||
CatChainBlockHash prev, std::vector<CatChainBlockHash> deps, std::vector<CatChainBlockHeight> vt,
|
||||
td::SharedSlice data);
|
||||
void on_blame(td::uint32 src_id);
|
||||
void on_custom_message(PublicKeyHash src, td::BufferSlice data);
|
||||
void on_custom_query(PublicKeyHash src, td::BufferSlice data, td::Promise<td::BufferSlice> promise);
|
||||
void on_broadcast(PublicKeyHash src, td::BufferSlice data);
|
||||
void on_receiver_started();
|
||||
void processed_block(td::BufferSlice payload) override;
|
||||
void need_new_block(td::Timestamp t) override;
|
||||
void debug_add_fork(td::BufferSlice payload, CatChainBlockHeight height) override {
|
||||
td::actor::send_closure(receiver_, &CatChainReceiverInterface::debug_add_fork, std::move(payload), height,
|
||||
std::vector<CatChainBlockHash>{});
|
||||
}
|
||||
|
||||
void send_broadcast(td::BufferSlice data) override {
|
||||
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_fec_broadcast, std::move(data));
|
||||
}
|
||||
void send_message(PublicKeyHash dst, td::BufferSlice data) override {
|
||||
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_custom_message_data, dst, std::move(data));
|
||||
}
|
||||
void send_query(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
|
||||
td::BufferSlice query) override {
|
||||
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_custom_query_data, dst, name,
|
||||
std::move(promise), timeout, std::move(query));
|
||||
}
|
||||
void send_query_via(PublicKeyHash dst, std::string name, td::Promise<td::BufferSlice> promise, td::Timestamp timeout,
|
||||
td::BufferSlice query, td::uint64 max_answer_size,
|
||||
td::actor::ActorId<adnl::AdnlSenderInterface> via) override {
|
||||
td::actor::send_closure(receiver_, &CatChainReceiverInterface::send_custom_query_data_via, dst, name,
|
||||
std::move(promise), timeout, std::move(query), max_answer_size, via);
|
||||
}
|
||||
void destroy() override;
|
||||
CatChainImpl(std::unique_ptr<Callback> callback, CatChainOptions opts, td::actor::ActorId<keyring::Keyring> keyring,
|
||||
td::actor::ActorId<adnl::Adnl> adnl, td::actor::ActorId<overlay::Overlays> overlay_manager,
|
||||
std::vector<CatChainNode> ids, PublicKeyHash local_id, CatChainSessionId unique_hash,
|
||||
std::string db_root);
|
||||
|
||||
void alarm() override;
|
||||
void start_up() override;
|
||||
};
|
||||
|
||||
} // namespace catchain
|
||||
|
||||
} // namespace ton
|
||||
|
||||
namespace td {
|
||||
|
||||
inline td::StringBuilder &operator<<(td::StringBuilder &sb, const ton::catchain::CatChainImpl *catchain) {
|
||||
sb << catchain->print_id();
|
||||
return sb;
|
||||
}
|
||||
|
||||
} // namespace td
|
Loading…
Add table
Add a link
Reference in a new issue