Skip to content

Commit 9f20389

Browse files
committed
Merge branch 'SpyCheese-mintless-util' into testnet
2 parents 1a5bbf3 + 76cda01 commit 9f20389

File tree

1 file changed

+160
-23
lines changed

1 file changed

+160
-23
lines changed

crypto/util/mintless-proof-generator.cpp

Lines changed: 160 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,27 +17,37 @@
1717

1818
#include "block-parse.h"
1919
#include "block.h"
20+
#include "td/actor/core/Actor.h"
2021
#include "td/db/utils/BlobView.h"
2122

2223
#include <iostream>
2324
#include "td/utils/OptionParser.h"
2425
#include "td/utils/Time.h"
26+
#include "td/utils/base64.h"
2527
#include "td/utils/filesystem.h"
2628
#include "td/utils/logging.h"
2729
#include "vm/cells/MerkleProof.h"
2830
#include "vm/db/StaticBagOfCellsDb.h"
2931

3032
#include <fstream>
33+
#include <common/delay.h>
34+
35+
const size_t KEY_LEN = 3 + 8 + 256;
3136

3237
void print_help() {
33-
std::cerr << "mintless-proof-generator - generates proofs for mintless jettons\n";
34-
std::cerr << "Usage:\n";
35-
std::cerr << " mintless-proof-generator generate <input-list> <output-file>\tGenerate a full tree for "
36-
"<input-list>, save boc to <output-file>\n";
37-
std::cerr << " mintless-proof-generator make_proof <input-boc> <address> <output-file>\tGenerate a proof for "
38-
"address <address> from tree <input-boc>, save boc to file <output-file>\n";
39-
std::cerr << " mintless-proof-generator parse <input-boc> <output-file>\tRead a tree from <input-boc> and output it "
40-
"as text to <output-file>\n";
38+
std::cerr << "mintless-proof-generator - generates proofs for mintless jettons. Usage:\n\n";
39+
std::cerr << "mintless-proof-generator generate <input-list> <output-file>\n";
40+
std::cerr << " Generate a full tree for <input-list>, save boc to <output-file>.\n";
41+
std::cerr << " Input format: each line is <address> <amount> <start_from> <expired_at>.\n\n";
42+
std::cerr << "mintless-proof-generator make_proof <input-boc> <address> <output-file>.\n";
43+
std::cerr << " Generate a proof for address <address> from tree <input-boc>, save boc to file <output-file>.\n\n";
44+
std::cerr << "mintless-proof-generator parse <input-boc> <output-file>\n";
45+
std::cerr << " Read a tree from <input-boc> and output it as text to <output-file>.\n";
46+
std::cerr << " Output format: same as input for 'generate'.\n\n";
47+
std::cerr << "mintless-proof-generator make_all_proofs <input-boc> <output-file> [--threads <threads>]\n";
48+
std::cerr << " Read a tree from <input-boc> and output proofs for all accounts to <output-file>.\n";
49+
std::cerr << " Output format: <address>,<proof-base64>\n";
50+
std::cerr << " Default <threads>: 1\n";
4151
exit(2);
4252
}
4353

@@ -53,7 +63,7 @@ void log_mem_stat() {
5363
<< ") virt=" << stat.virtual_size_ << " (peak=" << stat.virtual_size_peak_ << ")";
5464
}
5565

56-
td::BitArray<3 + 8 + 256> address_to_key(const block::StdAddress &address) {
66+
td::BitArray<KEY_LEN> address_to_key(const block::StdAddress &address) {
5767
// addr_std$10 anycast:(Maybe Anycast) workchain_id:int8 address:bits256 = MsgAddressInt;
5868
vm::CellBuilder cb;
5969
cb.store_long(0b100, 3);
@@ -62,12 +72,23 @@ td::BitArray<3 + 8 + 256> address_to_key(const block::StdAddress &address) {
6272
return cb.data_bits();
6373
}
6474

75+
block::StdAddress key_to_address(const td::BitArray<KEY_LEN> &key) {
76+
block::StdAddress addr;
77+
td::ConstBitPtr ptr = key.bits();
78+
LOG_CHECK(ptr.get_uint(3) == 0b100) << "Invalid address";
79+
ptr.advance(3);
80+
addr.workchain = (ton::WorkchainId)ptr.get_int(8);
81+
ptr.advance(8);
82+
addr.addr = ptr;
83+
return addr;
84+
}
85+
6586
struct Entry {
6687
block::StdAddress address;
6788
td::RefInt256 amount;
6889
td::uint64 start_from = 0, expired_at = 0;
6990

70-
td::BitArray<3 + 8 + 256> get_key() const {
91+
td::BitArray<KEY_LEN> get_key() const {
7192
return address_to_key(address);
7293
}
7394

@@ -80,14 +101,9 @@ struct Entry {
80101
return cb.as_cellslice_ref();
81102
}
82103

83-
static Entry parse(td::BitArray<3 + 8 + 256> key, vm::CellSlice value) {
104+
static Entry parse(const td::BitArray<KEY_LEN> &key, vm::CellSlice value) {
84105
Entry e;
85-
td::ConstBitPtr ptr = key.bits();
86-
LOG_CHECK(ptr.get_uint(3) == 0b100) << "Invalid address";
87-
ptr.advance(3);
88-
e.address.workchain = (ton::WorkchainId)ptr.get_int(8);
89-
ptr.advance(8);
90-
e.address.addr = ptr;
106+
e.address = key_to_address(key);
91107
bool ok = block::tlb::t_Grams.as_integer_skip_to(value, e.amount) && value.fetch_uint_to(48, e.start_from) &&
92108
value.fetch_uint_to(48, e.expired_at) && value.empty_ext();
93109
LOG_CHECK(ok) << "Failed to parse AirdropItem";
@@ -129,7 +145,7 @@ td::Status run_generate(std::string in_filename, std::string out_filename) {
129145
LOG_CHECK(in_file.is_open()) << "Cannot open file " << in_filename;
130146

131147
Entry entry;
132-
vm::Dictionary dict{3 + 8 + 256};
148+
vm::Dictionary dict{KEY_LEN};
133149
td::uint64 count = 0;
134150
td::Timestamp log_at = td::Timestamp::in(5.0);
135151
while (read_entry(in_file, entry)) {
@@ -168,7 +184,7 @@ td::Status run_make_proof(std::string in_filename, std::string s_address, std::s
168184
TRY_RESULT(root, boc->get_root_cell(0));
169185

170186
vm::MerkleProofBuilder mpb{root};
171-
vm::Dictionary dict{mpb.root(), 3 + 8 + 256};
187+
vm::Dictionary dict{mpb.root(), KEY_LEN};
172188
auto key = address_to_key(address);
173189
td::Ref<vm::CellSlice> value = dict.lookup(key);
174190
LOG_CHECK(value.not_null()) << "No entry for address " << s_address;
@@ -193,11 +209,11 @@ td::Status run_parse(std::string in_filename, std::string out_filename) {
193209
TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view)));
194210
TRY_RESULT(root, boc->get_root_cell(0));
195211
LOG(INFO) << "Root hash = " << root->get_hash().to_hex();
196-
vm::Dictionary dict{root, 3 + 8 + 256};
212+
vm::Dictionary dict{root, KEY_LEN};
197213
td::Timestamp log_at = td::Timestamp::in(5.0);
198214
td::uint64 count = 0;
199215
bool ok = dict.check_for_each([&](td::Ref<vm::CellSlice> value, td::ConstBitPtr key, int key_len) {
200-
CHECK(key_len == 3 + 8 + 256);
216+
CHECK(key_len == KEY_LEN);
201217
Entry e = Entry::parse(key, *value);
202218
out_file << e.address.workchain << ":" << e.address.addr.to_hex() << " " << e.amount->to_dec_string() << " "
203219
<< e.start_from << " " << e.expired_at << "\n";
@@ -212,7 +228,108 @@ td::Status run_parse(std::string in_filename, std::string out_filename) {
212228
LOG_CHECK(ok) << "Failed to parse dictionary";
213229
out_file.close();
214230
LOG_CHECK(!out_file.fail()) << "Failed to write to " << out_filename;
215-
LOG(INFO) << "Done: " << count << " entries";
231+
LOG(INFO) << "Written " << count << " entries to " << out_filename;
232+
log_mem_stat();
233+
return td::Status::OK();
234+
}
235+
236+
class MakeAllProofsActor : public td::actor::core::Actor {
237+
public:
238+
MakeAllProofsActor(std::string in_filename, std::string out_filename, int max_workers)
239+
: in_filename_(in_filename), out_filename_(out_filename), max_workers_(max_workers) {
240+
}
241+
242+
void start_up() override {
243+
auto S = [&]() -> td::Status {
244+
out_file_.open(out_filename_);
245+
LOG_CHECK(out_file_.is_open()) << "Cannot open file " << out_filename_;
246+
LOG(INFO) << "Reading " << in_filename_;
247+
TRY_RESULT(blob_view, td::FileBlobView::create(in_filename_));
248+
TRY_RESULT(boc, vm::StaticBagOfCellsDbLazy::create(std::move(blob_view)));
249+
TRY_RESULT(root, boc->get_root_cell(0));
250+
LOG(INFO) << "Root hash = " << root->get_hash().to_hex();
251+
dict_ = vm::Dictionary{root, KEY_LEN};
252+
return td::Status::OK();
253+
}();
254+
S.ensure();
255+
run();
256+
alarm_timestamp() = td::Timestamp::in(5.0);
257+
}
258+
259+
void alarm() override {
260+
alarm_timestamp() = td::Timestamp::in(5.0);
261+
LOG(INFO) << "Processed " << written_count_ << " entries";
262+
}
263+
264+
void run() {
265+
for (auto it = pending_results_.begin(); it != pending_results_.end() && !it->second.empty();) {
266+
out_file_ << it->second << "\n";
267+
LOG_CHECK(!out_file_.fail()) << "Failed to write to " << out_filename_;
268+
it = pending_results_.erase(it);
269+
++written_count_;
270+
}
271+
while (active_workers_ < max_workers_ && !eof_) {
272+
td::Ref<vm::CellSlice> value = dict_.lookup_nearest_key(current_key_, true, current_idx_ == 0);
273+
if (value.is_null()) {
274+
eof_ = true;
275+
break;
276+
}
277+
run_worker(current_key_, current_idx_);
278+
++current_idx_;
279+
++active_workers_;
280+
}
281+
if (eof_ && active_workers_ == 0) {
282+
out_file_.close();
283+
LOG_CHECK(!out_file_.fail()) << "Failed to write to " << out_filename_;
284+
LOG(INFO) << "Written " << written_count_ << " entries to " << out_filename_;
285+
stop();
286+
td::actor::SchedulerContext::get()->stop();
287+
}
288+
}
289+
290+
void run_worker(td::BitArray<KEY_LEN> key, td::uint64 idx) {
291+
pending_results_[idx] = "";
292+
ton::delay_action(
293+
[SelfId = actor_id(this), key, idx, root = dict_.get_root_cell()]() {
294+
vm::MerkleProofBuilder mpb{root};
295+
CHECK(vm::Dictionary(mpb.root(), KEY_LEN).lookup(key).not_null());
296+
auto r_proof = mpb.extract_proof_boc();
297+
r_proof.ensure();
298+
block::StdAddress addr = key_to_address(key);
299+
std::string result = PSTRING() << addr.workchain << ":" << addr.addr.to_hex() << ","
300+
<< td::base64_encode(r_proof.move_as_ok());
301+
td::actor::send_closure(SelfId, &MakeAllProofsActor::on_result, idx, std::move(result));
302+
},
303+
td::Timestamp::now());
304+
}
305+
306+
void on_result(td::uint64 idx, std::string result) {
307+
pending_results_[idx] = std::move(result);
308+
--active_workers_;
309+
run();
310+
}
311+
312+
private:
313+
std::string in_filename_, out_filename_;
314+
int max_workers_;
315+
316+
std::ofstream out_file_;
317+
vm::Dictionary dict_{KEY_LEN};
318+
td::BitArray<KEY_LEN> current_key_ = td::BitArray<KEY_LEN>::zero();
319+
td::uint64 current_idx_ = 0;
320+
bool eof_ = false;
321+
int active_workers_ = 0;
322+
323+
std::map<td::uint64, std::string> pending_results_;
324+
td::uint64 written_count_ = 0;
325+
};
326+
327+
td::Status run_make_all_proofs(std::string in_filename, std::string out_filename, int threads) {
328+
td::actor::Scheduler scheduler({(size_t)threads});
329+
scheduler.run_in_context(
330+
[&] { td::actor::create_actor<MakeAllProofsActor>("proofs", in_filename, out_filename, threads).release(); });
331+
while (scheduler.run(1)) {
332+
}
216333
log_mem_stat();
217334
return td::Status::OK();
218335
}
@@ -248,11 +365,31 @@ int main(int argc, char *argv[]) {
248365
run_parse(argv[2], argv[3]).ensure();
249366
return 0;
250367
}
368+
369+
if (command == "make_all_proofs") {
370+
std::vector<std::string> args;
371+
int threads = 1;
372+
for (int i = 2; i < argc; ++i) {
373+
if (!strcmp(argv[i], "--threads")) {
374+
++i;
375+
auto r = td::to_integer_safe<int>(td::as_slice(argv[i]));
376+
LOG_CHECK(r.is_ok() && r.ok() >= 1 && r.ok() <= 127) << "<threads> should be in [1..127]";
377+
threads = r.move_as_ok();
378+
} else {
379+
args.push_back(argv[i]);
380+
}
381+
}
382+
if (args.size() != 2) {
383+
print_help();
384+
}
385+
run_make_all_proofs(args[0], args[1], threads).ensure();
386+
return 0;
387+
}
251388
} catch (vm::VmError &e) {
252389
LOG(FATAL) << "VM error: " << e.get_msg();
253390
} catch (vm::VmVirtError &e) {
254391
LOG(FATAL) << "VM error: " << e.get_msg();
255392
}
256393

257394
LOG(FATAL) << "Unknown command '" << command << "'";
258-
}
395+
}

0 commit comments

Comments
 (0)