From 5fae8db7a03d9d16b8bbaedd16a3d70202d22c28 Mon Sep 17 00:00:00 2001 From: SpyCheese Date: Thu, 28 Nov 2024 13:09:40 +0300 Subject: [PATCH] Adapt "get msg queue sizes" in lite-client and tonlib to non-full liteservers --- lite-client/lite-client.cpp | 92 +++++++++++++++--- lite-client/lite-client.h | 3 +- tonlib/tonlib/TonlibClient.cpp | 167 ++++++++++++++++++++++++++++++--- tonlib/tonlib/tonlib-cli.cpp | 19 ++++ 4 files changed, 254 insertions(+), 27 deletions(-) diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index dc09ae52..ce3dd4b2 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -1627,27 +1627,93 @@ void TestNode::send_compute_complaint_price_query(ton::StdSmcAddress elector_add } bool TestNode::get_msg_queue_sizes() { - auto q = ton::serialize_tl_object(ton::create_tl_object(0, 0, 0), true); - return envelope_send_query(std::move(q), [Self = actor_id(this)](td::Result res) -> void { - if (res.is_error()) { - LOG(ERROR) << "liteServer.getOutMsgQueueSizes error: " << res.move_as_error(); + ton::BlockIdExt blkid = mc_last_id_; + if (!blkid.is_valid_full()) { + return set_error("must obtain last block information before making other queries"); + } + if (!(ready_ && !client_.empty())) { + return set_error("server connection not ready"); + } + auto b = + ton::create_serialize_tl_object(ton::create_tl_lite_block_id(blkid)); + LOG(INFO) << "requesting recent shard configuration"; + return envelope_send_query(std::move(b), [Self = actor_id(this), blkid](td::Result R) -> void { + if (R.is_error()) { return; } - auto F = ton::fetch_tl_object(res.move_as_ok(), true); + auto F = ton::fetch_tl_object(R.move_as_ok(), true); if (F.is_error()) { - LOG(ERROR) << "cannot parse answer to liteServer.getOutMsgQueueSizes"; - return; + LOG(ERROR) << "cannot parse answer to liteServer.getAllShardsInfo"; + } else { + auto f = F.move_as_ok(); + td::actor::send_closure_later(Self, &TestNode::get_msg_queue_sizes_cont, blkid, std::move(f->data_)); } - td::actor::send_closure_later(Self, &TestNode::got_msg_queue_sizes, F.move_as_ok()); }); } -void TestNode::got_msg_queue_sizes(ton::tl_object_ptr f) { - td::TerminalIO::out() << "Outbound message queue sizes:" << std::endl; - for (auto &x : f->shards_) { - td::TerminalIO::out() << ton::create_block_id(x->id_).id.to_str() << " " << x->size_ << std::endl; +void TestNode::get_msg_queue_sizes_cont(ton::BlockIdExt mc_blkid, td::BufferSlice data) { + LOG(INFO) << "got shard configuration with respect to block " << mc_blkid.to_str(); + std::vector blocks; + blocks.push_back(mc_blkid); + auto R = vm::std_boc_deserialize(data.clone()); + if (R.is_error()) { + set_error(R.move_as_error_prefix("cannot deserialize shard configuration: ")); + return; + } + auto root = R.move_as_ok(); + block::ShardConfig sh_conf; + if (!sh_conf.unpack(vm::load_cell_slice_ref(root))) { + set_error("cannot extract shard block list from shard configuration"); + return; + } + auto ids = sh_conf.get_shard_hash_ids(true); + for (auto id : ids) { + auto ref = sh_conf.get_shard_hash(ton::ShardIdFull(id)); + if (ref.not_null()) { + blocks.push_back(ref->top_block_id()); + } + } + + struct QueryInfo { + std::vector blocks; + std::vector sizes; + size_t pending; + }; + auto info = std::make_shared(); + info->blocks = std::move(blocks); + info->sizes.resize(info->blocks.size(), 0); + info->pending = info->blocks.size(); + + for (size_t i = 0; i < info->blocks.size(); ++i) { + ton::BlockIdExt block_id = info->blocks[i]; + auto b = ton::create_serialize_tl_object( + 0, ton::create_tl_lite_block_id(block_id), false); + LOG(DEBUG) << "requesting queue size for block " << block_id.to_str(); + envelope_send_query(std::move(b), [=, this](td::Result R) -> void { + if (R.is_error()) { + return; + } + auto F = ton::fetch_tl_object(R.move_as_ok(), true); + if (F.is_error()) { + set_error(F.move_as_error_prefix("failed to get queue size: ")); + return; + } + auto f = F.move_as_ok(); + LOG(DEBUG) << "got queue size for block " << block_id.to_str() << " : " << f->size_; + info->sizes[i] = f->size_; + if (--info->pending == 0) { + get_msg_queue_sizes_finish(std::move(info->blocks), std::move(info->sizes)); + } + }); + } +} + +void TestNode::get_msg_queue_sizes_finish(std::vector blocks, std::vector sizes) { + CHECK(blocks.size() == sizes.size()); + td::TerminalIO::out() << "Outbound message queue sizes:" << std::endl; + for (size_t i = 0; i < blocks.size(); ++i) { + td::TerminalIO::out() << blocks[i].id.to_str() << " " << sizes[i] << std::endl; } - td::TerminalIO::out() << "External message queue size limit: " << f->ext_msg_queue_size_limit_ << std::endl; } bool TestNode::get_dispatch_queue_info(ton::BlockIdExt block_id) { diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 721d2b20..57804418 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -324,7 +324,8 @@ class TestNode : public td::actor::Actor { void send_compute_complaint_price_query(ton::StdSmcAddress elector_addr, unsigned expires_in, unsigned bits, unsigned refs, td::Bits256 chash, std::string filename); bool get_msg_queue_sizes(); - void got_msg_queue_sizes(ton::tl_object_ptr f); + void get_msg_queue_sizes_cont(ton::BlockIdExt mc_blkid, td::BufferSlice data); + void get_msg_queue_sizes_finish(std::vector blocks, std::vector sizes); bool get_dispatch_queue_info(ton::BlockIdExt block_id); bool get_dispatch_queue_info_cont(ton::BlockIdExt block_id, bool first, td::Bits256 after_addr); void got_dispatch_queue_info(ton::BlockIdExt block_id, diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index b9ff4899..73cb2a16 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -1800,6 +1800,132 @@ class GetShardBlockProof : public td::actor::Actor { std::vector> links_; }; +class GetOutMsgQueueSizes : public td::actor::Actor { + public: + GetOutMsgQueueSizes(ExtClientRef ext_client_ref, std::vector blocks, + td::actor::ActorShared<> parent, + td::Promise>&& promise) + : blocks_(std::move(blocks)), parent_(std::move(parent)), promise_(std::move(promise)) { + client_.set_client(ext_client_ref); + } + + void start_up() override { + sizes_.resize(blocks_.size()); + pending_ = blocks_.size() + 1; + + for (size_t i = 0; i < blocks_.size(); ++i) { + client_.send_query( + ton::lite_api::liteServer_getBlockOutMsgQueueSize(1, ton::create_tl_lite_block_id(blocks_[i]), true), + [SelfId = actor_id(this), i](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &GetOutMsgQueueSizes::abort, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &GetOutMsgQueueSizes::got_block_queue_size, i, R.move_as_ok()); + } + }); + } + + client_.send_query( + ton::lite_api::liteServer_getOutMsgQueueSizes(1, ton::masterchainId, ton::shardIdAll), + [SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &GetOutMsgQueueSizes::abort, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &GetOutMsgQueueSizes::got_ext_msg_queue_size_limit, + R.ok()->ext_msg_queue_size_limit_); + } + }); + } + + void got_block_queue_size(size_t i, lite_api_ptr f) { + try { + auto S = [&, this]() -> td::Status { + TRY_RESULT_PREFIX(roots, vm::std_boc_deserialize_multi(f->proof_), "cannot deserialize proof: "); + if (roots.size() != 2) { + return td::Status::Error("expected 2 roots in proof"); + } + auto state_root = vm::MerkleProof::virtualize(std::move(roots[1]), 1); + if (state_root.is_null()) { + return td::Status::Error("state proof is invalid"); + } + ton::Bits256 state_hash = state_root->get_hash().bits(); + TRY_STATUS_PREFIX(block::check_block_header_proof(vm::MerkleProof::virtualize(std::move(roots[0]), 1), + blocks_[i], &state_hash, true, nullptr, nullptr), + "error in block header proof: "); + + block::gen::ShardStateUnsplit::Record sstate; + block::gen::OutMsgQueueInfo::Record out_msg_queue_info; + if (!tlb::unpack_cell(state_root, sstate) || !tlb::unpack_cell(sstate.out_msg_queue_info, out_msg_queue_info)) { + return td::Status::Error("cannot unpack shard state"); + } + vm::CellSlice& extra_slice = out_msg_queue_info.extra.write(); + if (extra_slice.fetch_long(1) == 0) { + return td::Status::Error("no out_msg_queue_size in shard state"); + } + block::gen::OutMsgQueueExtra::Record out_msg_queue_extra; + if (!tlb::unpack(extra_slice, out_msg_queue_extra)) { + return td::Status::Error("cannot unpack OutMsgQueueExtra"); + } + vm::CellSlice& size_slice = out_msg_queue_extra.out_queue_size.write(); + if (size_slice.fetch_long(1) == 0) { + return td::Status::Error("no out_msg_queue_size in shard state"); + } + td::uint64 size = size_slice.prefetch_ulong(48); + if (size != f->size_) { + return td::Status::Error("queue size mismatch"); + } + return td::Status::OK(); + }(); + if (S.is_error()) { + abort(std::move(S)); + return; + } + } catch (vm::VmError& err) { + abort(err.as_status()); + return; + } catch (vm::VmVirtError& err) { + abort(err.as_status()); + return; + } + + sizes_[i] = f->size_; + dec_pending(); + } + + void got_ext_msg_queue_size_limit(td::uint32 value) { + ext_msg_queue_size_limit_ = value; + dec_pending(); + } + + void dec_pending() { + if (--pending_ == 0) { + std::vector> shards; + for (size_t i = 0; i < blocks_.size(); ++i) { + shards.push_back( + tonlib_api::make_object(to_tonlib_api(blocks_[i]), sizes_[i])); + } + promise_.set_result( + tonlib_api::make_object(std::move(shards), ext_msg_queue_size_limit_)); + stop(); + } + } + + void abort(td::Status error) { + promise_.set_error(std::move(error)); + stop(); + } + + private: + std::vector blocks_; + td::actor::ActorShared<> parent_; + td::Promise> promise_; + ExtClient client_; + + std::vector sizes_; + td::uint32 ext_msg_queue_size_limit_ = 0; + size_t pending_ = 0; +}; + auto to_lite_api(const tonlib_api::ton_blockIdExt& blk) -> td::Result>; auto to_tonlib_api(const ton::lite_api::liteServer_transactionId& txid) -> tonlib_api_ptr; @@ -6129,19 +6255,34 @@ td::Status TonlibClient::do_request(const tonlib_api::blocks_getShardBlockProof& td::Status TonlibClient::do_request(const tonlib_api::blocks_getOutMsgQueueSizes& request, td::Promise>&& promise) { - client_.send_query(ton::lite_api::liteServer_getOutMsgQueueSizes(request.mode_, request.wc_, request.shard_), - promise.wrap([](lite_api_ptr&& queue_sizes) { - tonlib_api::blocks_outMsgQueueSizes result; - result.ext_msg_queue_size_limit_ = queue_sizes->ext_msg_queue_size_limit_; - for (auto &x : queue_sizes->shards_) { - tonlib_api::blocks_outMsgQueueSize shard; - shard.id_ = to_tonlib_api(*x->id_); - shard.size_ = x->size_; - result.shards_.push_back(tonlib_api::make_object(std::move(shard))); - } - return tonlib_api::make_object(std::move(result)); - })); - + auto req_mode = request.mode_; + auto req_shard = ton::ShardIdFull{request.wc_, (ton::ShardId)request.shard_}; + if ((req_mode & 1) && !req_shard.is_valid_ext()) { + return td::Status::Error("invalid shard"); + } + client_.with_last_block( + [=, self = this, promise = std::move(promise)](td::Result r_last_block) mutable { + TRY_RESULT_PROMISE_PREFIX(promise, last_block, std::move(r_last_block), "get last block failed: "); + do_request(tonlib_api::blocks_getShards(to_tonlib_api(last_block.last_block_id)), + [=, mc_blkid = last_block.last_block_id, + promise = std::move(promise)](td::Result> R) mutable { + TRY_RESULT_PROMISE_PREFIX(promise, shards, std::move(R), "get shards failed: "); + std::vector blocks; + if (!(req_mode & 1) || ton::shard_intersects(mc_blkid.shard_full(), req_shard)) { + blocks.push_back(mc_blkid); + } + for (const auto& shard : shards->shards_) { + TRY_RESULT_PROMISE(promise, block_id, to_block_id(*shard)); + if (!(req_mode & 1) || ton::shard_intersects(block_id.shard_full(), req_shard)) { + blocks.push_back(block_id); + } + } + auto actor_id = self->actor_id_++; + self->actors_[actor_id] = td::actor::create_actor( + "GetOutMsgQueueSizes", self->client_.get_client(), std::move(blocks), + actor_shared(this, actor_id), std::move(promise)); + }); + }); return td::Status::OK(); } diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 2c7100f2..364b8f66 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -430,6 +430,7 @@ class TonlibCli : public td::actor::Actor { << "\t 'k' modifier - use fake key\n" << "\t 'c' modifier - just esmitate fees\n"; td::TerminalIO::out() << "getmasterchainsignatures - get sigratures of masterchain block \n"; + td::TerminalIO::out() << "msgqueuesizes - get out msg queue sizes in the latest shard states\n"; } else if (cmd == "genkey") { generate_key(); } else if (cmd == "exit" || cmd == "quit") { @@ -517,6 +518,8 @@ class TonlibCli : public td::actor::Actor { } else if (cmd == "getmasterchainsignatures") { auto seqno = parser.read_word(); run_get_masterchain_block_signatures(seqno, std::move(cmd_promise)); + } else if (cmd == "msgqueuesizes") { + run_get_out_msg_queue_sizes(std::move(cmd_promise)); } else if (cmd == "showtransactions") { run_show_transactions(parser, std::move(cmd_promise)); } else { @@ -2161,6 +2164,22 @@ class TonlibCli : public td::actor::Actor { })); } + void run_get_out_msg_queue_sizes(td::Promise promise) { + send_query(make_object(0, 0, 0), + promise.wrap([](tonlib_api::object_ptr&& f) { + td::TerminalIO::out() << "Outbound message queue sizes:" << std::endl; + for (const auto& shard : f->shards_) { + td::TerminalIO::out() << ton::BlockId{shard->id_->workchain_, (ton::ShardId)shard->id_->shard_, + (ton::BlockSeqno)shard->id_->seqno_} + .to_str() + << " " << shard->size_ << std::endl; + } + td::TerminalIO::out() << "External message queue size limit: " << f->ext_msg_queue_size_limit_ + << std::endl; + return td::Unit(); + })); + } + void run_show_transactions(td::ConstParser& parser, td::Promise promise) { TRY_RESULT_PROMISE(promise, address, to_account_address(parser.read_word(), false)); TRY_RESULT_PROMISE(promise, lt, td::to_integer_safe(parser.read_word()));