1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-15 04:32:21 +00:00

Merge branch 'testnet' into accelerator

This commit is contained in:
SpyCheese 2024-09-19 12:12:46 +03:00
commit 09da42e38f
20 changed files with 798 additions and 19 deletions

View file

@ -28,5 +28,5 @@ jobs:
- name: Upload artifacts
uses: actions/upload-artifact@master
with:
name: tonlib-android
name: ton-android-tonlib
path: artifacts

View file

@ -83,6 +83,14 @@ jobs:
workflow_conclusion: success
skip_unpack: true
- name: Download Android Tonlib artifacts
uses: dawidd6/action-download-artifact@v2
with:
workflow: build-ton-linux-android-tonlib.yml
path: artifacts
workflow_conclusion: success
skip_unpack: true
- name: Show all artifacts
run: |
tree artifacts
@ -501,3 +509,11 @@ jobs:
file: artifacts/ton-wasm-binaries.zip
asset_name: ton-wasm-binaries.zip
tag: ${{ steps.tag.outputs.TAG }}
- name: Upload Android Tonlib artifacts
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: artifacts/ton-android-tonlib.zip
asset_name: ton-android-tonlib.zip
tag: ${{ steps.tag.outputs.TAG }}

View file

@ -192,8 +192,6 @@ if [ "$with_artifacts" = true ]; then
echo Creating artifacts...
rm -rf artifacts
mkdir artifacts
cp crypto/fift/lib artifacts/
cp -R crypto/smartcont/ artifacts/
cp build/storage/storage-daemon/storage-daemon artifacts/
cp build/storage/storage-daemon/storage-daemon-cli artifacts/
cp build/blockchain-explorer/blockchain-explorer artifacts/
@ -213,9 +211,9 @@ if [ "$with_artifacts" = true ]; then
cp build/utils/json2tlo artifacts/
cp build/adnl/adnl-proxy artifacts/
cp build/emulator/libemulator.dylib artifacts/
chmod +x artifacts/*
rsync -r crypto/smartcont artifacts/
rsync -r crypto/fift/lib artifacts/
chmod -R +x artifacts/*
fi
if [ "$with_tests" = true ]; then

View file

@ -140,9 +140,9 @@ if [ "$with_artifacts" = true ]; then
cp build/utils/json2tlo artifacts/
cp build/adnl/adnl-proxy artifacts/
cp build/emulator/libemulator.dylib artifacts/
chmod +x artifacts/*
rsync -r crypto/smartcont artifacts/
rsync -r crypto/fift/lib artifacts/
cp -R crypto/smartcont artifacts/
cp -R crypto/fift/lib artifacts/
chmod -R +x artifacts/*
fi
if [ "$with_tests" = true ]; then

View file

@ -193,8 +193,6 @@ cd ..
if [ "$with_artifacts" = true ]; then
rm -rf artifacts
mkdir artifacts
cp crypto/fift/lib artifacts/
cp -R crypto/smartcont/ artifacts/
mv build/tonlib/libtonlibjson.so.0.5 build/tonlib/libtonlibjson.so
cp build/storage/storage-daemon/storage-daemon build/storage/storage-daemon/storage-daemon-cli \
build/crypto/fift build/crypto/tlbc build/crypto/func build/crypto/create-state build/blockchain-explorer/blockchain-explorer \
@ -204,9 +202,9 @@ if [ "$with_artifacts" = true ]; then
build/utils/generate-random-id build/utils/json2tlo build/adnl/adnl-proxy build/emulator/libemulator.so \
artifacts
test $? -eq 0 || { echo "Can't copy final binaries"; exit 1; }
chmod +x artifacts/*
cp -R crypto/smartcont artifacts
cp -R crypto/fift/lib artifacts
chmod -R +x artifacts/*
fi
if [ "$with_tests" = true ]; then

View file

@ -112,9 +112,9 @@ if [ "$with_artifacts" = true ]; then
build/utils/generate-random-id build/utils/json2tlo build/adnl/adnl-proxy build/emulator/libemulator.so \
artifacts
test $? -eq 0 || { echo "Can't copy final binaries"; exit 1; }
chmod +x artifacts/*
cp -R crypto/smartcont artifacts
cp -R crypto/fift/lib artifacts
chmod -R +x artifacts/*
fi
if [ "$with_tests" = true ]; then

View file

@ -35,3 +35,4 @@ cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so
cp ./result/lib/libemulator.so artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/
chmod -R +x artifacts

View file

@ -35,3 +35,4 @@ cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so
cp ./result/lib/libemulator.so artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/
chmod -R +x artifacts

View file

@ -35,3 +35,4 @@ cp ./result/lib/libtonlibjson.dylib artifacts/
cp ./result/lib/libemulator.dylib artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/
chmod -R +x artifacts

View file

@ -433,6 +433,14 @@ if (WINGETOPT_FOUND)
target_link_libraries_system(pow-miner wingetopt)
endif()
add_executable(mintless-proof-generator util/mintless-proof-generator.cpp)
target_link_libraries(mintless-proof-generator PRIVATE ton_crypto ton_block git ${JEMALLOC_LIBRARIES})
if (JEMALLOC_FOUND)
target_include_directories(mintless-proof-generator PRIVATE ${JEMALLOC_INCLUDE_DIR})
target_compile_definitions(mintless-proof-generator PRIVATE -DTON_USE_JEMALLOC=1)
endif()
set(TURN_OFF_LSAN cd .)
if (TON_USE_ASAN AND NOT WIN32)
set(TURN_OFF_LSAN export LSAN_OPTIONS=detect_leaks=0)

View file

@ -0,0 +1,395 @@
/*
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/>.
*/
#include "block-parse.h"
#include "block.h"
#include "td/actor/core/Actor.h"
#include "td/db/utils/BlobView.h"
#include <iostream>
#include "td/utils/OptionParser.h"
#include "td/utils/Time.h"
#include "td/utils/base64.h"
#include "td/utils/filesystem.h"
#include "td/utils/logging.h"
#include "vm/cells/MerkleProof.h"
#include "vm/db/StaticBagOfCellsDb.h"
#include <fstream>
#include <common/delay.h>
const size_t KEY_LEN = 3 + 8 + 256;
void print_help() {
std::cerr << "mintless-proof-generator - generates proofs for mintless jettons. Usage:\n\n";
std::cerr << "mintless-proof-generator generate <input-list> <output-file>\n";
std::cerr << " Generate a full tree for <input-list>, save boc to <output-file>.\n";
std::cerr << " Input format: each line is <address> <amount> <start_from> <expired_at>.\n\n";
std::cerr << "mintless-proof-generator make_proof <input-boc> <address> <output-file>.\n";
std::cerr << " Generate a proof for address <address> from tree <input-boc>, save boc to file <output-file>.\n\n";
std::cerr << "mintless-proof-generator parse <input-boc> <output-file>\n";
std::cerr << " Read a tree from <input-boc> and output it as text to <output-file>.\n";
std::cerr << " Output format: same as input for 'generate'.\n\n";
std::cerr << "mintless-proof-generator make_all_proofs <input-boc> <output-file> [--threads <threads>]\n";
std::cerr << " Read a tree from <input-boc> and output proofs for all accounts to <output-file>.\n";
std::cerr << " Output format: <address>,<proof-base64>\n";
std::cerr << " Default <threads>: 1\n";
exit(2);
}
void log_mem_stat() {
auto r_stat = td::mem_stat();
if (r_stat.is_error()) {
LOG(WARNING) << "Memory: " << r_stat.move_as_error();
return;
}
auto stat = r_stat.move_as_ok();
LOG(WARNING) << "Memory: "
<< "res=" << stat.resident_size_ << " (peak=" << stat.resident_size_peak_
<< ") virt=" << stat.virtual_size_ << " (peak=" << stat.virtual_size_peak_ << ")";
}
td::BitArray<KEY_LEN> address_to_key(const block::StdAddress &address) {
// addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt;
vm::CellBuilder cb;
cb.store_long(0b100, 3);
cb.store_long(address.workchain, 8);
cb.store_bits(address.addr.as_bitslice());
return cb.data_bits();
}
block::StdAddress key_to_address(const td::BitArray<KEY_LEN> &key) {
block::StdAddress addr;
td::ConstBitPtr ptr = key.bits();
LOG_CHECK(ptr.get_uint(3) == 0b100) << "Invalid address";
ptr.advance(3);
addr.workchain = (ton::WorkchainId)ptr.get_int(8);
ptr.advance(8);
addr.addr = ptr;
return addr;
}
struct Entry {
block::StdAddress address;
td::RefInt256 amount;
td::uint64 start_from = 0, expired_at = 0;
td::BitArray<KEY_LEN> get_key() const {
return address_to_key(address);
}
td::Ref<vm::CellSlice> get_value() const {
// _ amount:Coins start_from:uint48 expired_at:uint48 = AirdropItem;
vm::CellBuilder cb;
bool ok = block::tlb::t_Grams.store_integer_value(cb, *amount) && cb.store_ulong_rchk_bool(start_from, 48) &&
cb.store_ulong_rchk_bool(expired_at, 48);
LOG_CHECK(ok) << "Failed to serialize AirdropItem";
return cb.as_cellslice_ref();
}
static Entry parse(const td::BitArray<KEY_LEN> &key, vm::CellSlice value) {
Entry e;
e.address = key_to_address(key);
bool ok = block::tlb::t_Grams.as_integer_skip_to(value, e.amount) && value.fetch_uint_to(48, e.start_from) &&
value.fetch_uint_to(48, e.expired_at) && value.empty_ext();
LOG_CHECK(ok) << "Failed to parse AirdropItem";
return e;
}
};
bool read_entry(std::istream &f, Entry &entry) {
std::string line;
while (std::getline(f, line)) {
std::vector<std::string> v = td::full_split(line, ' ');
if (v.empty()) {
continue;
}
auto S = [&]() -> td::Status {
if (v.size() != 4) {
return td::Status::Error("Invalid line in input");
}
TRY_RESULT_PREFIX_ASSIGN(entry.address, block::StdAddress::parse(v[0]), "Invalid address in input: ");
entry.amount = td::string_to_int256(v[1]);
if (entry.amount.is_null() || !entry.amount->is_valid() || entry.amount->sgn() < 0) {
return td::Status::Error(PSTRING() << "Invalid amount in input: " << v[1]);
}
TRY_RESULT_PREFIX_ASSIGN(entry.start_from, td::to_integer_safe<td::uint64>(v[2]),
"Invalid start_from in input: ");
TRY_RESULT_PREFIX_ASSIGN(entry.expired_at, td::to_integer_safe<td::uint64>(v[3]),
"Invalid expired_at in input: ");
return td::Status::OK();
}();
S.ensure();
return true;
}
return false;
}
td::Status run_generate(std::string in_filename, std::string out_filename) {
LOG(INFO) << "Generating tree from " << in_filename;
std::ifstream in_file{in_filename};
LOG_CHECK(in_file.is_open()) << "Cannot open file " << in_filename;
Entry entry;
vm::Dictionary dict{KEY_LEN};
td::uint64 count = 0;
td::Timestamp log_at = td::Timestamp::in(5.0);
while (read_entry(in_file, entry)) {
++count;
bool ok = dict.set(entry.get_key(), entry.get_value(), vm::DictionaryBase::SetMode::Add);
LOG_CHECK(ok) << "Failed to add entry " << entry.address.rserialize() << " (line #" << count << ")";
if (log_at.is_in_past()) {
LOG(INFO) << "Added " << count << " entries";
log_at = td::Timestamp::in(5.0);
}
}
LOG_CHECK(in_file.eof()) << "Failed to read file " << in_filename;
in_file.close();
LOG_CHECK(count != 0) << "Input is empty";
td::Ref<vm::Cell> root = dict.get_root_cell();
LOG(INFO) << "Total: " << count << " entries, root hash: " << root->get_hash().to_hex();
vm::BagOfCells boc;
boc.add_root(root);
TRY_STATUS(boc.import_cells());
LOG(INFO) << "Writing to " << out_filename;
TRY_RESULT(fd, td::FileFd::open(out_filename, td::FileFd::Write | td::FileFd::Truncate | td::FileFd::Create));
TRY_STATUS(boc.serialize_to_file(fd, 31));
TRY_STATUS(fd.sync());
fd.close();
log_mem_stat();
return td::Status::OK();
}
td::Status run_make_proof(std::string in_filename, std::string s_address, std::string out_filename) {
LOG(INFO) << "Generating proof for " << s_address << ", input file is " << in_filename;
TRY_RESULT(address, block::StdAddress::parse(s_address));
TRY_RESULT(blob_view, td::FileBlobView::create(in_filename));
TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view)));
TRY_RESULT(root, boc->get_root_cell(0));
vm::MerkleProofBuilder mpb{root};
vm::Dictionary dict{mpb.root(), KEY_LEN};
auto key = address_to_key(address);
td::Ref<vm::CellSlice> value = dict.lookup(key);
LOG_CHECK(value.not_null()) << "No entry for address " << s_address;
Entry e = Entry::parse(key, *value);
LOG(INFO) << "Entry: address=" << e.address.workchain << ":" << e.address.addr.to_hex()
<< " amount=" << e.amount->to_dec_string() << " start_from=" << e.start_from
<< " expire_at=" << e.expired_at;
TRY_RESULT(proof, mpb.extract_proof_boc());
LOG(INFO) << "Writing proof to " << out_filename << " (" << td::format::as_size(proof.size()) << ")";
TRY_STATUS(td::write_file(out_filename, proof));
log_mem_stat();
return td::Status::OK();
}
td::Status run_parse(std::string in_filename, std::string out_filename) {
LOG(INFO) << "Parsing " << in_filename;
std::ofstream out_file{out_filename};
LOG_CHECK(out_file.is_open()) << "Cannot open file " << out_filename;
TRY_RESULT(blob_view, td::FileBlobView::create(in_filename));
TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view)));
TRY_RESULT(root, boc->get_root_cell(0));
LOG(INFO) << "Root hash = " << root->get_hash().to_hex();
vm::Dictionary dict{root, KEY_LEN};
td::Timestamp log_at = td::Timestamp::in(5.0);
td::uint64 count = 0;
bool ok = dict.check_for_each([&](td::Ref<vm::CellSlice> value, td::ConstBitPtr key, int key_len) {
CHECK(key_len == KEY_LEN);
Entry e = Entry::parse(key, *value);
out_file << e.address.workchain << ":" << e.address.addr.to_hex() << " " << e.amount->to_dec_string() << " "
<< e.start_from << " " << e.expired_at << "\n";
LOG_CHECK(!out_file.fail()) << "Failed to write to " << out_filename;
++count;
if (log_at.is_in_past()) {
LOG(INFO) << "Parsed " << count << " entries";
log_at = td::Timestamp::in(5.0);
}
return true;
});
LOG_CHECK(ok) << "Failed to parse dictionary";
out_file.close();
LOG_CHECK(!out_file.fail()) << "Failed to write to " << out_filename;
LOG(INFO) << "Written " << count << " entries to " << out_filename;
log_mem_stat();
return td::Status::OK();
}
class MakeAllProofsActor : public td::actor::core::Actor {
public:
MakeAllProofsActor(std::string in_filename, std::string out_filename, int max_workers)
: in_filename_(in_filename), out_filename_(out_filename), max_workers_(max_workers) {
}
void start_up() override {
auto S = [&]() -> td::Status {
out_file_.open(out_filename_);
LOG_CHECK(out_file_.is_open()) << "Cannot open file " << out_filename_;
LOG(INFO) << "Reading " << in_filename_;
TRY_RESULT(blob_view, td::FileBlobView::create(in_filename_));
TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view)));
TRY_RESULT(root, boc->get_root_cell(0));
LOG(INFO) << "Root hash = " << root->get_hash().to_hex();
dict_ = vm::Dictionary{root, KEY_LEN};
return td::Status::OK();
}();
S.ensure();
run();
alarm_timestamp() = td::Timestamp::in(5.0);
}
void alarm() override {
alarm_timestamp() = td::Timestamp::in(5.0);
LOG(INFO) << "Processed " << written_count_ << " entries";
}
void run() {
for (auto it = pending_results_.begin(); it != pending_results_.end() && !it->second.empty();) {
out_file_ << it->second << "\n";
LOG_CHECK(!out_file_.fail()) << "Failed to write to " << out_filename_;
it = pending_results_.erase(it);
++written_count_;
}
while (active_workers_ < max_workers_ && !eof_) {
td::Ref<vm::CellSlice> value = dict_.lookup_nearest_key(current_key_, true, current_idx_ == 0);
if (value.is_null()) {
eof_ = true;
break;
}
run_worker(current_key_, current_idx_);
++current_idx_;
++active_workers_;
}
if (eof_ && active_workers_ == 0) {
out_file_.close();
LOG_CHECK(!out_file_.fail()) << "Failed to write to " << out_filename_;
LOG(INFO) << "Written " << written_count_ << " entries to " << out_filename_;
stop();
td::actor::SchedulerContext::get()->stop();
}
}
void run_worker(td::BitArray<KEY_LEN> key, td::uint64 idx) {
pending_results_[idx] = "";
ton::delay_action(
[SelfId = actor_id(this), key, idx, root = dict_.get_root_cell()]() {
vm::MerkleProofBuilder mpb{root};
CHECK(vm::Dictionary(mpb.root(), KEY_LEN).lookup(key).not_null());
auto r_proof = mpb.extract_proof_boc();
r_proof.ensure();
block::StdAddress addr = key_to_address(key);
std::string result = PSTRING() << addr.workchain << ":" << addr.addr.to_hex() << ","
<< td::base64_encode(r_proof.move_as_ok());
td::actor::send_closure(SelfId, &MakeAllProofsActor::on_result, idx, std::move(result));
},
td::Timestamp::now());
}
void on_result(td::uint64 idx, std::string result) {
pending_results_[idx] = std::move(result);
--active_workers_;
run();
}
private:
std::string in_filename_, out_filename_;
int max_workers_;
std::ofstream out_file_;
vm::Dictionary dict_{KEY_LEN};
td::BitArray<KEY_LEN> current_key_ = td::BitArray<KEY_LEN>::zero();
td::uint64 current_idx_ = 0;
bool eof_ = false;
int active_workers_ = 0;
std::map<td::uint64, std::string> pending_results_;
td::uint64 written_count_ = 0;
};
td::Status run_make_all_proofs(std::string in_filename, std::string out_filename, int threads) {
td::actor::Scheduler scheduler({(size_t)threads});
scheduler.run_in_context(
[&] { td::actor::create_actor<MakeAllProofsActor>("proofs", in_filename, out_filename, threads).release(); });
while (scheduler.run(1)) {
}
log_mem_stat();
return td::Status::OK();
}
int main(int argc, char *argv[]) {
SET_VERBOSITY_LEVEL(verbosity_INFO);
td::set_log_fatal_error_callback([](td::CSlice) { exit(2); });
if (argc <= 1) {
print_help();
return 2;
}
std::string command = argv[1];
try {
if (command == "generate") {
if (argc != 4) {
print_help();
}
run_generate(argv[2], argv[3]).ensure();
return 0;
}
if (command == "make_proof") {
if (argc != 5) {
print_help();
}
run_make_proof(argv[2], argv[3], argv[4]).ensure();
return 0;
}
if (command == "parse") {
if (argc != 4) {
print_help();
}
run_parse(argv[2], argv[3]).ensure();
return 0;
}
if (command == "make_all_proofs") {
std::vector<std::string> args;
int threads = 1;
for (int i = 2; i < argc; ++i) {
if (!strcmp(argv[i], "--threads")) {
++i;
auto r = td::to_integer_safe<int>(td::as_slice(argv[i]));
LOG_CHECK(r.is_ok() && r.ok() >= 1 && r.ok() <= 127) << "<threads> should be in [1..127]";
threads = r.move_as_ok();
} else {
args.push_back(argv[i]);
}
}
if (args.size() != 2) {
print_help();
}
run_make_all_proofs(args[0], args[1], threads).ensure();
return 0;
}
} catch (vm::VmError &e) {
LOG(FATAL) << "VM error: " << e.get_msg();
} catch (vm::VmVirtError &e) {
LOG(FATAL) << "VM error: " << e.get_msg();
}
LOG(FATAL) << "Unknown command '" << command << "'";
}

View file

@ -167,7 +167,7 @@ td::Result<Ref<Cell>> StaticBagOfCellsDb::create_ext_cell(Cell::LevelMask level_
//
class StaticBagOfCellsDbBaselineImpl : public StaticBagOfCellsDb {
public:
StaticBagOfCellsDbBaselineImpl(std::vector<Ref<Cell>> roots) : roots_(std::move(roots)) {
explicit StaticBagOfCellsDbBaselineImpl(std::vector<Ref<Cell>> roots) : roots_(std::move(roots)) {
}
td::Result<size_t> get_root_count() override {
return roots_.size();
@ -233,7 +233,7 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
return create_root_cell(std::move(data_cell));
};
~StaticBagOfCellsDbLazyImpl() {
~StaticBagOfCellsDbLazyImpl() override {
//LOG(ERROR) << deserialize_cell_cnt_ << " " << deserialize_cell_hash_cnt_;
get_thread_safe_counter().add(-1);
}
@ -314,11 +314,11 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
td::RwMutex::ReadLock guard;
if (info_.has_index) {
TRY_RESULT(new_offset_view, data_.view(td::MutableSlice(arr, info_.offset_byte_size),
info_.index_offset + idx * info_.offset_byte_size));
info_.index_offset + (td::int64)idx * info_.offset_byte_size));
offset_view = new_offset_view;
} else {
guard = index_data_rw_mutex_.lock_read().move_as_ok();
offset_view = td::Slice(index_data_).substr(idx * info_.offset_byte_size, info_.offset_byte_size);
offset_view = td::Slice(index_data_).substr((td::int64)idx * info_.offset_byte_size, info_.offset_byte_size);
}
CHECK(offset_view.size() == (size_t)info_.offset_byte_size);
@ -332,7 +332,7 @@ class StaticBagOfCellsDbLazyImpl : public StaticBagOfCellsDb {
}
char arr[8];
TRY_RESULT(idx_view, data_.view(td::MutableSlice(arr, info_.ref_byte_size),
info_.roots_offset + root_i * info_.ref_byte_size));
info_.roots_offset + (td::int64)root_i * info_.ref_byte_size));
CHECK(idx_view.size() == (size_t)info_.ref_byte_size);
return info_.read_ref(idx_view.ubegin());
}

View file

@ -957,6 +957,11 @@ bool TestNode::show_help(std::string command) {
"into files <filename-pfx><complaint-hash>.boc\n"
"complaintprice <expires-in> <complaint-boc>\tComputes the price (in nanograms) for creating a complaint\n"
"msgqueuesizes\tShows current sizes of outbound message queues in all shards\n"
"dispatchqueueinfo <block-id>\tShows list of account dispatch queue of a block\n"
"dispatchqueuemessages <block-id> <addr> [<after-lt>]\tShows deferred messages from account <addr>, lt > "
"<after_lt>\n"
"dispatchqueuemessagesall <block-id> [<after-addr> [<after-lt>]]\tShows messages from dispatch queue of a "
"block, starting after <after_addr>, <after-lt>\n"
"known\tShows the list of all known block ids\n"
"knowncells\tShows the list of hashes of all known (cached) cells\n"
"dumpcell <hex-hash-pfx>\nDumps a cached cell by a prefix of its hash\n"
@ -971,9 +976,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{};
@ -1101,6 +1106,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") {
@ -1628,6 +1643,81 @@ void TestNode::got_msg_queue_sizes(ton::tl_object_ptr<ton::lite_api::liteServer_
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) {
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<ton::lite_api::liteServer_getDispatchQueueInfo>(
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<td::BufferSlice> res) -> void {
if (res.is_error()) {
LOG(ERROR) << "liteServer.getDispatchQueueInfo error: " << res.move_as_error();
return;
}
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_dispatchQueueInfo>(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<ton::lite_api::liteServer_dispatchQueueInfo> 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<ton::lite_api::liteServer_getDispatchQueueMessages>(
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<td::BufferSlice> res) -> void {
if (res.is_error()) {
LOG(ERROR) << "liteServer.getDispatchQueueMessages error: " << res.move_as_error();
return;
}
auto F = ton::fetch_tl_object<ton::lite_api::liteServer_dispatchQueueMessages>(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<ton::lite_api::liteServer_dispatchQueueMessages> 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() == '"') {

View file

@ -305,6 +305,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<ton::lite_api::liteServer_outMsgQueueSizes> 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<ton::lite_api::liteServer_dispatchQueueInfo> 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<ton::lite_api::liteServer_dispatchQueueMessages> msgs);
bool cache_cell(Ref<vm::Cell> cell);
bool list_cached_cells() const;
bool dump_cached_cell(td::Slice hash_pfx, td::Slice type_name = {});

View file

@ -80,7 +80,7 @@
TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)
#define TRY_RESULT_PREFIX_ASSIGN(name, result, prefix) \
TRY_RESULT_PREFIX_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result, prefix)
TRY_RESULT_PREFIX_IMPL(TD_CONCAT(r_response, __LINE__), name, result, prefix)
#define TRY_RESULT_PROMISE_PREFIX(promise_name, name, result, prefix) \
TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result, prefix)

View file

@ -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);

View file

@ -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;

Binary file not shown.

View file

@ -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<td::int64>(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<vm::Cell> state_root = state_->root_cell();
vm::MerkleProofBuilder pb;
if (with_proof) {
pb = vm::MerkleProofBuilder{state_root};
state_root = pb.root();
}
std::unique_ptr<vm::AugmentedDictionary> 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<vm::AugmentedDictionary>(out_msg_queue_extra.dispatch_queue, 256,
block::tlb::aug_DispatchQueue);
} else {
dispatch_queue = std::make_unique<vm::AugmentedDictionary>(256, block::tlb::aug_DispatchQueue);
}
int remaining = std::min(max_accounts, 64);
bool complete = false;
std::vector<tl_object_ptr<lite_api::liteServer_accountDispatchQueueInfo>> 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<lite_api::liteServer_accountDispatchQueueInfo>(addr, dict_size, min_lt.to_ulong(),
max_lt.to_ulong()));
}
td::BufferSlice proof;
if (with_proof) {
Ref<vm::Cell> 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<ton::lite_api::liteServer_dispatchQueueInfo>(
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<vm::Cell> state_root = state_->root_cell();
vm::MerkleProofBuilder pb;
if (with_proof) {
pb = vm::MerkleProofBuilder{state_root};
state_root = pb.root();
}
std::unique_ptr<vm::AugmentedDictionary> 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<vm::AugmentedDictionary>(out_msg_queue_extra.dispatch_queue, 256,
block::tlb::aug_DispatchQueue);
} else {
dispatch_queue = std::make_unique<vm::AugmentedDictionary>(256, block::tlb::aug_DispatchQueue);
}
int remaining = std::min(max_messages, with_messages_boc ? 16 : 64);
bool complete = false;
std::vector<tl_object_ptr<lite_api::liteServer_dispatchQueueMessage>> result;
std::vector<td::Ref<vm::Cell>> 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<lite_api::liteServer_transactionMetadata> metadata_tl;
if (env.metadata) {
auto& metadata = env.metadata.value();
metadata_tl = create_tl_object<lite_api::liteServer_transactionMetadata>(
0, metadata.depth,
create_tl_object<lite_api::liteServer_accountId>(metadata.initiator_wc, metadata.initiator_addr),
metadata.initiator_lt);
} else {
metadata_tl = create_tl_object<lite_api::liteServer_transactionMetadata>(
0, -1, create_tl_object<lite_api::liteServer_accountId>(workchainInvalid, td::Bits256::zero()), -1);
}
result.push_back(create_tl_object<lite_api::liteServer_dispatchQueueMessage>(addr, lt, env.msg->get_hash().bits(),
std::move(metadata_tl)));
}
first = false;
lt = 0;
}
td::BufferSlice proof;
if (with_proof) {
Ref<vm::Cell> 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<ton::lite_api::liteServer_dispatchQueueMessages>(
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";

View file

@ -172,6 +172,11 @@ class LiteQuery : public td::actor::Actor {
void continue_getOutMsgQueueSizes(td::optional<ShardIdFull> shard, Ref<MasterchainState> 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);