1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-13 03:32:22 +00:00
ton/validator/impl/accept-block.cpp
ton 13140ddf29 updated block header
1. Updated block header, proofs now contain more data
   Notice, that old proofs may become invalid in the future
2. Fixed message routing
3. Fixed block creator id in block header
4. Support for full proofs in tonlib
5. Support for partial state download
6. Some other bugfixes
2019-09-18 21:46:32 +04:00

826 lines
35 KiB
C++

/*
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::BlockExtra::Record extra;
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) &&
tlb::unpack_cell(std::move(blk.extra), extra) && 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::McBlockExtra::Record mc_extra;
if (!(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::McBlockExtra::Record mc_extra;
if (!(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;
block::gen::BlockExtra::Record extra;
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");
}
/* TEMP (uncomment later)
if (!tlb::unpack_cell(std::move(block.extra), extra)) {
return fatal_error("block proof link for block "s + id.to_str() + " does not contain BlockExtra 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(),
ErrorCode::cancelled);
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(),
ErrorCode::cancelled);
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