mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	Merge branch 'testnet' into accelerator
This commit is contained in:
		
						commit
						09da42e38f
					
				
					 20 changed files with 798 additions and 19 deletions
				
			
		|  | @ -28,5 +28,5 @@ jobs: | |||
|     - name: Upload artifacts | ||||
|       uses: actions/upload-artifact@master | ||||
|       with: | ||||
|         name: tonlib-android | ||||
|         name: ton-android-tonlib | ||||
|         path: artifacts | ||||
|  |  | |||
							
								
								
									
										16
									
								
								.github/workflows/create-release.yml
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.github/workflows/create-release.yml
									
										
									
									
										vendored
									
									
								
							|  | @ -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 }} | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
							
								
								
									
										395
									
								
								crypto/util/mintless-proof-generator.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										395
									
								
								crypto/util/mintless-proof-generator.cpp
									
										
									
									
									
										Normal 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 << "'"; | ||||
| } | ||||
|  | @ -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()); | ||||
|   } | ||||
|  |  | |||
|  | @ -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() == '"') { | ||||
|  |  | |||
|  | @ -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 = {}); | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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.
										
									
								
							|  | @ -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"; | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue