1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

initial commit

This commit is contained in:
initial commit 2019-09-07 14:03:22 +04:00 committed by vvaltman
commit c2da007f40
1610 changed files with 398047 additions and 0 deletions

View file

@ -0,0 +1,52 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
endif()
set(TON_VALIDATOR_SOURCE
accept-block.cpp
block.cpp
check-proof.cpp
collator.cpp
external-message.cpp
fabric.cpp
ihr-message.cpp
liteserver.cpp
message-queue.cpp
proof.cpp
shard.cpp
signature-set.cpp
top-shard-descr.cpp
validate-query.cpp
validator-set.cpp
accept-block.hpp
block.hpp
check-proof.hpp
collate-query-impl.h
collator-impl.h
collator.h
external-message.hpp
ihr-message.hpp
liteserver.hpp
message-queue.hpp
proof.hpp
shard.hpp
signature-set.hpp
top-shard-descr.hpp
validate-query.hpp
validator-set.hpp
)
add_library(ton_validator STATIC ${TON_VALIDATOR_SOURCE})
target_include_directories(ton_validator PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/../crypto
${OPENSSL_INCLUDE_DIR}
)
target_link_libraries(ton_validator PRIVATE tdutils tdactor adnl tl_api tl_lite_api tl-lite-utils dht tdfec
overlay catchain validatorsession ton_crypto ton_block)

View file

@ -0,0 +1,820 @@
/*
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 "accept-block.hpp"
#include "adnl/utils.hpp"
#include "interfaces/validator-manager.h"
#include "ton/ton-tl.hpp"
#include "ton/ton-io.hpp"
#include "fabric.h"
#include "top-shard-descr.hpp"
#include "vm/cells.h"
#include "vm/cells/MerkleProof.h"
#include "vm/boc.h"
#include "block/block.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "validator/invariants.hpp"
namespace ton {
namespace validator {
using namespace std::literals::string_literals;
AcceptBlockQuery::AcceptBlockQuery(BlockIdExt id, td::Ref<BlockData> data, std::vector<BlockIdExt> prev,
td::Ref<ValidatorSet> validator_set, td::Ref<BlockSignatureSet> signatures,
bool send_broadcast, td::actor::ActorId<ValidatorManager> manager,
td::Promise<td::Unit> promise)
: id_(id)
, data_(std::move(data))
, prev_(std::move(prev))
, validator_set_(std::move(validator_set))
, signatures_(std::move(signatures))
, is_fake_(false)
, send_broadcast_(send_broadcast)
, manager_(manager)
, promise_(std::move(promise)) {
state_keep_old_hash_.clear();
state_old_hash_.clear();
state_hash_.clear();
CHECK(prev_.size() > 0);
}
AcceptBlockQuery::AcceptBlockQuery(AcceptBlockQuery::IsFake fake, BlockIdExt id, td::Ref<BlockData> data,
std::vector<BlockIdExt> prev, td::Ref<ValidatorSet> validator_set,
td::actor::ActorId<ValidatorManager> manager, td::Promise<td::Unit> promise)
: id_(id)
, data_(std::move(data))
, prev_(std::move(prev))
, validator_set_(std::move(validator_set))
, is_fake_(true)
, send_broadcast_(false)
, manager_(manager)
, promise_(std::move(promise)) {
state_keep_old_hash_.clear();
state_old_hash_.clear();
state_hash_.clear();
CHECK(prev_.size() > 0);
}
bool AcceptBlockQuery::create_new_proof() {
// 0. check block's root hash
VLOG(VALIDATOR_DEBUG) << "create_new_proof() : start";
RootHash blk_rhash{block_root_->get_hash().bits()};
if (blk_rhash != id_.root_hash) {
return fatal_error("block root hash mismatch: expected "s + id_.root_hash.to_hex() + ", found " +
blk_rhash.to_hex());
}
// 1. visit block header while building a Merkle proof
auto usage_tree = std::make_shared<vm::CellUsageTree>();
auto usage_cell = vm::UsageCell::create(block_root_, usage_tree->root_ptr());
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
block::gen::ExtBlkRef::Record mcref{}; // _ ExtBlkRef = BlkMasterInfo;
block::CurrencyCollection fees;
ShardIdFull shard;
if (!(tlb::unpack_cell(usage_cell, blk) && tlb::unpack_cell(blk.info, info) && !info.version &&
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard) && !info.vert_seq_no &&
block::gen::BlkPrevInfo{info.after_merge}.validate_ref(info.prev_ref) &&
block::gen::t_ValueFlow.force_validate_ref(blk.value_flow) &&
(!info.not_master || tlb::unpack_cell(info.master_ref, mcref)))) {
return fatal_error("cannot unpack block header");
}
is_key_block_ = info.key_block;
// 2. check some header fields, especially shard
if (info.not_master != !shard.is_masterchain()) {
return fatal_error("block has invalid not_master flag in its header");
}
BlockId blk_id{shard, (unsigned)info.seq_no};
if (blk_id != id_.id) {
return fatal_error("block header corresponds to another block id: expected "s + id_.id.to_str() + ", found " +
blk_id.to_str());
}
if (info.after_merge + 1U != prev_.size()) {
return fatal_error(PSTRING() << "block header of " << id_.to_str() << " announces " << info.after_merge + 1
<< " previous blocks, but " << prev_.size() << " are actually present");
}
if (is_masterchain() && (info.after_merge | info.after_split | info.before_split)) {
return fatal_error("masterchain block header of "s + id_.to_str() + " announces merge/split in its header");
}
if (!is_masterchain() && is_key_block_) {
return fatal_error("non-masterchain block header of "s + id_.to_str() + " announces this block to be a key block");
}
// 3. check state update
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
&& upd_cs.size_ext() == 0x20228)) {
return fatal_error("invalid Merkle update in block");
}
// 4. visit validator-set related fields in key blocks
if (is_key_block_) {
block::gen::BlockExtra::Record extra;
block::gen::McBlockExtra::Record mc_extra;
if (!(tlb::unpack_cell(std::move(blk.extra), extra) && tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra) &&
mc_extra.key_block && mc_extra.config.not_null())) {
return fatal_error("cannot unpack extra header of key masterchain block "s + blk_id.to_str());
}
auto cfg = block::Config::unpack_config(std::move(mc_extra.config));
if (cfg.is_error()) {
return fatal_error("cannot extract configuration from extra header of key masterchain block "s + blk_id.to_str() +
" : " + cfg.move_as_error().to_string());
}
auto res = cfg.move_as_ok()->visit_validator_params();
if (res.is_error()) {
return fatal_error("cannot extract validator set configuration from extra header of key masterchain block "s +
blk_id.to_str() + " : " + res.to_string());
}
}
// 5. finish constructing Merkle proof from visited cells
auto proof = vm::MerkleProof::generate(block_root_, usage_tree.get());
proof_roots_.push_back(proof);
// 6. extract some information from state update
state_old_hash_ = upd_cs.prefetch_ref(0)->get_hash(0).bits();
state_hash_ = upd_cs.prefetch_ref(1)->get_hash(0).bits();
lt_ = info.end_lt;
created_at_ = info.gen_utime;
if (!is_masterchain()) {
mc_blkid_.id = BlockId{masterchainId, shardIdAll, mcref.seq_no};
mc_blkid_.root_hash = mcref.root_hash;
mc_blkid_.file_hash = mcref.file_hash;
} else if (!is_key_block_) {
block::gen::BlockExtra::Record extra;
block::gen::McBlockExtra::Record mc_extra;
if (!(tlb::unpack_cell(std::move(blk.extra), extra) && tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra) &&
!mc_extra.key_block)) {
return fatal_error("extra header of non-key masterchain block "s + blk_id.to_str() +
" is invalid or contains extra information reserved for key blocks only");
}
}
// 7. check signatures
td::Result<td::uint64> sign_chk;
if (!is_fake_) {
sign_chk = validator_set_->check_signatures(id_.root_hash, id_.file_hash, signatures_);
if (sign_chk.is_error()) {
auto err = sign_chk.move_as_error();
VLOG(VALIDATOR_WARNING) << "signature check failed : " << err.to_string();
abort_query(std::move(err));
return false;
}
}
// 8. serialize signatures
if (!is_fake_) {
vm::CellBuilder cb2;
Ref<vm::Cell> sign_cell;
if (!(cb2.store_long_bool(0x11, 8) // block_signatures#11
&& cb2.store_long_bool(validator_set_->get_validator_set_hash(),
32) // validator_info$_ validator_set_hash_short:uint32
&& cb2.store_long_bool(validator_set_->get_catchain_seqno(),
32) // validator_set_ts:uint32 = ValidatorInfo
&& cb2.store_long_bool(signatures_->size(), 32) // sig_count:uint32
&& cb2.store_long_bool(sign_chk.move_as_ok(), 64) // sig_weight:uint32
&& signatures_->serialize_to(sign_cell) // (HashmapE 16 CryptoSignaturePair)
&& cb2.store_maybe_ref(std::move(sign_cell)) && cb2.finalize_to(signatures_cell_))) {
return fatal_error("cannot serialize BlockSignatures for the newly-accepted block");
}
} else { // FAKE
vm::CellBuilder cb2;
if (!(cb2.store_long_bool(0x11, 8) // block_signatures#11
&& cb2.store_long_bool(validator_set_->get_validator_set_hash(),
32) // validator_info$_ validator_set_hash_short:uint32
&& cb2.store_long_bool(validator_set_->get_catchain_seqno(),
32) // validator_set_ts:uint32 = ValidatorInfo
&& cb2.store_long_bool(0, 32) // sig_count:uint32
&& cb2.store_long_bool(0, 64) // sig_weight:uint32
&& cb2.store_bool_bool(false) // (HashmapE 16 CryptoSignaturePair)
&& cb2.finalize_to(signatures_cell_))) {
return fatal_error("cannot serialize fake BlockSignatures for the newly-accepted block");
}
}
Ref<vm::Cell> bs_cell;
if (is_masterchain()) {
// 9a. now create serialized proof
vm::CellBuilder cb;
if (!(cb.store_long_bool(0xc3, 8) // block_proof#c3
&& block::tlb::t_BlockIdExt.pack(cb, id_) // proof_for:BlockIdExt
&& cb.store_ref_bool(std::move(proof)) // proof:^Cell
&& cb.store_bool_bool(true) // signatures:(Maybe
&& cb.store_ref_bool(signatures_cell_) // ^BlockSignatures)
&& cb.finalize_to(bs_cell))) {
return fatal_error("cannot serialize BlockProof for the newly-accepted block");
}
} else {
// 9b. create serialized proof (link)
vm::CellBuilder cb;
if (!(cb.store_long_bool(0xc3, 8) // block_proof#c3
&& block::tlb::t_BlockIdExt.pack(cb, id_) // proof_for:BlockIdExt
&& cb.store_ref_bool(std::move(proof)) // proof:^Cell
&& cb.store_bool_bool(false) // signatures:(Maybe ^BlockSignatures)
&& cb.finalize_to(bs_cell))) {
return fatal_error("cannot serialize BlockProof for the newly-accepted block");
}
}
// 10. check resulting object
if (!block::gen::t_BlockProof.validate_ref(bs_cell)) {
block::gen::t_BlockProof.print_ref(std::cerr, bs_cell);
vm::load_cell_slice(bs_cell).print_rec(std::cerr);
return fatal_error("BlockProof object just created failed to pass automated consistency checks");
}
// 11. create a proof object from this cell
if (is_masterchain()) {
proof_ = create_proof(id_, vm::std_boc_serialize(bs_cell, 0).move_as_ok()).move_as_ok();
} else {
proof_link_ = create_proof_link(id_, vm::std_boc_serialize(bs_cell, 0).move_as_ok()).move_as_ok();
}
VLOG(VALIDATOR_DEBUG) << "create_new_proof() : end";
return true;
}
void AcceptBlockQuery::abort_query(td::Status reason) {
if (promise_) {
VLOG(VALIDATOR_WARNING) << "aborting accept block query: " << reason;
promise_.set_error(std::move(reason));
}
stop();
}
bool AcceptBlockQuery::fatal_error(std::string msg, int code) {
abort_query(td::Status::Error(code, std::move(msg)));
return false;
}
bool AcceptBlockQuery::check_send_error(td::actor::ActorId<AcceptBlockQuery> SelfId, td::Status error) {
if (error.is_error()) {
td::actor::send_closure(std::move(SelfId), &AcceptBlockQuery::abort_query, std::move(error));
return true;
} else {
return false;
}
}
void AcceptBlockQuery::finish_query() {
ValidatorInvariants::check_post_accept(handle_);
if (is_masterchain()) {
CHECK(handle_->inited_proof());
} else {
CHECK(handle_->inited_proof_link());
}
if (promise_) {
promise_.set_value(td::Unit());
}
stop();
}
void AcceptBlockQuery::alarm() {
abort_query(td::Status::Error(ErrorCode::timeout, "timeout"));
}
void AcceptBlockQuery::start_up() {
VLOG(VALIDATOR_DEBUG) << "start_up()";
alarm_timestamp() = timeout_;
if (validator_set_.is_null()) {
fatal_error("no real ValidatorSet passed to AcceptBlockQuery");
return;
}
if (!is_fake_ && signatures_.is_null()) {
fatal_error("no real SignatureSet passed to AcceptBlockQuery");
return;
}
td::actor::send_closure(
manager_, &ValidatorManager::get_block_handle, id_, true, [SelfId = actor_id(this)](td::Result<BlockHandle> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_block_handle, R.move_as_ok());
});
}
void AcceptBlockQuery::got_block_handle(BlockHandle handle) {
VLOG(VALIDATOR_DEBUG) << "got_block_handle()";
handle_ = std::move(handle);
if (handle_->received() && handle_->received_state() && handle_->inited_signatures() &&
handle_->inited_split_after() && handle_->inited_merge_before() && handle_->inited_prev() &&
handle_->inited_logical_time() && handle_->inited_state_root_hash() &&
(is_masterchain() ? handle_->inited_proof() && handle_->is_applied() && handle_->inited_is_key_block()
: handle_->inited_proof_link())) {
finish_query();
return;
}
if (data_.not_null() && !handle_->received()) {
td::actor::send_closure(
manager_, &ValidatorManager::set_block_data, handle_, data_, [SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_data);
});
} else {
written_block_data();
}
}
void AcceptBlockQuery::written_block_data() {
VLOG(VALIDATOR_DEBUG) << "written_block_data()";
if (handle_->inited_signatures()) {
written_block_signatures();
return;
}
if (is_fake_) {
signatures_ = Ref<BlockSignatureSetQ>(create_signature_set(std::vector<BlockSignature>{}));
}
td::actor::send_closure(manager_, &ValidatorManager::set_block_signatures, handle_, signatures_,
[SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_signatures);
});
}
void AcceptBlockQuery::written_block_signatures() {
VLOG(VALIDATOR_DEBUG) << "written_block_signatures()";
handle_->set_merge(prev_.size() == 2);
for (auto& p : prev_) {
handle_->set_prev(p);
}
if (handle_->need_flush()) {
handle_->flush(manager_, handle_, [SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_info);
});
} else {
written_block_info();
}
}
void AcceptBlockQuery::written_block_info() {
VLOG(VALIDATOR_DEBUG) << "written block info";
if (data_.not_null()) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Ref<ShardState>> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_prev_state, R.move_as_ok());
});
td::actor::send_closure(manager_, &ValidatorManager::wait_prev_block_state, handle_, priority(), timeout_,
std::move(P));
} else {
td::actor::send_closure(manager_, &ValidatorManager::wait_block_data, handle_, priority(), timeout_,
[SelfId = actor_id(this)](td::Result<td::Ref<BlockData>> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_block_data,
R.move_as_ok());
});
}
}
void AcceptBlockQuery::got_block_data(td::Ref<BlockData> data) {
VLOG(VALIDATOR_DEBUG) << "got_block_data()";
data_ = std::move(data);
CHECK(data_.not_null());
if (data_->root_cell().is_null()) {
fatal_error("block data does not contain a root cell");
return;
}
if (handle_->received()) {
written_block_data();
} else {
td::actor::send_closure(
manager_, &ValidatorManager::set_block_data, handle_, data_, [SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_data);
});
}
}
void AcceptBlockQuery::got_prev_state(td::Ref<ShardState> state) {
VLOG(VALIDATOR_DEBUG) << "got prev state";
state_ = std::move(state);
state_keep_old_hash_ = state_->root_hash();
auto err = state_.write().apply_block(id_, data_);
if (err.is_error()) {
abort_query(std::move(err));
return;
}
handle_->set_split(state_->before_split());
td::actor::send_closure(manager_, &ValidatorManager::set_block_state, handle_, state_,
[SelfId = actor_id(this)](td::Result<td::Ref<ShardState>> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_state, R.move_as_ok());
});
}
void AcceptBlockQuery::written_state(td::Ref<ShardState> upd_state) {
VLOG(VALIDATOR_DEBUG) << "written state";
CHECK(data_.not_null());
state_ = std::move(upd_state);
block_root_ = data_->root_cell();
if (block_root_.is_null()) {
fatal_error("block data does not contain a root cell");
return;
}
// generate proof
if (!create_new_proof()) {
fatal_error("cannot generate proof for block "s + id_.to_str());
return;
}
if (state_keep_old_hash_ != state_old_hash_) {
fatal_error(PSTRING() << "invalid previous state hash in newly-created proof: expected "
<< state_->root_hash().to_hex() << ", found in update " << state_old_hash_.to_hex());
return;
}
//handle_->set_masterchain_block(prev_[0]);
handle_->set_state_root_hash(state_hash_);
handle_->set_logical_time(lt_);
handle_->set_unix_time(created_at_);
handle_->set_is_key_block(is_key_block_);
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_proof);
});
if (is_masterchain()) {
td::actor::send_closure(manager_, &ValidatorManager::set_block_proof, handle_, proof_, std::move(P));
} else {
td::actor::send_closure(manager_, &ValidatorManager::set_block_proof_link, handle_, proof_link_, std::move(P));
}
}
void AcceptBlockQuery::written_block_proof() {
VLOG(VALIDATOR_DEBUG) << "written_block_proof()";
if (!is_masterchain()) {
td::actor::send_closure(manager_, &ValidatorManager::get_top_masterchain_state_block,
[SelfId = actor_id(this)](td::Result<std::pair<td::Ref<MasterchainState>, BlockIdExt>> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_last_mc_block,
R.move_as_ok());
});
return;
}
CHECK(prev_.size() == 1);
td::actor::send_closure(
manager_, &ValidatorManager::set_next_block, prev_[0], id_, [SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_next);
});
}
void AcceptBlockQuery::got_last_mc_block(std::pair<td::Ref<MasterchainState>, BlockIdExt> last) {
VLOG(VALIDATOR_DEBUG) << "got_last_mc_block(): " << last.second.to_str();
last_mc_state_ = Ref<MasterchainStateQ>(std::move(last.first));
last_mc_id_ = std::move(last.second);
CHECK(last_mc_state_.not_null());
if (last_mc_id_.id.seqno < mc_blkid_.id.seqno) {
VLOG(VALIDATOR_DEBUG) << "shardchain block refers to newer masterchain block " << mc_blkid_.to_str()
<< ", trying to obtain it";
td::actor::send_closure_later(manager_, &ValidatorManager::wait_block_state_short, mc_blkid_, priority(), timeout_,
[SelfId = actor_id(this)](td::Result<Ref<ShardState>> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_mc_state,
R.move_as_ok());
});
return;
} else if (last_mc_id_.id.seqno > mc_blkid_.id.seqno) {
if (!last_mc_state_->check_old_mc_block_id(mc_blkid_)) {
fatal_error("shardchain block refers to masterchain block "s + mc_blkid_.to_str() +
" which is not a antecessor of last masterchain block " + last_mc_id_.to_str());
return;
}
} else if (last_mc_id_ != mc_blkid_) {
fatal_error("shardchain block refers to masterchain block "s + mc_blkid_.to_str() +
" distinct from last masterchain block " + last_mc_id_.to_str() + " of the same height");
return;
}
find_known_ancestors();
}
void AcceptBlockQuery::got_mc_state(Ref<ShardState> res) {
VLOG(VALIDATOR_DEBUG) << "got_mc_state()";
auto new_state = Ref<MasterchainStateQ>(std::move(res));
CHECK(new_state.not_null());
if (!new_state->check_old_mc_block_id(last_mc_id_)) {
fatal_error("shardchain block refers to masterchain block "s + mc_blkid_.to_str() +
" which is not a successor of last masterchain block " + last_mc_id_.to_str());
return;
}
last_mc_id_ = mc_blkid_;
last_mc_state_ = std::move(new_state);
find_known_ancestors();
}
void AcceptBlockQuery::find_known_ancestors() {
VLOG(VALIDATOR_DEBUG) << "find_known_ancestors()";
prev_mc_blkid_ = mc_blkid_;
auto config = last_mc_state_->get_config();
CHECK(config);
auto shard = ton::ShardIdFull(id_);
auto ancestor = config->get_shard_hash(shard, false);
if (ancestor.is_null()) {
ancestor = config->get_shard_hash(ton::shard_child(shard, true));
auto ancestor2 = config->get_shard_hash(ton::shard_child(shard, false));
if (ancestor.is_null() || ancestor2.is_null()) {
VLOG(VALIDATOR_WARNING) << " cannot retrieve information about shard " + shard.to_str() +
" from masterchain block " + last_mc_id_.to_str() +
", skipping ShardTopBlockDescr creation";
if (last_mc_id_.id.seqno <= mc_blkid_.id.seqno) {
fatal_error(" cannot retrieve information about shard "s + shard.to_str() + " from masterchain block " +
last_mc_id_.to_str());
return;
}
written_block_next();
return;
}
VLOG(VALIDATOR_DEBUG) << "found two ancestors: " << ancestor->blk_.to_str() << " and " << ancestor2->blk_.to_str();
ancestors_seqno_ = std::max(ancestor->blk_.id.seqno, ancestor2->blk_.id.seqno);
ancestors_.emplace_back(std::move(ancestor));
ancestors_.emplace_back(std::move(ancestor2));
} else if (ancestor->shard() == shard) {
VLOG(VALIDATOR_DEBUG) << "found one regular ancestor " << ancestor->blk_.to_str();
ancestors_seqno_ = ancestor->seqno();
ancestors_.emplace_back(std::move(ancestor));
} else if (ton::shard_is_parent(ancestor->shard(), shard)) {
VLOG(VALIDATOR_DEBUG) << "found one parent ancestor " << ancestor->blk_.to_str();
ancestors_seqno_ = ancestor->seqno();
ancestors_.emplace_back(std::move(ancestor));
ancestors_split_ = true;
} else {
VLOG(VALIDATOR_WARNING) << " cannot retrieve information about shard " + shard.to_str() +
" from masterchain block " + last_mc_id_.to_str() +
", skipping ShardTopBlockDescr creation";
if (last_mc_id_.id.seqno <= mc_blkid_.id.seqno || ancestor->seqno() <= id_.id.seqno) {
fatal_error(" cannot retrieve information about shard "s + shard.to_str() + " from masterchain block " +
last_mc_id_.to_str());
return;
}
written_block_next();
return;
}
if (ancestors_seqno_ >= id_.id.seqno) {
VLOG(VALIDATOR_WARNING) << "skipping ShardTopBlockDescr creation for " << id_.to_str() << " because a newer block "
<< ancestors_.at(0)->blk_.to_str() << " is already present in masterchain block "
<< last_mc_id_.to_str();
written_block_next();
return;
}
if (id_.id.seqno > ancestors_seqno_ + 8) {
fatal_error("cannot accept shardchain block "s + id_.to_str() +
" because it requires including a chain of more than eight new shardchain blocks");
return;
}
if (id_.id.seqno == ancestors_seqno_ + 1) {
create_topshard_blk_descr();
return;
}
CHECK(prev_.size() == 1);
require_proof_link(prev_[0]);
}
void AcceptBlockQuery::require_proof_link(BlockIdExt id) {
VLOG(VALIDATOR_DEBUG) << "require_proof_link(" << id.to_str() << ")";
CHECK(ton::ShardIdFull(id) == ton::ShardIdFull(id_));
CHECK(id.id.seqno == id_.id.seqno - 1 - proof_links_.size());
td::actor::send_closure_later(manager_, &ValidatorManager::wait_block_proof_link_short, id, timeout_,
[SelfId = actor_id(this), id](td::Result<Ref<ProofLink>> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_proof_link, id,
R.move_as_ok());
});
}
bool AcceptBlockQuery::unpack_proof_link(BlockIdExt id, Ref<ProofLink> proof_link) {
auto res0 = vm::std_boc_deserialize(proof_link->data());
if (res0.is_error()) {
return fatal_error("cannot deserialize proof link for "s + id.to_str() + ": " + res0.move_as_error().to_string());
}
Ref<vm::Cell> proof_root = res0.move_as_ok();
block::gen::BlockProof::Record proof;
BlockIdExt proof_blk_id;
if (!(tlb::unpack_cell(proof_root, proof) &&
block::tlb::t_BlockIdExt.unpack(proof.proof_for.write(), proof_blk_id))) {
return false;
}
if (proof_blk_id != id) {
return fatal_error("block proof link is for another block: expected "s + id.to_str() + ", found " +
proof_blk_id.to_str());
}
auto virt_root = vm::MerkleProof::virtualize(proof.root, 1);
if (virt_root.is_null()) {
return fatal_error("block proof link for block "s + id.to_str() +
" does not contain a valid Merkle proof for the block header");
}
RootHash virt_hash{virt_root->get_hash().bits()};
if (virt_hash != id.root_hash) {
return fatal_error("block proof link for block "s + id.to_str() +
" contains a Merkle proof with incorrect root hash: expected " + id.root_hash.to_hex() +
", found " + virt_hash.to_hex());
}
bool after_split;
BlockIdExt mc_blkid;
auto res = block::unpack_block_prev_blk_try(virt_root, id, link_prev_, mc_blkid, after_split);
if (res.is_error()) {
return fatal_error("error in block header in proof link for "s + id.to_str() + ": " +
res.move_as_error().to_string());
}
if (mc_blkid.id.seqno > prev_mc_blkid_.id.seqno) {
return fatal_error("previous shardchain block "s + id.to_str() + " refers to a newer masterchain block " +
mc_blkid.id.to_str() + " than that referred to by the next one: " + prev_mc_blkid_.id.to_str());
} else if (mc_blkid.id.seqno < prev_mc_blkid_.id.seqno) {
if (!last_mc_state_->check_old_mc_block_id(mc_blkid)) {
return fatal_error(
"previous shardchain block "s + id.to_str() + " refers to masterchain block " + mc_blkid.id.to_str() +
" which is not an ancestor of that referred to by the next one: " + prev_mc_blkid_.id.to_str());
}
prev_mc_blkid_ = mc_blkid;
} else if (mc_blkid != prev_mc_blkid_) {
return fatal_error("previous shardchain block "s + id.to_str() + " refers to masterchain block " +
mc_blkid.id.to_str() +
" with the same height as, but distinct from that referred to by the next shardchain block: " +
prev_mc_blkid_.id.to_str());
}
try {
block::gen::Block::Record block;
if (!(tlb::unpack_cell(virt_root, block) && block::gen::t_ValueFlow.force_validate_ref(block.value_flow))) {
return fatal_error("block proof link for block "s + id.to_str() + " does not contain value flow information");
}
} catch (vm::VmError& err) {
return fatal_error("error unpacking proof link for block "s + id.to_str() + " : " + err.get_msg());
} catch (vm::VmVirtError& err) {
return fatal_error("virtualization error unpacking proof link for block "s + id.to_str() + " : " + err.get_msg());
}
proof_roots_.push_back(std::move(proof.root));
return true;
}
void AcceptBlockQuery::got_proof_link(BlockIdExt id, Ref<ProofLink> proof) {
VLOG(VALIDATOR_DEBUG) << "got_proof_link(" << id.to_str() << ")";
CHECK(proof.not_null());
proof_links_.push_back(proof);
if (!unpack_proof_link(id, std::move(proof))) {
fatal_error("cannot unpack proof link for "s + id.to_str());
return;
}
if (id.id.seqno == ancestors_seqno_ + 1) {
// first link in chain
if (ancestors_.size() != link_prev_.size() || ancestors_[0]->blk_ != link_prev_[0] ||
(ancestors_.size() == 2 && ancestors_[1]->blk_ != link_prev_[1])) {
fatal_error("invalid first link at block "s + id.to_str() + " for shardchain block " + id_.to_str());
return;
}
create_topshard_blk_descr();
} else {
CHECK(id.id.seqno > ancestors_seqno_);
// intermediate link
if (link_prev_.size() != 1 || ton::ShardIdFull(link_prev_[0].id) != ton::ShardIdFull(id_) ||
link_prev_[0].id.seqno + 1 != id.id.seqno) {
fatal_error("invalid intermediate link at block "s + id.to_str() + " for shardchain block " + id_.to_str());
return;
}
require_proof_link(link_prev_[0]);
}
}
bool AcceptBlockQuery::create_top_shard_block_description() {
VLOG(VALIDATOR_DEBUG) << "create_top_shard_block_description()";
CHECK(proof_roots_.size() == proof_links_.size() + 1);
int n = (int)proof_roots_.size();
CHECK(n <= 8);
Ref<vm::Cell> root;
for (int i = n - 1; i > 0; i--) {
vm::CellBuilder cb;
if (!(cb.store_ref_bool(proof_roots_[i]) && (root.is_null() || cb.store_ref_bool(root)) && cb.finalize_to(root))) {
return fatal_error("error serializing ProofChain");
}
}
vm::CellBuilder cb;
Ref<vm::Cell> td_cell;
if (!(cb.store_long_bool(0xd5, 8) // top_block_descr#d5
&& block::tlb::t_BlockIdExt.pack(cb, id_) // proof_for:BlockIdExt
&& cb.store_bool_bool(true) // signatures:(Maybe
&& cb.store_ref_bool(signatures_cell_) // ^BlockSignatures)
&& cb.store_long_bool(n, 8) // len:(## 8)
&& n <= 8 // { len <= 8 }
&& cb.store_ref_bool(proof_roots_[0]) // chain:(ProofChain len)
&& (root.is_null() || cb.store_ref_bool(std::move(root))) && cb.finalize_to(td_cell))) {
return fatal_error("cannot serialize ShardTopBlockDescription for the newly-accepted block "s + id_.to_str());
}
if (false) {
// debug output
std::cerr << "new ShardTopBlockDescription: ";
block::gen::t_TopBlockDescr.print_ref(std::cerr, td_cell);
vm::load_cell_slice(td_cell).print_rec(std::cerr);
}
if (!block::gen::t_TopBlockDescr.validate_ref(td_cell)) {
block::gen::t_TopBlockDescr.print_ref(std::cerr, td_cell);
vm::load_cell_slice(td_cell).print_rec(std::cerr);
return fatal_error("just created ShardTopBlockDescription for "s + id_.to_str() + " is invalid");
}
auto res = vm::std_boc_serialize(td_cell, 0);
if (res.is_error()) {
return fatal_error("cannot serialize a ShardTopBlockDescription object for "s + id_.to_str() + ": " +
res.move_as_error().to_string());
}
top_block_descr_data_ = res.move_as_ok();
VLOG(VALIDATOR_DEBUG) << "create_top_shard_block_description() : end";
return true;
}
void AcceptBlockQuery::create_topshard_blk_descr() {
VLOG(VALIDATOR_DEBUG) << "create_topshard_blk_descr()";
// generate top shard block description
if (!create_top_shard_block_description()) {
fatal_error("cannot generate top shard block description for "s + id_.to_str());
return;
}
CHECK(top_block_descr_data_.size());
td::actor::create_actor<ValidateShardTopBlockDescr>(
"topshardfetchchk", std::move(top_block_descr_data_), last_mc_id_, BlockHandle{}, last_mc_state_, manager_,
timeout_, is_fake_,
[SelfId = actor_id(this)](td::Result<Ref<ShardTopBlockDescription>> R) {
td::actor::send_closure_later(SelfId, &AcceptBlockQuery::top_block_descr_validated, std::move(R));
})
.release();
}
void AcceptBlockQuery::top_block_descr_validated(td::Result<Ref<ShardTopBlockDescription>> R) {
VLOG(VALIDATOR_DEBUG) << "top_block_descr_validated()";
if (R.is_error()) {
VLOG(VALIDATOR_WARNING) << "error validating newly-created ShardTopBlockDescr for " << id_.to_str() << ": "
<< R.move_as_error().to_string();
} else {
top_block_descr_ = R.move_as_ok();
CHECK(top_block_descr_.not_null());
td::actor::send_closure_later(manager_, &ValidatorManager::send_top_shard_block_description, top_block_descr_);
}
written_block_next();
}
void AcceptBlockQuery::written_block_next() {
VLOG(VALIDATOR_DEBUG) << "written_block_next()";
if (handle_->need_flush()) {
handle_->flush(manager_, handle_, [SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_info_2);
});
} else {
written_block_info_2();
}
}
void AcceptBlockQuery::written_block_info_2() {
VLOG(VALIDATOR_DEBUG) << "written_block_info_2()";
if (handle_->id().is_masterchain()) {
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::applied);
});
run_apply_block_query(handle_->id(), data_, manager_, timeout_, std::move(P));
} else {
applied();
}
}
void AcceptBlockQuery::applied() {
if (!send_broadcast_) {
finish_query();
return;
}
BlockBroadcast b;
b.data = data_->data();
b.block_id = id_;
std::vector<BlockSignature> sigs;
if (!is_fake_) {
for (auto& v : signatures_->signatures()) {
sigs.emplace_back(BlockSignature{v.node, v.signature.clone()});
}
}
b.signatures = std::move(sigs);
b.catchain_seqno = validator_set_->get_catchain_seqno();
b.validator_set_hash = validator_set_->get_validator_set_hash();
if (is_masterchain()) {
b.proof = proof_->data();
} else {
b.proof = proof_link_->data();
}
// do not wait for answer
td::actor::send_closure_later(manager_, &ValidatorManager::send_block_broadcast, std::move(b));
finish_query();
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,141 @@
/*
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 "ton/ton-types.h"
#include "ton/ton-shard.h"
#include "interfaces/validator-manager.h"
#include "validator-set.hpp"
#include "signature-set.hpp"
#include "shard.hpp"
namespace ton {
namespace validator {
using td::Ref;
/*
*
* block data (if not given) can be obtained from:
* db as part of collated block
* db as block
* net
* must write block data, block signatures and block state
* initialize prev, before_split, after_merge
* for masterchain write block proof and set next for prev block
* for masterchain run new_block callback
*
*/
class AcceptBlockQuery : public td::actor::Actor {
public:
struct IsFake {};
AcceptBlockQuery(BlockIdExt id, td::Ref<BlockData> data, std::vector<BlockIdExt> prev,
td::Ref<ValidatorSet> validator_set, td::Ref<BlockSignatureSet> signatures, bool send_broadcast,
td::actor::ActorId<ValidatorManager> manager, td::Promise<td::Unit> promise);
AcceptBlockQuery(IsFake fake, BlockIdExt id, td::Ref<BlockData> data, std::vector<BlockIdExt> prev,
td::Ref<ValidatorSet> validator_set, td::actor::ActorId<ValidatorManager> manager,
td::Promise<td::Unit> promise);
private:
static constexpr td::uint32 priority() {
return 2;
}
void abort_query(td::Status reason);
void finish_query();
void alarm() override;
void start_up() override;
void written_block_data();
void written_block_signatures();
void got_block_handle(BlockHandle handle);
void written_block_info();
void got_block_data(td::Ref<BlockData> data);
void got_prev_state(td::Ref<ShardState> state);
void written_state(td::Ref<ShardState> state);
void written_block_proof();
void got_last_mc_block(std::pair<td::Ref<MasterchainState>, BlockIdExt> last);
void got_mc_state(Ref<ShardState> res);
void find_known_ancestors();
void require_proof_link(BlockIdExt id);
void got_proof_link(BlockIdExt id, Ref<ProofLink> proof);
bool create_top_shard_block_description();
void create_topshard_blk_descr();
void top_block_descr_validated(td::Result<Ref<ShardTopBlockDescription>> R);
void written_block_next();
void written_block_info_2();
void applied();
private:
BlockIdExt id_;
Ref<BlockData> data_;
std::vector<BlockIdExt> prev_;
Ref<ValidatorSetQ> validator_set_;
Ref<BlockSignatureSetQ> signatures_;
bool is_fake_;
bool send_broadcast_;
bool ancestors_split_{false}, is_key_block_{false};
td::Timestamp timeout_ = td::Timestamp::in(600.0);
td::actor::ActorId<ValidatorManager> manager_;
td::Promise<td::Unit> promise_;
FileHash signatures_hash_;
BlockHandle handle_;
Ref<Proof> proof_;
Ref<ProofLink> proof_link_;
Ref<ShardState> state_;
Ref<vm::Cell> block_root_;
LogicalTime lt_;
UnixTime created_at_;
RootHash state_keep_old_hash_, state_old_hash_, state_hash_;
BlockIdExt mc_blkid_, prev_mc_blkid_;
Ref<MasterchainStateQ> last_mc_state_;
BlockIdExt last_mc_id_;
std::vector<Ref<block::McShardHash>> ancestors_;
BlockSeqno ancestors_seqno_;
std::vector<Ref<ProofLink>> proof_links_;
std::vector<Ref<vm::Cell>> proof_roots_;
std::vector<BlockIdExt> link_prev_;
Ref<vm::Cell> signatures_cell_;
td::BufferSlice top_block_descr_data_;
Ref<ShardTopBlockDescription> top_block_descr_;
td::PerfWarningTimer perf_timer_{"acceptblock", 0.1};
bool fatal_error(std::string msg, int code = -666);
static bool check_send_error(td::actor::ActorId<AcceptBlockQuery> SelfId, td::Status error);
template <typename T>
static bool check_send_error(td::actor::ActorId<AcceptBlockQuery> SelfId, td::Result<T>& res) {
return res.is_error() && check_send_error(std::move(SelfId), res.move_as_error());
}
bool create_new_proof();
bool unpack_proof_link(BlockIdExt id, Ref<ProofLink> proof);
bool is_masterchain() const {
return id_.id.is_masterchain();
}
};
} // namespace validator
} // namespace ton

79
validator/impl/block.cpp Normal file
View file

@ -0,0 +1,79 @@
/*
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 "block.hpp"
namespace ton {
namespace validator {
using td::Ref;
BlockQ::BlockQ(const BlockQ& other) : data_(other.data_.clone()), id_(other.id_), root_(other.root_), boc_(other.boc_) {
}
BlockQ::BlockQ(BlockIdExt id, td::BufferSlice data) : data_(std::move(data)), id_(id) {
}
BlockQ::~BlockQ() {
}
td::Status BlockQ::init() {
if (root_.not_null()) {
return td::Status::OK();
}
if (data_.is_null()) {
return td::Status::Error(-668, "cannot initialize a block from an empty BufferSlice");
}
vm::StaticBagOfCellsDbLazy::Options options;
options.check_crc32c = true;
auto res = vm::StaticBagOfCellsDbLazy::create(vm::BufferSliceBlobView::create(data_.clone()), options);
if (res.is_error()) {
return res.move_as_error();
}
boc_ = res.move_as_ok();
auto rc = boc_->get_root_count();
if (rc.is_error()) {
return rc.move_as_error();
}
if (rc.move_as_ok() != 1) {
return td::Status::Error(-668, "shardchain block BoC is invalid");
}
auto res3 = boc_->get_root_cell(0);
if (res3.is_error()) {
return res3.move_as_error();
}
root_ = res3.move_as_ok();
if (root_.is_null()) {
return td::Status::Error(-668, "cannot extract root cell out of a shardchain block BoC");
}
return td::Status::OK();
}
td::Result<td::Ref<BlockQ>> BlockQ::create(BlockIdExt id, td::BufferSlice data) {
td::Ref<BlockQ> res{true, id, std::move(data)};
auto err = res.unique_write().init();
if (err.is_error()) {
return err.move_as_error();
} else {
return std::move(res);
}
}
} // namespace validator
} // namespace ton

62
validator/impl/block.hpp Normal file
View file

@ -0,0 +1,62 @@
/*
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 "ton/ton-types.h"
#include "interfaces/block.h"
#include "vm/db/StaticBagOfCellsDb.h"
namespace ton {
namespace validator {
class BlockQ : public BlockData {
private:
td::BufferSlice data_;
BlockIdExt id_;
td::Ref<vm::Cell> root_;
std::shared_ptr<vm::StaticBagOfCellsDb> boc_;
//
BlockQ(const BlockQ& other);
td::Status init();
public:
BlockQ(BlockIdExt id, td::BufferSlice data);
BlockQ(BlockQ&& other) = default;
~BlockQ() override;
td::BufferSlice data() const override {
return data_.clone();
}
FileHash file_hash() const override {
return id_.file_hash;
}
BlockIdExt block_id() const override {
return id_;
}
td::Ref<vm::Cell> root_cell() const override {
return root_;
}
BlockQ* make_copy() const override {
return new BlockQ(*this);
}
static td::Result<td::Ref<BlockQ>> create(BlockIdExt id, td::BufferSlice data);
};
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,478 @@
/*
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 "check-proof.hpp"
#include "adnl/utils.hpp"
#include "ton/ton-io.hpp"
#include "ton/ton-tl.hpp"
#include "fabric.h"
#include "signature-set.hpp"
#include "validator-set.hpp"
#include "shard.hpp"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "vm/boc.h"
#include "vm/cells/MerkleProof.h"
#include "validator/invariants.hpp"
namespace ton {
namespace validator {
using namespace std::literals::string_literals;
void CheckProof::alarm() {
abort_query(td::Status::Error(ErrorCode::notready, "timeout"));
}
void CheckProof::abort_query(td::Status reason) {
if (promise_) {
VLOG(VALIDATOR_WARNING) << "aborting check proof for " << id_ << " query: " << reason;
promise_.set_error(std::move(reason));
}
stop();
}
bool CheckProof::fatal_error(td::Status error) {
abort_query(std::move(error));
return false;
}
bool CheckProof::fatal_error(std::string err_msg, int err_code) {
abort_query(td::Status::Error(err_code, err_msg));
return false;
}
void CheckProof::finish_query() {
if (skip_check_signatures_) {
// TODO: check other invariants
} else if (is_proof()) {
ValidatorInvariants::check_post_check_proof(handle_);
} else {
ValidatorInvariants::check_post_check_proof_link(handle_);
}
if (promise_) {
VLOG(VALIDATOR_DEBUG) << "checked proof for " << handle_->id();
promise_.set_result(handle_);
}
stop();
}
bool CheckProof::check_send_error(td::actor::ActorId<CheckProof> SelfId, td::Status error) {
if (error.is_error()) {
td::actor::send_closure(std::move(SelfId), &CheckProof::abort_query, std::move(error));
return true;
} else {
return false;
}
}
bool CheckProof::init_parse(bool is_aux) {
block::gen::BlockProof::Record proof;
BlockIdExt proof_blk_id;
if (!(tlb::unpack_cell(is_aux ? old_proof_root_ : proof_root_, proof) &&
block::tlb::t_BlockIdExt.unpack(proof.proof_for.write(), proof_blk_id))) {
return false;
}
BlockIdExt decl_id = (is_aux ? old_proof_ : proof_)->block_id();
if (proof_blk_id != decl_id) {
return fatal_error("block proof is for another block: declared "s + decl_id.to_str() + ", found " +
proof_blk_id.to_str());
}
if (!is_aux) {
if (proof_blk_id != id_) {
return fatal_error("block proof is for another block: expected "s + id_.to_str() + ", found " +
proof_blk_id.to_str());
}
if (!is_masterchain() && is_proof()) {
return fatal_error("have a proof for non-masterchain block "s + id_.to_str());
}
} else {
key_id_ = proof_blk_id;
if (!is_masterchain()) {
return fatal_error("cannot verify non-masterchain block "s + id_.to_str() +
" using previous key masterchain block");
}
if (!key_id_.is_masterchain()) {
return fatal_error("auxiliary key block "s + key_id_.to_str() + " does not belong to the masterchain");
}
if (key_id_.seqno() != prev_key_seqno_) {
return fatal_error(
PSTRING() << "cannot verify newer block " << id_.to_str() << " using key block " << key_id_.to_str()
<< " because the newer block declares different previous key block seqno " << prev_key_seqno_);
}
if (key_id_.seqno() >= id_.seqno()) {
return fatal_error("cannot verify block "s + id_.to_str() + " using key block " + key_id_.to_str() +
" with larger or equal seqno");
}
}
auto keep_cc_seqno = catchain_seqno_;
auto keep_utime = created_at_;
Ref<vm::Cell> sig_root = proof.signatures->prefetch_ref();
if (sig_root.not_null()) {
vm::CellSlice cs{vm::NoVmOrd(), sig_root};
bool have_sig;
if (!(cs.fetch_ulong(8) == 0x11 // block_signatures#11
&& cs.fetch_uint_to(32, validator_hash_) // validator_set_hash:uint32
&& cs.fetch_uint_to(32, catchain_seqno_) // catchain_seqno:uint32
&& cs.fetch_uint_to(32, sig_count_) // sig_count:uint32
&& cs.fetch_uint_to(64, sig_weight_) // sig_weight:uint64
&& cs.fetch_bool_to(have_sig) && have_sig == (sig_count_ > 0) &&
cs.size_ext() == ((unsigned)have_sig << 16))) {
return fatal_error("cannot parse BlockSignatures");
}
sig_root_ = cs.prefetch_ref();
if (!proof_blk_id.is_masterchain()) {
return fatal_error("invalid ProofLink for non-masterchain block "s + proof_blk_id.to_str() +
" with validator signatures present");
}
} else {
validator_hash_ = 0;
catchain_seqno_ = 0;
sig_count_ = 0;
sig_weight_ = 0;
sig_root_.clear();
}
auto virt_root = vm::MerkleProof::virtualize(proof.root, 1);
if (virt_root.is_null()) {
return fatal_error("block proof for block "s + proof_blk_id.to_str() +
" does not contain a valid Merkle proof for the block header");
}
RootHash virt_hash{virt_root->get_hash().bits()};
if (virt_hash != proof_blk_id.root_hash) {
return fatal_error("block proof for block "s + proof_blk_id.to_str() +
" contains a Merkle proof with incorrect root hash: expected " +
proof_blk_id.root_hash.to_hex() + ", found " + virt_hash.to_hex());
}
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
block::gen::ExtBlkRef::Record mcref; // _ ExtBlkRef = BlkMasterInfo;
ShardIdFull shard;
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(blk.info, info) && !info.version &&
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard) && !info.vert_seq_no &&
block::gen::BlkPrevInfo{info.after_merge}.validate_ref(info.prev_ref) &&
block::gen::t_ValueFlow.force_validate_ref(blk.value_flow) &&
(!info.not_master || tlb::unpack_cell(info.master_ref, mcref)))) {
return fatal_error("cannot unpack block header in the Merkle proof");
}
BlockId blk_id{shard, (unsigned)info.seq_no};
if (blk_id != proof_blk_id.id) {
return fatal_error("block header in the Merkle proof corresponds to another block id: expected "s +
proof_blk_id.id.to_str() + ", found " + blk_id.to_str());
}
if (info.not_master != !shard.is_masterchain()) {
return fatal_error("block has invalid not_master flag in its (Merkelized) header");
}
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
&& upd_cs.size_ext() == 0x20228)) {
return fatal_error("invalid Merkle update in block");
}
state_old_hash_ = upd_cs.prefetch_ref(0)->get_hash(0).bits();
state_hash_ = upd_cs.prefetch_ref(1)->get_hash(0).bits();
lt_ = info.end_lt;
created_at_ = info.gen_utime;
after_merge_ = info.after_merge;
before_split_ = info.before_split;
// after_split_ = info.after_split;
want_merge_ = info.want_merge;
want_split_ = info.want_split;
is_key_block_ = info.key_block;
prev_key_seqno_ = info.prev_key_block_seqno;
{
auto res = block::unpack_block_prev_blk_ext(virt_root, proof_blk_id, prev_, mc_blkid_, after_split_);
if (res.is_error()) {
return fatal_error(res.message().str());
}
}
CHECK(after_split_ == info.after_split);
if (shard.is_masterchain() && (after_merge_ | before_split_ | after_split_)) {
return fatal_error("block header declares split/merge for a masterchain block");
}
if (after_merge_ && after_split_) {
return fatal_error("a block cannot be both after merge and after split at the same time");
}
int shard_pfx_len = ton::shard_prefix_length(shard.shard);
if (after_split_ && !shard_pfx_len) {
return fatal_error("a block with empty shard prefix cannot be after split");
}
if (after_merge_ && shard_pfx_len >= 60) {
return fatal_error("a block split 60 times cannot be after merge");
}
if (is_key_block_ && !shard.is_masterchain()) {
return fatal_error("a non-masterchain block cannot be a key block");
}
if (is_key_block_ && !is_aux) {
// visit validator-set related fields in key blocks
block::gen::BlockExtra::Record extra;
block::gen::McBlockExtra::Record mc_extra;
if (!(tlb::unpack_cell(std::move(blk.extra), extra) && tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra) &&
mc_extra.key_block && mc_extra.config.not_null())) {
return fatal_error("cannot unpack extra header of key masterchain block "s + blk_id.to_str());
}
auto cfg = block::Config::unpack_config(std::move(mc_extra.config));
if (cfg.is_error()) {
return fatal_error("cannot extract configuration from extra header of key masterchain block "s + blk_id.to_str() +
" : " + cfg.move_as_error().to_string());
}
auto res = cfg.move_as_ok()->visit_validator_params();
if (res.is_error()) {
return fatal_error("cannot extract validator set configuration from extra header of key masterchain block "s +
blk_id.to_str() + " : " + res.to_string());
}
}
if (is_aux) {
if (!is_key_block_) {
return fatal_error("auxiliary proof passed for verification of the proof of block "s + id_.to_str() +
" belongs to non-key block " + key_id_.to_str());
}
auto config_r = block::Config::extract_from_key_block(virt_root, block::Config::needValidatorSet);
if (config_r.is_error()) {
return fatal_error(config_r.move_as_error());
}
auto config = config_r.move_as_ok();
if (!config) {
return fatal_error("cannot extract configuration from previous key block " + key_id_.to_str());
}
ValidatorSetCompute vs_comp;
auto res = vs_comp.init(config.get());
if (res.is_error()) {
return fatal_error(std::move(res));
}
vset_ = vs_comp.get_validator_set(id_.shard_full(), keep_utime, keep_cc_seqno);
if (vset_.is_null()) {
return fatal_error("cannot extract current validator set for block "s + id_.to_str() +
" from previous key block " + key_id_.to_str());
}
}
return true;
}
void CheckProof::start_up() {
alarm_timestamp() = timeout_;
auto res = vm::std_boc_deserialize(proof_->data());
if (res.is_error()) {
abort_query(res.move_as_error());
return;
}
proof_root_ = res.move_as_ok();
if (mode_ == m_relproof) {
CHECK(old_proof_.not_null());
res = vm::std_boc_deserialize(old_proof_->data());
if (res.is_error()) {
abort_query(res.move_as_error());
return;
}
old_proof_root_ = res.move_as_ok();
}
try {
if (!init_parse()) {
fatal_error("cannot parse proof for block "s + id_.to_str());
return;
}
if (mode_ == m_relproof) {
if (!init_parse(true)) {
fatal_error("cannot parse proof of previous key block "s + key_id_.to_str());
return;
}
if (!init_parse()) {
fatal_error("cannot parse proof for block "s + id_.to_str());
return;
}
}
} catch (vm::VmError err) {
fatal_error("error while processing Merkle proof: "s + err.get_msg());
return;
} catch (vm::VmVirtError err) {
fatal_error("error while processing Merkle proof: "s + err.get_msg());
return;
}
td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, id_,
true, [SelfId = actor_id(this)](td::Result<BlockHandle> R) {
if (R.is_error()) {
td::actor::send_closure(SelfId, &CheckProof::abort_query, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &CheckProof::got_block_handle, R.move_as_ok());
}
});
}
void CheckProof::got_block_handle(BlockHandle handle) {
handle_ = std::move(handle);
CHECK(handle_);
if (!is_proof() || skip_check_signatures_) {
got_block_handle_2(handle_);
return;
}
if (handle_->inited_proof()) {
finish_query();
return;
}
CHECK(is_proof() && prev_.size() == 1);
if (mode_ == m_relproof) {
CHECK(vset_.not_null());
check_signatures(vset_);
return;
}
if (mode_ == m_relstate) {
process_masterchain_state();
return;
}
td::actor::send_closure(manager_, &ValidatorManager::wait_block_state_short, prev_[0], priority(),
timeout_, [SelfId = actor_id(this)](td::Result<td::Ref<ShardState>> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &CheckProof::got_masterchain_state,
td::Ref<MasterchainState>{R.move_as_ok()});
});
}
void CheckProof::got_masterchain_state(td::Ref<MasterchainState> state) {
CHECK(is_proof());
state_ = std::move(state);
if (state_->root_hash() != state_old_hash_) {
fatal_error(PSTRING() << "invalid previous state hash in proof: expected " << state_->root_hash().to_hex()
<< ", found in update " << state_old_hash_.to_hex());
return;
}
vset_ = state_->get_validator_set(id_.shard_full());
check_signatures(vset_);
}
void CheckProof::process_masterchain_state() {
CHECK(is_proof());
CHECK(state_.not_null());
auto id = state_->get_block_id();
if (!id.is_masterchain()) {
fatal_error("cannot check a masterchain block proof starting from non-masterchain state for "s + id.to_str());
return;
}
if (!is_masterchain()) {
fatal_error("cannot check a non-masterchain block proof starting from masterchain state");
return;
}
if (id.seqno() < prev_key_seqno_) {
fatal_error(PSTRING() << "cannot check masterchain block proof for " << id_.to_str()
<< " starting from masterchain state for " << id.to_str()
<< " older than the previous key block with seqno " << prev_key_seqno_);
return;
}
if (id.seqno() >= id_.seqno()) {
fatal_error("cannot check masterchain block proof for "s + id_.to_str() +
" starting from newer masterchain state for " + id.to_str());
return;
}
auto state_q = Ref<MasterchainStateQ>(state_);
CHECK(state_q.not_null());
vset_ = state_q->get_validator_set(id_.shard_full(), created_at_, catchain_seqno_);
check_signatures(vset_);
}
void CheckProof::check_signatures(Ref<ValidatorSet> s) {
if (s->get_catchain_seqno() != catchain_seqno_) {
abort_query(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad validator catchain seqno: expected "
<< s->get_catchain_seqno() << ", found "
<< catchain_seqno_));
return;
}
if (s->get_validator_set_hash() != validator_hash_) {
abort_query(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "bad validator set hash: expected "
<< s->get_validator_set_hash() << ", found "
<< validator_hash_));
return;
}
if (sig_root_.is_null()) {
fatal_error("no block signatures present in proof to check");
return;
}
auto sigs = BlockSignatureSetQ::fetch(sig_root_);
if (sigs.is_null()) {
fatal_error("cannot deserialize signature set");
return;
}
if (sigs->signatures().size() != sig_count_) {
fatal_error(PSTRING() << "signature count mismatch: present " << sigs->signatures().size() << ", declared "
<< sig_count_);
return;
}
auto S = s->check_signatures(id_.root_hash, id_.file_hash, sigs);
if (S.is_error()) {
abort_query(S.move_as_error());
return;
}
auto s_weight = S.move_as_ok();
if (s_weight != sig_weight_) {
fatal_error(PSTRING() << "total signature weight mismatch: declared " << sig_weight_ << ", actual " << s_weight);
return;
}
sig_ok_ = true;
if (handle_) {
got_block_handle_2(handle_);
} else {
td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, id_,
true, [SelfId = actor_id(this)](td::Result<BlockHandle> R) {
check_send_error(SelfId, R) ||
td::actor::send_closure_bool(SelfId, &CheckProof::got_block_handle_2, R.move_as_ok());
});
}
}
void CheckProof::got_block_handle_2(BlockHandle handle) {
handle_ = std::move(handle);
handle_->set_split(before_split_);
handle_->set_merge(after_merge_);
handle_->set_is_key_block(is_key_block_);
handle_->set_state_root_hash(state_hash_);
handle_->set_logical_time(lt_);
handle_->set_unix_time(created_at_);
for (auto &prev : prev_) {
handle_->set_prev(prev);
}
auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Unit> R) {
check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &CheckProof::finish_query);
});
if (skip_check_signatures_) {
// do not save proof if we skipped signatures
handle_->flush(manager_, handle_, std::move(P));
} else if (is_proof()) {
auto proof = Ref<Proof>(proof_);
CHECK(proof.not_null());
CHECK(sig_ok_);
td::actor::send_closure_later(manager_, &ValidatorManager::set_block_proof, handle_, std::move(proof),
std::move(P));
} else {
CHECK(proof_.not_null());
td::actor::send_closure_later(manager_, &ValidatorManager::set_block_proof_link, handle_, proof_, std::move(P));
}
}
} // namespace validator
} // namespace ton

View 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 "td/actor/actor.h"
#include "interfaces/block-handle.h"
#include "interfaces/validator-manager.h"
namespace ton {
namespace validator {
using td::Ref;
/*
*
* check block proof
* write proof
* initialize prev, before_split, after_merge
* initialize prev's next
*
*/
class CheckProof : public td::actor::Actor {
public:
CheckProof(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<BlockHandle> promise, bool skip_check_signatures, td::Ref<ProofLink> prev_key_proof = {})
: mode_(prev_key_proof.is_null() ? m_normal : m_relproof)
, id_(id)
, proof_(std::move(proof))
, old_proof_(std::move(prev_key_proof))
, manager_(manager)
, timeout_(timeout)
, promise_(std::move(promise))
, skip_check_signatures_(skip_check_signatures) {
}
CheckProof(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<BlockHandle> promise, bool skip_check_signatures, td::Ref<MasterchainState> known_state)
: mode_(m_relstate)
, id_(id)
, proof_(std::move(proof))
, manager_(manager)
, timeout_(timeout)
, promise_(std::move(promise))
, state_(std::move(known_state))
, skip_check_signatures_(skip_check_signatures) {
}
CheckProof(BlockIdExt id, td::Ref<ProofLink> proof_link, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<BlockHandle> promise)
: mode_(m_prooflink)
, id_(id)
, proof_(std::move(proof_link))
, manager_(manager)
, timeout_(timeout)
, promise_(std::move(promise)) {
}
private:
static constexpr td::uint32 priority() {
return 2;
}
void abort_query(td::Status reason);
void finish_query();
void alarm() override;
void start_up() override;
void got_block_handle(BlockHandle handle);
void got_masterchain_state(td::Ref<MasterchainState> state);
void process_masterchain_state();
void check_signatures(Ref<ValidatorSet> vset);
void got_block_handle_2(BlockHandle handle);
private:
enum { m_normal, m_relproof, m_relstate, m_prooflink } mode_{m_normal};
BlockIdExt id_, key_id_;
td::Ref<ProofLink> proof_, old_proof_;
td::actor::ActorId<ValidatorManager> manager_;
td::Timestamp timeout_;
td::Promise<BlockHandle> promise_;
BlockHandle handle_;
td::Ref<MasterchainState> state_;
td::Ref<ValidatorSet> vset_;
Ref<vm::Cell> proof_root_, sig_root_, old_proof_root_;
RootHash state_hash_, state_old_hash_;
LogicalTime lt_;
UnixTime created_at_;
bool after_merge_, after_split_, before_split_, want_merge_, want_split_, is_key_block_;
BlockIdExt mc_blkid_;
std::vector<BlockIdExt> prev_;
BlockSeqno prev_key_seqno_{~0U};
CatchainSeqno catchain_seqno_{0};
td::uint32 validator_hash_{0};
td::uint32 sig_count_;
ValidatorWeight sig_weight_;
bool skip_check_signatures_{false};
bool sig_ok_{false};
td::PerfWarningTimer perf_timer_{"checkproof", 0.1};
static bool check_send_error(td::actor::ActorId<CheckProof> SelfId, td::Status error);
template <typename T>
static bool check_send_error(td::actor::ActorId<CheckProof> SelfId, td::Result<T>& res) {
return res.is_error() && check_send_error(std::move(SelfId), res.move_as_error());
}
bool fatal_error(std::string err_msg, int err_code = -666);
bool fatal_error(td::Status error);
bool init_parse(bool is_aux = false);
bool is_proof() const {
return mode_ != m_prooflink;
}
bool is_masterchain() const {
return id_.is_masterchain();
}
};
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,63 @@
/*
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 "validator/interfaces/validator-manager.h"
namespace ton {
namespace validator {
class CollateQuery : public td::actor::Actor {
public:
CollateQuery(ShardIdFull shard, td::uint32 min_ts, BlockIdExt min_masterchain_block_id, std::vector<BlockIdExt> prev,
td::Ref<ValidatorSet> validator_set, td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<BlockCandidate> promise);
CollateQuery(ShardIdFull shard, td::uint32 min_ts, BlockIdExt min_masterchain_block_id, ZeroStateIdExt zero_state_id,
td::Ref<ValidatorSet> validator_set, td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<BlockCandidate> promise);
void alarm() override;
void abort_query(td::Status reason);
void finish_query();
void start_up() override;
void got_prev_state(td::Ref<MasterchainState> state);
void written_block_data();
void written_block_collated_data();
private:
ShardIdFull shard_;
UnixTime min_ts_;
BlockIdExt min_masterchain_block_id_;
std::vector<BlockIdExt> prev_;
ZeroStateIdExt zero_state_id_;
td::Ref<ValidatorSet> validator_set_;
td::actor::ActorId<ValidatorManager> manager_;
td::Timestamp timeout_;
td::Promise<BlockCandidate> promise_;
BlockCandidate candidate_;
UnixTime ts_;
};
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,293 @@
/*
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 "interfaces/validator-manager.h"
#include "shard.hpp"
#include "top-shard-descr.hpp"
#include "common/refcnt.hpp"
#include "vm/cells.h"
#include "vm/dict.h"
#include "block/mc-config.h"
#include "block/block.h"
#include "block/transaction.h"
#include "block/block-db.h"
#include "block/output-queue-merger.h"
#include "vm/cells/MerkleProof.h"
#include "vm/cells/MerkleUpdate.h"
#include <map>
#include <queue>
namespace ton {
namespace validator {
using td::Ref;
class Collator final : public td::actor::Actor {
using LtCellRef = block::LtCellRef;
using NewOutMsg = block::NewOutMsg;
const ShardIdFull shard;
ton::BlockId new_id;
bool busy{false};
bool before_split_{false};
bool after_split_{false};
bool after_merge_{false};
bool want_split_{false};
bool want_merge_{false};
bool right_child_{false};
bool preinit_complete{false};
bool is_key_block_{false};
bool block_full_{false};
bool inbound_queues_empty_{false};
bool libraries_changed_{false};
UnixTime min_ts;
BlockIdExt min_mc_block_id;
std::vector<BlockIdExt> prev_blocks;
std::vector<Ref<ShardState>> prev_states;
std::vector<Ref<BlockData>> prev_block_data;
td::Bits256 created_by;
Ref<ValidatorSet> validator_set;
td::actor::ActorId<ValidatorManager> manager;
td::Timestamp timeout;
td::Promise<BlockCandidate> main_promise;
ton::BlockSeqno last_block_seqno{0};
ton::BlockSeqno prev_mc_block_seqno{0};
ton::BlockSeqno new_block_seqno{0};
ton::BlockSeqno prev_key_block_seqno_{0};
int step{0};
int pending{0};
static constexpr int max_ihr_msg_size = 65535; // 64k
static constexpr int max_ext_msg_size = 65535; // 64k
static constexpr int max_blk_sign_size = 65535; // 64k
static constexpr bool shard_splitting_enabled = true;
public:
Collator(ShardIdFull shard, td::uint32 min_ts, BlockIdExt min_masterchain_block_id, std::vector<BlockIdExt> prev,
Ref<ValidatorSet> validator_set, td::Bits256 collator_id, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<BlockCandidate> promise);
~Collator() override = default;
bool is_busy() const {
return busy;
}
ShardId get_shard() const {
return shard.shard;
}
WorkchainId workchain() const {
return shard.workchain;
}
static constexpr td::uint32 priority() {
return 2;
}
private:
void start_up() override;
void alarm() override;
int verbosity{3 * 0};
int verify{1};
ton::LogicalTime start_lt, max_lt;
ton::UnixTime now_;
ton::UnixTime prev_now_;
ton::UnixTime now_upper_limit_{~0U};
unsigned out_msg_queue_ops_{}, in_descr_cnt_{}, out_descr_cnt_{};
Ref<MasterchainStateQ> mc_state_;
Ref<BlockData> prev_mc_block;
BlockIdExt mc_block_id_;
Ref<vm::Cell> mc_state_root;
Ref<vm::Cell> mc_block_root;
td::BitArray<256> rand_seed_;
std::unique_ptr<block::ConfigInfo> config_;
std::unique_ptr<block::ShardConfig> shard_conf_;
std::map<BlockSeqno, Ref<MasterchainStateQ>> aux_mc_states_;
std::vector<block::McShardDescr> neighbors_;
std::unique_ptr<block::OutputQueueMerger> nb_out_msgs_;
std::vector<ton::StdSmcAddress> special_smcs;
std::vector<std::pair<ton::StdSmcAddress, int>> ticktock_smcs;
Ref<vm::Cell> prev_block_root;
Ref<vm::Cell> prev_state_root_, prev_state_root_pure_;
Ref<vm::Cell> state_root; // (new) shardchain state
Ref<vm::Cell> state_update; // Merkle update from prev_state_root to state_root
std::shared_ptr<vm::CellUsageTree> state_usage_tree_; // used to construct Merkle update
Ref<vm::CellSlice> new_config_params_;
ton::LogicalTime prev_state_lt_;
ton::LogicalTime shards_max_end_lt_{0};
ton::UnixTime prev_state_utime_;
int global_id_{0};
ton::BlockSeqno min_ref_mc_seqno_{~0U};
ton::BlockIdExt prev_key_block_;
ton::LogicalTime prev_key_block_lt_;
bool accept_msgs_{true};
bool shard_conf_adjusted_{false};
td::uint64 overload_history_{0}, underload_history_{0};
td::uint64 block_size_estimate_{};
Ref<block::WorkchainInfo> wc_info_;
std::vector<Ref<ShardTopBlockDescription>> shard_block_descr_;
std::vector<Ref<ShardTopBlockDescrQ>> used_shard_block_descr_;
std::unique_ptr<vm::Dictionary> shard_libraries_;
Ref<vm::Cell> mc_state_extra;
std::unique_ptr<vm::AugmentedDictionary> account_dict;
std::map<ton::StdSmcAddress, std::unique_ptr<block::Account>> accounts;
std::vector<block::StoragePrices> storage_prices_;
block::StoragePhaseConfig storage_phase_cfg_{&storage_prices_};
block::ComputePhaseConfig compute_phase_cfg_;
block::ActionPhaseConfig action_phase_cfg_;
td::RefInt256 masterchain_create_fee_, basechain_create_fee_;
std::unique_ptr<block::BlockLimits> block_limits_;
std::unique_ptr<block::BlockLimitStatus> block_limit_status_;
ton::LogicalTime min_new_msg_lt{std::numeric_limits<td::uint64>::max()};
block::CurrencyCollection total_balance_, old_total_balance_, total_validator_fees_;
block::CurrencyCollection global_balance_, old_global_balance_, import_created_{0};
Ref<vm::Cell> recover_create_msg_, mint_msg_;
Ref<vm::Cell> new_block;
block::ValueFlow value_flow_{block::ValueFlow::SetZero()};
std::unique_ptr<vm::AugmentedDictionary> fees_import_dict_;
std::map<ton::Bits256, int> ext_msg_map;
std::vector<std::pair<Ref<vm::Cell>, ExtMessage::Hash>> ext_msg_list_;
std::priority_queue<NewOutMsg, std::vector<NewOutMsg>, std::greater<NewOutMsg>> new_msgs;
std::pair<ton::LogicalTime, ton::Bits256> last_proc_int_msg_, first_unproc_int_msg_;
std::unique_ptr<vm::AugmentedDictionary> in_msg_dict, out_msg_dict, out_msg_queue_, sibling_out_msg_queue_;
std::unique_ptr<vm::Dictionary> ihr_pending;
std::shared_ptr<block::MsgProcessedUptoCollection> processed_upto_, sibling_processed_upto_;
std::vector<ExtMessage::Hash> bad_ext_msgs_, delay_ext_msgs_;
Ref<vm::Cell> shard_account_blocks_; // ShardAccountBlocks
std::vector<Ref<vm::Cell>> collated_roots_;
std::unique_ptr<ton::BlockCandidate> block_candidate;
td::PerfWarningTimer perf_timer_{"collate", 0.1};
//
block::Account* lookup_account(td::ConstBitPtr addr) const;
std::unique_ptr<block::Account> make_account_from(td::ConstBitPtr addr, Ref<vm::CellSlice> account,
Ref<vm::CellSlice> extra, bool force_create = false);
td::Result<block::Account*> make_account(td::ConstBitPtr addr, bool force_create = false);
td::actor::ActorId<Collator> get_self() {
return actor_id(this);
}
bool init_utime();
bool init_lt();
bool fetch_config_params();
bool fatal_error(td::Status error);
bool fatal_error(int err_code, std::string err_msg);
bool fatal_error(std::string err_msg, int err_code = -666);
void check_pending();
void after_get_mc_state(td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res);
void after_get_shard_state(int idx, td::Result<Ref<ShardState>> res);
void after_get_block_data(int idx, td::Result<Ref<BlockData>> res);
void after_get_shard_blocks(td::Result<std::vector<Ref<ShardTopBlockDescription>>> res);
bool preprocess_prev_mc_state();
bool register_mc_state(Ref<MasterchainStateQ> other_mc_state);
bool request_aux_mc_state(BlockSeqno seqno, Ref<MasterchainStateQ>& state);
Ref<MasterchainStateQ> get_aux_mc_state(BlockSeqno seqno) const;
void after_get_aux_shard_state(ton::BlockIdExt blkid, td::Result<Ref<ShardState>> res);
bool fix_one_processed_upto(block::MsgProcessedUpto& proc, const ton::ShardIdFull& owner);
bool fix_processed_upto(block::MsgProcessedUptoCollection& upto);
void got_neighbor_out_queue(int i, td::Result<Ref<MessageQueue>> res);
bool adjust_shard_config();
bool store_shard_fees(ShardIdFull shard, const block::CurrencyCollection& fees,
const block::CurrencyCollection& created);
bool store_shard_fees(Ref<block::McShardHash> descr);
bool import_new_shard_top_blocks();
bool init_block_limits();
bool compute_minted_amount(block::CurrencyCollection& to_mint);
bool init_value_create();
bool try_collate();
bool do_preinit();
bool do_collate();
bool create_special_transactions();
bool create_special_transaction(block::CurrencyCollection amount, Ref<vm::Cell> dest_addr_cell,
Ref<vm::Cell>& in_msg);
bool create_ticktock_transactions(int mask);
bool create_ticktock_transaction(const ton::StdSmcAddress& smc_addr, ton::LogicalTime req_start_lt, int mask);
Ref<vm::Cell> create_ordinary_transaction(Ref<vm::Cell> msg_root);
bool unpack_last_mc_state();
bool unpack_last_state();
bool unpack_merge_last_state();
bool unpack_one_last_state(block::ShardState& ss, BlockIdExt blkid, Ref<vm::Cell> prev_state_root);
bool split_last_state(block::ShardState& ss);
bool import_shard_state_data(block::ShardState& ss);
bool add_trivial_neighbor();
bool add_trivial_neighbor_after_merge();
bool out_msg_queue_cleanup();
bool dequeue_message(Ref<vm::Cell> msg_envelope, ton::LogicalTime delivered_lt);
bool check_prev_block(const BlockIdExt& listed, const BlockIdExt& prev, bool chk_chain_len = true);
bool check_prev_block_exact(const BlockIdExt& listed, const BlockIdExt& prev);
bool check_this_shard_mc_info();
bool request_neighbor_msg_queues();
void update_max_lt(ton::LogicalTime lt);
bool is_masterchain() const {
return shard.is_masterchain();
}
bool is_our_address(Ref<vm::CellSlice> addr_ref) const;
bool is_our_address(ton::AccountIdPrefixFull addr_prefix) const;
bool is_our_address(const ton::StdSmcAddress& addr) const;
void after_get_external_messages(td::Result<std::vector<Ref<ExtMessage>>> res);
td::Result<bool> register_external_message_cell(Ref<vm::Cell> ext_msg, const ExtMessage::Hash& ext_hash);
// td::Result<bool> register_external_message(td::Slice ext_msg_boc);
td::Result<bool> register_ihr_message_cell(Ref<vm::Cell> ihr_msg);
td::Result<bool> register_ihr_message(td::Slice ihr_msg_boc);
td::Result<bool> register_shard_signatures_cell(Ref<vm::Cell> shard_blk_signatures);
td::Result<bool> register_shard_signatures(td::Slice shard_blk_signatures_boc);
void register_new_msg(block::NewOutMsg msg);
void register_new_msgs(block::Transaction& trans);
bool process_new_messages(bool enqueue_only = false);
int process_one_new_message(block::NewOutMsg msg, bool enqueue_only = false, Ref<vm::Cell>* is_special = nullptr);
bool process_inbound_internal_messages();
bool process_inbound_message(Ref<vm::CellSlice> msg, ton::LogicalTime lt, td::ConstBitPtr key,
const block::McShardDescr& src_nb);
bool process_inbound_external_messages();
int process_external_message(Ref<vm::Cell> msg);
bool enqueue_message(block::NewOutMsg msg, td::RefInt256 fwd_fees_remaining, ton::LogicalTime enqueued_lt);
bool enqueue_transit_message(Ref<vm::Cell> msg, Ref<vm::Cell> old_msg_env, ton::AccountIdPrefixFull prev_prefix,
ton::AccountIdPrefixFull cur_prefix, ton::AccountIdPrefixFull dest_prefix,
td::RefInt256 fwd_fee_remaining, ton::LogicalTime enqueued_lt);
bool delete_out_msg_queue_msg(td::ConstBitPtr key);
bool insert_in_msg(Ref<vm::Cell> in_msg);
bool insert_out_msg(Ref<vm::Cell> out_msg);
bool register_out_msg_queue_op(bool force = false);
bool update_min_mc_seqno(ton::BlockSeqno some_mc_seqno);
bool combine_account_transactions();
bool update_public_libraries();
bool update_account_public_libraries(Ref<vm::Cell> orig_libs, Ref<vm::Cell> final_libs, const td::Bits256& addr);
bool add_public_library(td::ConstBitPtr key, td::ConstBitPtr addr, Ref<vm::Cell> library);
bool remove_public_library(td::ConstBitPtr key, td::ConstBitPtr addr);
bool check_block_overload();
bool create_mc_state_extra();
bool create_shard_state();
td::Result<Ref<vm::Cell>> get_config_data_from_smc(const ton::StdSmcAddress& cfg_addr);
bool try_fetch_new_config(const ton::StdSmcAddress& cfg_addr, Ref<vm::Cell>& new_config);
bool update_processed_upto();
bool compute_out_msg_queue_info(Ref<vm::Cell>& out_msg_queue_info);
bool compute_total_balance();
bool store_master_ref(vm::CellBuilder& cb);
bool store_prev_blk_ref(vm::CellBuilder& cb, bool after_merge);
bool store_zero_state_ref(vm::CellBuilder& cb);
bool create_block_info(Ref<vm::Cell>& block_info);
bool check_value_flow();
bool create_block_extra(Ref<vm::Cell>& block_extra);
bool update_shard_config(const block::WorkchainSet& wc_set, const block::CatchainValidatorsConfig& ccvc,
bool update_cc);
bool create_mc_block_extra(Ref<vm::Cell>& mc_block_extra);
bool create_block();
Ref<vm::Cell> collate_shard_block_descr_set();
bool create_collated_data();
bool create_block_candidate();
void return_block_candidate(td::Result<td::Unit> saved);
bool update_last_proc_int_msg(const std::pair<ton::LogicalTime, ton::Bits256>& new_lt_hash);
};
} // namespace validator
} // namespace ton

3800
validator/impl/collator.cpp Normal file

File diff suppressed because it is too large Load diff

49
validator/impl/collator.h Normal file
View file

@ -0,0 +1,49 @@
/*
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 "ton/ton-types.h"
#include "validator/validator.h"
#include "block/block-db.h"
#include "vm/cells.h"
namespace ton {
using td::Ref;
extern int collator_settings; // +1 = force want_split, +2 = force want_merge
class Collator : public td::actor::Actor {
protected:
Collator() = default;
public:
virtual ~Collator() = default;
static td::actor::ActorOwn<Collator> create_collator(
td::actor::ActorId<block::BlockDb> block_db,
ShardIdFull shard /* , td::actor::ActorId<ValidatorManager> validator_manager */);
virtual void generate_block_candidate(ShardIdFull shard, td::Promise<BlockCandidate> promise) = 0;
virtual td::Result<bool> register_external_message_cell(Ref<vm::Cell> ext_msg) = 0;
virtual td::Result<bool> register_external_message(td::Slice ext_msg_boc) = 0;
virtual td::Result<bool> register_ihr_message_cell(Ref<vm::Cell> ihr_msg) = 0;
virtual td::Result<bool> register_ihr_message(td::Slice ihr_msg_boc) = 0;
virtual td::Result<bool> register_shard_signatures_cell(Ref<vm::Cell> shard_blk_signatures) = 0;
virtual td::Result<bool> register_shard_signatures(td::Slice shard_blk_signatures_boc) = 0;
};
} // namespace ton

View file

@ -0,0 +1,74 @@
/*
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 "external-message.hpp"
#include "vm/boc.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "block/block-db.h"
namespace ton {
namespace validator {
using td::Ref;
ExtMessageQ::ExtMessageQ(td::BufferSlice data, td::Ref<vm::Cell> root, AccountIdPrefixFull addr_prefix)
: root_(std::move(root)), addr_prefix_(addr_prefix), data_(std::move(data)) {
hash_ = block::compute_file_hash(data_);
}
td::Result<Ref<ExtMessageQ>> ExtMessageQ::create_ext_message(td::BufferSlice data) {
if (data.size() > max_ext_msg_size) {
return td::Status::Error("external message too large, rejecting");
}
vm::BagOfCells boc;
auto res = boc.deserialize(data.as_slice());
if (res.is_error()) {
return res.move_as_error();
}
if (boc.get_root_count() != 1) {
return td::Status::Error("external message is not a valid bag of cells"); // not a valid bag-of-Cells
}
auto ext_msg = boc.get_root_cell();
if (ext_msg->get_level() != 0) {
return td::Status::Error("external message must have zero level");
}
vm::CellSlice cs{vm::NoVmOrd{}, ext_msg};
if (cs.prefetch_ulong(2) != 2) { // ext_in_msg_info$10
return td::Status::Error("external message must begin with ext_in_msg_info$10");
}
ton::Bits256 hash{ext_msg->get_hash().bits()};
if (!block::gen::t_Message_Any.validate_ref(ext_msg)) {
return td::Status::Error("external message is not a (Message Any) according to automated checks");
}
if (!block::tlb::t_Message.validate_ref(ext_msg)) {
return td::Status::Error("external message is not a (Message Any) according to hand-written checks");
}
block::gen::CommonMsgInfo::Record_ext_in_msg_info info;
if (!tlb::unpack_cell_inexact(ext_msg, info)) {
return td::Status::Error("cannot unpack external message header");
}
auto dest_prefix = block::tlb::t_MsgAddressInt.get_prefix(info.dest);
if (!dest_prefix.is_valid()) {
return td::Status::Error("destination of an inbound external message is an invalid blockchain address");
}
return Ref<ExtMessageQ>{true, std::move(data), std::move(ext_msg), dest_prefix};
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,55 @@
/*
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 "validator/interfaces/external-message.h"
#include "auto/tl/ton_api.h"
#include "adnl/utils.hpp"
namespace ton {
namespace validator {
class ExtMessageQ : public ExtMessage {
td::Ref<vm::Cell> root_;
AccountIdPrefixFull addr_prefix_;
td::BufferSlice data_;
Hash hash_;
public:
static constexpr unsigned max_ext_msg_size = 65535;
AccountIdPrefixFull shard() const override {
return addr_prefix_;
}
td::BufferSlice serialize() const override {
return data_.clone();
}
td::Ref<vm::Cell> root_cell() const override {
return root_;
}
Hash hash() const override {
return hash_;
}
ExtMessageQ(td::BufferSlice data, td::Ref<vm::Cell> root, AccountIdPrefixFull shard);
static td::Result<td::Ref<ExtMessageQ>> create_ext_message(td::BufferSlice data);
};
} // namespace validator
} // namespace ton

215
validator/impl/fabric.cpp Normal file
View file

@ -0,0 +1,215 @@
/*
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 "fabric.h"
#include "collator-impl.h"
#include "validator/db/rootdb.hpp"
#include "validator/block-handle.hpp"
#include "apply-block.hpp"
#include "accept-block.hpp"
#include "shard.hpp"
#include "block.hpp"
#include "proof.hpp"
#include "signature-set.hpp"
#include "external-message.hpp"
#include "ihr-message.hpp"
#include "validate-query.hpp"
#include "check-proof.hpp"
#include "top-shard-descr.hpp"
#include "ton/ton-io.hpp"
#include "liteserver.hpp"
namespace ton {
namespace validator {
td::actor::ActorOwn<Db> create_db_actor(td::actor::ActorId<ValidatorManager> manager, std::string db_root_) {
return td::actor::create_actor<RootDb>("db", manager, db_root_);
}
td::Result<td::Ref<BlockData>> create_block(BlockIdExt block_id, td::BufferSlice data) {
auto res = BlockQ::create(block_id, std::move(data));
if (res.is_error()) {
return res.move_as_error();
} else {
return td::Ref<BlockData>{res.move_as_ok()};
}
}
td::Result<td::Ref<BlockData>> create_block(ReceivedBlock data) {
return create_block(data.id, std::move(data.data));
}
td::Result<td::Ref<Proof>> create_proof(BlockIdExt masterchain_block_id, td::BufferSlice proof) {
return Ref<ProofQ>{true, masterchain_block_id, std::move(proof)};
}
td::Result<td::Ref<ProofLink>> create_proof_link(BlockIdExt block_id, td::BufferSlice proof_link) {
return Ref<ProofLinkQ>{true, block_id, std::move(proof_link)};
}
td::Result<td::Ref<BlockSignatureSet>> create_signature_set(td::BufferSlice sig_set) {
return BlockSignatureSetQ::fetch(std::move(sig_set));
}
td::Result<td::Ref<ShardState>> create_shard_state(BlockIdExt block_id, td::BufferSlice data) {
auto res = ShardStateQ::fetch(block_id, std::move(data));
if (res.is_error()) {
return res.move_as_error();
} else {
return td::Ref<ShardState>{res.move_as_ok()};
}
}
td::Result<td::Ref<ShardState>> create_shard_state(BlockIdExt block_id, td::Ref<vm::DataCell> root_cell) {
auto res = ShardStateQ::fetch(block_id, {}, std::move(root_cell));
if (res.is_error()) {
return res.move_as_error();
} else {
return td::Ref<ShardState>{res.move_as_ok()};
}
}
td::Result<BlockHandle> create_block_handle(td::BufferSlice data) {
return ton::validator::BlockHandleImpl::create(std::move(data));
}
BlockHandle create_empty_block_handle(BlockIdExt id) {
return ton::validator::BlockHandleImpl::create_empty(id);
}
td::Ref<BlockSignatureSet> create_signature_set(std::vector<BlockSignature> sig_set) {
return td::Ref<BlockSignatureSetQ>{true, std::move(sig_set)};
}
td::Result<td::Ref<ExtMessage>> create_ext_message(td::BufferSlice data) {
TRY_RESULT(res, ExtMessageQ::create_ext_message(std::move(data)));
return std::move(res);
}
td::Result<td::Ref<IhrMessage>> create_ihr_message(td::BufferSlice data) {
TRY_RESULT(res, IhrMessageQ::create_ihr_message(std::move(data)));
return std::move(res);
}
void run_accept_block_query(BlockIdExt id, td::Ref<BlockData> data, std::vector<BlockIdExt> prev,
td::Ref<ValidatorSet> validator_set, td::Ref<BlockSignatureSet> signatures,
td::Ref<BlockSignatureSet> approve_signatures, bool send_broadcast,
td::actor::ActorId<ValidatorManager> manager, td::Promise<td::Unit> promise) {
td::actor::create_actor<AcceptBlockQuery>("accept", id, std::move(data), prev, std::move(validator_set),
std::move(signatures), send_broadcast, manager, std::move(promise))
.release();
}
void run_fake_accept_block_query(BlockIdExt id, td::Ref<BlockData> data, std::vector<BlockIdExt> prev,
td::Ref<ValidatorSet> validator_set, td::actor::ActorId<ValidatorManager> manager,
td::Promise<td::Unit> promise) {
td::actor::create_actor<AcceptBlockQuery>("fakeaccept", AcceptBlockQuery::IsFake(), id, std::move(data),
std::move(prev), std::move(validator_set), std::move(manager),
std::move(promise))
.release();
}
void run_apply_block_query(BlockIdExt id, td::Ref<BlockData> block, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<td::Unit> promise) {
td::actor::create_actor<ApplyBlock>(PSTRING() << "apply " << id, id, std::move(block), manager, timeout,
std::move(promise))
.release();
}
void run_check_proof_query(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<BlockHandle> promise, bool skip_check_signatures) {
td::actor::create_actor<CheckProof>("checkproof", id, std::move(proof), manager, timeout, std::move(promise),
skip_check_signatures)
.release();
}
void run_check_proof_query(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<BlockHandle> promise,
td::Ref<ProofLink> rel_key_block_proof, bool skip_check_signatures) {
td::actor::create_actor<CheckProof>("checkproof/key", id, std::move(proof), manager, timeout, std::move(promise),
skip_check_signatures, std::move(rel_key_block_proof))
.release();
}
void run_check_proof_query(BlockIdExt id, td::Ref<Proof> proof, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<BlockHandle> promise,
td::Ref<MasterchainState> rel_mc_state, bool skip_check_signatures) {
td::actor::create_actor<CheckProof>("checkproof/st", id, std::move(proof), manager, timeout, std::move(promise),
skip_check_signatures, std::move(rel_mc_state))
.release();
}
void run_check_proof_link_query(BlockIdExt id, td::Ref<ProofLink> proof, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, td::Promise<BlockHandle> promise) {
td::actor::create_actor<CheckProof>("checkprooflink", id, std::move(proof), manager, timeout, std::move(promise))
.release();
}
void run_validate_query(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_masterchain_block_id,
std::vector<BlockIdExt> prev, BlockCandidate candidate, td::Ref<ValidatorSet> validator_set,
td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<ValidateCandidateResult> promise, bool is_fake) {
BlockSeqno seqno = 0;
for (auto& p : prev) {
if (p.seqno() > seqno) {
seqno = p.seqno();
}
}
td::actor::create_actor<ValidateQuery>(
PSTRING() << (is_fake ? "fakevalidate" : "validateblock") << shard.to_str() << ":" << (seqno + 1), shard, min_ts,
min_masterchain_block_id, std::move(prev), std::move(candidate), std::move(validator_set), std::move(manager),
timeout, std::move(promise), is_fake)
.release();
}
void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& min_masterchain_block_id,
std::vector<BlockIdExt> prev, PublicKeyHash collator_id, td::Ref<ValidatorSet> validator_set,
td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<BlockCandidate> promise) {
BlockSeqno seqno = 0;
for (auto& p : prev) {
if (p.seqno() > seqno) {
seqno = p.seqno();
}
}
td::actor::create_actor<Collator>(PSTRING() << "collate" << shard.to_str() << ":" << (seqno + 1), shard, min_ts,
min_masterchain_block_id, std::move(prev), std::move(validator_set),
collator_id.bits256_value(), std::move(manager), timeout, std::move(promise))
.release();
}
void run_liteserver_query(td::BufferSlice data, td::actor::ActorId<ValidatorManager> manager,
td::Promise<td::BufferSlice> promise) {
LiteQuery::run_query(std::move(data), std::move(manager), std::move(promise));
}
void run_validate_shard_block_description(td::BufferSlice data, BlockHandle masterchain_block,
td::Ref<MasterchainState> masterchain_state,
td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<td::Ref<ShardTopBlockDescription>> promise, bool is_fake) {
auto id = masterchain_block->id();
td::actor::create_actor<ValidateShardTopBlockDescr>("topshardfetch", std::move(data), id,
std::move(masterchain_block), std::move(masterchain_state),
manager, timeout, is_fake, std::move(promise))
.release();
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,132 @@
/*
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 "ihr-message.hpp"
#include "vm/boc.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "block/block-db.h"
#include "vm/cells/MerkleProof.h"
namespace ton {
namespace validator {
using td::Ref;
using namespace std::literals::string_literals;
IhrMessageQ::IhrMessageQ(td::BufferSlice data, td::Ref<vm::Cell> root, BlockIdExt block,
AccountIdPrefixFull addr_prefix)
: root_(std::move(root)), addr_prefix_(addr_prefix), data_(std::move(data)), blkid_(block) {
hash_ = block::compute_file_hash(data_);
}
td::Result<Ref<IhrMessageQ>> IhrMessageQ::create_ihr_message(td::BufferSlice data) {
if (data.size() > max_ihr_msg_size) {
return td::Status::Error("IHR message too large, rejecting");
}
vm::BagOfCells boc;
auto res = boc.deserialize(data.as_slice());
if (res.is_error()) {
return res.move_as_error();
}
if (boc.get_root_count() != 3) {
return td::Status::Error("IHR message is not a valid bag of cells with three roots"); // not a valid bag-of-Cells
}
auto ihr_msg = boc.get_root_cell(0), blk = boc.get_root_cell(1), proof = boc.get_root_cell(2);
if (ihr_msg->get_level() != 0 || blk->get_level() != 0 || proof->get_level() != 0) {
return td::Status::Error("IHR message must have zero level");
}
vm::CellSlice cs{vm::NoVmOrd{}, ihr_msg};
if (cs.prefetch_ulong(1) != 0) { // int_msg_info$0
return td::Status::Error("IHR message must begin with int_msg_info$0");
}
ton::Bits256 hash{ihr_msg->get_hash().bits()};
if (!block::gen::t_Message_Any.validate_ref(ihr_msg)) {
return td::Status::Error("IHR message is not a (Message Any) according to automated checks");
}
if (!block::tlb::t_Message.validate_ref(ihr_msg)) {
return td::Status::Error("IHR message is not a (Message Any) according to hand-written checks");
}
block::gen::CommonMsgInfo::Record_int_msg_info info;
if (!tlb::unpack_cell_inexact(ihr_msg, info)) {
return td::Status::Error("cannot unpack IHR message header");
}
auto dest_prefix = block::tlb::t_MsgAddressInt.get_prefix(info.dest);
if (!dest_prefix.is_valid()) {
return td::Status::Error("destination of an IHR message is an invalid blockchain address");
}
cs.load_ord(std::move(blk));
BlockIdExt blkid;
if (!(block::tlb::t_BlockIdExt.unpack(cs, blkid) && cs.empty_ext())) {
return td::Status::Error("IHR message does not contain a valid source BlockIdExt");
}
try {
auto virt_root = vm::MerkleProof::virtualize(proof, 1);
if (virt_root.is_null()) {
return td::Status::Error("IHR message does not contain a valid Merkle proof");
}
RootHash virt_hash{virt_root->get_hash().bits()};
if (virt_hash != blkid.root_hash) {
return td::Status::Error("IHR message contains a Merkle proof with incorrect root hash: expected " +
blkid.root_hash.to_hex() + ", found " + virt_hash.to_hex());
}
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
block::gen::BlockExtra::Record extra;
ShardIdFull shard;
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(blk.info, info) && !info.version &&
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard) && !info.vert_seq_no &&
tlb::unpack_cell(blk.extra, extra))) {
return td::Status::Error("cannot unpack block header in the Merkle proof of an IHR message");
}
if (blkid.shard_full() != shard || blkid.seqno() != BlockSeqno(info.seq_no)) {
return td::Status::Error(
"block header in the Merkle proof of an IHR message does not belong to the declared source block");
}
vm::AugmentedDictionary out_msg_dict{vm::load_cell_slice_ref(extra.out_msg_descr), 256,
block::tlb::aug_OutMsgDescr};
Bits256 key{ihr_msg->get_hash().bits()};
auto descr = out_msg_dict.lookup(key);
out_msg_dict.reset();
if (descr.is_null()) {
return td::Status::Error(
"IHR message contains an invalid proof with OutMsgDescr not containing a key equal to the hash of the "
"message");
}
if (descr->prefetch_ulong(3) != 1 || !descr->size_refs()) { // expect msg_export_new$001
return td::Status::Error(
"IHR message contains an invalid proof with OutMsg record not of type msg_export_new$001");
}
cs.load_ord(descr->prefetch_ref());
if (!cs.size_refs()) {
return td::Status::Error("IHR message contains an invalid MsgEnvelope");
}
if (key != cs.prefetch_ref()->get_hash().bits()) {
return td::Status::Error(
"IHR message contains an invalid proof with MsgEnvelope not pointing to the message included");
}
} catch (vm::VmError err) {
return td::Status::Error("error while processing Merkle proof provided in IHR message: "s + err.get_msg());
} catch (vm::VmVirtError err) {
return td::Status::Error("error while processing Merkle proof provided in IHR message: "s + err.get_msg());
}
return Ref<IhrMessageQ>{true, std::move(data), std::move(ihr_msg), blkid, dest_prefix};
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,56 @@
/*
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 "validator/interfaces/ihr-message.h"
#include "auto/tl/ton_api.h"
#include "adnl/utils.hpp"
namespace ton {
namespace validator {
class IhrMessageQ : public IhrMessage {
td::Ref<vm::Cell> root_;
AccountIdPrefixFull addr_prefix_;
td::BufferSlice data_;
Hash hash_;
BlockIdExt blkid_;
public:
static constexpr unsigned max_ihr_msg_size = 65535;
AccountIdPrefixFull shard() const override {
return addr_prefix_;
}
td::BufferSlice serialize() const override {
return data_.clone();
}
td::Ref<vm::Cell> root_cell() const override {
return root_;
}
Hash hash() const override {
return hash_;
}
IhrMessageQ(td::BufferSlice data, td::Ref<vm::Cell> root, BlockIdExt block, AccountIdPrefixFull shard);
static td::Result<td::Ref<IhrMessageQ>> create_ihr_message(td::BufferSlice data);
};
} // namespace validator
} // namespace ton

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,139 @@
/*
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 "ton/ton-types.h"
#include "td/actor/actor.h"
#include "td/utils/Time.h"
#include "interfaces/block-handle.h"
#include "interfaces/validator-manager.h"
#include "interfaces/shard.h"
#include "shard.hpp"
namespace ton {
namespace validator {
using td::Ref;
class LiteQuery : public td::actor::Actor {
td::BufferSlice query_;
td::actor::ActorId<ton::validator::ValidatorManager> manager_;
td::Timestamp timeout_;
td::Promise<td::BufferSlice> promise_;
int pending_{0};
WorkchainId acc_workchain_;
StdSmcAddress acc_addr_;
LogicalTime trans_lt_;
Bits256 trans_hash_;
BlockIdExt base_blk_id_, blk_id_;
Ref<MasterchainStateQ> mc_state_, mc_state0_;
Ref<ShardStateQ> state_;
Ref<BlockData> mc_block_, block_;
std::function<void()> continuation_;
bool cont_set_{false};
td::BufferSlice shard_proof_;
std::vector<Ref<vm::Cell>> roots_;
std::vector<Ref<td::CntObject>> aux_objs_;
std::vector<ton::BlockIdExt> blk_ids_;
std::unique_ptr<block::BlkProofChain> chain_;
public:
static constexpr double default_timeout_seconds = 4.5;
LiteQuery(td::BufferSlice data, td::actor::ActorId<ton::validator::ValidatorManager> manager,
td::Promise<td::BufferSlice> promise);
static void run_query(td::BufferSlice data, td::actor::ActorId<ton::validator::ValidatorManager> manager,
td::Promise<td::BufferSlice> promise);
private:
bool fatal_error(td::Status error);
bool fatal_error(std::string err_msg, int err_code = -400);
bool fatal_error(int err_code, std::string err_msg = "");
void abort_query(td::Status reason);
bool finish_query(td::BufferSlice result);
void alarm() override;
void start_up() override;
void perform_getTime();
void perform_getVersion();
void perform_getMasterchainInfo();
void continue_getMasterchainInfo(Ref<MasterchainState> mc_state, BlockIdExt blkid);
void perform_getBlock(BlockIdExt blkid);
void continue_getBlock(BlockIdExt blkid, Ref<BlockData> block);
void perform_getBlockHeader(BlockIdExt blkid, int mode);
void continue_getBlockHeader(BlockIdExt blkid, int mode, Ref<BlockData> block);
void perform_getState(BlockIdExt blkid);
void continue_getState(BlockIdExt blkid, Ref<ShardState> state);
void perform_sendMessage(td::BufferSlice ext_msg);
void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr);
void continue_getAccountState_0(Ref<MasterchainState> mc_state, BlockIdExt blkid);
void continue_getAccountState();
void finish_getAccountState(td::BufferSlice shard_proof);
void perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt);
void continue_getOneTransaction();
void perform_getTransactions(WorkchainId workchain, StdSmcAddress addr, LogicalTime lt, Bits256 hash, unsigned count);
void continue_getTransactions(unsigned remaining, bool exact);
void continue_getTransactions_2(BlockIdExt blkid, Ref<BlockData> block, unsigned remaining);
void abort_getTransactions(td::Status error, ton::BlockIdExt blkid);
void finish_getTransactions();
void perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool exact);
void perform_getAllShardsInfo(BlockIdExt blkid);
void continue_getShardInfo(ShardIdFull shard, bool exact);
void continue_getAllShardsInfo();
void perform_getConfigParams(BlockIdExt blkid, int mode, std::vector<int> param_list = {});
void continue_getConfigParams(int mode, std::vector<int> param_list);
void perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, UnixTime utime);
void perform_listBlockTransactions(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt);
void finish_listBlockTransactions(int mode, int count);
void perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode);
void continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, Ref<MasterchainStateQ> state);
bool construct_proof_chain(ton::BlockIdExt id);
bool construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdExt next);
bool construct_proof_link_forward_cont(ton::BlockIdExt cur, ton::BlockIdExt next);
bool construct_proof_link_back(ton::BlockIdExt cur, ton::BlockIdExt next);
bool construct_proof_link_back_cont(ton::BlockIdExt cur, ton::BlockIdExt next);
bool finish_proof_chain(ton::BlockIdExt id);
bool request_block_data(BlockIdExt blkid);
bool request_block_state(BlockIdExt blkid);
bool request_block_data_state(BlockIdExt blkid);
bool request_mc_block_data(BlockIdExt blkid);
bool request_mc_block_state(BlockIdExt blkid);
bool request_mc_block_data_state(BlockIdExt blkid);
void got_block_state(BlockIdExt blkid, Ref<ShardState> state);
void got_mc_block_state(BlockIdExt blkid, Ref<ShardState> state);
void got_block_data(BlockIdExt blkid, Ref<BlockData> data);
void got_mc_block_data(BlockIdExt blkid, Ref<BlockData> data);
void dec_pending() {
if (!--pending_) {
check_pending();
}
}
void check_pending();
bool set_continuation(std::function<void()>&& cont);
bool make_mc_state_root_proof(Ref<vm::Cell>& proof);
bool make_state_root_proof(Ref<vm::Cell>& proof);
bool make_state_root_proof(Ref<vm::Cell>& proof, Ref<ShardStateQ> state, Ref<BlockData> block,
const BlockIdExt& blkid);
bool make_shard_info_proof(Ref<vm::Cell>& proof, vm::CellSlice& cs, ShardIdFull shard, ShardIdFull& true_shard,
Ref<vm::Cell>& leaf, bool& found, bool exact = true);
bool make_shard_info_proof(Ref<vm::Cell>& proof, Ref<block::McShardHash>& info, ShardIdFull shard, bool exact = true);
bool make_shard_info_proof(Ref<vm::Cell>& proof, Ref<block::McShardHash>& info, AccountIdPrefixFull prefix);
bool make_shard_info_proof(Ref<vm::Cell>& proof, BlockIdExt& blkid, AccountIdPrefixFull prefix);
};
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,30 @@
/*
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 "message-queue.hpp"
namespace ton {
namespace validator {
using td::Ref;
MessageQueueQ* MessageQueueQ::make_copy() const {
return new MessageQueueQ(*this);
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,66 @@
/*
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 "interfaces/message-queue.h"
namespace ton {
namespace validator {
using td::Ref;
class ShardStateQ;
class MessageQueueQ : public MessageQueue {
BlockIdExt blkid;
RootHash rhash;
Ref<vm::Cell> root;
MessageQueueQ* make_copy() const override;
protected:
friend class ShardStateQ;
MessageQueueQ(const MessageQueueQ& other) = default;
MessageQueueQ(MessageQueueQ&& other) = default;
public:
MessageQueueQ(const BlockIdExt& _id, Ref<vm::Cell> _root) : blkid(_id), root(std::move(_root)) {
if (root.is_null()) {
rhash.set_zero();
} else {
rhash = root->get_hash().bits();
}
}
virtual ~MessageQueueQ() = default;
ShardIdFull get_shard() const override {
return ShardIdFull(blkid);
}
BlockSeqno get_seqno() const override {
return blkid.id.seqno;
}
BlockIdExt get_block_id() const override {
return blkid;
}
RootHash root_hash() const override {
return rhash;
}
td::Ref<vm::Cell> root_cell() const override {
return root;
}
};
} // namespace validator
} // namespace ton

143
validator/impl/proof.cpp Normal file
View file

@ -0,0 +1,143 @@
/*
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 "proof.hpp"
#include "block/block-parse.h"
#include "block/block-auto.h"
#include "vm/boc.h"
#include "vm/cells/MerkleProof.h"
#include "validator-set.hpp"
namespace ton {
namespace validator {
using td::Ref;
using namespace std::literals::string_literals;
td::Result<Ref<ProofLink>> ProofQ::export_as_proof_link() const {
TRY_RESULT(root, vm::std_boc_deserialize(data_));
block::gen::BlockProof::Record proof;
if (!(tlb::unpack_cell(std::move(root), proof))) {
return td::Status::Error("cannot unpack BlockProof");
}
proof.signatures = vm::load_cell_slice_ref(vm::CellBuilder().store_long(0, 1).finalize());
if (!(tlb::pack_cell(root, proof))) {
return td::Status::Error("cannot pack new BlockProof");
}
TRY_RESULT(data, vm::std_boc_serialize(std::move(root)));
return Ref<ProofLink>(td::make_ref<ProofLinkQ>(id_, std::move(data)));
}
td::Result<BlockSeqno> ProofLinkQ::prev_key_mc_seqno() const {
if (!id_.is_masterchain()) {
return td::Status::Error(
-668, "cannot compute previous key masterchain block from ProofLink of non-masterchain block "s + id_.to_str());
}
TRY_RESULT(pair, get_virtual_root(true));
try {
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
if (!(tlb::unpack_cell(std::move(pair.first), blk) && tlb::unpack_cell(blk.info, info) && !info.version)) {
return td::Status::Error(-668,
"cannot unpack block header in the Merkle proof for masterchain block "s + id_.to_str());
}
return info.prev_key_block_seqno;
} catch (vm::VmVirtError &) {
return td::Status::Error(-668, "virtualization error in masterchain block proof for "s + id_.to_str());
}
}
td::Result<td::Ref<ConfigHolder>> ProofLinkQ::get_key_block_config() const {
if (!id_.is_masterchain()) {
return td::Status::Error(
-668, "cannot compute previous key masterchain block from ProofLink of non-masterchain block "s + id_.to_str());
}
TRY_RESULT(pair, get_virtual_root(true));
try {
TRY_RESULT(cfg, block::Config::extract_from_key_block(
std::move(pair.first), block::Config::needValidatorSet | block::Config::needWorkchainInfo));
return td::make_ref<ConfigHolderQ>(std::move(cfg), std::move(pair.second));
} catch (vm::VmVirtError &) {
return td::Status::Error(-668,
"virtualization error while traversing masterchain block proof for "s + id_.to_str());
}
}
td::Result<std::pair<Ref<vm::Cell>, std::shared_ptr<vm::StaticBagOfCellsDb>>> ProofLinkQ::get_virtual_root(
bool lazy) const {
if (data_.empty()) {
return td::Status::Error(-668, "block proof is empty");
}
std::shared_ptr<vm::StaticBagOfCellsDb> boc;
Ref<vm::Cell> root;
if (lazy) {
vm::StaticBagOfCellsDbLazy::Options options;
options.check_crc32c = true;
auto res = vm::StaticBagOfCellsDbLazy::create(vm::BufferSliceBlobView::create(data_.clone()), options);
if (res.is_error()) {
return res.move_as_error();
}
boc = res.move_as_ok();
TRY_RESULT(rc, boc->get_root_count());
if (rc != 1) {
return td::Status::Error(-668, "masterchain block proof BoC is invalid");
}
TRY_RESULT(t_root, boc->get_root_cell(0));
root = std::move(t_root);
} else {
TRY_RESULT(t_root, vm::std_boc_deserialize(data_.as_slice()));
root = std::move(t_root);
}
if (root.is_null()) {
return td::Status::Error(-668, "cannot extract root cell out of a masterchain block proof BoC");
}
block::gen::BlockProof::Record proof;
BlockIdExt proof_blk_id;
if (!(tlb::unpack_cell(root, proof) && block::tlb::t_BlockIdExt.unpack(proof.proof_for.write(), proof_blk_id))) {
return td::Status::Error(-668, "masterchain block proof is invalid");
}
if (proof_blk_id != id_) {
return td::Status::Error(-668, "masterchain block proof is for another block");
}
auto virt_root = vm::MerkleProof::virtualize(proof.root, 1);
if (virt_root.is_null()) {
return td::Status::Error(-668, "block proof for block "s + proof_blk_id.to_str() +
" does not contain a valid Merkle proof for the block header");
}
RootHash virt_hash{virt_root->get_hash().bits()};
if (virt_hash != proof_blk_id.root_hash) {
return td::Status::Error(-668, "block proof for block "s + proof_blk_id.to_str() +
" contains a Merkle proof with incorrect root hash: expected " +
proof_blk_id.root_hash.to_hex() + ", found " + virt_hash.to_hex());
}
return std::make_pair(std::move(virt_root), std::move(boc));
}
td::Ref<ValidatorSet> ConfigHolderQ::get_total_validator_set(int next) const {
if (!config_) {
LOG(ERROR) << "MasterchainStateQ::get_total_validator_set() : no config";
return {};
}
auto nodes = config_->compute_total_validator_set(next);
if (nodes.empty()) {
return {};
}
return Ref<ValidatorSetQ>{true, 0, ton::ShardIdFull{}, std::move(nodes)};
}
} // namespace validator
} // namespace ton

94
validator/impl/proof.hpp Normal file
View file

@ -0,0 +1,94 @@
/*
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 "interfaces/proof.h"
#include "interfaces/config.h"
#include "block/block-db.h"
#include "block/mc-config.h"
#include "vm/db/StaticBagOfCellsDb.h"
namespace ton {
namespace validator {
using td::Ref;
class ConfigHolderQ : public ConfigHolder {
std::shared_ptr<block::Config> config_;
std::shared_ptr<vm::StaticBagOfCellsDb> boc_;
public:
ConfigHolderQ() = default;
ConfigHolderQ(std::shared_ptr<block::Config> config, std::shared_ptr<vm::StaticBagOfCellsDb> boc)
: config_(std::move(config)), boc_(std::move(boc)) {
}
ConfigHolderQ(std::shared_ptr<block::Config> config) : config_(std::move(config)) {
}
const block::Config *get_config() const {
return config_.get();
}
ConfigHolderQ *make_copy() const override {
return new ConfigHolderQ(*this);
}
// if necessary, add more public methods providing interface to config_->...()
td::Ref<ValidatorSet> get_total_validator_set(int next) const override; // next = -1 -> prev, next = 0 -> cur
};
class ProofLinkQ : virtual public ProofLink {
protected:
BlockIdExt id_;
td::BufferSlice data_;
public:
ProofLinkQ(const BlockIdExt &id, td::BufferSlice data) : id_(id), data_(std::move(data)) {
}
ProofLinkQ *make_copy() const override {
return new ProofLinkQ(id_, data_.clone());
}
BlockIdExt block_id() const override {
return id_;
}
td::BufferSlice data() const override {
return data_.clone();
}
td::Result<BlockSeqno> prev_key_mc_seqno() const override;
td::Result<td::Ref<ConfigHolder>> get_key_block_config() const override;
protected:
td::Result<std::pair<Ref<vm::Cell>, std::shared_ptr<vm::StaticBagOfCellsDb>>> get_virtual_root(
bool lazy = false) const;
};
#if TD_MSVC
#pragma warning(push)
#pragma warning(disable : 4250) // Proof is an interface, so there is no problem here
#endif
class ProofQ : public Proof, public ProofLinkQ {
public:
ProofQ(BlockIdExt masterchain_block_id, td::BufferSlice data) : ProofLinkQ(masterchain_block_id, std::move(data)) {
}
ProofQ *make_copy() const override {
return new ProofQ(id_, data_.clone());
}
td::Result<Ref<ProofLink>> export_as_proof_link() const override;
};
#if TD_MSVC
#pragma warning(pop)
#endif
} // namespace validator
} // namespace ton

534
validator/impl/shard.cpp Normal file
View file

@ -0,0 +1,534 @@
/*
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 "shard.hpp"
#include "message-queue.hpp"
#include "validator-set.hpp"
#include "vm/boc.h"
#include "vm/db/BlobView.h"
#include "vm/db/StaticBagOfCellsDb.h"
#include "vm/cellslice.h"
#include "vm/cells/MerkleUpdate.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
#define LAZY_STATE_DESERIALIZE 1
namespace ton {
namespace validator {
using td::Ref;
using namespace std::literals::string_literals;
ShardStateQ::ShardStateQ(const ShardStateQ& other)
: blkid(other.blkid)
, rhash(other.rhash)
, data(other.data.is_null() ? td::BufferSlice{} : other.data.clone())
, bocs_(other.bocs_)
, root(other.root)
, lt(other.lt)
, utime(other.utime)
, before_split_(other.before_split_)
, fake_split_(other.fake_split_)
, fake_merge_(other.fake_merge_) {
}
ShardStateQ* ShardStateQ::make_copy() const {
return new ShardStateQ(*this);
}
ShardStateQ::ShardStateQ(const BlockIdExt& _id, td::BufferSlice _data) : blkid(_id), data(std::move(_data)) {
}
ShardStateQ::ShardStateQ(const BlockIdExt& _id, Ref<vm::Cell> _root, td::BufferSlice _data)
: blkid(_id), data(std::move(_data)), root(std::move(_root)) {
}
td::Result<Ref<ShardStateQ>> ShardStateQ::fetch(const BlockIdExt& _id, td::BufferSlice _data, Ref<vm::Cell> _root) {
if (_id.is_masterchain()) {
auto res = MasterchainStateQ::fetch(_id, std::move(_data), std::move(_root));
if (res.is_error()) {
return res.move_as_error();
} else {
return Ref<ShardStateQ>{res.move_as_ok()};
}
}
Ref<ShardStateQ> res{true, _id, std::move(_root), std::move(_data)};
td::Status err = res.unique_write().init();
if (err.is_error()) {
return err;
} else {
return std::move(res);
}
}
td::Status ShardStateQ::init() {
if (root.is_null()) {
if (data.empty()) {
return td::Status::Error(
-668, "cannot initialize shardchain state without either a root cell or a BufferSlice with serialized data");
}
#if LAZY_STATE_DESERIALIZE
vm::StaticBagOfCellsDbLazy::Options options;
options.check_crc32c = true;
auto res = vm::StaticBagOfCellsDbLazy::create(vm::BufferSliceBlobView::create(data.clone()), options);
if (res.is_error()) {
return res.move_as_error();
}
auto boc = res.move_as_ok();
auto rc = boc->get_root_count();
if (rc.is_error()) {
return rc.move_as_error();
}
if (rc.move_as_ok() != 1) {
return td::Status::Error(-668, "shardchain state BoC is invalid");
}
auto res3 = boc->get_root_cell(0);
bocs_.clear();
bocs_.push_back(std::move(boc));
#else
auto res3 = vm::std_boc_deserialize(data.as_slice());
#endif
if (res3.is_error()) {
return res3.move_as_error();
}
root = res3.move_as_ok();
if (root.is_null()) {
return td::Status::Error(-668, "cannot extract root cell out of a shardchain state BoC");
}
}
rhash = root->get_hash().bits();
block::gen::ShardStateUnsplit::Record info;
if (!tlb::unpack_cell(root, info)) {
return td::Status::Error(-668,
"shardchain state for block "s + blkid.id.to_str() + " does not contain a valid header");
}
lt = info.gen_lt;
utime = info.gen_utime;
before_split_ = info.before_split;
block::ShardId id{info.shard_id};
ton::BlockId hdr_id{ton::ShardIdFull(id), info.seq_no};
if (!id.is_valid() || get_shard() != ton::ShardIdFull(id) || get_seqno() != info.seq_no) {
return td::Status::Error(-668, "header of unpacked shardchain state for block "s + blkid.id.to_str() +
" contains BlockId " + hdr_id.to_str() +
" different from the one originally required");
}
return td::Status::OK();
}
td::Status ShardStateQ::validate_deep() const {
if (data.empty()) {
return td::Status::Error(-668,
"cannot validate serialized shard state because no serialized shard state is present");
}
auto res = vm::std_boc_deserialize(data.as_slice());
if (res.is_error()) {
return res.move_as_error();
}
auto root = res.move_as_ok();
if (root.is_null()) {
return td::Status::Error(-668, "cannot extract root cell out of a shardchain state BoC");
}
if (rhash != root->get_hash().bits()) {
return td::Status::Error(-668, "root hash mismatch in a shardchain state BoC : expected "s + rhash.to_hex() +
" , found " + root->get_hash().bits().to_hex(256));
}
return td::Status::OK();
}
td::Result<Ref<MessageQueue>> ShardStateQ::message_queue() const {
if (root.is_null()) {
return {}; // GIGO
}
vm::CellSlice cs{vm::NoVmOrd(), root};
if (!cs.have(64, 1) || cs.prefetch_ulong(32) != (unsigned)block::tlb::ShardState::shard_state) {
return td::Status::Error(-668, "state for block "s + blkid.id.to_str() + " is invalid");
}
if (fake_split_ || fake_merge_) {
return td::Status::Error(-668, "cannot obtain message queue from a virtually split or merged state");
}
auto out_queue_info = cs.prefetch_ref();
return Ref<MessageQueue>(Ref<MessageQueueQ>{true, blkid, std::move(out_queue_info)});
}
td::Status ShardStateQ::apply_block(BlockIdExt newid, td::Ref<BlockData> block) {
if (block.is_null()) {
return td::Status::Error(-666, "the block to be applied to a previous state is absent");
}
Ref<vm::Cell> block_root = block->root_cell();
if (root.is_null() || block_root.is_null()) {
return td::Status::Error(-666, "cannot apply an (empty) block to an (empty) state");
}
if (newid != block->block_id()) {
return td::Status::Error(-666, "block id mismatch in apply_block()");
}
RootHash blk_rhash{block_root->get_hash().bits()};
if (blk_rhash != newid.root_hash) {
return td::Status::Error(-666, "cannot apply a block because its root hash differs from expected");
}
if (before_split_ != fake_split_) {
return td::Status::Error(
-666, "cannot apply a block because previous state has before_split set, but it has not been split virtually");
}
vm::CellSlice cs{vm::NoVmOrd{}, block_root};
if (cs.prefetch_ulong(32) != 0x11ef55aa || !cs.have_refs(4)) {
return td::Status::Error(-666, "invalid shardchain block header for block "s + block->block_id().id.to_str());
}
Ref<vm::Cell> update = cs.prefetch_ref(2); // Merkle update
auto next_state_root = vm::MerkleUpdate::apply(root, update);
if (next_state_root.is_null()) {
return td::Status::Error("cannot apply Merkle update from block "s + block->block_id().id.to_str() +
" to previous state");
}
blkid = block->block_id();
// boc.reset(); // keep old lazy static bag of cells in case undeserialized branches are inherited by the current state
data.clear();
root = std::move(next_state_root);
rhash = root->get_hash().bits();
block::gen::ShardStateUnsplit::Record info;
if (!tlb::unpack_cell(root, info)) {
return td::Status::Error(
-668, "newly-computed shardchain state for block "s + blkid.id.to_str() + " does not contain a valid header");
}
lt = info.gen_lt;
utime = info.gen_utime;
before_split_ = info.before_split;
fake_split_ = fake_merge_ = false;
block::ShardId id{info.shard_id};
ton::BlockId hdr_id{ton::ShardIdFull(id), info.seq_no};
if (!id.is_valid() || get_shard() != ton::ShardIdFull(id) || get_seqno() != info.seq_no) {
return td::Status::Error(-668, "header of newly-computed shardchain state for block "s + blkid.id.to_str() +
" contains a BlockId " + hdr_id.to_str() +
" different from the one originally required");
}
return td::Status::OK();
}
td::Result<td::Ref<ShardState>> ShardStateQ::merge_with(const ShardState& with) const {
const ShardStateQ& other = dynamic_cast<const ShardStateQ&>(with);
if (fake_split_ || fake_merge_ || other.fake_split_ || other.fake_merge_) {
return td::Status::Error(-666, "cannot merge blockchain states which have been split or merged immediately before");
}
if (before_split_ || other.before_split_) {
return td::Status::Error(-666, "cannot merge blockchain states which have before_split flag set");
}
if (blkid.is_masterchain()) {
return td::Status::Error(-666, "cannot merge masterchain states");
}
auto shard1 = blkid.shard_full(), shard2 = other.blkid.shard_full();
if (shard1 == shard2 || !ton::shard_is_sibling(shard1, shard2)) {
return td::Status::Error(-666, PSTRING() << "cannot merge states of shards " << shard1.to_str() << " and "
<< shard2.to_str() << " that are not siblings");
}
Ref<vm::Cell> root, root1 = root_cell(), root2 = other.root_cell();
if (shard1.shard > shard2.shard) {
std::swap(root1, root2);
}
if (!block::gen::t_ShardState.cell_pack_split_state(root, std::move(root1), std::move(root2))) {
return td::Status::Error(-667, "cannot construct a virtual split_state after a merge");
}
auto m = Ref<ShardStateQ>{
true,
ton::BlockIdExt{blkid.id.workchain, ton::shard_parent(blkid.id.shard),
std::max(blkid.seqno(), other.blkid.seqno()), ton::Bits256::zero(), ton::Bits256::zero()},
root};
auto& ms = m.unique_write();
ms.fake_merge_ = true;
ms.rhash = root->get_hash().bits();
ms.lt = std::max(lt, other.lt);
ms.utime = std::max(utime, other.utime);
ms.bocs_ = bocs_;
ms.bocs_.insert(ms.bocs_.end(), other.bocs_.begin(), other.bocs_.end());
return std::move(m);
}
td::Result<std::pair<td::Ref<ShardState>, td::Ref<ShardState>>> ShardStateQ::split() const {
if (fake_split_ || fake_merge_) {
return td::Status::Error(-666, "cannot split blockchain state which has been split or merged immediately before");
}
if (!before_split_) {
return td::Status::Error(-666, "cannot split blockchain state which does not have before_split flag set");
}
if (blkid.is_masterchain()) {
return td::Status::Error(-666, "cannot split masterchain state");
}
auto l = Ref<ShardStateQ>{true, *this};
auto r = Ref<ShardStateQ>{true, *this};
auto& ls = l.unique_write();
auto& rs = r.unique_write();
ls.fake_split_ = rs.fake_split_ = true;
ls.blkid.id.shard = ton::shard_child(blkid.id.shard, true);
rs.blkid.id.shard = ton::shard_child(blkid.id.shard, false);
return std::make_pair<Ref<ShardState>, Ref<ShardState>>(std::move(l), std::move(r));
}
td::Result<td::BufferSlice> ShardStateQ::serialize() const {
td::PerfWarningTimer perf_timer_{"serializestate", 0.1};
if (!data.is_null()) {
return data.clone();
}
if (root.is_null()) {
return td::Status::Error(-666, "cannot serialize an uninitialized state");
}
vm::BagOfCells new_boc;
new_boc.set_root(root);
auto res = new_boc.import_cells();
if (res.is_error()) {
return res.move_as_error();
}
auto st_res = new_boc.serialize_to_slice(31);
if (st_res.is_error()) {
LOG(ERROR) << "cannot serialize a shardchain state";
return st_res.move_as_error();
}
// data = st_res.move_as_ok();
// return data.clone();
return st_res.move_as_ok();
}
MasterchainStateQ::MasterchainStateQ(const BlockIdExt& _id, td::BufferSlice _data)
: MasterchainState(), ShardStateQ(_id, std::move(_data)) {
}
MasterchainStateQ::MasterchainStateQ(const BlockIdExt& _id, Ref<vm::Cell> _root, td::BufferSlice _data)
: MasterchainState(), ShardStateQ(_id, std::move(_root), std::move(_data)) {
}
MasterchainStateQ* MasterchainStateQ::make_copy() const {
return new MasterchainStateQ(*this);
}
td::Result<Ref<MasterchainStateQ>> MasterchainStateQ::fetch(const BlockIdExt& _id, td::BufferSlice _data,
Ref<vm::Cell> _root) {
if (!ShardIdFull(_id).is_masterchain_ext()) {
return td::Status::Error(-666,
"invalid masterchain block/state id passed for creating a new masterchain state object");
}
Ref<MasterchainStateQ> res{true, _id, std::move(_root), std::move(_data)};
td::Status err = res.unique_write().mc_init();
if (err.is_error()) {
return err;
} else {
return std::move(res);
}
}
td::Status MasterchainStateQ::mc_init() {
auto err = init();
if (err.is_error()) {
return err;
}
return mc_reinit();
}
td::Status MasterchainStateQ::mc_reinit() {
auto res = block::ConfigInfo::extract_config(
root_cell(), block::ConfigInfo::needStateRoot | block::ConfigInfo::needValidatorSet |
block::ConfigInfo::needShardHashes | block::ConfigInfo::needPrevBlocks);
cur_validators_.reset();
next_validators_.reset();
if (res.is_error()) {
return res.move_as_error();
}
config_ = res.move_as_ok();
CHECK(config_);
CHECK(config_->set_block_id_ext(get_block_id()));
auto cv_root = config_->get_config_param(35, 34);
if (cv_root.not_null()) {
TRY_RESULT(validators, block::Config::unpack_validator_set(std::move(cv_root)));
cur_validators_ = std::move(validators);
}
auto nv_root = config_->get_config_param(37, 36);
if (nv_root.not_null()) {
TRY_RESULT(validators, block::Config::unpack_validator_set(std::move(nv_root)));
next_validators_ = std::move(validators);
}
zerostate_id_ = config_->get_zerostate_id();
return td::Status::OK();
}
td::Status MasterchainStateQ::apply_block(BlockIdExt id, td::Ref<BlockData> block) {
auto err = ShardStateQ::apply_block(id, block);
if (err.is_error()) {
return err;
}
config_.reset();
err = mc_reinit();
if (err.is_error()) {
LOG(ERROR) << "cannot extract masterchain-specific state data from newly-computed state for block "
<< id.id.to_str() << " : " << err.to_string();
}
return err;
}
td::Status MasterchainStateQ::prepare() {
if (config_) {
return td::Status::OK();
}
return mc_reinit();
}
Ref<ValidatorSet> MasterchainStateQ::compute_validator_set(ShardIdFull shard, const block::ValidatorSet& vset,
UnixTime time, CatchainSeqno ccseqno) const {
if (!config_) {
return {};
}
LOG(DEBUG) << "in compute_validator_set() for " << shard.to_str();
auto nodes = config_->compute_validator_set_cc(shard, vset, time, &ccseqno);
if (nodes.empty()) {
return {};
}
return Ref<ValidatorSetQ>{true, ccseqno, shard, std::move(nodes)};
}
Ref<ValidatorSet> MasterchainStateQ::get_validator_set(ShardIdFull shard) const {
if (!config_ || !cur_validators_) {
LOG(ERROR) << "MasterchainStateQ::get_validator_set() : no config or no cur_validators";
return {};
}
return compute_validator_set(shard, *cur_validators_, config_->utime, 0);
}
Ref<ValidatorSet> MasterchainStateQ::get_validator_set(ShardIdFull shard, UnixTime ts, CatchainSeqno cc_seqno) const {
if (!config_ || !cur_validators_) {
LOG(ERROR) << "MasterchainStateQ::get_validator_set() : no config or no cur_validators";
return {};
}
auto nodes = config_->compute_validator_set(shard, *cur_validators_, ts, cc_seqno);
if (nodes.empty()) {
return {};
}
return Ref<ValidatorSetQ>{true, cc_seqno, shard, std::move(nodes)};
}
// next = -1 -> prev, next = 0 -> cur
Ref<ValidatorSet> MasterchainStateQ::get_total_validator_set(int next) const {
if (!config_) {
LOG(ERROR) << "MasterchainStateQ::get_total_validator_set() : no config";
return {};
}
auto nodes = config_->compute_total_validator_set(next);
if (nodes.empty()) {
return {};
}
return Ref<ValidatorSetQ>{true, 0, ton::ShardIdFull{}, std::move(nodes)};
}
Ref<ValidatorSet> MasterchainStateQ::get_next_validator_set(ShardIdFull shard) const {
if (!config_ || !cur_validators_) {
LOG(ERROR) << "MasterchainStateQ::get_next_validator_set() : no config or no cur_validators";
return {};
}
if (!next_validators_) {
return compute_validator_set(shard, *cur_validators_, config_->utime, 1);
}
bool is_mc = shard.is_masterchain();
auto ccv_cfg = config_->get_catchain_validators_config();
unsigned cc_lifetime = is_mc ? ccv_cfg.mc_cc_lifetime : ccv_cfg.shard_cc_lifetime;
if (next_validators_->utime_since > (config_->utime / cc_lifetime + 1) * cc_lifetime) {
return compute_validator_set(shard, *cur_validators_, config_->utime, 1);
} else {
return compute_validator_set(shard, *next_validators_, config_->utime, 1);
}
}
std::vector<Ref<McShardHash>> MasterchainStateQ::get_shards() const {
if (!config_) {
return {};
}
std::vector<ton::BlockId> shard_ids = config_->get_shard_hash_ids(true);
std::vector<Ref<McShardHash>> v;
for (const auto& b : shard_ids) {
v.emplace_back(config_->get_shard_hash(ton::ShardIdFull(b)));
CHECK(v.back().not_null());
}
return v;
}
td::Ref<McShardHash> MasterchainStateQ::get_shard_from_config(ShardIdFull shard) const {
if (!config_) {
return {};
}
return config_->get_shard_hash(shard);
}
bool MasterchainStateQ::rotated_all_shards() const {
if (!config_) {
return false;
}
return config_->rotated_all_shards();
}
bool MasterchainStateQ::get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt) const {
return config_ && config_->get_old_mc_block_id(seqno, blkid, end_lt);
}
bool MasterchainStateQ::check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict) const {
return config_ && config_->check_old_mc_block_id(blkid, strict);
}
td::uint32 MasterchainStateQ::min_split_depth(WorkchainId workchain_id) const {
if (!config_) {
return 0;
}
auto wc_info = config_->get_workchain_info(workchain_id);
return wc_info.not_null() ? wc_info->actual_min_split : 0;
}
td::uint32 MasterchainStateQ::soft_min_split_depth(WorkchainId workchain_id) const {
if (!config_) {
return 0;
}
auto wc_info = config_->get_workchain_info(workchain_id);
return wc_info.not_null() ? wc_info->min_split : 0;
}
BlockSeqno MasterchainStateQ::min_ref_masterchain_seqno() const {
return config_ ? config_->min_ref_mc_seqno_ : 0;
}
BlockIdExt MasterchainStateQ::last_key_block_id() const {
BlockIdExt block_id;
LogicalTime lt = 0;
if (config_) {
config_->get_last_key_block(block_id, lt);
}
return block_id;
}
BlockIdExt MasterchainStateQ::next_key_block_id(BlockSeqno seqno) const {
BlockIdExt block_id;
if (config_) {
config_->get_next_key_block(seqno, block_id);
}
return block_id;
}
BlockIdExt MasterchainStateQ::prev_key_block_id(BlockSeqno seqno) const {
BlockIdExt block_id;
if (config_) {
config_->get_prev_key_block(seqno, block_id);
}
return block_id;
}
} // namespace validator
} // namespace ton

157
validator/impl/shard.hpp Normal file
View file

@ -0,0 +1,157 @@
/*
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 "interfaces/shard.h"
#include "vm/db/StaticBagOfCellsDb.h"
#include "block/mc-config.h"
namespace ton {
namespace validator {
using td::Ref;
class ShardStateQ : virtual public ShardState {
protected:
BlockIdExt blkid;
private:
RootHash rhash;
td::BufferSlice data;
std::vector<std::shared_ptr<vm::StaticBagOfCellsDb>> bocs_;
Ref<vm::Cell> root;
LogicalTime lt{0};
UnixTime utime{0};
bool before_split_{false};
bool fake_split_{false};
bool fake_merge_{false};
protected:
friend class Ref<ShardStateQ>;
ShardStateQ(const ShardStateQ& other);
ShardStateQ(ShardStateQ&& other) = default;
public:
td::Status init();
ShardStateQ(const BlockIdExt& _id, td::BufferSlice _data);
ShardStateQ(const BlockIdExt& _id, Ref<vm::Cell> _root, td::BufferSlice _data = {});
virtual ~ShardStateQ() = default;
static td::Result<Ref<ShardStateQ>> fetch(const BlockIdExt& _id, td::BufferSlice _data, Ref<vm::Cell> _root = {});
bool disable_boc() const override {
return false;
}
ShardIdFull get_shard() const override {
return ShardIdFull(blkid);
}
BlockSeqno get_seqno() const override {
return blkid.id.seqno;
}
BlockIdExt get_block_id() const override {
return blkid;
}
RootHash root_hash() const override {
return rhash;
}
Ref<vm::Cell> root_cell() const override {
return root;
}
bool before_split() const override {
return before_split_;
}
UnixTime get_unix_time() const override {
return utime;
}
LogicalTime get_logical_time() const override {
return lt;
}
td::Status validate_deep() const override;
ShardStateQ* make_copy() const override;
td::Result<Ref<MessageQueue>> message_queue() const override;
td::Status apply_block(BlockIdExt id, Ref<BlockData> block) override;
td::Result<Ref<ShardState>> merge_with(const ShardState& with) const override;
td::Result<std::pair<Ref<ShardState>, Ref<ShardState>>> split() const override;
td::Result<td::BufferSlice> serialize() const override;
};
#if TD_MSVC
#pragma warning(push)
#pragma warning(disable : 4250) // MasterchainState is an interface, so there is no problem here
#endif
class MasterchainStateQ : public MasterchainState, public ShardStateQ {
public:
MasterchainStateQ(const BlockIdExt& _id, td::BufferSlice _data);
MasterchainStateQ(const BlockIdExt& _id, Ref<vm::Cell> _root, td::BufferSlice _data = {});
virtual ~MasterchainStateQ() = default;
td::Status apply_block(BlockIdExt id, Ref<BlockData> block) override;
Ref<ValidatorSet> get_validator_set(ShardIdFull shard) const override;
Ref<ValidatorSet> get_next_validator_set(ShardIdFull shard) const override;
Ref<ValidatorSet> get_total_validator_set(int next) const override; // next = -1 -> prev, next = 0 -> cur
Ref<ValidatorSet> get_validator_set(ShardIdFull shard, UnixTime ts, CatchainSeqno cc_seqno) const;
bool rotated_all_shards() const override;
std::vector<Ref<McShardHash>> get_shards() const override;
td::Ref<McShardHash> get_shard_from_config(ShardIdFull shard) const override;
bool ancestor_is_valid(BlockIdExt id) const override {
return check_old_mc_block_id(id);
}
bool workchain_is_active(WorkchainId workchain_id) const override {
return has_workchain(workchain_id);
}
bool has_workchain(WorkchainId workchain) const {
return config_ && config_->has_workchain(workchain);
}
td::uint32 min_split_depth(WorkchainId workchain_id) const override;
td::uint32 soft_min_split_depth(WorkchainId workchain_id) const override;
BlockSeqno min_ref_masterchain_seqno() const override;
td::Status prepare() override;
ZeroStateIdExt get_zerostate_id() const {
return zerostate_id_;
}
ValidatorSessionConfig get_consensus_config() const override {
return config_->get_consensus_config();
}
BlockIdExt last_key_block_id() const override;
BlockIdExt next_key_block_id(BlockSeqno seqno) const override;
BlockIdExt prev_key_block_id(BlockSeqno seqno) const override;
MasterchainStateQ* make_copy() const override;
static td::Result<Ref<MasterchainStateQ>> fetch(const BlockIdExt& _id, td::BufferSlice _data,
Ref<vm::Cell> _root = {});
bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
ton::LogicalTime* end_lt = nullptr) const override;
bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const override;
std::shared_ptr<block::ConfigInfo> get_config() const {
return config_;
}
private:
ZeroStateIdExt zerostate_id_;
std::shared_ptr<block::ConfigInfo> config_;
std::shared_ptr<block::ValidatorSet> cur_validators_, next_validators_;
MasterchainStateQ(const MasterchainStateQ& other) = default;
td::Status mc_init();
td::Status mc_reinit();
Ref<ValidatorSet> compute_validator_set(ShardIdFull shard, const block::ValidatorSet& vset, UnixTime time,
CatchainSeqno cc_seqno) const;
};
#if TD_MSVC
#pragma warning(pop)
#endif
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,118 @@
/*
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 "signature-set.hpp"
#include "auto/tl/ton_api.hpp"
#include "adnl/utils.hpp"
#include "vm/dict.h"
#include "vm/boc.h"
namespace ton {
namespace validator {
using td::Ref;
BlockSignatureSetQ *BlockSignatureSetQ::make_copy() const {
std::vector<BlockSignature> vec;
auto &sigs = signatures();
for (auto &s : sigs) {
vec.emplace_back(BlockSignature{s.node, s.signature.clone()});
}
return new BlockSignatureSetQ{std::move(vec)};
}
td::BufferSlice BlockSignatureSetQ::serialize() const {
if (!signatures().size()) {
return {};
}
Ref<vm::Cell> root;
CHECK(serialize_to(root));
//std::cerr << "serializing BlockSignatureSet: ";
//vm::CellSlice{vm::NoVm{}, root}.print_rec(std::cerr);
//std::cerr << std::endl;
auto res = vm::std_boc_serialize(std::move(root));
LOG_CHECK(res.is_ok()) << res.move_as_error();
return res.move_as_ok();
}
bool BlockSignatureSetQ::serialize_to(Ref<vm::Cell> &ref) const {
auto &sigs = signatures();
vm::Dictionary dict{16}; // HashmapE 16 CryptoSignaturePair
for (unsigned i = 0; i < sigs.size(); i++) {
vm::CellBuilder cb;
if (!(cb.store_bits_bool(sigs[i].node) // sig_pair$_ node_id_short:bits256
&& cb.store_long_bool(5, 4) // ed25519_signature#5
&& sigs[i].signature.size() == 64 // signature must be 64 bytes long
&& cb.store_bytes_bool(sigs[i].signature.data(), 64) // R:bits256 s:bits256
&& dict.set_builder(td::BitArray<16>{i}, cb, vm::Dictionary::SetMode::Add))) {
return false;
}
}
ref = std::move(dict).extract_root_cell();
CHECK(sigs.size());
CHECK(ref.not_null());
return true;
}
Ref<BlockSignatureSet> BlockSignatureSetQ::fetch(td::BufferSlice data) {
if (!data.size()) {
return Ref<BlockSignatureSetQ>{true, std::vector<BlockSignature>{}};
}
auto res = vm::std_boc_deserialize(std::move(data));
if (res.is_error()) {
return {};
}
return fetch(res.move_as_ok());
}
Ref<BlockSignatureSet> BlockSignatureSetQ::fetch(Ref<vm::Cell> cell) {
if (cell.is_null()) {
return {};
}
try {
std::vector<BlockSignature> vec;
vm::Dictionary dict{std::move(cell), 16}; // HashmapE 16 CryptoSignaturePair
unsigned i = 0;
if (!dict.check_for_each([&](Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n) -> bool {
if (key.get_int(n) != i || cs_ref->size_ext() != 256 + 4 + 256 + 256) {
return false;
}
vm::CellSlice cs{*cs_ref};
NodeIdShort node_id;
unsigned char signature[64];
if (!(cs.fetch_bits_to(node_id) // sig_pair$_ node_id_short:bits256
&& cs.fetch_ulong(4) == 5 // ed25519_signature#5
&& cs.fetch_bytes(signature, 64) // R:bits256 s:bits256
&& !cs.size_ext())) {
return false;
}
vec.emplace_back(BlockSignature{node_id, td::BufferSlice{td::Slice{signature, 64}}});
++i;
return i <= max_signatures;
})) {
return {};
}
return Ref<BlockSignatureSetQ>{true, std::move(vec)};
} catch (vm::VmError) {
return {};
}
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,45 @@
/*
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 "ton/ton-types.h"
#include "validator/interfaces/signature-set.h"
#include "vm/cells.h"
namespace ton {
namespace validator {
using td::Ref;
class BlockSignatureSetQ : public BlockSignatureSet {
enum { max_signatures = 1024 };
public:
BlockSignatureSetQ(std::vector<BlockSignature> signatures) : BlockSignatureSet{std::move(signatures)} {
}
BlockSignatureSetQ* make_copy() const override;
td::BufferSlice serialize() const override;
bool serialize_to(Ref<vm::Cell>& ref) const;
static Ref<BlockSignatureSet> fetch(td::BufferSlice data);
static Ref<BlockSignatureSet> fetch(td::Ref<vm::Cell> cell);
};
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,541 @@
/*
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 "top-shard-descr.hpp"
#include "common/errorcode.h"
#include "shard.hpp"
#include "signature-set.hpp"
#include "validator-set.hpp"
#include "vm/cells.h"
#include "vm/cells/MerkleProof.h"
#include "vm/boc.h"
#include "block/block-parse.h"
#include "block/block-auto.h"
namespace ton {
namespace validator {
using td::Ref;
using namespace std::literals::string_literals;
ShardTopBlockDescrQ* ShardTopBlockDescrQ::make_copy() const {
return new ShardTopBlockDescrQ{*this};
}
td::Status ShardTopBlockDescrQ::unpack_one_proof(BlockIdExt& cur_id, Ref<vm::Cell> proof_root, bool is_head) {
auto virt_root = vm::MerkleProof::virtualize(proof_root, 1);
if (virt_root.is_null()) {
return td::Status::Error(-666, std::string{} + "link for block " + cur_id.to_str() +
" inside ShardTopBlockDescr of " + block_id_.to_str() +
" does not contain a valid Merkle proof for the block header");
}
RootHash virt_hash{virt_root->get_hash().bits()};
if (virt_hash != cur_id.root_hash) {
return td::Status::Error(-666, std::string{} + "link for block " + cur_id.to_str() +
" inside ShardTopBlockDescr of " + block_id_.to_str() +
" contains a Merkle proof with incorrect root hash: expected " +
cur_id.root_hash.to_hex() + ", found " + virt_hash.to_hex());
}
bool after_split;
BlockIdExt mc_blkid;
auto res = block::unpack_block_prev_blk_try(virt_root, cur_id, link_prev_, mc_blkid, after_split);
if (res.is_error()) {
return td::Status::Error(-666, std::string{"error in link for block "} + cur_id.to_str() +
" inside ShardTopBlockDescr of " + block_id_.to_str() + ": " +
res.move_as_error().to_string());
}
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
block::gen::ValueFlow::Record flow;
block::CurrencyCollection fees_collected, funds_created;
if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(blk.info, info) && !info.version &&
block::gen::t_ValueFlow.force_validate_ref(blk.value_flow) && tlb::unpack_cell(blk.value_flow, flow) &&
fees_collected.unpack(flow.fees_collected) && funds_created.unpack(flow.r2.created))) {
return td::Status::Error(-666, std::string{"cannot unpack block header in link for block "} + cur_id.to_str());
}
CHECK(after_split == info.after_split);
if (info.gen_catchain_seqno != catchain_seqno_) {
return td::Status::Error(
-666, PSTRING() << "link for block " << cur_id.to_str()
<< " is invalid because block header has catchain_seqno = " << info.gen_catchain_seqno
<< " while ShardTopBlockDescr declares " << catchain_seqno_);
}
if (info.gen_validator_list_hash_short != validator_set_hash_) {
return td::Status::Error(-666, PSTRING() << "link for block " << cur_id.to_str()
<< " is invalid because block header has validator_set_hash = "
<< info.gen_validator_list_hash_short
<< " while ShardTopBlockDescr declares " << validator_set_hash_);
}
if (chain_mc_blk_ids_.empty()) {
after_split_ = info.after_split;
after_merge_ = info.after_merge;
before_split_ = info.before_split;
gen_utime_ = first_gen_utime_ = info.gen_utime;
} else {
auto nx_mc_seqno = chain_mc_blk_ids_.back().id.seqno;
if (nx_mc_seqno < mc_blkid.id.seqno) {
return td::Status::Error(
-666, std::string{"link for block "} + cur_id.to_str() + " refers to masterchain block " + mc_blkid.to_str() +
" while the next block refers to an older masterchain block " + chain_mc_blk_ids_.back().to_str());
} else if (nx_mc_seqno == mc_blkid.id.seqno && chain_mc_blk_ids_.back() != mc_blkid) {
return td::Status::Error(-666, std::string{"link for block "} + cur_id.to_str() +
" refers to masterchain block " + mc_blkid.to_str() +
" while the next block refers to a different same height masterchain block " +
chain_mc_blk_ids_.back().to_str());
}
if (info.before_split) {
return td::Status::Error(
-666, std::string{"intermediate link for block "} + cur_id.to_str() + " is declared to be before a split");
}
if (info.gen_utime > first_gen_utime_) {
return td::Status::Error(-666, PSTRING() << "block creation unixtime goes back from " << info.gen_utime << " to "
<< first_gen_utime_ << " in intermediate link for blocks "
<< cur_id.to_str() << " and " << chain_blk_ids_.back().to_str());
}
first_gen_utime_ = info.gen_utime;
}
chain_mc_blk_ids_.push_back(mc_blkid);
chain_blk_ids_.push_back(cur_id);
chain_fees_.emplace_back(std::move(fees_collected), std::move(funds_created));
if (!is_head) {
if (info.after_split || info.after_merge) {
return td::Status::Error(
-666, std::string{"intermediate link for block "} + cur_id.to_str() + " is after a split or a merge");
}
CHECK(link_prev_.size() == 1);
CHECK(link_prev_[0].id.shard == cur_id.id.shard);
if (link_prev_[0].id.seqno + 1 != cur_id.id.seqno) {
return td::Status::Error(-666, std::string{"intermediate link for block "} + cur_id.to_str() +
" increases seqno by more than one from " + link_prev_[0].to_str());
}
cur_id = link_prev_[0];
} else {
hd_after_split_ = info.after_split;
hd_after_merge_ = info.after_merge;
CHECK(link_prev_.size() == 1U + info.after_merge);
BlockSeqno sq = link_prev_[0].id.seqno;
if (hd_after_merge_) {
sq = std::max(sq, link_prev_[1].id.seqno);
}
if (sq + 1 != cur_id.id.seqno) {
return td::Status::Error(
-666, std::string{"initial link for block "} + cur_id.to_str() + " increases seqno by more than one from " +
link_prev_[0].to_str() +
(hd_after_merge_ ? std::string{" + "} + link_prev_[1].to_str() : std::string{""}));
}
}
return td::Status::OK();
}
td::Status ShardTopBlockDescrQ::unpack() {
if (root_.is_null()) {
if (data_.empty()) {
return td::Status::Error(-666, "Shard top block description has no serialized data and no root cell");
}
auto res = vm::std_boc_deserialize(data_.clone());
if (res.is_error()) {
return res.move_as_error();
}
root_ = res.move_as_ok();
}
block::gen::TopBlockDescr::Record rec;
if (!(block::gen::t_TopBlockDescr.force_validate_ref(root_) && tlb::unpack_cell(root_, rec) &&
block::tlb::t_BlockIdExt.unpack(rec.proof_for.write(), block_id_))) {
std::cerr << "invalid ShardTopBlockDescr: ";
block::gen::t_TopBlockDescr.print_ref(std::cerr, root_);
vm::load_cell_slice(root_).print_rec(std::cerr);
return td::Status::Error(-666, "Shard top block description is not a valid TopBlockDescr TL-B object");
}
LOG(DEBUG) << "unpacking a ShardTopBlockDescr for " << block_id_.to_str() << " with " << rec.len << " links";
CHECK(rec.len > 0 && rec.len <= 8);
// unpack signatures
Ref<vm::Cell> sig_root = rec.signatures->prefetch_ref();
if (sig_root.not_null()) {
vm::CellSlice cs{vm::NoVmOrd(), sig_root};
bool have_sig;
if (!(cs.fetch_ulong(8) == 0x11 // block_signatures#11
&& cs.fetch_uint_to(32, validator_set_hash_) // validator_set_hash:uint32
&& cs.fetch_uint_to(32, catchain_seqno_) // catchain_seqno:uint32
&& cs.fetch_uint_to(32, sig_count_) // sig_count:uint32
&& cs.fetch_uint_to(64, sig_weight_) // sig_weight:uint64
&& cs.fetch_bool_to(have_sig) && have_sig == (sig_count_ > 0) &&
cs.size_ext() == ((unsigned)have_sig << 16))) {
return td::Status::Error(
-666, std::string{"cannot parse BlockSignatures in ShardTopBlockDescr for "} + block_id_.to_str());
}
sig_root_ = cs.prefetch_ref();
sig_set_ = BlockSignatureSetQ::fetch(sig_root_);
if (sig_set_.is_null() && sig_count_) {
return td::Status::Error(
-666, std::string{"cannot deserialize signature list in ShardTopBlockDescr for "} + block_id_.to_str());
}
} else {
validator_set_hash_ = 0;
catchain_seqno_ = 0;
sig_count_ = 0;
sig_weight_ = 0;
sig_root_.clear();
}
if (!sig_count_ && !is_fake_) {
return td::Status::Error(-666, std::string{"invalid BlockSignatures in ShardTopBlockDescr for "} +
block_id_.to_str() + ": no signatures present, and fake mode is not enabled");
}
is_fake_ = !sig_count_;
// unpack proof link chain
auto chain = std::move(rec.chain);
BlockIdExt cur_id = block_id_;
for (int i = 0; i < rec.len; i++) {
CHECK(chain->size_ext() == (i == rec.len - 1 ? 0x10000u : 0x20000u));
auto proof = chain->prefetch_ref();
proof_roots_.push_back(proof);
if (i < rec.len - 1) {
chain = vm::load_cell_slice_ref(chain->prefetch_ref(1));
}
try {
auto res = unpack_one_proof(cur_id, std::move(proof), i == rec.len - 1);
if (res.is_error()) {
return res;
}
} catch (vm::VmError& err) {
return td::Status::Error("error unpacking proof link for "s + cur_id.to_str() + " in ShardTopBlockDescr for " +
block_id_.to_str() + " : " + err.get_msg());
} catch (vm::VmVirtError& err) {
return td::Status::Error("virtualization error unpacking proof link for "s + cur_id.to_str() +
" in ShardTopBlockDescr for " + block_id_.to_str() + " : " + err.get_msg());
}
}
is_valid_ = true;
return td::Status::OK();
}
td::Result<Ref<ShardTopBlockDescrQ>> ShardTopBlockDescrQ::fetch(td::BufferSlice data, bool is_fake) {
Ref<ShardTopBlockDescrQ> ref{true, std::move(data), is_fake};
auto err = ref.unique_write().unpack();
if (err.is_error()) {
return err;
} else {
return std::move(ref);
}
}
td::Result<Ref<ShardTopBlockDescrQ>> ShardTopBlockDescrQ::fetch(Ref<vm::Cell> root, bool is_fake) {
Ref<ShardTopBlockDescrQ> ref{true, std::move(root), is_fake};
auto err = ref.unique_write().unpack();
if (err.is_error()) {
return err;
} else {
return std::move(ref);
}
}
bool ShardTopBlockDescrQ::may_be_valid(BlockHandle last_masterchain_block_handle,
Ref<MasterchainState> last_masterchain_block_state) const {
int res_flags = 0;
return prevalidate(last_masterchain_block_handle->id(), std::move(last_masterchain_block_state),
Mode::allow_next_vset, res_flags)
.is_ok();
}
td::Result<int> ShardTopBlockDescrQ::validate_internal(BlockIdExt last_mc_block_id, Ref<MasterchainState> last_mc_state,
int& res_flags, int mode) const {
if (!is_valid()) {
return td::Status::Error(-666, "ShardTopBlockDescr is invalid or uninitialized");
}
CHECK(chain_blk_ids_.size() > 0 && chain_blk_ids_.size() <= 8);
CHECK(chain_mc_blk_ids_.size() == chain_blk_ids_.size());
Ref<MasterchainStateQ> state = Ref<MasterchainStateQ>(last_mc_state);
if (state.is_null()) {
return td::Status::Error(-666, "cannot validate ShardTopBlockDescr: no masterchain state given");
}
if (last_mc_block_id.id.seqno < chain_mc_blk_ids_[0].id.seqno) {
BlockSeqno delta = chain_mc_blk_ids_[0].id.seqno - last_mc_block_id.id.seqno;
// too new
if ((mode & Mode::fail_new) || (delta > 8 && (mode & Mode::fail_too_new))) {
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is too new for us: it refers to masterchain block " +
chain_mc_blk_ids_[0].id.to_str() + " but we know only " +
last_mc_block_id.to_str());
}
return -1; // valid, but too new
}
auto config = state->get_config();
BlockSeqno next_mc_seqno = ~BlockSeqno(0);
for (const auto& mcid : chain_mc_blk_ids_) {
if (mcid.id.seqno > next_mc_seqno) {
res_flags |= 1; // permanently invalid
return td::Status::Error(
-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid because its chain refers to masterchain blocks with non-monotonic seqno");
}
next_mc_seqno = mcid.id.seqno;
auto valid =
(mcid.id.seqno == last_mc_block_id.id.seqno) ? (mcid == last_mc_block_id) : config->check_old_mc_block_id(mcid);
if (!valid) {
res_flags |= 1; // permanently invalid
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid because it refers to masterchain block " + mcid.to_str() +
" which is not an ancestor of our block " + last_mc_block_id.to_str());
}
}
auto oldl = config->get_shard_hash(ShardIdFull{block_id_.id.workchain, block_id_.id.shard - 1}, false);
if (oldl.is_null()) {
return td::Status::Error(
-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid or too new because this workchain is absent from known masterchain configuration");
}
if (oldl->seqno() >= block_id_.id.seqno) {
// we know a shardchain block that it is at least as new as this one
if (!(mode & allow_old)) {
res_flags |= 1; // permanently invalidate unless old ShardTopBlockDescr are allowed
}
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is too old: we already know a newer shardchain block " + oldl->blk_.to_str());
}
if (oldl->seqno() < link_prev_[0].id.seqno) {
if (mode & Mode::fail_new) {
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is too new for us: it starts from shardchain block " +
link_prev_[0].id.to_str() + " but we know only " + oldl->blk_.to_str());
}
return -1; // valid, but too new
}
auto oldr = oldl;
if (ton::shard_is_proper_ancestor(shard(), oldl->shard())) {
oldr = config->get_shard_hash(ShardIdFull{block_id_.id.workchain, block_id_.id.shard + 1}, false);
if (oldr.is_null()) {
return td::Status::Error(
-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid or too new because this workchain is absent from known masterchain configuration (?)");
}
if (oldr->seqno() >= block_id_.id.seqno) {
// we know a shardchain block that it is at least as new as this one
res_flags |= 1; // permanently invalidate unless old ShardTopBlockDescr are allowed
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid in a strange fashion: we already know a newer shardchain block " +
oldr->blk_.to_str() +
" but only in the right branch; corresponds to a shardchain fork?");
}
CHECK(ton::shard_is_proper_ancestor(shard(), oldr->shard()));
CHECK(oldl->shard() < oldr->shard());
} else {
CHECK(ton::shard_is_ancestor(oldl->shard(), shard()));
}
if (oldr->seqno() < link_prev_.back().id.seqno) {
if (mode & Mode::fail_new) {
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is too new for us: it starts from shardchain block " +
link_prev_.back().id.to_str() + " but we know only " + oldr->blk_.to_str());
}
return -1; // valid, but too new
}
unsigned clen = block_id_.id.seqno - std::max(oldl->seqno(), oldr->seqno());
CHECK(clen > 0 && clen <= 8);
CHECK(clen <= size());
if (clen < size()) {
if (chain_blk_ids_[clen] != oldl->blk_) {
res_flags |= 1;
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid: it contains a reference to its ancestor " +
chain_blk_ids_[clen].to_str() +
" but the masterchain refers to another shardchain block " +
(oldl->seqno() < oldr->seqno() ? oldr->blk_.to_str() : oldl->blk_.to_str()) +
" of the same height");
}
CHECK(oldl->shard() == shard());
CHECK(oldl == oldr);
} else {
if (link_prev_[0] != oldl->blk_) {
res_flags |= 1;
return td::Status::Error(
-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid: it contains a reference to its ancestor " + link_prev_[0].to_str() +
" but the masterchain instead refers to another shardchain block " + oldl->blk_.to_str());
}
if (link_prev_.back() != oldr->blk_) {
res_flags |= 1;
return td::Status::Error(
-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" is invalid: it contains a reference to its ancestor " + link_prev_.back().to_str() +
" but the masterchain instead refers to another shardchain block " + oldr->blk_.to_str());
}
}
LOG(DEBUG) << "ShardTopBlockDescr for " << block_id_.to_str() << " appears to have a valid chain of " << clen
<< " new links out of " << size();
// check validator_set_{ts,hash}
int vset_ok = 0;
auto vset = state->get_validator_set(shard());
CHECK(vset.not_null());
if (vset->get_catchain_seqno() == catchain_seqno_ && vset->get_validator_set_hash() == validator_set_hash_) {
res_flags |= 4;
vset_ok = 1;
} else if (mode & allow_next_vset) {
auto nvset = state->get_next_validator_set(shard());
if (nvset->get_catchain_seqno() == catchain_seqno_ && nvset->get_validator_set_hash() == validator_set_hash_) {
vset = std::move(nvset);
res_flags |= 8;
vset_ok = 2;
}
}
if (!vset_ok) {
res_flags |= 1;
return td::Status::Error(-666, PSTRING()
<< "ShardTopBlockDescr for " << block_id_.to_str()
<< " is invalid because it refers to shard validator set with hash "
<< validator_set_hash_ << " and catchain_seqno " << catchain_seqno_
<< " while the current masterchain configuration expects "
<< vset->get_validator_set_hash() << " and " << vset->get_catchain_seqno());
}
// check signatures
if ((mode & skip_check_sig) || is_fake_ || sig_ok_) {
return (int)clen;
}
if (sig_bad_) {
return td::Status::Error(
-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() + " does not have valid signatures");
}
CHECK(sig_set_.not_null());
auto sig_chk = vset->check_signatures(block_id_.root_hash, block_id_.file_hash, sig_set_);
if (sig_chk.is_error()) {
res_flags |= 0x21;
return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() +
" does not have valid signatures: " + sig_chk.move_as_error().to_string());
}
res_flags |= 0x10; // signatures checked ok
auto wt = sig_chk.move_as_ok();
if (wt != sig_weight_) {
res_flags |= 1;
return td::Status::Error(-666, PSTRING() << "ShardTopBlockDescr for " << block_id_.to_str()
<< " has incorrect signature weight " << sig_weight_
<< " (actual weight is " << wt << ")");
}
LOG(DEBUG) << "ShardTopBlockDescr for " << block_id_.to_str() << " has valid validator signatures of total weight "
<< sig_weight_ << " out of " << Ref<ValidatorSetQ>(vset)->get_total_weight();
return (int)clen;
}
td::Result<int> ShardTopBlockDescrQ::prevalidate(BlockIdExt last_mc_block_id, Ref<MasterchainState> last_mc_state,
int mode, int& res_flags) const {
res_flags = 0;
auto res = validate_internal(last_mc_block_id, last_mc_state, res_flags, mode);
return res;
}
td::Result<int> ShardTopBlockDescrQ::validate(BlockIdExt last_mc_block_id, Ref<MasterchainState> last_mc_state,
int mode, int& res_flags) {
res_flags = 0;
auto res = validate_internal(last_mc_block_id, last_mc_state, res_flags, mode);
if (res_flags & 1) {
// permanently invalid
is_valid_ = false;
}
if (res_flags & 0x10) {
sig_ok_ = true;
}
if (res_flags & 0x20) {
sig_bad_ = true;
}
if (res_flags & 4) {
vset_cur_ = true;
vset_next_ = false;
} else if (res_flags & 8) {
vset_cur_ = false;
vset_next_ = true;
}
return res;
}
std::vector<BlockIdExt> ShardTopBlockDescrQ::get_prev_at(int pos) const {
if (!is_valid() || pos < 0 || (unsigned)pos > size()) {
return {};
}
if ((unsigned)pos < size()) {
return {chain_blk_ids_.at(pos)};
} else {
return link_prev_;
}
}
Ref<block::McShardHash> ShardTopBlockDescrQ::get_prev_descr(int pos, int sum_cnt) const {
if (!is_valid() || pos < 0 || sum_cnt < 0 || (unsigned)pos >= size() || (unsigned)sum_cnt > size() ||
(unsigned)(pos + sum_cnt) > size()) {
return {};
}
auto virt_root = vm::MerkleProof::virtualize(proof_roots_.at(pos), 1);
auto res = block::McShardHash::from_block(std::move(virt_root), chain_blk_ids_.at(pos).file_hash);
if (res.not_null()) {
auto& total_fees = res.write().fees_collected_;
auto& funds_created = res.write().funds_created_;
total_fees.set_zero();
funds_created.set_zero();
for (int i = 0; i < sum_cnt; i++) {
total_fees += chain_fees_.at(pos + i).first;
funds_created += chain_fees_[pos + i].second;
}
}
return res;
}
void ValidateShardTopBlockDescr::finish_query() {
if (promise_) {
promise_.set_value(std::move(descr_));
}
stop();
}
void ValidateShardTopBlockDescr::abort_query(td::Status reason) {
if (promise_) {
promise_.set_error(std::move(reason));
}
stop();
}
bool ValidateShardTopBlockDescr::fatal_error(td::Status error) {
abort_query(std::move(error));
return false;
}
bool ValidateShardTopBlockDescr::fatal_error(std::string err_msg, int err_code) {
return fatal_error(td::Status::Error(err_code, err_msg));
}
void ValidateShardTopBlockDescr::alarm() {
abort_query(td::Status::Error(ErrorCode::timeout, "timeout in ValidateShardTopBlockDescr"));
}
void ValidateShardTopBlockDescr::start_up() {
auto res = ShardTopBlockDescrQ::fetch(std::move(data_), is_fake_);
if (res.is_error()) {
abort_query(res.move_as_error());
return;
}
descr_ = res.move_as_ok();
CHECK(descr_->is_valid());
int res_flags = 0;
auto val_res = descr_.write().validate(mc_blkid_, state_, ShardTopBlockDescrQ::Mode::allow_next_vset, res_flags);
if (val_res.is_error()) {
abort_query(val_res.move_as_error());
return;
}
CHECK(descr_->is_valid());
finish_query();
}
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,183 @@
/*
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 "interfaces/shard-block.h"
#include "interfaces/validator-manager.h"
#include "block/mc-config.h"
namespace ton {
namespace validator {
using td::Ref;
class ValidateShardTopBlockDescr;
class ShardTopBlockDescrQBase : public ShardTopBlockDescription {
protected:
td::BufferSlice data_;
ShardTopBlockDescrQBase() = default;
ShardTopBlockDescrQBase(const ShardTopBlockDescrQBase& other) : data_(other.data_.clone()) {
}
ShardTopBlockDescrQBase(ShardTopBlockDescrQBase&&) = default;
ShardTopBlockDescrQBase(td::BufferSlice data) : data_(std::move(data)) {
}
};
class ShardTopBlockDescrQ final : public ShardTopBlockDescrQBase {
public:
enum Mode { fail_new = 1, fail_too_new = 2, allow_old = 4, allow_next_vset = 8, skip_check_sig = 16 };
ShardIdFull shard() const override {
return block_id_.shard_full();
}
BlockIdExt block_id() const override {
return block_id_;
}
bool may_be_valid(BlockHandle last_masterchain_block_handle,
Ref<MasterchainState> last_masterchain_block_state) const override;
td::Result<int> prevalidate(BlockIdExt last_mc_block_id, Ref<MasterchainState> last_mc_state, int mode,
int& res_flags) const;
td::Result<int> validate(BlockIdExt last_mc_block_id, Ref<MasterchainState> last_mc_state, int mode, int& res_flags);
td::BufferSlice serialize() const override {
return data_.clone();
}
bool before_split() const override {
return before_split_;
}
bool after_split() const override {
return after_split_;
}
bool after_merge() const override {
return after_merge_;
}
bool is_valid() const {
return is_valid_;
}
std::size_t size() const {
return chain_blk_ids_.size();
}
UnixTime generated_at() const override {
return gen_utime_;
}
CatchainSeqno catchain_seqno() const override {
return catchain_seqno_;
}
std::vector<BlockIdExt> get_prev_at(int pos) const;
Ref<block::McShardHash> get_prev_descr(int pos, int sum_cnt = 0) const;
Ref<block::McShardHash> get_top_descr(int sum_cnt = 0) const {
return get_prev_descr(0, sum_cnt);
}
Ref<vm::Cell> get_root() const {
return root_;
}
ShardTopBlockDescrQ(td::BufferSlice data, bool is_fake = false)
: ShardTopBlockDescrQBase(std::move(data)), is_fake_(is_fake) {
}
ShardTopBlockDescrQ(Ref<vm::Cell> root, bool is_fake = false)
: ShardTopBlockDescrQBase(), root_(std::move(root)), is_fake_(is_fake) {
}
ShardTopBlockDescrQ* make_copy() const override;
static td::Result<Ref<ShardTopBlockDescrQ>> fetch(td::BufferSlice data, bool is_fake = false);
static td::Result<Ref<ShardTopBlockDescrQ>> fetch(Ref<vm::Cell> root, bool is_fake = false);
protected:
friend class ValidateShardTopBlockDescr;
td::Status unpack();
private:
BlockIdExt block_id_;
Ref<vm::Cell> root_;
bool is_valid_{false};
bool is_fake_{false};
bool after_split_{false};
bool after_merge_{false};
bool before_split_{false};
bool hd_after_split_{false};
bool hd_after_merge_{false};
bool sig_ok_{false};
bool sig_bad_{false};
bool vset_cur_{false};
bool vset_next_{false};
UnixTime gen_utime_{0};
CatchainSeqno catchain_seqno_{0};
td::uint32 validator_set_hash_{0};
td::uint32 sig_count_;
ValidatorWeight sig_weight_;
Ref<vm::Cell> sig_root_;
Ref<BlockSignatureSet> sig_set_;
std::vector<Ref<vm::Cell>> proof_roots_;
std::vector<BlockIdExt> chain_blk_ids_;
std::vector<BlockIdExt> chain_mc_blk_ids_;
std::vector<BlockIdExt> link_prev_;
std::vector<std::pair<block::CurrencyCollection, block::CurrencyCollection>> chain_fees_;
UnixTime first_gen_utime_;
ShardTopBlockDescrQ(const ShardTopBlockDescrQ& other) = default;
ShardTopBlockDescrQ(ShardTopBlockDescrQ&& other) = default;
td::Status unpack_one_proof(BlockIdExt& cur_id, Ref<vm::Cell> proof_root, bool is_head);
td::Result<int> validate_internal(BlockIdExt last_mc_block_id, Ref<MasterchainState> last_mc_state, int& res_flags,
int mode) const;
};
class ValidateShardTopBlockDescr : public td::actor::Actor {
public:
ValidateShardTopBlockDescr(td::BufferSlice data, BlockIdExt masterchain_block, BlockHandle masterchain_handle,
Ref<MasterchainState> masterchain_state, td::actor::ActorId<ValidatorManager> manager,
td::Timestamp timeout, bool is_fake, td::Promise<Ref<ShardTopBlockDescription>> promise)
: data_(std::move(data))
, mc_blkid_(masterchain_block)
, handle_(std::move(masterchain_handle))
, state_(std::move(masterchain_state))
, manager_(manager)
, timeout_(timeout)
, is_fake_(is_fake)
, promise_(std::move(promise)) {
}
void finish_query();
void abort_query(td::Status reason);
bool fatal_error(td::Status error);
bool fatal_error(std::string err_msg, int err_code = -666);
void alarm() override;
void start_up() override;
private:
td::BufferSlice data_;
Ref<ShardTopBlockDescrQ> descr_;
BlockIdExt mc_blkid_;
BlockHandle handle_;
Ref<MasterchainState> state_;
td::actor::ActorId<ValidatorManager> manager_;
td::Timestamp timeout_;
bool is_fake_;
td::Promise<Ref<ShardTopBlockDescription>> promise_;
};
} // namespace validator
} // namespace ton

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,352 @@
/*
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 "interfaces/validator-manager.h"
#include "vm/cells.h"
#include "vm/dict.h"
#include "block/mc-config.h"
#include "block/transaction.h"
#include "shard.hpp"
#include "signature-set.hpp"
#include <vector>
#include <string>
#include <map>
namespace ton {
namespace validator {
using td::Ref;
class ErrorCtxAdd;
class ErrorCtxSet;
struct ErrorCtx {
protected:
friend class ErrorCtxAdd;
friend class ErrorCtxSet;
std::vector<std::string> entries_;
public:
ErrorCtx() = default;
ErrorCtx(std::vector<std::string> str_list) : entries_(std::move(str_list)) {
}
ErrorCtx(std::string str) : entries_{str} {
}
std::string as_string() const;
ErrorCtxAdd add_guard(std::string str_add);
ErrorCtxSet set_guard(std::string str);
ErrorCtxSet set_guard(std::vector<std::string> str_list);
};
class ErrorCtxAdd {
ErrorCtx& ctx_;
public:
ErrorCtxAdd(ErrorCtx& ctx, std::string ctx_elem) : ctx_(ctx) {
ctx_.entries_.push_back(std::move(ctx_elem));
}
~ErrorCtxAdd() {
ctx_.entries_.pop_back();
}
};
class ErrorCtxSet {
ErrorCtx& ctx_;
std::vector<std::string> old_ctx_;
public:
ErrorCtxSet(ErrorCtx& ctx, std::vector<std::string> new_ctx) : ctx_(ctx) {
old_ctx_ = std::move(ctx_.entries_);
ctx_.entries_ = std::move(new_ctx);
}
ErrorCtxSet(ErrorCtx& ctx, std::string new_ctx) : ErrorCtxSet(ctx, std::vector<std::string>{new_ctx}) {
}
~ErrorCtxSet() {
ctx_.entries_ = std::move(old_ctx_);
}
};
inline ErrorCtxAdd ErrorCtx::add_guard(std::string str) {
return ErrorCtxAdd(*this, std::move(str));
}
inline ErrorCtxSet ErrorCtx::set_guard(std::string str) {
return ErrorCtxSet(*this, std::move(str));
}
inline ErrorCtxSet ErrorCtx::set_guard(std::vector<std::string> str_list) {
return ErrorCtxSet(*this, std::move(str_list));
}
/*
*
* must write candidate to disk, if accepted
* can reject block only if it is invalid (i.e. in case of
* internal errors must retry or crash)
* only exception: block can be rejected, if it is known from
* masterchain, that it will not be part of shardchain finalized
* state
*
*/
class ValidateQuery : public td::actor::Actor {
public:
ValidateQuery(ShardIdFull shard, UnixTime min_ts, BlockIdExt min_masterchain_block_id, std::vector<BlockIdExt> prev,
BlockCandidate candidate, td::Ref<ValidatorSet> validator_set,
td::actor::ActorId<ValidatorManager> manager, td::Timestamp timeout,
td::Promise<ValidateCandidateResult> promise, bool is_fake = false);
private:
int verbosity{3 * 1};
int pending{0};
const ShardIdFull shard_;
const BlockIdExt id_;
UnixTime min_ts;
BlockIdExt min_mc_block_id;
std::vector<BlockIdExt> prev_blocks;
std::vector<Ref<ShardState>> prev_states;
BlockCandidate block_candidate;
td::Ref<ValidatorSet> validator_set_;
td::actor::ActorId<ValidatorManager> manager;
td::Timestamp timeout;
td::Promise<ValidateCandidateResult> main_promise;
bool after_merge_{false};
bool after_split_{false};
bool before_split_{false};
bool want_split_{false};
bool want_merge_{false};
bool is_key_block_{false};
bool update_shard_cc_{false};
bool is_fake_{false};
BlockSeqno prev_key_seqno_{~0u};
int stage_{0};
td::BitArray<64> shard_pfx_;
int shard_pfx_len_;
Ref<vm::Cell> prev_state_root_;
Ref<vm::Cell> state_root_;
Ref<vm::Cell> state_update_;
ton::Bits256 prev_state_hash_, state_hash_;
ErrorCtx error_ctx_;
td::Ref<MasterchainStateQ> mc_state_, latest_mc_state_;
td::Ref<vm::Cell> mc_state_root_;
BlockIdExt mc_blkid_, latest_mc_blkid_;
ton::BlockSeqno mc_seqno_{0}, latest_mc_seqno_;
Ref<vm::Cell> block_root_;
std::vector<Ref<vm::Cell>> collated_roots_;
std::map<RootHash, Ref<vm::Cell>> virt_roots_;
std::unique_ptr<vm::Dictionary> top_shard_descr_dict_;
Ref<vm::CellSlice> shard_hashes_; // from McBlockExtra
Ref<vm::CellSlice> blk_config_params_; // from McBlockExtra
Ref<BlockSignatureSet> prev_signatures_; // from McBlockExtra (UNCHECKED)
Ref<vm::Cell> recover_create_msg_, mint_msg_; // from McBlockExtra (UNCHECKED)
std::unique_ptr<block::ConfigInfo> config_, new_config_;
std::unique_ptr<block::ShardConfig> old_shard_conf_; // from reference mc state
std::unique_ptr<block::ShardConfig> new_shard_conf_; // from shard_hashes_ in mc blocks
Ref<block::WorkchainInfo> wc_info_;
std::unique_ptr<vm::AugmentedDictionary> fees_import_dict_;
bool accept_msgs_{true};
ton::BlockSeqno min_shard_ref_mc_seqno_{~0U};
ton::UnixTime max_shard_utime_{0};
ton::LogicalTime max_shard_lt_{0};
int global_id_{0};
ton::BlockSeqno prev_key_block_seqno_;
ton::BlockIdExt prev_key_block_;
ton::LogicalTime prev_key_block_lt_;
std::unique_ptr<block::BlockLimits> block_limits_;
std::unique_ptr<block::BlockLimitStatus> block_limit_status_;
LogicalTime start_lt_, end_lt_;
UnixTime prev_now_{~0u}, now_{~0u};
ton::Bits256 rand_seed_;
std::vector<block::StoragePrices> storage_prices_;
block::StoragePhaseConfig storage_phase_cfg_{&storage_prices_};
block::ComputePhaseConfig compute_phase_cfg_;
block::ActionPhaseConfig action_phase_cfg_;
td::RefInt256 masterchain_create_fee_, basechain_create_fee_;
std::vector<block::McShardDescr> neighbors_;
std::map<BlockSeqno, Ref<MasterchainStateQ>> aux_mc_states_;
block::ShardState ps_, ns_;
std::unique_ptr<vm::AugmentedDictionary> sibling_out_msg_queue_;
std::shared_ptr<block::MsgProcessedUptoCollection> sibling_processed_upto_;
std::unique_ptr<vm::AugmentedDictionary> in_msg_dict_, out_msg_dict_, account_blocks_dict_;
block::ValueFlow value_flow_;
block::CurrencyCollection import_created_, transaction_fees_;
td::RefInt256 import_fees_;
ton::LogicalTime proc_lt_{0}, claimed_proc_lt_{0}, min_enq_lt_{~0ULL};
ton::Bits256 proc_hash_, claimed_proc_hash_, min_enq_hash_;
bool inbound_queues_empty_{false};
std::vector<std::tuple<Bits256, LogicalTime, LogicalTime>> msg_proc_lt_;
std::vector<std::tuple<Bits256, Bits256, bool>> lib_publishers_, lib_publishers2_;
td::PerfWarningTimer perf_timer_{"validateblock", 0.1};
static constexpr td::uint32 priority() {
return 2;
}
WorkchainId workchain() const {
return shard_.workchain;
}
void finish_query();
void abort_query(td::Status error);
bool reject_query(std::string error, td::BufferSlice reason = {});
bool reject_query(std::string err_msg, td::Status error, td::BufferSlice reason = {});
bool soft_reject_query(std::string error, td::BufferSlice reason = {});
void alarm() override;
void start_up() override;
bool save_candidate();
void written_candidate();
bool fatal_error(td::Status error);
bool fatal_error(int err_code, std::string err_msg);
bool fatal_error(int err_code, std::string err_msg, td::Status error);
bool fatal_error(std::string err_msg, int err_code = -666);
std::string error_ctx() const {
return error_ctx_.as_string();
}
ErrorCtxAdd error_ctx_add_guard(std::string str) {
return error_ctx_.add_guard(std::move(str));
}
ErrorCtxSet error_ctx_set_guard(std::string str) {
return error_ctx_.set_guard(std::move(str));
}
bool is_masterchain() const {
return shard_.is_masterchain();
}
td::actor::ActorId<ValidateQuery> get_self() {
return actor_id(this);
}
void after_get_latest_mc_state(td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res);
void after_get_mc_state(td::Result<Ref<ShardState>> res);
void got_mc_handle(td::Result<BlockHandle> res);
void after_get_shard_state(int idx, td::Result<Ref<ShardState>> res);
bool process_mc_state(Ref<MasterchainState> mc_state);
bool try_unpack_mc_state();
bool fetch_config_params();
bool check_prev_block(const BlockIdExt& listed, const BlockIdExt& prev, bool chk_chain_len = true);
bool check_prev_block_exact(const BlockIdExt& listed, const BlockIdExt& prev);
bool check_this_shard_mc_info();
bool init_parse();
bool unpack_block_candidate();
bool extract_collated_data_from(Ref<vm::Cell> croot, int idx);
bool extract_collated_data();
bool try_validate();
bool compute_prev_state();
bool compute_next_state();
bool unpack_merge_prev_state();
bool unpack_prev_state();
bool unpack_next_state();
bool unpack_one_prev_state(block::ShardState& ss, BlockIdExt blkid, Ref<vm::Cell> prev_state_root);
bool split_prev_state(block::ShardState& ss);
bool request_neighbor_queues();
void got_neighbor_out_queue(int i, td::Result<Ref<MessageQueue>> res);
bool register_mc_state(Ref<MasterchainStateQ> other_mc_state);
bool request_aux_mc_state(BlockSeqno seqno, Ref<MasterchainStateQ>& state);
Ref<MasterchainStateQ> get_aux_mc_state(BlockSeqno seqno) const;
void after_get_aux_shard_state(ton::BlockIdExt blkid, td::Result<Ref<ShardState>> res);
bool check_one_shard(const block::McShardHash& info, const block::McShardHash* sibling,
const block::WorkchainInfo* wc_info, const block::CatchainValidatorsConfig& ccvc);
bool check_shard_layout();
bool check_cur_validator_set();
bool check_mc_validator_info(bool update_mc_cc);
bool check_utime_lt();
bool fix_one_processed_upto(block::MsgProcessedUpto& proc, ton::ShardIdFull owner, bool allow_cur = false);
bool fix_processed_upto(block::MsgProcessedUptoCollection& upto, bool allow_cur = false);
bool fix_all_processed_upto();
bool add_trivial_neighbor_after_merge();
bool add_trivial_neighbor();
bool unpack_block_data();
bool unpack_precheck_value_flow(Ref<vm::Cell> value_flow_root);
bool compute_minted_amount(block::CurrencyCollection& to_mint);
bool precheck_one_account_update(td::ConstBitPtr acc_id, Ref<vm::CellSlice> old_value, Ref<vm::CellSlice> new_value);
bool precheck_account_updates();
bool precheck_one_transaction(td::ConstBitPtr acc_id, ton::LogicalTime trans_lt, Ref<vm::CellSlice> trans_csr,
ton::Bits256& prev_trans_hash, ton::LogicalTime& prev_trans_lt,
unsigned& prev_trans_lt_len, ton::Bits256& acc_state_hash);
bool precheck_one_account_block(td::ConstBitPtr acc_id, Ref<vm::CellSlice> acc_blk);
bool precheck_account_transactions();
Ref<vm::Cell> lookup_transaction(const ton::StdSmcAddress& addr, ton::LogicalTime lt) const;
bool is_valid_transaction_ref(Ref<vm::Cell> trans_ref) const;
bool precheck_one_message_queue_update(td::ConstBitPtr out_msg_id, Ref<vm::CellSlice> old_value,
Ref<vm::CellSlice> new_value);
bool precheck_message_queue_update();
bool update_max_processed_lt_hash(ton::LogicalTime lt, const ton::Bits256& hash);
bool update_min_enqueued_lt_hash(ton::LogicalTime lt, const ton::Bits256& hash);
bool check_imported_message(Ref<vm::Cell> msg_env);
bool is_special_in_msg(const vm::CellSlice& in_msg) const;
bool check_in_msg(td::ConstBitPtr key, Ref<vm::CellSlice> in_msg);
bool check_in_msg_descr();
bool check_out_msg(td::ConstBitPtr key, Ref<vm::CellSlice> out_msg);
bool check_out_msg_descr();
bool check_processed_upto();
bool check_neighbor_outbound_message(Ref<vm::CellSlice> enq_msg, ton::LogicalTime lt, td::ConstBitPtr key,
const block::McShardDescr& src_nb, bool& unprocessed);
bool check_in_queue();
bool check_delivered_dequeued();
std::unique_ptr<block::Account> make_account_from(td::ConstBitPtr addr, Ref<vm::CellSlice> account,
Ref<vm::CellSlice> extra);
std::unique_ptr<block::Account> unpack_account(td::ConstBitPtr addr);
bool check_one_transaction(block::Account& account, LogicalTime lt, Ref<vm::Cell> trans_root, bool is_first,
bool is_last);
bool check_account_transactions(const StdSmcAddress& acc_addr, Ref<vm::CellSlice> acc_tr);
bool check_transactions();
bool scan_account_libraries(Ref<vm::Cell> orig_libs, Ref<vm::Cell> final_libs, const td::Bits256& addr);
bool check_all_ticktock_processed();
bool check_message_processing_order();
bool check_special_message(Ref<vm::Cell> in_msg_root, const block::CurrencyCollection& amount,
Ref<vm::Cell> addr_cell);
bool check_special_messages();
bool check_one_library_update(td::ConstBitPtr key, Ref<vm::CellSlice> old_value, Ref<vm::CellSlice> new_value);
bool check_shard_libraries();
bool check_new_state();
bool check_config_update(Ref<vm::CellSlice> old_conf_params, Ref<vm::CellSlice> new_conf_params);
bool check_one_prev_dict_update(ton::BlockSeqno seqno, Ref<vm::CellSlice> old_val_extra,
Ref<vm::CellSlice> new_val_extra);
bool check_mc_state_extra();
bool check_one_shard_fee(ShardIdFull shard, const block::CurrencyCollection& fees,
const block::CurrencyCollection& create);
bool check_mc_block_extra();
};
} // namespace validator
} // namespace ton

View file

@ -0,0 +1,193 @@
/*
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 "validator-set.hpp"
#include "auto/tl/ton_api.h"
// #include "adnl/utils.hpp"
#include "block/block.h"
#include <set>
namespace ton {
namespace validator {
using td::Ref;
const ValidatorDescr *ValidatorSetQ::find_validator(const NodeIdShort &id) const {
auto it =
std::lower_bound(ids_map_.begin(), ids_map_.end(), id, [](const auto &p, const auto &x) { return p.first < x; });
return it < ids_map_.end() && it->first == id ? &ids_[it->second] : nullptr;
}
bool ValidatorSetQ::is_validator(NodeIdShort id) const {
return find_validator(id);
}
td::Result<ValidatorWeight> ValidatorSetQ::check_signatures(RootHash root_hash, FileHash file_hash,
td::Ref<BlockSignatureSet> signatures) const {
auto &sigs = signatures->signatures();
auto block = create_serialize_tl_object<ton_api::ton_blockId>(root_hash, file_hash);
ValidatorWeight weight = 0;
std::set<NodeIdShort> nodes;
for (auto &sig : sigs) {
if (nodes.count(sig.node) == 1) {
return td::Status::Error(ErrorCode::protoviolation, "duplicate node to sign");
}
nodes.insert(sig.node);
auto vdescr = find_validator(sig.node);
if (!vdescr) {
return td::Status::Error(ErrorCode::protoviolation, "unknown node to sign");
}
auto E = ValidatorFullId{vdescr->key}.create_encryptor().move_as_ok();
TRY_STATUS(E->check_signature(block.as_slice(), sig.signature.as_slice()));
weight += vdescr->weight;
}
if (weight * 3 <= total_weight_ * 2) {
return td::Status::Error(ErrorCode::protoviolation, "too small sig weight");
}
return weight;
}
td::Result<ValidatorWeight> ValidatorSetQ::check_approve_signatures(RootHash root_hash, FileHash file_hash,
td::Ref<BlockSignatureSet> signatures) const {
auto &sigs = signatures->signatures();
auto block = create_serialize_tl_object<ton_api::ton_blockIdApprove>(root_hash, file_hash);
ValidatorWeight weight = 0;
std::set<NodeIdShort> nodes;
for (auto &sig : sigs) {
if (nodes.count(sig.node) == 1) {
return td::Status::Error(ErrorCode::protoviolation, "duplicate node to sign");
}
nodes.insert(sig.node);
auto vdescr = find_validator(sig.node);
if (!vdescr) {
return td::Status::Error(ErrorCode::protoviolation, "unknown node to sign");
}
auto E = ValidatorFullId{vdescr->key}.create_encryptor().move_as_ok();
TRY_STATUS(E->check_signature(block.as_slice(), sig.signature.as_slice()));
weight += vdescr->weight;
}
if (weight * 3 <= total_weight_ * 2) {
return td::Status::Error(ErrorCode::protoviolation, "too small sig weight");
}
return weight;
}
ValidatorSetQ::ValidatorSetQ(CatchainSeqno cc_seqno, ShardIdFull from, std::vector<ValidatorDescr> nodes)
: cc_seqno_(cc_seqno), for_(from), ids_(std::move(nodes)) {
total_weight_ = 0;
ids_map_.reserve(ids_.size());
for (std::size_t i = 0; i < ids_.size(); i++) {
total_weight_ += ids_[i].weight;
ids_map_.emplace_back(ValidatorFullId{ids_[i].key}.short_id(), i);
}
std::sort(ids_map_.begin(), ids_map_.end());
for (std::size_t i = 1; i < ids_map_.size(); i++) {
CHECK(ids_map_[i - 1].first != ids_map_[i].first);
}
hash_ = block::compute_validator_set_hash(cc_seqno, from, ids_);
}
ValidatorSetQ *ValidatorSetQ::make_copy() const {
return new ValidatorSetQ{*this};
}
std::vector<ValidatorDescr> ValidatorSetQ::export_vector() const {
return ids_;
}
td::Status ValidatorSetCompute::init(const block::Config *config) {
config_ = nullptr;
cur_validators_.reset();
next_validators_.reset();
if (!config) {
return td::Status::Error("null configuration pointer passed to ValidatorSetCompute");
}
config_ = config;
auto cv_root = config_->get_config_param(34);
if (cv_root.not_null()) {
TRY_RESULT(validators, block::Config::unpack_validator_set(std::move(cv_root)));
cur_validators_ = std::move(validators);
}
auto nv_root = config_->get_config_param(36);
if (nv_root.not_null()) {
TRY_RESULT(validators, block::Config::unpack_validator_set(std::move(nv_root)));
next_validators_ = std::move(validators);
}
return td::Status::OK();
}
Ref<ValidatorSet> ValidatorSetCompute::compute_validator_set(ShardIdFull shard, const block::ValidatorSet &vset,
UnixTime time, CatchainSeqno ccseqno) const {
if (!config_) {
return {};
}
LOG(DEBUG) << "in compute_validator_set() for " << shard.to_str();
auto nodes = config_->compute_validator_set(shard, vset, time, ccseqno);
if (nodes.empty()) {
return {};
}
return Ref<ValidatorSetQ>{true, ccseqno, shard, std::move(nodes)};
}
Ref<ValidatorSet> ValidatorSetCompute::get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno cc) const {
if (!config_ || !cur_validators_) {
LOG(ERROR) << "ValidatorSetCompute::get_validator_set() : no config or no cur_validators";
return {};
}
return compute_validator_set(shard, *cur_validators_, utime, cc);
}
Ref<ValidatorSet> ValidatorSetCompute::get_next_validator_set(ShardIdFull shard, UnixTime utime,
CatchainSeqno cc) const {
if (!config_ || !cur_validators_) {
LOG(ERROR) << "ValidatorSetCompute::get_next_validator_set() : no config or no cur_validators";
return {};
}
if (!next_validators_) {
return compute_validator_set(shard, *cur_validators_, utime, cc + 1);
}
bool is_mc = shard.is_masterchain();
auto ccv_cfg = config_->get_catchain_validators_config();
unsigned cc_lifetime = is_mc ? ccv_cfg.mc_cc_lifetime : ccv_cfg.shard_cc_lifetime;
if (next_validators_->utime_since > (utime / cc_lifetime + 1) * cc_lifetime) {
return compute_validator_set(shard, *cur_validators_, utime, cc + 1);
} else {
return compute_validator_set(shard, *next_validators_, utime, cc + 1);
}
}
} // namespace validator
} // namespace ton

View 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 "validator/interfaces/validator-set.h"
#include "validator/interfaces/signature-set.h"
#include "ton/ton-types.h"
#include "keys/encryptor.h"
#include "block/mc-config.h"
#include <map>
namespace ton {
namespace validator {
class ValidatorSetQ : public ValidatorSet {
public:
bool is_validator(NodeIdShort id) const override;
CatchainSeqno get_catchain_seqno() const override {
return cc_seqno_;
}
td::uint32 get_validator_set_hash() const override {
return hash_;
}
ShardId get_validator_set_from() const override {
return for_.shard;
}
ValidatorWeight get_total_weight() const {
return total_weight_;
}
std::vector<ValidatorDescr> export_vector() const override;
td::Result<ValidatorWeight> check_signatures(RootHash root_hash, FileHash file_hash,
td::Ref<BlockSignatureSet> signatures) const override;
td::Result<ValidatorWeight> check_approve_signatures(RootHash root_hash, FileHash file_hash,
td::Ref<BlockSignatureSet> signatures) const override;
ValidatorSetQ* make_copy() const override;
ValidatorSetQ(CatchainSeqno cc_seqno, ShardIdFull from, std::vector<ValidatorDescr> nodes);
private:
CatchainSeqno cc_seqno_;
ShardIdFull for_;
td::uint32 hash_;
ValidatorWeight total_weight_;
std::vector<ValidatorDescr> ids_;
std::vector<std::pair<NodeIdShort, size_t>> ids_map_;
const ValidatorDescr* find_validator(const NodeIdShort& id) const;
};
class ValidatorSetCompute {
public:
td::Ref<ValidatorSet> get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno cc) const;
td::Ref<ValidatorSet> get_next_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno cc) const;
td::Status init(const block::Config* config);
ValidatorSetCompute() = default;
private:
const block::Config* config_{nullptr};
std::unique_ptr<block::ValidatorSet> cur_validators_, next_validators_;
td::Ref<ValidatorSet> compute_validator_set(ShardIdFull shard, const block::ValidatorSet& vset, UnixTime time,
CatchainSeqno seqno) const;
};
} // namespace validator
} // namespace ton