mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
liteclient signature check support
1. update liteclient/liteserver. Now liteserver sends signatures of blocks and liteclient checks them. I.e. liteclient completely checks received data. 2. validator-engine: more GC options 3. blockchain-explorer: show all block transactions (instead of 256) 4. some bugfixes
This commit is contained in:
parent
d8244eff53
commit
9d6853ef24
58 changed files with 1480 additions and 325 deletions
|
@ -34,6 +34,8 @@
|
|||
#include "vm/dict.h"
|
||||
#include "vm/cells/MerkleProof.h"
|
||||
#include "shard.hpp"
|
||||
#include "validator-set.hpp"
|
||||
#include "signature-set.hpp"
|
||||
#include "fabric.h"
|
||||
#include <ctime>
|
||||
|
||||
|
@ -66,6 +68,14 @@ void LiteQuery::abort_query(td::Status reason) {
|
|||
stop();
|
||||
}
|
||||
|
||||
void LiteQuery::abort_query_ext(td::Status reason, std::string comment) {
|
||||
LOG(INFO) << "aborted liteserver query: " << comment << " : " << reason.to_string();
|
||||
if (promise_) {
|
||||
promise_.set_error(reason.move_as_error_prefix(comment + " : "));
|
||||
}
|
||||
stop();
|
||||
}
|
||||
|
||||
bool LiteQuery::fatal_error(td::Status error) {
|
||||
abort_query(std::move(error));
|
||||
return false;
|
||||
|
@ -167,7 +177,7 @@ void LiteQuery::perform_getTime() {
|
|||
void LiteQuery::perform_getVersion() {
|
||||
LOG(INFO) << "started a getVersion() liteserver query";
|
||||
td::int32 now = static_cast<td::int32>(std::time(nullptr));
|
||||
auto b = ton::create_serialize_tl_object<ton::lite_api::liteServer_version>(0, 0x100, 0, now);
|
||||
auto b = ton::create_serialize_tl_object<ton::lite_api::liteServer_version>(0, ls_version, ls_capabilities, now);
|
||||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
|
@ -279,11 +289,10 @@ void LiteQuery::continue_getBlockHeader(BlockIdExt blkid, int mode, Ref<ton::val
|
|||
// create block header proof
|
||||
RootHash rhash{block_root->get_hash().bits()};
|
||||
CHECK(rhash == blkid.root_hash);
|
||||
auto usage_tree = std::make_shared<vm::CellUsageTree>();
|
||||
auto usage_cell = vm::UsageCell::create(block_root, usage_tree->root_ptr());
|
||||
vm::MerkleProofBuilder mpb{block_root};
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockInfo::Record info;
|
||||
if (!(tlb::unpack_cell(usage_cell, blk) && tlb::unpack_cell(blk.info, info))) {
|
||||
if (!(tlb::unpack_cell(mpb.root(), blk) && tlb::unpack_cell(blk.info, info))) {
|
||||
fatal_error("cannot unpack block header");
|
||||
return;
|
||||
}
|
||||
|
@ -327,12 +336,7 @@ void LiteQuery::continue_getBlockHeader(BlockIdExt blkid, int mode, Ref<ton::val
|
|||
}
|
||||
}
|
||||
}
|
||||
auto proof = vm::MerkleProof::generate(std::move(block_root), usage_tree.get());
|
||||
if (proof.is_null()) {
|
||||
fatal_error("cannot create Merkle proof for block header");
|
||||
return;
|
||||
}
|
||||
auto proof_data = vm::std_boc_serialize(std::move(proof));
|
||||
auto proof_data = mpb.extract_proof_boc();
|
||||
if (proof_data.is_error()) {
|
||||
fatal_error(proof_data.move_as_error());
|
||||
return;
|
||||
|
@ -353,15 +357,27 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
|
|||
fatal_error("cannot request total state: possibly too large");
|
||||
return;
|
||||
}
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<ton::validator::ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::continue_getState, blkid,
|
||||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
if (blkid.id.seqno) {
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<ton::validator::ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::continue_getState, blkid,
|
||||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<td::BufferSlice> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::continue_getZeroState, blkid,
|
||||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getState(BlockIdExt blkid, Ref<ton::validator::ShardState> state) {
|
||||
|
@ -380,6 +396,14 @@ void LiteQuery::continue_getState(BlockIdExt blkid, Ref<ton::validator::ShardSta
|
|||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getZeroState(BlockIdExt blkid, td::BufferSlice state) {
|
||||
LOG(INFO) << "obtained data for getZeroState(" << blkid.to_str() << ")";
|
||||
CHECK(!state.empty());
|
||||
auto b = ton::create_serialize_tl_object<ton::lite_api::liteServer_blockState>(
|
||||
ton::create_tl_lite_block_id(blkid), blkid.root_hash, blkid.file_hash, std::move(state));
|
||||
finish_query(std::move(b));
|
||||
}
|
||||
|
||||
void LiteQuery::perform_sendMessage(td::BufferSlice data) {
|
||||
LOG(INFO) << "started a sendMessage(<" << data.size() << " bytes>) liteserver query";
|
||||
auto res = ton::validator::create_ext_message(std::move(data));
|
||||
|
@ -402,15 +426,42 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) {
|
|||
}
|
||||
base_blk_id_ = blkid;
|
||||
++pending_;
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_mc_block_data, blkid,
|
||||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_mc_block_data, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) {
|
||||
if (!blkid.is_masterchain() || !blkid.is_valid_full()) {
|
||||
return fatal_error("reference block must belong to the masterchain");
|
||||
}
|
||||
if (!cont_set_) {
|
||||
return fatal_error("continuation not set");
|
||||
}
|
||||
if (mode) {
|
||||
base_blk_id_alt_ = blkid;
|
||||
} else {
|
||||
base_blk_id_ = blkid;
|
||||
}
|
||||
++pending_;
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_block_proof_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid, mode ](td::Result<Ref<Proof>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_mc_block_proof, blkid, mode, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -423,15 +474,16 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) {
|
|||
}
|
||||
base_blk_id_ = blkid;
|
||||
++pending_;
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_mc_block_state, blkid,
|
||||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_mc_block_state, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -453,15 +505,16 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) {
|
|||
}
|
||||
blk_id_ = blkid;
|
||||
++pending_;
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_block_state, blkid,
|
||||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_block_state, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -474,15 +527,63 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) {
|
|||
}
|
||||
blk_id_ = blkid;
|
||||
++pending_;
|
||||
td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_block_data, blkid,
|
||||
res.move_as_ok());
|
||||
}
|
||||
});
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_block_data, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LiteQuery::request_proof_link(BlockIdExt blkid) {
|
||||
if (!blkid.is_valid_full()) {
|
||||
return fatal_error("invalid block id requested");
|
||||
}
|
||||
if (!cont_set_) {
|
||||
return fatal_error("continuation not set");
|
||||
}
|
||||
blk_id_ = blkid;
|
||||
++pending_;
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<Ref<ProofLink>> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_block_proof_link, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LiteQuery::request_zero_state(BlockIdExt blkid) {
|
||||
if (!blkid.is_valid_full()) {
|
||||
return fatal_error("invalid block id requested");
|
||||
}
|
||||
if (blkid.seqno()) {
|
||||
return fatal_error("invalid zerostate requested");
|
||||
}
|
||||
if (!cont_set_) {
|
||||
return fatal_error("continuation not set");
|
||||
}
|
||||
blk_id_ = blkid;
|
||||
++pending_;
|
||||
td::actor::send_closure_later(
|
||||
manager_, &ValidatorManager::get_zero_state, blkid,
|
||||
[ Self = actor_id(this), blkid ](td::Result<td::BufferSlice> res) {
|
||||
if (res.is_error()) {
|
||||
td::actor::send_closure(Self, &LiteQuery::abort_query,
|
||||
res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : "));
|
||||
} else {
|
||||
td::actor::send_closure_later(Self, &LiteQuery::got_zero_state, blkid, res.move_as_ok());
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -595,6 +696,38 @@ void LiteQuery::got_mc_block_data(BlockIdExt blkid, Ref<BlockData> data) {
|
|||
dec_pending();
|
||||
}
|
||||
|
||||
void LiteQuery::got_mc_block_proof(BlockIdExt blkid, int mode, Ref<Proof> proof) {
|
||||
LOG(INFO) << "obtained data for getBlockProof(" << blkid.to_str() << ") needed by a liteserver query";
|
||||
CHECK(proof.not_null());
|
||||
if (mode) {
|
||||
mc_proof_alt_ = Ref<ProofQ>(std::move(proof));
|
||||
CHECK(mc_proof_alt_.not_null());
|
||||
CHECK(blkid == base_blk_id_alt_);
|
||||
} else {
|
||||
mc_proof_ = Ref<ProofQ>(std::move(proof));
|
||||
CHECK(mc_proof_.not_null());
|
||||
CHECK(blkid == base_blk_id_);
|
||||
}
|
||||
dec_pending();
|
||||
}
|
||||
|
||||
void LiteQuery::got_block_proof_link(BlockIdExt blkid, Ref<ProofLink> proof_link) {
|
||||
LOG(INFO) << "obtained data for getBlockProofLink(" << blkid.to_str() << ") needed by a liteserver query";
|
||||
CHECK(proof_link.not_null());
|
||||
proof_link_ = Ref<ProofLinkQ>(std::move(proof_link));
|
||||
CHECK(proof_link_.not_null());
|
||||
CHECK(blkid == blk_id_);
|
||||
dec_pending();
|
||||
}
|
||||
|
||||
void LiteQuery::got_zero_state(BlockIdExt blkid, td::BufferSlice zerostate) {
|
||||
LOG(INFO) << "obtained data for getZeroState(" << blkid.to_str() << ") needed by a liteserver query";
|
||||
CHECK(!zerostate.empty());
|
||||
buffer_ = std::move(zerostate);
|
||||
CHECK(blkid == blk_id_);
|
||||
dec_pending();
|
||||
}
|
||||
|
||||
void LiteQuery::check_pending() {
|
||||
CHECK(pending_ >= 0);
|
||||
if (!pending_) {
|
||||
|
@ -627,8 +760,11 @@ bool LiteQuery::make_state_root_proof(Ref<vm::Cell>& proof) {
|
|||
bool LiteQuery::make_state_root_proof(Ref<vm::Cell>& proof, Ref<ShardStateQ> state, Ref<BlockData> block,
|
||||
const BlockIdExt& blkid) {
|
||||
CHECK(block.not_null() && state.not_null());
|
||||
auto block_root = block->root_cell();
|
||||
auto state_root = state->root_cell();
|
||||
return make_state_root_proof(proof, state->root_cell(), block->root_cell(), blkid);
|
||||
}
|
||||
|
||||
bool LiteQuery::make_state_root_proof(Ref<vm::Cell>& proof, Ref<vm::Cell> state_root, Ref<vm::Cell> block_root,
|
||||
const BlockIdExt& blkid) {
|
||||
CHECK(block_root.not_null() && state_root.not_null());
|
||||
RootHash rhash{block_root->get_hash().bits()};
|
||||
CHECK(rhash == blkid.root_hash);
|
||||
|
@ -714,6 +850,24 @@ bool LiteQuery::make_shard_info_proof(Ref<vm::Cell>& proof, BlockIdExt& blkid, A
|
|||
return true;
|
||||
}
|
||||
|
||||
bool LiteQuery::make_ancestor_block_proof(Ref<vm::Cell>& proof, Ref<vm::Cell> state_root, const BlockIdExt& old_blkid) {
|
||||
vm::MerkleProofBuilder mpb{std::move(state_root)};
|
||||
auto rconfig = block::ConfigInfo::extract_config(mpb.root(), block::ConfigInfo::needPrevBlocks);
|
||||
if (rconfig.is_error()) {
|
||||
return fatal_error(
|
||||
"cannot extract previous block configuration from masterchain state while constructing Merkle proof for "s +
|
||||
old_blkid.to_str());
|
||||
}
|
||||
if (!rconfig.move_as_ok()->check_old_mc_block_id(old_blkid, true)) {
|
||||
return fatal_error("cannot check that "s + old_blkid.to_str() +
|
||||
" is indeed a previous masterchain block while constructing Merkle proof");
|
||||
}
|
||||
if (!mpb.extract_proof_to(proof)) {
|
||||
return fatal_error("error while constructing Merkle proof for old masterchain block "s + old_blkid.to_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LiteQuery::continue_getAccountState() {
|
||||
LOG(INFO) << "continue getAccountState() query";
|
||||
if (acc_workchain_ == masterchainId) {
|
||||
|
@ -1000,10 +1154,8 @@ void LiteQuery::continue_getConfigParams(int mode, std::vector<int> param_list)
|
|||
if (!make_mc_state_root_proof(proof1)) {
|
||||
return;
|
||||
}
|
||||
auto state_root = mc_state_->root_cell();
|
||||
auto usage_tree = std::make_shared<vm::CellUsageTree>();
|
||||
auto usage_cell = vm::UsageCell::create(state_root, usage_tree->root_ptr());
|
||||
auto res = block::Config::extract_from_state(usage_cell, mode);
|
||||
vm::MerkleProofBuilder mpb{mc_state_->root_cell()};
|
||||
auto res = block::Config::extract_from_state(mpb.root(), mode);
|
||||
if (res.is_error()) {
|
||||
fatal_error(res.move_as_error());
|
||||
return;
|
||||
|
@ -1025,15 +1177,12 @@ void LiteQuery::continue_getConfigParams(int mode, std::vector<int> param_list)
|
|||
fatal_error("error while traversing required configuration parameters: "s + err.get_msg());
|
||||
return;
|
||||
}
|
||||
auto proof2 = vm::MerkleProof::generate(state_root, usage_tree.get());
|
||||
usage_tree.reset();
|
||||
usage_cell.clear();
|
||||
auto res1 = vm::std_boc_serialize(std::move(proof1));
|
||||
if (res1.is_error()) {
|
||||
fatal_error("cannot serialize Merkle proof : "s + res1.move_as_error().to_string());
|
||||
return;
|
||||
}
|
||||
auto res2 = vm::std_boc_serialize(std::move(proof2));
|
||||
auto res2 = mpb.extract_proof_boc();
|
||||
if (res2.is_error()) {
|
||||
fatal_error("cannot serialize Merkle proof : "s + res2.move_as_error().to_string());
|
||||
return;
|
||||
|
@ -1094,22 +1243,21 @@ void LiteQuery::continue_getShardInfo(ShardIdFull shard, bool exact) {
|
|||
|
||||
void LiteQuery::continue_getAllShardsInfo() {
|
||||
LOG(INFO) << "completing getAllShardsInfo() query";
|
||||
Ref<vm::Cell> proof1;
|
||||
Ref<vm::Cell> proof1, proof2;
|
||||
if (!make_mc_state_root_proof(proof1)) {
|
||||
return;
|
||||
}
|
||||
auto state_root = mc_state_->root_cell();
|
||||
auto usage_tree = std::make_shared<vm::CellUsageTree>();
|
||||
auto usage_cell = vm::UsageCell::create(state_root, usage_tree->root_ptr());
|
||||
auto shards_dict = block::ShardConfig::extract_shard_hashes_dict(usage_cell);
|
||||
vm::MerkleProofBuilder mpb{mc_state_->root_cell()};
|
||||
auto shards_dict = block::ShardConfig::extract_shard_hashes_dict(mpb.root());
|
||||
if (!shards_dict) {
|
||||
fatal_error("cannot extract ShardHashes from last mc state");
|
||||
return;
|
||||
}
|
||||
auto proof2 = vm::MerkleProof::generate(state_root, usage_tree.get());
|
||||
usage_tree.reset();
|
||||
usage_cell.clear();
|
||||
shards_dict = block::ShardConfig::extract_shard_hashes_dict(state_root);
|
||||
if (!mpb.extract_proof_to(proof2)) {
|
||||
fatal_error("cannot construct Merkle proof for all shards dictionary");
|
||||
return;
|
||||
}
|
||||
shards_dict = block::ShardConfig::extract_shard_hashes_dict(mc_state_->root_cell());
|
||||
vm::CellBuilder cb;
|
||||
Ref<vm::Cell> cell;
|
||||
if (!(std::move(shards_dict)->append_dict_to_bool(cb) && cb.finalize_to(cell))) {
|
||||
|
@ -1329,7 +1477,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
|
|||
|
||||
void LiteQuery::continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode,
|
||||
Ref<MasterchainStateQ> state) {
|
||||
if (mode & 1) {
|
||||
if (!(mode & 1)) {
|
||||
base_blk_id_ = to;
|
||||
if (!to.is_masterchain_ext()) {
|
||||
fatal_error("last masterchain block id "s + to.to_str() + " is invalid");
|
||||
|
@ -1362,7 +1510,7 @@ void LiteQuery::continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
|
|||
base_blk_id_.to_str());
|
||||
return;
|
||||
}
|
||||
chain_ = std::make_unique<block::BlkProofChain>(from, to, mode);
|
||||
chain_ = std::make_unique<block::BlockProofChain>(from, to, mode);
|
||||
blk_id_ = from;
|
||||
construct_proof_chain(from);
|
||||
}
|
||||
|
@ -1370,7 +1518,12 @@ void LiteQuery::continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
|
|||
bool LiteQuery::construct_proof_chain(ton::BlockIdExt id) {
|
||||
CHECK(chain_);
|
||||
if (chain_->link_count() >= 16 || id == chain_->to) {
|
||||
return finish_proof_chain(std::move(id));
|
||||
if (!(chain_->last_link_incomplete() && chain_->last_link().to.seqno())) {
|
||||
return finish_proof_chain(std::move(id));
|
||||
} else {
|
||||
set_continuation([this, id]() { finish_proof_chain(id); });
|
||||
return request_proof_link(id);
|
||||
}
|
||||
}
|
||||
if (chain_->to.seqno() == id.seqno()) {
|
||||
return fatal_error("cannot have two different masterchain blocks "s + chain_->to.to_str() + " and " + id.to_str() +
|
||||
|
@ -1400,6 +1553,28 @@ bool LiteQuery::construct_proof_chain(ton::BlockIdExt id) {
|
|||
}
|
||||
}
|
||||
|
||||
// adjust dest_proof and is_key of the last link of existing proof
|
||||
bool LiteQuery::adjust_last_proof_link(ton::BlockIdExt cur, Ref<vm::Cell> block_root) {
|
||||
CHECK(chain_);
|
||||
if (!(chain_->last_link_incomplete() && chain_->last_link().to.seqno())) {
|
||||
return true;
|
||||
}
|
||||
auto& link = chain_->last_link();
|
||||
CHECK(link.dest_proof.is_null());
|
||||
CHECK(link.to == cur);
|
||||
if (cur.root_hash != block_root->get_hash().bits()) {
|
||||
return fatal_error("root hash mismatch in block root of "s + cur.to_str());
|
||||
}
|
||||
vm::MerkleProofBuilder mpb{std::move(block_root)};
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockInfo::Record info;
|
||||
if (!(tlb::unpack_cell(mpb.root(), blk) && tlb::unpack_cell(blk.info, info))) {
|
||||
return fatal_error("cannot unpack header of block "s + cur.to_str());
|
||||
}
|
||||
link.is_key = info.key_block;
|
||||
return mpb.extract_proof_to(link.dest_proof);
|
||||
}
|
||||
|
||||
bool LiteQuery::construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdExt next) {
|
||||
LOG(INFO) << "constructing a forward proof link from " << cur.to_str() << " to " << next.to_str();
|
||||
if (!(cur.is_masterchain_ext() && next.is_masterchain_ext() && mc_state0_->check_old_mc_block_id(cur) &&
|
||||
|
@ -1410,17 +1585,144 @@ bool LiteQuery::construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdEx
|
|||
if (cur.seqno() >= next.seqno()) {
|
||||
return fatal_error("cannot construct forward proof link from "s + cur.to_str() + " to " + next.to_str());
|
||||
}
|
||||
set_continuation([this, cur, next]() { construct_proof_link_back_cont(cur, next); });
|
||||
return request_mc_block_data(cur);
|
||||
set_continuation([this, cur, next]() { construct_proof_link_forward_cont(cur, next); });
|
||||
return (cur.seqno() ? request_proof_link(cur) : request_zero_state(cur)) && request_mc_proof(next);
|
||||
}
|
||||
|
||||
bool LiteQuery::construct_proof_link_forward_cont(ton::BlockIdExt cur, ton::BlockIdExt next) {
|
||||
// ...
|
||||
LOG(INFO) << "continue constructing a forward proof link from " << cur.to_str() << " to " << next.to_str();
|
||||
CHECK(cur.seqno() ? proof_link_.not_null() && proof_link_->block_id() == cur : !buffer_.empty());
|
||||
CHECK(mc_proof_.not_null() && mc_proof_->block_id() == next);
|
||||
try {
|
||||
Ref<vm::Cell> cur_root, next_root;
|
||||
// virtualize roots
|
||||
ton::validator::ProofQ::VirtualizedProof virt1;
|
||||
if (cur.seqno()) {
|
||||
auto vres1 = proof_link_->get_virtual_root();
|
||||
if (vres1.is_error()) {
|
||||
return fatal_error(vres1.move_as_error());
|
||||
}
|
||||
virt1 = vres1.move_as_ok();
|
||||
cur_root = virt1.root;
|
||||
} else {
|
||||
// for zero state, lazily deserialize buffer_ instead
|
||||
vm::StaticBagOfCellsDbLazy::Options options;
|
||||
options.check_crc32c = true;
|
||||
auto res = vm::StaticBagOfCellsDbLazy::create(vm::BufferSliceBlobView::create(std::move(buffer_)), options);
|
||||
if (res.is_error()) {
|
||||
return fatal_error(res.move_as_error());
|
||||
}
|
||||
virt1.boc = res.move_as_ok();
|
||||
auto t_root = virt1.boc->get_root_cell(0);
|
||||
if (t_root.is_error()) {
|
||||
return fatal_error(t_root.move_as_error());
|
||||
}
|
||||
cur_root = t_root.move_as_ok();
|
||||
}
|
||||
auto vres2 = mc_proof_->get_virtual_root();
|
||||
if (vres2.is_error()) {
|
||||
return fatal_error(vres2.move_as_error());
|
||||
}
|
||||
next_root = vres2.ok().root;
|
||||
if (cur.root_hash != cur_root->get_hash().bits()) {
|
||||
return fatal_error("incorrect root hash in ProofLink for block "s + cur.to_str());
|
||||
}
|
||||
if (next.root_hash != next_root->get_hash().bits()) {
|
||||
return fatal_error("incorrect root hash in ProofLink for block "s + cur.to_str());
|
||||
}
|
||||
// adjust dest_proof and is_key of the last link of existing proof
|
||||
if (!adjust_last_proof_link(cur, cur_root)) {
|
||||
return false;
|
||||
}
|
||||
// extract configuration from current block
|
||||
vm::MerkleProofBuilder cur_mpb{cur_root}, next_mpb{next_root};
|
||||
if (cur.seqno()) {
|
||||
auto err = block::check_block_header(cur_mpb.root(), cur);
|
||||
if (err.is_error()) {
|
||||
return fatal_error("incorrect header in ProofLink for block "s + cur.to_str());
|
||||
}
|
||||
}
|
||||
auto cfg_res = cur.seqno()
|
||||
? block::Config::extract_from_key_block(cur_mpb.root(), block::ConfigInfo::needValidatorSet)
|
||||
: block::Config::extract_from_state(cur_mpb.root(), block::ConfigInfo::needValidatorSet);
|
||||
if (cfg_res.is_error()) {
|
||||
return fatal_error(cfg_res.move_as_error());
|
||||
}
|
||||
auto config = cfg_res.move_as_ok();
|
||||
// unpack header of next block
|
||||
auto err = block::check_block_header(next_mpb.root(), next);
|
||||
if (err.is_error()) {
|
||||
return fatal_error("incorrect header in ProofLink for block "s + next.to_str());
|
||||
}
|
||||
block::gen::Block::Record blk;
|
||||
block::gen::BlockInfo::Record info;
|
||||
if (!(tlb::unpack_cell(next_mpb.root(), blk) && tlb::unpack_cell(blk.info, info))) {
|
||||
return fatal_error("cannot unpack header of block "s + cur.to_str());
|
||||
}
|
||||
// compute validator set
|
||||
ShardIdFull shard{masterchainId};
|
||||
auto nodes = config->compute_validator_set(shard, info.gen_utime, info.gen_catchain_seqno);
|
||||
if (nodes.empty()) {
|
||||
return fatal_error(PSTRING() << "cannot compute validator set for block " << next.to_str() << " with utime "
|
||||
<< info.gen_utime << " and cc_seqno " << info.gen_catchain_seqno
|
||||
<< " starting from previous key block " << cur.to_str());
|
||||
}
|
||||
auto vset = Ref<ValidatorSetQ>{true, info.gen_catchain_seqno, shard, std::move(nodes)};
|
||||
if (vset.is_null()) {
|
||||
return fatal_error(PSTRING() << "cannot create validator set for block " << next.to_str() << " with utime "
|
||||
<< info.gen_utime << " and cc_seqno " << info.gen_catchain_seqno
|
||||
<< " starting from previous key block " << cur.to_str());
|
||||
}
|
||||
auto vset_hash = vset->get_validator_set_hash();
|
||||
if (vset_hash != info.gen_validator_list_hash_short) {
|
||||
return fatal_error(PSTRING() << "computed validator set for block " << next.to_str() << " with utime "
|
||||
<< info.gen_utime << " and cc_seqno " << info.gen_catchain_seqno
|
||||
<< " starting from previous key block " << cur.to_str() << " has hash " << vset_hash
|
||||
<< " different from " << info.gen_validator_list_hash_short
|
||||
<< " stated in block header");
|
||||
}
|
||||
// extract signatures
|
||||
auto sig_outer_root = vres2.ok().sig_root;
|
||||
block::gen::BlockSignatures::Record sign_rec;
|
||||
block::gen::BlockSignaturesPure::Record sign_pure;
|
||||
if (!(sig_outer_root.not_null() && tlb::unpack_cell(sig_outer_root, sign_rec) &&
|
||||
tlb::csr_unpack(sign_rec.pure_signatures, sign_pure))) {
|
||||
return fatal_error("cannot extract signature set from proof for block "s + next.to_str());
|
||||
}
|
||||
auto sigs = BlockSignatureSetQ::fetch(sign_pure.signatures->prefetch_ref());
|
||||
if (sigs.is_null()) {
|
||||
return fatal_error("cannot deserialize signature set from proof for block "s + next.to_str());
|
||||
}
|
||||
// check signatures (sanity check; comment later for better performance)
|
||||
/*
|
||||
auto S = vset->check_signatures(next.root_hash, next.file_hash, sigs);
|
||||
if (S.is_error()) {
|
||||
return fatal_error("error checking signatures from proof for block "s + next.to_str() + " : " +
|
||||
S.move_as_error().to_string());
|
||||
}
|
||||
*/
|
||||
// serialize signatures
|
||||
auto& link = chain_->new_link(cur, next, info.key_block);
|
||||
link.cc_seqno = info.gen_catchain_seqno;
|
||||
link.validator_set_hash = info.gen_validator_list_hash_short;
|
||||
link.signatures = std::move(sigs.write().signatures());
|
||||
// serialize proofs
|
||||
if (!(cur_mpb.extract_proof_to(link.proof) && next_mpb.extract_proof_to(link.dest_proof))) {
|
||||
return fatal_error("error constructing Merkle proof for forward proof link from "s + cur.to_str() + " to " +
|
||||
next.to_str());
|
||||
}
|
||||
// continue constructing from `next`
|
||||
return construct_proof_chain(next);
|
||||
} catch (vm::VmVirtError&) {
|
||||
return fatal_error("virtualization error during construction of forward proof link from "s + cur.to_str() + " to " +
|
||||
next.to_str());
|
||||
}
|
||||
return fatal_error("construction of forward proof links not implemented yet");
|
||||
}
|
||||
|
||||
bool LiteQuery::construct_proof_link_back(ton::BlockIdExt cur, ton::BlockIdExt next) {
|
||||
LOG(INFO) << "constructing a backward proof link from " << cur.to_str() << " to " << next.to_str();
|
||||
CHECK(chain_);
|
||||
if (!(cur.is_masterchain_ext() && next.is_masterchain_ext() && mc_state0_->check_old_mc_block_id(cur) &&
|
||||
mc_state0_->check_old_mc_block_id(next))) {
|
||||
return fatal_error("cannot construct backward proof link from "s + cur.to_str() + " to " + next.to_str() +
|
||||
|
@ -1430,26 +1732,114 @@ bool LiteQuery::construct_proof_link_back(ton::BlockIdExt cur, ton::BlockIdExt n
|
|||
return fatal_error("cannot construct backward proof link from "s + cur.to_str() + " to " + next.to_str());
|
||||
}
|
||||
set_continuation([this, cur, next]() { construct_proof_link_back_cont(cur, next); });
|
||||
return request_mc_block_data_state(cur);
|
||||
return request_proof_link(cur) && request_mc_block_state(cur);
|
||||
}
|
||||
|
||||
bool LiteQuery::construct_proof_link_back_cont(ton::BlockIdExt cur, ton::BlockIdExt next) {
|
||||
LOG(INFO) << "continue constructing a backward proof link from " << cur.to_str() << " to " << next.to_str();
|
||||
CHECK(mc_state_.not_null() && mc_block_.not_null() && mc_state_->get_block_id() == cur &&
|
||||
mc_block_->block_id() == cur);
|
||||
// ...
|
||||
return fatal_error("construction of backward proof links not implemented yet");
|
||||
CHECK(mc_state_.not_null() && proof_link_.not_null() && mc_state_->get_block_id() == cur &&
|
||||
proof_link_->block_id() == cur);
|
||||
try {
|
||||
// virtualize proof link
|
||||
auto vres1 = proof_link_->get_virtual_root();
|
||||
if (vres1.is_error()) {
|
||||
return fatal_error(vres1.move_as_error());
|
||||
}
|
||||
auto vroot = vres1.ok().root;
|
||||
// adjust dest_proof and is_key of the last link of existing proof
|
||||
if (!adjust_last_proof_link(cur, vroot)) {
|
||||
return false;
|
||||
}
|
||||
// construct proof that `mc_state_` is the state of `cur`
|
||||
Ref<vm::Cell> state_proof, proof;
|
||||
if (!make_state_root_proof(proof, mc_state_->root_cell(), vroot, cur)) {
|
||||
return fatal_error("cannot construct proof for state of masterchain block "s + cur.to_str());
|
||||
}
|
||||
// construct proof that `next` is listed in OldMcBlocksInfo of `mc_state_`
|
||||
if (!make_ancestor_block_proof(state_proof, mc_state_->root_cell(), next)) {
|
||||
return fatal_error("cannot prove that "s + next.to_str() +
|
||||
" is in the previous block set of the masterchain state of " + cur.to_str());
|
||||
}
|
||||
// create a BlockProofLink for cur -> next (without dest_proof)
|
||||
auto& link = chain_->new_link(cur, next, !next.seqno());
|
||||
link.proof = std::move(proof);
|
||||
link.state_proof = std::move(state_proof);
|
||||
// continue constructing proof chain from `next`
|
||||
return construct_proof_chain(next);
|
||||
} catch (vm::VmVirtError&) {
|
||||
return fatal_error("virtualization error during construction of backward proof link from "s + cur.to_str() +
|
||||
" to " + next.to_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool LiteQuery::finish_proof_chain(ton::BlockIdExt id) {
|
||||
CHECK(chain_);
|
||||
LOG(INFO) << "finish constructing block proof chain from " << chain_->from.to_str() << " to " << chain_->to.to_str()
|
||||
<< " (constructed " << chain_->link_count() << " up to " << id.to_str() << ")";
|
||||
// bool complete = (id == chain_->to);
|
||||
chain_->to = id;
|
||||
// serialize answer
|
||||
// ...
|
||||
return fatal_error("cannot serialize PartialBlockProof from "s + chain_->from.to_str() + " to " + id.to_str());
|
||||
try {
|
||||
if (chain_->last_link_incomplete() && chain_->last_link().to.seqno()) {
|
||||
CHECK(proof_link_.not_null() && proof_link_->block_id() == id);
|
||||
auto vres1 = proof_link_->get_virtual_root();
|
||||
if (vres1.is_error()) {
|
||||
return fatal_error(vres1.move_as_error());
|
||||
}
|
||||
if (!adjust_last_proof_link(id, vres1.ok().root)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
chain_->complete = (id == chain_->to);
|
||||
chain_->to = id;
|
||||
// serialize answer
|
||||
std::vector<ton::tl_object_ptr<lite_api::liteServer_BlockLink>> a;
|
||||
for (auto& link : chain_->links) {
|
||||
td::BufferSlice dest_proof_boc;
|
||||
if (link.to.seqno()) {
|
||||
auto res = vm::std_boc_serialize(link.dest_proof);
|
||||
if (res.is_error()) {
|
||||
return fatal_error("error while serializing destination block Merkle proof in block proof link from "s +
|
||||
link.from.to_str() + " to " + link.to.to_str() + " : " + res.move_as_error().to_string());
|
||||
}
|
||||
dest_proof_boc = res.move_as_ok();
|
||||
}
|
||||
auto src_proof_boc = vm::std_boc_serialize(link.proof);
|
||||
if (src_proof_boc.is_error()) {
|
||||
return fatal_error("error while serializing source block Merkle proof in block proof link from "s +
|
||||
link.from.to_str() + " to " + link.to.to_str() + " : " +
|
||||
src_proof_boc.move_as_error().to_string());
|
||||
}
|
||||
if (link.is_fwd) {
|
||||
// serialize forward link
|
||||
std::vector<ton::tl_object_ptr<lite_api::liteServer_signature>> b;
|
||||
for (auto& sig : link.signatures) {
|
||||
b.push_back(create_tl_object<lite_api::liteServer_signature>(sig.node, std::move(sig.signature)));
|
||||
}
|
||||
a.push_back(create_tl_object<lite_api::liteServer_blockLinkForward>(
|
||||
link.is_key, ton::create_tl_lite_block_id(link.from), ton::create_tl_lite_block_id(link.to),
|
||||
std::move(dest_proof_boc), src_proof_boc.move_as_ok(),
|
||||
create_tl_object<lite_api::liteServer_signatureSet>(link.validator_set_hash, link.cc_seqno, std::move(b))));
|
||||
} else {
|
||||
// serialize backward link
|
||||
auto state_proof_boc = vm::std_boc_serialize(link.state_proof);
|
||||
if (state_proof_boc.is_error()) {
|
||||
return fatal_error("error while serializing source state Merkle proof in block proof link from "s +
|
||||
link.from.to_str() + " to " + link.to.to_str() + " : " +
|
||||
state_proof_boc.move_as_error().to_string());
|
||||
}
|
||||
a.push_back(create_tl_object<lite_api::liteServer_blockLinkBack>(
|
||||
link.is_key, ton::create_tl_lite_block_id(link.from), ton::create_tl_lite_block_id(link.to),
|
||||
std::move(dest_proof_boc), src_proof_boc.move_as_ok(), state_proof_boc.move_as_ok()));
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "getBlockProof() query completed";
|
||||
auto c = ton::create_serialize_tl_object<ton::lite_api::liteServer_partialBlockProof>(
|
||||
chain_->complete, ton::create_tl_lite_block_id(chain_->from), ton::create_tl_lite_block_id(chain_->to),
|
||||
std::move(a));
|
||||
return finish_query(std::move(c));
|
||||
} catch (vm::VmError& err) {
|
||||
return fatal_error("vm error while constructing block proof chain : "s + err.get_msg());
|
||||
} catch (vm::VmVirtError& err) {
|
||||
return fatal_error("virtualization error while constructing block proof chain : "s + err.get_msg());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace validator
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue