1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +00:00

Tonlib method getShardBlockProof (#465)

* Tonlib method getShardBlockProof

* Clarify limit of result.size() in getShardBlockProof
This commit is contained in:
SpyCheese 2022-09-20 10:49:28 +03:00 committed by GitHub
parent 9c6787d2ff
commit 2512f0287b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 337 additions and 0 deletions

View file

@ -52,6 +52,8 @@ liteServer.partialBlockProof complete:Bool from:tonNode.blockIdExt to:tonNode.bl
liteServer.configInfo mode:# id:tonNode.blockIdExt state_proof:bytes config_proof:bytes = liteServer.ConfigInfo;
liteServer.validatorStats mode:# id:tonNode.blockIdExt count:int complete:Bool state_proof:bytes data_proof:bytes = liteServer.ValidatorStats;
liteServer.libraryResult result:(vector liteServer.libraryEntry) = liteServer.LibraryResult;
liteServer.shardBlockLink id:tonNode.blockIdExt proof:bytes = liteServer.ShardBlockLink;
liteServer.shardBlockProof masterchain_id:tonNode.blockIdExt links:(vector liteServer.shardBlockLink) = liteServer.ShardBlockProof;
liteServer.debug.verbosity value:int = liteServer.debug.Verbosity;
@ -78,6 +80,7 @@ liteServer.getConfigAll mode:# id:tonNode.blockIdExt = liteServer.ConfigInfo;
liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) = liteServer.ConfigInfo;
liteServer.getValidatorStats#091a58bc mode:# id:tonNode.blockIdExt limit:int start_after:mode.0?int256 modified_after:mode.2?int = liteServer.ValidatorStats;
liteServer.getLibraries library_list:(vector int256) = liteServer.LibraryResult;
liteServer.getShardBlockProof id:tonNode.blockIdExt = liteServer.ShardBlockProof;
liteServer.queryPrefix = Object;
liteServer.query data:bytes = Object;

Binary file not shown.

View file

@ -219,6 +219,9 @@ blocks.header id:ton.blockIdExt global_id:int32 version:int32 flags:# after_merg
blocks.signature node_id_short:int256 signature:bytes = blocks.Signature;
blocks.blockSignatures id:ton.blockIdExt signatures:(vector blocks.signature) = blocks.BlockSignatures;
blocks.shardBlockLink id:ton.blockIdExt proof:bytes = blocks.ShardBlockLink;
blocks.blockLinkBack to_key_block:Bool from:ton.blockIdExt to:ton.blockIdExt dest_proof:bytes proof:bytes state_proof:bytes = blocks.BlockLinkBack;
blocks.shardBlockProof from:ton.blockIdExt mc_id:ton.blockIdExt links:(vector blocks.shardBlockLink) mc_proof:(vector blocks.blockLinkBack) = blocks.ShardBlockProof;
configInfo config:tvm.cell = ConfigInfo;
@ -311,6 +314,7 @@ blocks.lookupBlock mode:int32 id:ton.blockId lt:int64 utime:int32 = ton.BlockIdE
blocks.getTransactions id:ton.blockIdExt mode:# count:# after:blocks.accountTransactionId = blocks.Transactions;
blocks.getBlockHeader id:ton.blockIdExt = blocks.Header;
blocks.getMasterchainBlockSignatures seqno:int32 = blocks.BlockSignatures;
blocks.getShardBlockProof id:ton.blockIdExt mode:# from:mode.0?ton.blockIdExt = blocks.ShardBlockProof;
onLiteServerQueryResult id:int64 bytes:bytes = Ok;
onLiteServerQueryError id:int64 error:error = Ok;

Binary file not shown.

View file

@ -1460,6 +1460,179 @@ class GetMasterchainBlockSignatures : public td::actor::Actor {
ton::BlockIdExt last_block_;
};
class GetShardBlockProof : public td::actor::Actor {
public:
GetShardBlockProof(ExtClientRef ext_client_ref, ton::BlockIdExt id, ton::BlockIdExt from,
td::actor::ActorShared<> parent,
td::Promise<tonlib_api_ptr<tonlib_api::blocks_shardBlockProof>>&& promise)
: id_(id), from_(from), parent_(std::move(parent)), promise_(std::move(promise)) {
client_.set_client(ext_client_ref);
}
void start_up() override {
if (from_.is_masterchain_ext()) {
got_from_block(from_);
} else {
client_.with_last_block([SelfId = actor_id(this)](td::Result<LastBlockState> R) {
if (R.is_error()) {
td::actor::send_closure(SelfId, &GetShardBlockProof::abort, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &GetShardBlockProof::got_from_block, R.move_as_ok().last_block_id);
}
});
}
}
void got_from_block(ton::BlockIdExt from) {
from_ = from;
CHECK(from_.is_masterchain_ext());
client_.send_query(
ton::lite_api::liteServer_getShardBlockProof(ton::create_tl_lite_block_id(id_)),
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_shardBlockProof>> R) {
if (R.is_error()) {
td::actor::send_closure(SelfId, &GetShardBlockProof::abort, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &GetShardBlockProof::got_shard_block_proof, R.move_as_ok());
}
});
}
void got_shard_block_proof(lite_api_ptr<ton::lite_api::liteServer_shardBlockProof> result) {
mc_id_ = create_block_id(std::move(result->masterchain_id_));
if (!mc_id_.is_masterchain_ext()) {
abort(td::Status::Error("got invalid masterchain block id"));
return;
}
if (result->links_.size() > 8) {
abort(td::Status::Error("chain is too long"));
return;
}
ton::BlockIdExt cur_id = mc_id_;
try {
for (auto& link : result->links_) {
ton::BlockIdExt prev_id = create_block_id(link->id_);
td::BufferSlice proof = std::move(link->proof_);
auto R = vm::std_boc_deserialize(proof);
if (R.is_error()) {
abort(TonlibError::InvalidBagOfCells("proof"));
return;
}
auto block_root = vm::MerkleProof::virtualize(R.move_as_ok(), 1);
if (cur_id.root_hash != block_root->get_hash().bits()) {
abort(td::Status::Error("invalid block hash in proof"));
return;
}
if (cur_id.is_masterchain()) {
block::gen::Block::Record blk;
block::gen::BlockExtra::Record extra;
block::gen::McBlockExtra::Record mc_extra;
if (!tlb::unpack_cell(block_root, blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() ||
!tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) {
abort(td::Status::Error("cannot unpack block header"));
return;
}
block::ShardConfig shards(mc_extra.shard_hashes->prefetch_ref());
td::Ref<block::McShardHash> shard_hash = shards.get_shard_hash(prev_id.shard_full(), true);
if (shard_hash.is_null() || shard_hash->top_block_id() != prev_id) {
abort(td::Status::Error("invalid proof chain: prev block is not in mc shard list"));
return;
}
} else {
std::vector<ton::BlockIdExt> prev;
ton::BlockIdExt mc_blkid;
bool after_split;
td::Status S = block::unpack_block_prev_blk_try(block_root, cur_id, prev, mc_blkid, after_split);
if (S.is_error()) {
abort(std::move(S));
return;
}
CHECK(prev.size() == 1 || prev.size() == 2);
bool found = prev_id == prev[0] || (prev.size() == 2 && prev_id == prev[1]);
if (!found) {
abort(td::Status::Error("invalid proof chain: prev block is not in prev blocks list"));
return;
}
}
links_.emplace_back(prev_id, std::move(proof));
cur_id = prev_id;
}
} catch (vm::VmVirtError& err) {
abort(err.as_status());
return;
}
if (cur_id != id_) {
abort(td::Status::Error("got invalid proof chain"));
return;
}
if (mc_id_.seqno() > from_.seqno()) {
abort(td::Status::Error("from mc block is too old"));
return;
}
client_.send_query(
ton::lite_api::liteServer_getBlockProof(0x1001, ton::create_tl_lite_block_id(from_),
ton::create_tl_lite_block_id(mc_id_)),
[SelfId = actor_id(this)](td::Result<lite_api_ptr<ton::lite_api::liteServer_partialBlockProof>> R) {
if (R.is_error()) {
td::actor::send_closure(SelfId, &GetShardBlockProof::abort, R.move_as_error());
} else {
td::actor::send_closure(SelfId, &GetShardBlockProof::got_mc_proof, R.move_as_ok());
}
});
}
void got_mc_proof(lite_api_ptr<ton::lite_api::liteServer_partialBlockProof> result) {
auto R = liteclient::deserialize_proof_chain(std::move(result));
if (R.is_error()) {
abort(R.move_as_error());
return;
}
auto chain = R.move_as_ok();
if (chain->from != from_ || chain->to != mc_id_ || !chain->complete || chain->link_count() > 1) {
abort(td::Status::Error("got invalid proof chain"));
return;
}
auto S = chain->validate();
if (S.is_error()) {
abort(std::move(S));
return;
}
std::vector<ton::tl_object_ptr<tonlib_api::blocks_shardBlockLink>> links;
std::vector<ton::tl_object_ptr<tonlib_api::blocks_blockLinkBack>> mc_proof;
for (const auto& p : links_) {
links.push_back(
ton::create_tl_object<tonlib_api::blocks_shardBlockLink>(to_tonlib_api(p.first), p.second.as_slice().str()));
}
if (chain->link_count() == 1) {
auto& link = chain->last_link();
td::BufferSlice dest_proof = vm::std_boc_serialize(link.dest_proof).move_as_ok();
td::BufferSlice proof = vm::std_boc_serialize(link.proof).move_as_ok();
td::BufferSlice state_proof = vm::std_boc_serialize(link.state_proof).move_as_ok();
mc_proof.push_back(ton::create_tl_object<tonlib_api::blocks_blockLinkBack>(
link.is_key, to_tonlib_api(link.from), to_tonlib_api(link.to), dest_proof.as_slice().str(),
proof.as_slice().str(), state_proof.as_slice().str()));
}
promise_.set_result(ton::create_tl_object<tonlib_api::blocks_shardBlockProof>(
to_tonlib_api(from_), to_tonlib_api(mc_id_), std::move(links), std::move(mc_proof)));
stop();
}
void abort(td::Status error) {
promise_.set_error(std::move(error));
stop();
}
private:
ton::BlockIdExt id_, from_, mc_id_;
td::actor::ActorShared<> parent_;
td::Promise<tonlib_api_ptr<tonlib_api::blocks_shardBlockProof>> promise_;
ExtClient client_;
std::vector<std::pair<ton::BlockIdExt, td::BufferSlice>> links_;
};
TonlibClient::TonlibClient(td::unique_ptr<TonlibCallback> callback) : callback_(std::move(callback)) {
}
TonlibClient::~TonlibClient() = default;
@ -4413,6 +4586,12 @@ auto to_lite_api(const tonlib_api::ton_blockIdExt& blk) -> td::Result<lite_api_p
blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash);
}
td::Result<ton::BlockIdExt> to_block_id(const tonlib_api::ton_blockIdExt& blk) {
TRY_RESULT(root_hash, to_bits256(blk.root_hash_, "blk.root_hash"))
TRY_RESULT(file_hash, to_bits256(blk.file_hash_, "blk.file_hash"))
return ton::BlockIdExt(blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash);
}
td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
TRY_RESULT(lite_block, to_lite_api(*request.id_))
@ -4624,6 +4803,19 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getMasterchainBlock
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::blocks_getShardBlockProof& request,
td::Promise<object_ptr<tonlib_api::blocks_shardBlockProof>>&& promise) {
TRY_RESULT(id, to_block_id(*request.id_));
ton::BlockIdExt from;
if (request.mode_ & 1) {
TRY_RESULT_ASSIGN(from, to_block_id(*request.id_));
}
auto actor_id = actor_id_++;
actors_[actor_id] = td::actor::create_actor<GetShardBlockProof>("GetShardBlockProof", client_.get_client(), id, from,
actor_shared(this, actor_id), std::move(promise));
return td::Status::OK();
}
void TonlibClient::load_libs_from_disk() {
LOG(DEBUG) << "loading libraries from disk cache";
auto r_data = kv_->get("tonlib.libcache");

View file

@ -366,6 +366,8 @@ class TonlibClient : public td::actor::Actor {
td::Promise<object_ptr<tonlib_api::blocks_header>>&& promise);
td::Status do_request(const tonlib_api::blocks_getMasterchainBlockSignatures& request,
td::Promise<object_ptr<tonlib_api::blocks_blockSignatures>>&& promise);
td::Status do_request(const tonlib_api::blocks_getShardBlockProof& request,
td::Promise<object_ptr<tonlib_api::blocks_shardBlockProof>>&& promise);
td::Status do_request(const tonlib_api::getConfigParam& request,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);

View file

@ -201,6 +201,9 @@ void LiteQuery::start_up() {
[&](lite_api::liteServer_getLibraries& q) {
this->perform_getLibraries(q.library_list_);
},
[&](lite_api::liteServer_getShardBlockProof& q) {
this->perform_getShardBlockProof(create_block_id(q.id_));
},
[&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); }));
}
@ -2432,5 +2435,135 @@ void LiteQuery::continue_getValidatorStats(int mode, int limit, Bits256 start_af
finish_query(std::move(b));
}
void LiteQuery::perform_getShardBlockProof(BlockIdExt blkid) {
LOG(INFO) << "started a getMasterchainInfo(" << blkid.to_str() << ") liteserver query";
if (!blkid.is_valid_ext()) {
fatal_error("invalid block id");
return;
}
if (blkid.is_masterchain()) {
LOG(INFO) << "getShardBlockProof() query completed";
auto b = create_serialize_tl_object<lite_api::liteServer_shardBlockProof>(
create_tl_lite_block_id(blkid), std::vector<tl_object_ptr<lite_api::liteServer_shardBlockLink>>());
finish_query(std::move(b));
return;
}
blk_id_ = blkid;
get_block_handle_checked(blkid, [manager = manager_, Self = actor_id(this)](td::Result<ConstBlockHandle> R) {
if (R.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
return;
}
ConstBlockHandle handle = R.move_as_ok();
if (!handle->inited_masterchain_ref_block()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, td::Status::Error("block doesn't have masterchain ref"));
return;
}
AccountIdPrefixFull pfx{masterchainId, shardIdAll};
td::actor::send_closure_later(
manager, &ValidatorManager::get_block_by_seqno_from_db, pfx, handle->masterchain_ref_block(),
[Self, manager](td::Result<ConstBlockHandle> R) {
if (R.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
} else {
ConstBlockHandle handle = R.move_as_ok();
td::actor::send_closure_later(
manager, &ValidatorManager::get_block_data_from_db, handle, [Self](td::Result<Ref<BlockData>> R) {
if (R.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
} else {
td::actor::send_closure_later(Self, &LiteQuery::continue_getShardBlockProof, R.move_as_ok(),
std::vector<std::pair<BlockIdExt, td::BufferSlice>>());
}
});
}
});
});
}
void LiteQuery::continue_getShardBlockProof(Ref<BlockData> cur_block,
std::vector<std::pair<BlockIdExt, td::BufferSlice>> result) {
BlockIdExt cur_id = cur_block->block_id();
BlockIdExt prev_id;
vm::MerkleProofBuilder mpb{cur_block->root_cell()};
if (cur_id.is_masterchain()) {
base_blk_id_ = cur_id;
block::gen::Block::Record blk;
block::gen::BlockExtra::Record extra;
block::gen::McBlockExtra::Record mc_extra;
if (!tlb::unpack_cell(mpb.root(), blk) || !tlb::unpack_cell(blk.extra, extra) || !extra.custom->have_refs() ||
!tlb::unpack_cell(extra.custom->prefetch_ref(), mc_extra)) {
fatal_error("cannot unpack header of block "s + cur_id.to_str());
return;
}
block::ShardConfig shards(mc_extra.shard_hashes->prefetch_ref());
ShardIdFull shard_id = blk_id_.shard_full();
shard_id.shard = (shard_id.shard & ~(1 << (63 - shard_id.pfx_len()))) | 1;
Ref<block::McShardHash> shard_hash = shards.get_shard_hash(shard_id, false);
if (shard_hash.is_null()) {
fatal_error("shard not found");
return;
}
prev_id = shard_hash->top_block_id();
} else {
std::vector<BlockIdExt> prev;
BlockIdExt mc_blkid;
bool after_split;
td::Status S = block::unpack_block_prev_blk_try(mpb.root(), cur_id, prev, mc_blkid, after_split);
if (S.is_error()) {
fatal_error(std::move(S));
return;
}
bool found = false;
for (const BlockIdExt& id : prev) {
if (shard_intersects(id.shard_full(), blk_id_.shard_full())) {
found = true;
prev_id = id;
break;
}
}
if (!found) {
fatal_error("failed to find block chain");
return;
}
}
auto proof = mpb.extract_proof_boc();
if (proof.is_error()) {
fatal_error(proof.move_as_error_prefix("cannot serialize Merkle proof : "));
return;
}
result.emplace_back(prev_id, proof.move_as_ok());
if (prev_id == blk_id_) {
CHECK(base_blk_id_.is_masterchain());
std::vector<tl_object_ptr<lite_api::liteServer_shardBlockLink>> links;
for (auto& p : result) {
links.push_back(
create_tl_object<lite_api::liteServer_shardBlockLink>(create_tl_lite_block_id(p.first), std::move(p.second)));
}
LOG(INFO) << "getShardBlockProof() query completed";
auto b = create_serialize_tl_object<lite_api::liteServer_shardBlockProof>(create_tl_lite_block_id(base_blk_id_),
std::move(links));
finish_query(std::move(b));
return;
}
if (result.size() == 8) {
// Chains of shardblocks between masterchain blocks can't be longer than 8 (see collator.cpp:991)
fatal_error("proof chain is too long");
return;
}
td::actor::send_closure_later(
manager_, &ValidatorManager::get_block_data_from_db_short, prev_id,
[Self = actor_id(this), result = std::move(result)](td::Result<Ref<BlockData>> R) mutable {
if (R.is_error()) {
td::actor::send_closure(Self, &LiteQuery::abort_query, R.move_as_error());
} else {
td::actor::send_closure_later(Self, &LiteQuery::continue_getShardBlockProof, R.move_as_ok(),
std::move(result));
}
});
}
} // namespace validator
} // namespace ton

View file

@ -145,6 +145,9 @@ class LiteQuery : public td::actor::Actor {
bool construct_proof_link_back_cont(ton::BlockIdExt cur, ton::BlockIdExt next);
bool adjust_last_proof_link(ton::BlockIdExt cur, Ref<vm::Cell> block_root);
bool finish_proof_chain(ton::BlockIdExt id);
void perform_getShardBlockProof(BlockIdExt blkid);
void continue_getShardBlockProof(Ref<BlockData> cur_block,
std::vector<std::pair<BlockIdExt, td::BufferSlice>> result);
void load_prevKeyBlock(ton::BlockIdExt blkid, td::Promise<std::pair<BlockIdExt, Ref<BlockQ>>>);
void continue_loadPrevKeyBlock(ton::BlockIdExt blkid, td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res,