diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 02a5fab67..748aff8f6 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -974,6 +974,11 @@ bool TestNode::show_help(std::string command) { "into files .boc\n" "complaintprice \tComputes the price (in nanograms) for creating a complaint\n" "msgqueuesizes\tShows current sizes of outbound message queues in all shards\n" + "dispatchqueueinfo \tShows list of account dispatch queue of a block\n" + "dispatchqueuemessages []\tShows deferred messages from account , lt > " + "\n" + "dispatchqueuemessagesall [ []]\tShows messages from dispatch queue of a " + "block, starting after , \n" "known\tShows the list of all known block ids\n" "knowncells\tShows the list of hashes of all known (cached) cells\n" "dumpcell \nDumps a cached cell by a prefix of its hash\n" @@ -988,9 +993,9 @@ bool TestNode::show_help(std::string command) { bool TestNode::do_parse_line() { ton::WorkchainId workchain = ton::masterchainId; // change to basechain later int addr_ext = 0; - ton::StdSmcAddress addr{}; + ton::StdSmcAddress addr = ton::StdSmcAddress::zero(); ton::BlockIdExt blkid{}; - ton::LogicalTime lt{}; + ton::LogicalTime lt = 0; ton::Bits256 hash{}; ton::ShardIdFull shard{}; ton::BlockSeqno seqno{}; @@ -1118,6 +1123,16 @@ bool TestNode::do_parse_line() { set_error(get_complaint_price(expire_in, filename)); } else if (word == "msgqueuesizes") { return get_msg_queue_sizes(); + } else if (word == "dispatchqueueinfo") { + return parse_block_id_ext(blkid) && seekeoln() && get_dispatch_queue_info(blkid); + } else if (word == "dispatchqueuemessages" || word == "dispatchqueuemessagesall") { + bool one_account = word == "dispatchqueuemessages"; + if (!parse_block_id_ext(blkid)) { + return false; + } + workchain = blkid.id.workchain; + return ((!one_account && seekeoln()) || parse_account_addr(workchain, addr)) && (seekeoln() || parse_lt(lt)) && + seekeoln() && get_dispatch_queue_messages(blkid, workchain, addr, lt, one_account); } else if (word == "known") { return eoln() && show_new_blkids(true); } else if (word == "knowncells") { @@ -1645,6 +1660,81 @@ void TestNode::got_msg_queue_sizes(ton::tl_object_ptrext_msg_queue_size_limit_ << std::endl; } +bool TestNode::get_dispatch_queue_info(ton::BlockIdExt block_id) { + td::TerminalIO::out() << "Dispatch queue in block: " << block_id.id.to_str() << std::endl; + return get_dispatch_queue_info_cont(block_id, true, td::Bits256::zero()); +} + +bool TestNode::get_dispatch_queue_info_cont(ton::BlockIdExt block_id, bool first, td::Bits256 after_addr) { + auto q = ton::create_serialize_tl_object( + first ? 0 : 2, ton::create_tl_lite_block_id(block_id), after_addr, 32, false); + return envelope_send_query(std::move(q), [=, Self = actor_id(this)](td::Result res) -> void { + if (res.is_error()) { + LOG(ERROR) << "liteServer.getDispatchQueueInfo error: " << res.move_as_error(); + return; + } + auto F = ton::fetch_tl_object(res.move_as_ok(), true); + if (F.is_error()) { + LOG(ERROR) << "cannot parse answer to liteServer.getDispatchQueueInfo"; + return; + } + td::actor::send_closure_later(Self, &TestNode::got_dispatch_queue_info, block_id, F.move_as_ok()); + }); +} + +void TestNode::got_dispatch_queue_info(ton::BlockIdExt block_id, + ton::tl_object_ptr info) { + for (auto& acc : info->account_dispatch_queues_) { + td::TerminalIO::out() << block_id.id.workchain << ":" << acc->addr_.to_hex() << " : size=" << acc->size_ + << " lt=" << acc->min_lt_ << ".." << acc->max_lt_ << std::endl; + } + if (info->complete_) { + td::TerminalIO::out() << "Done" << std::endl; + return; + } + get_dispatch_queue_info_cont(block_id, false, info->account_dispatch_queues_.back()->addr_); +} + +bool TestNode::get_dispatch_queue_messages(ton::BlockIdExt block_id, ton::WorkchainId wc, ton::StdSmcAddress addr, + ton::LogicalTime lt, bool one_account) { + if (wc != block_id.id.workchain) { + return set_error("workchain mismatch"); + } + auto q = ton::create_serialize_tl_object( + one_account ? 2 : 0, ton::create_tl_lite_block_id(block_id), addr, lt, 64, false, one_account, false); + return envelope_send_query(std::move(q), [=, Self = actor_id(this)](td::Result res) -> void { + if (res.is_error()) { + LOG(ERROR) << "liteServer.getDispatchQueueMessages error: " << res.move_as_error(); + return; + } + auto F = ton::fetch_tl_object(res.move_as_ok(), true); + if (F.is_error()) { + LOG(ERROR) << "cannot parse answer to liteServer.getDispatchQueueMessages"; + return; + } + td::actor::send_closure_later(Self, &TestNode::got_dispatch_queue_messages, F.move_as_ok()); + }); +} + +void TestNode::got_dispatch_queue_messages(ton::tl_object_ptr msgs) { + td::TerminalIO::out() << "Dispatch queue messages (" << msgs->messages_.size() << "):\n"; + int count = 0; + for (auto& m : msgs->messages_) { + auto& meta = m->metadata_; + td::TerminalIO::out() << "Msg #" << ++count << ": " << msgs->id_->workchain_ << ":" << m->addr_.to_hex() << " " + << m->lt_ << " : " + << (meta->initiator_->workchain_ == ton::workchainInvalid + ? "[ no metadata ]" + : block::MsgMetadata{(td::uint32)meta->depth_, meta->initiator_->workchain_, + meta->initiator_->id_, (ton::LogicalTime)meta->initiator_lt_} + .to_str()) + << "\n"; + } + if (!msgs->complete_) { + td::TerminalIO::out() << "(incomplete list)\n"; + } +} + bool TestNode::dns_resolve_start(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt blkid, std::string domain, td::Bits256 cat, int mode) { if (domain.size() >= 2 && domain[0] == '"' && domain.back() == '"') { @@ -4322,7 +4412,7 @@ int main(int argc, char* argv[]) { }); p.add_option('V', "version", "shows lite-client build information", [&]() { std::cout << "lite-client build information: [ Commit: " << GitMetadata::CommitSHA1() << ", Date: " << GitMetadata::CommitDate() << "]\n"; - + std::exit(0); }); p.add_option('i', "idx", "set liteserver idx", [&](td::Slice arg) { diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 17680f448..6fb7f9ab5 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -307,6 +307,13 @@ class TestNode : public td::actor::Actor { unsigned refs, td::Bits256 chash, std::string filename); bool get_msg_queue_sizes(); void got_msg_queue_sizes(ton::tl_object_ptr f); + 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, + ton::tl_object_ptr info); + bool get_dispatch_queue_messages(ton::BlockIdExt block_id, ton::WorkchainId wc, ton::StdSmcAddress addr, + ton::LogicalTime lt, bool one_account); + void got_dispatch_queue_messages(ton::tl_object_ptr msgs); bool cache_cell(Ref cell); bool list_cached_cells() const; bool dump_cached_cell(td::Slice hash_pfx, td::Slice type_name = {}); diff --git a/tl-utils/lite-utils.cpp b/tl-utils/lite-utils.cpp index daa3dbaf0..387474cfa 100644 --- a/tl-utils/lite-utils.cpp +++ b/tl-utils/lite-utils.cpp @@ -160,6 +160,8 @@ std::string lite_query_name_by_id(int id) { {lite_api::liteServer_getShardBlockProof::ID, "getShardBlockProof"}, {lite_api::liteServer_getOutMsgQueueSizes::ID, "getOutMsgQueueSizes"}, {lite_api::liteServer_getBlockOutMsgQueueSize::ID, "getBlockOutMsgQueueSize"}, + {lite_api::liteServer_getDispatchQueueInfo::ID, "getDispatchQueueInfo"}, + {lite_api::liteServer_getDispatchQueueMessages::ID, "getDispatchQueueMessages"}, {lite_api::liteServer_nonfinal_getCandidate::ID, "nonfinal.getCandidate"}, {lite_api::liteServer_nonfinal_getValidatorGroups::ID, "nonfinal.getValidatorGroups"}}; auto it = names.find(id); diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index 879d7ff4a..b00951da4 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -61,6 +61,11 @@ liteServer.lookupBlockResult id:tonNode.blockIdExt mode:# mc_block_id:tonNode.bl liteServer.outMsgQueueSize id:tonNode.blockIdExt size:int = liteServer.OutMsgQueueSize; liteServer.outMsgQueueSizes shards:(vector liteServer.outMsgQueueSize) ext_msg_queue_size_limit:int = liteServer.OutMsgQueueSizes; liteServer.blockOutMsgQueueSize mode:# id:tonNode.blockIdExt size:long proof:mode.0?bytes = liteServer.BlockOutMsgQueueSize; +liteServer.accountDispatchQueueInfo addr:int256 size:long min_lt:long max_lt:long = liteServer.AccountDispatchQueueInfo; +liteServer.dispatchQueueInfo mode:# id:tonNode.blockIdExt account_dispatch_queues:(vector liteServer.accountDispatchQueueInfo) complete:Bool proof:mode.0?bytes = liteServer.DispatchQueueInfo; +liteServer.dispatchQueueMessage addr:int256 lt:long hash:int256 metadata:liteServer.transactionMetadata = liteServer.DispatchQueueMessage; +liteServer.dispatchQueueMessages mode:# id:tonNode.blockIdExt messages:(vector liteServer.dispatchQueueMessage) complete:Bool + proof:mode.0?bytes messages_boc:mode.2?bytes = liteServer.DispatchQueueMessages; liteServer.debug.verbosity value:int = liteServer.debug.Verbosity; @@ -100,6 +105,9 @@ liteServer.getLibrariesWithProof id:tonNode.blockIdExt mode:# library_list:(vect liteServer.getShardBlockProof id:tonNode.blockIdExt = liteServer.ShardBlockProof; liteServer.getOutMsgQueueSizes mode:# wc:mode.0?int shard:mode.0?long = liteServer.OutMsgQueueSizes; liteServer.getBlockOutMsgQueueSize mode:# id:tonNode.blockIdExt want_proof:mode.0?true = liteServer.BlockOutMsgQueueSize; +liteServer.getDispatchQueueInfo mode:# id:tonNode.blockIdExt after_addr:mode.1?int256 max_accounts:int want_proof:mode.0?true = liteServer.DispatchQueueInfo; +liteServer.getDispatchQueueMessages mode:# id:tonNode.blockIdExt addr:int256 after_lt:long max_messages:int + want_proof:mode.0?true one_account:mode.1?true messages_boc:mode.2?true = liteServer.DispatchQueueMessages; liteServer.nonfinal.getValidatorGroups mode:# wc:mode.0?int shard:mode.0?long = liteServer.nonfinal.ValidatorGroups; liteServer.nonfinal.getCandidate id:liteServer.nonfinal.candidateId = liteServer.nonfinal.Candidate; diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index 6ece1d20f..c576a7f45 100644 Binary files a/tl/generate/scheme/lite_api.tlo and b/tl/generate/scheme/lite_api.tlo differ diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index d6fad7ee2..f804d96be 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -290,6 +290,13 @@ void LiteQuery::perform() { [&](lite_api::liteServer_getBlockOutMsgQueueSize& q) { this->perform_getBlockOutMsgQueueSize(q.mode_, create_block_id(q.id_)); }, + [&](lite_api::liteServer_getDispatchQueueInfo& q) { + this->perform_getDispatchQueueInfo(q.mode_, create_block_id(q.id_), q.after_addr_, q.max_accounts_); + }, + [&](lite_api::liteServer_getDispatchQueueMessages& q) { + this->perform_getDispatchQueueMessages(q.mode_, create_block_id(q.id_), q.addr_, + std::max(q.after_lt_, 0), q.max_messages_); + }, [&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); })); } @@ -3432,6 +3439,248 @@ void LiteQuery::finish_getBlockOutMsgQueueSize() { finish_query(std::move(b)); } +void LiteQuery::perform_getDispatchQueueInfo(int mode, BlockIdExt blkid, StdSmcAddress after_addr, int max_accounts) { + LOG(INFO) << "started a getDispatchQueueInfo(" << blkid.to_str() << ", " << mode << ") liteserver query"; + mode_ = mode; + if (!blkid.is_valid_full()) { + fatal_error("invalid BlockIdExt"); + return; + } + if (max_accounts <= 0) { + fatal_error("invalid max_accounts"); + return; + } + set_continuation([=]() -> void { finish_getDispatchQueueInfo(after_addr, max_accounts); }); + request_block_data_state(blkid); +} + +void LiteQuery::finish_getDispatchQueueInfo(StdSmcAddress after_addr, int max_accounts) { + LOG(INFO) << "completing getDispatchQueueInfo() query"; + bool with_proof = mode_ & 1; + Ref state_root = state_->root_cell(); + vm::MerkleProofBuilder pb; + if (with_proof) { + pb = vm::MerkleProofBuilder{state_root}; + state_root = pb.root(); + } + + std::unique_ptr dispatch_queue; + + block::gen::ShardStateUnsplit::Record sstate; + block::gen::OutMsgQueueInfo::Record out_msg_queue_info; + block::gen::OutMsgQueueExtra::Record out_msg_queue_extra; + if (!tlb::unpack_cell(state_root, sstate) || !tlb::unpack_cell(sstate.out_msg_queue_info, out_msg_queue_info)) { + fatal_error("cannot unpack shard state"); + return; + } + vm::CellSlice& extra_slice = out_msg_queue_info.extra.write(); + if (extra_slice.fetch_long(1)) { + if (!tlb::unpack(extra_slice, out_msg_queue_extra)) { + fatal_error("cannot unpack OutMsgQueueExtra"); + return; + } + dispatch_queue = std::make_unique(out_msg_queue_extra.dispatch_queue, 256, + block::tlb::aug_DispatchQueue); + } else { + dispatch_queue = std::make_unique(256, block::tlb::aug_DispatchQueue); + } + + int remaining = std::min(max_accounts, 64); + bool complete = false; + std::vector> result; + bool allow_eq; + if (mode_ & 2) { + allow_eq = false; + } else { + allow_eq = true; + after_addr = td::Bits256::zero(); + } + while (true) { + auto value = dispatch_queue->extract_value(dispatch_queue->lookup_nearest_key(after_addr, true, allow_eq)); + allow_eq = false; + if (value.is_null()) { + complete = true; + break; + } + if (remaining == 0) { + break; + } + --remaining; + StdSmcAddress addr = after_addr; + vm::Dictionary dict{64}; + td::uint64 dict_size; + if (!block::unpack_account_dispatch_queue(value, dict, dict_size)) { + fatal_error(PSTRING() << "invalid account dispatch queue for account " << addr.to_hex()); + return; + } + CHECK(dict_size > 0); + td::BitArray<64> min_lt, max_lt; + dict.get_minmax_key(min_lt.bits(), 64, false, false); + dict.get_minmax_key(max_lt.bits(), 64, true, false); + result.push_back(create_tl_object(addr, dict_size, min_lt.to_ulong(), + max_lt.to_ulong())); + } + + td::BufferSlice proof; + if (with_proof) { + Ref proof1, proof2; + if (!make_state_root_proof(proof1)) { + return; + } + if (!pb.extract_proof_to(proof2)) { + fatal_error("unknown error creating Merkle proof"); + return; + } + auto r_proof = vm::std_boc_serialize_multi({std::move(proof1), std::move(proof2)}); + if (r_proof.is_error()) { + fatal_error(r_proof.move_as_error()); + return; + } + proof = r_proof.move_as_ok(); + } + LOG(INFO) << "getDispatchQueueInfo(" << blk_id_.to_str() << ", " << mode_ << ") query completed"; + auto b = ton::create_serialize_tl_object( + mode_, ton::create_tl_lite_block_id(blk_id_), std::move(result), complete, std::move(proof)); + finish_query(std::move(b)); +} + +void LiteQuery::perform_getDispatchQueueMessages(int mode, BlockIdExt blkid, StdSmcAddress addr, LogicalTime lt, + int max_messages) { + LOG(INFO) << "started a getDispatchQueueMessages(" << blkid.to_str() << ", " << mode << ") liteserver query"; + mode_ = mode; + if (!blkid.is_valid_full()) { + fatal_error("invalid BlockIdExt"); + return; + } + if (max_messages <= 0) { + fatal_error("invalid max_messages"); + return; + } + set_continuation([=]() -> void { finish_getDispatchQueueMessages(addr, lt, max_messages); }); + request_block_data_state(blkid); +} + +void LiteQuery::finish_getDispatchQueueMessages(StdSmcAddress addr, LogicalTime lt, int max_messages) { + LOG(INFO) << "completing getDispatchQueueMessages() query"; + bool with_proof = mode_ & lite_api::liteServer_getDispatchQueueMessages::WANT_PROOF_MASK; + bool one_account = mode_ & lite_api::liteServer_getDispatchQueueMessages::ONE_ACCOUNT_MASK; + bool with_messages_boc = mode_ & lite_api::liteServer_getDispatchQueueMessages::MESSAGES_BOC_MASK; + Ref state_root = state_->root_cell(); + vm::MerkleProofBuilder pb; + if (with_proof) { + pb = vm::MerkleProofBuilder{state_root}; + state_root = pb.root(); + } + + std::unique_ptr dispatch_queue; + + block::gen::ShardStateUnsplit::Record sstate; + block::gen::OutMsgQueueInfo::Record out_msg_queue_info; + block::gen::OutMsgQueueExtra::Record out_msg_queue_extra; + if (!tlb::unpack_cell(state_root, sstate) || !tlb::unpack_cell(sstate.out_msg_queue_info, out_msg_queue_info)) { + fatal_error("cannot unpack shard state"); + return; + } + vm::CellSlice& extra_slice = out_msg_queue_info.extra.write(); + if (extra_slice.fetch_long(1)) { + if (!tlb::unpack(extra_slice, out_msg_queue_extra)) { + fatal_error("cannot unpack OutMsgQueueExtra"); + return; + } + dispatch_queue = std::make_unique(out_msg_queue_extra.dispatch_queue, 256, + block::tlb::aug_DispatchQueue); + } else { + dispatch_queue = std::make_unique(256, block::tlb::aug_DispatchQueue); + } + + int remaining = std::min(max_messages, with_messages_boc ? 16 : 64); + bool complete = false; + std::vector> result; + std::vector> message_roots; + td::Bits256 orig_addr = addr; + bool first = true; + while (remaining > 0) { + auto value = dispatch_queue->extract_value(dispatch_queue->lookup_nearest_key(addr, true, first)); + if (value.is_null() || (one_account && addr != orig_addr)) { + complete = true; + break; + } + vm::Dictionary account_queue{64}; + td::uint64 dict_size; + if (!block::unpack_account_dispatch_queue(value, account_queue, dict_size)) { + fatal_error(PSTRING() << "invalid account dispatch queue for account " << addr.to_hex()); + return; + } + CHECK(dict_size > 0); + while (true) { + td::BitArray<64> lt_key; + lt_key.store_ulong(lt); + auto value2 = account_queue.lookup_nearest_key(lt_key, true, false); + if (value2.is_null()) { + break; + } + lt = lt_key.to_ulong(); + if (remaining == 0) { + break; + } + --remaining; + auto msg_env = value2->prefetch_ref(); + block::tlb::MsgEnvelope::Record_std env; + if (msg_env.is_null() || !tlb::unpack_cell(msg_env, env)) { + fatal_error(PSTRING() << "invalid message in dispatch queue for account " << addr.to_hex() << ", lt " << lt); + return; + } + message_roots.push_back(env.msg); + tl_object_ptr metadata_tl; + if (env.metadata) { + auto& metadata = env.metadata.value(); + metadata_tl = create_tl_object( + 0, metadata.depth, + create_tl_object(metadata.initiator_wc, metadata.initiator_addr), + metadata.initiator_lt); + } else { + metadata_tl = create_tl_object( + 0, -1, create_tl_object(workchainInvalid, td::Bits256::zero()), -1); + } + result.push_back(create_tl_object(addr, lt, env.msg->get_hash().bits(), + std::move(metadata_tl))); + } + first = false; + lt = 0; + } + + td::BufferSlice proof; + if (with_proof) { + Ref proof1, proof2; + if (!make_state_root_proof(proof1)) { + return; + } + if (!pb.extract_proof_to(proof2)) { + fatal_error("unknown error creating Merkle proof"); + return; + } + auto r_proof = vm::std_boc_serialize_multi({std::move(proof1), std::move(proof2)}); + if (r_proof.is_error()) { + fatal_error(r_proof.move_as_error()); + return; + } + proof = r_proof.move_as_ok(); + } + td::BufferSlice messages_boc; + if (with_messages_boc) { + auto r_messages_boc = vm::std_boc_serialize_multi(std::move(message_roots)); + if (r_messages_boc.is_error()) { + fatal_error(r_messages_boc.move_as_error()); + return; + } + messages_boc = std::move(messages_boc); + } + LOG(INFO) << "getDispatchQueueMessages(" << blk_id_.to_str() << ", " << mode_ << ") query completed"; + auto b = ton::create_serialize_tl_object( + mode_, ton::create_tl_lite_block_id(blk_id_), std::move(result), complete, std::move(proof), + std::move(messages_boc)); + finish_query(std::move(b)); +} void LiteQuery::perform_nonfinal_getCandidate(td::Bits256 source, BlockIdExt blkid, td::Bits256 collated_data_hash) { LOG(INFO) << "started a nonfinal.getCandidate liteserver query"; diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 2d75dc61c..447e1dad4 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -172,6 +172,11 @@ class LiteQuery : public td::actor::Actor { void continue_getOutMsgQueueSizes(td::optional shard, Ref state); void perform_getBlockOutMsgQueueSize(int mode, BlockIdExt blkid); void finish_getBlockOutMsgQueueSize(); + void perform_getDispatchQueueInfo(int mode, BlockIdExt blkid, StdSmcAddress after_addr, int max_accounts); + void finish_getDispatchQueueInfo(StdSmcAddress after_addr, int max_accounts); + void perform_getDispatchQueueMessages(int mode, BlockIdExt blkid, StdSmcAddress addr, LogicalTime lt, + int max_messages); + void finish_getDispatchQueueMessages(StdSmcAddress addr, LogicalTime lt, int max_messages); void perform_nonfinal_getCandidate(td::Bits256 source, BlockIdExt blkid, td::Bits256 collated_data_hash); void perform_nonfinal_getValidatorGroups(int mode, ShardIdFull shard);