Ton
1464 строки · 54.1 Кб
1/*
2This file is part of TON Blockchain source code.
3
4TON Blockchain is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9TON Blockchain is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License
15along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
16
17In addition, as a special exception, the copyright holders give permission
18to link the code of portions of this program with the OpenSSL library.
19You must obey the GNU General Public License in all respects for all
20of the code used other than OpenSSL. If you modify file(s) with this
21exception, you may extend this exception to your version of the file(s),
22but you are not obligated to do so. If you do not wish to do so, delete this
23exception statement from your version. If you delete this exception statement
24from all source files in the program, then also delete it here.
25along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
26
27Copyright 2017-2020 Telegram Systems LLP
28*/
29#include "blockchain-explorer-query.hpp"
30#include "blockchain-explorer-http.hpp"
31#include "block/mc-config.h"
32#include "crypto/block/check-proof.h"
33
34#include "auto/tl/lite_api.h"
35
36#include "tl-utils/tl-utils.hpp"
37#include "tl-utils/lite-utils.hpp"
38
39#include "ton/ton-tl.hpp"
40#include "ton/lite-tl.hpp"
41
42#include "common/errorcode.h"
43#include "block/block-auto.h"
44#include "crypto/vm/utils.h"
45#include "td/utils/crypto.h"
46
47#include "vm/boc.h"
48#include "vm/cellops.h"
49#include "vm/cells/MerkleProof.h"
50#include "vm/vm.h"
51#include "vm/cp0.h"
52
53td::Result<ton::BlockIdExt> parse_block_id(std::map<std::string, std::string> &opts, bool allow_empty) {
54if (allow_empty) {
55if (opts.count("workchain") == 0 && opts.count("shard") == 0 && opts.count("seqno") == 0) {
56return ton::BlockIdExt{};
57}
58}
59try {
60ton::BlockIdExt block_id;
61auto it = opts.find("workchain");
62if (it == opts.end()) {
63return td::Status::Error(ton::ErrorCode::protoviolation, "workchain not set");
64}
65block_id.id.workchain = std::stoi(it->second);
66it = opts.find("shard");
67if (it == opts.end()) {
68return td::Status::Error(ton::ErrorCode::protoviolation, "shard not set");
69}
70block_id.id.shard = std::stoull(it->second, nullptr, 16);
71it = opts.find("seqno");
72if (it == opts.end()) {
73return td::Status::Error(ton::ErrorCode::protoviolation, "seqno not set");
74}
75auto s = std::stoull(it->second);
76auto seqno = static_cast<ton::BlockSeqno>(s);
77if (s != seqno) {
78return td::Status::Error(ton::ErrorCode::protoviolation, "seqno too big");
79}
80block_id.id.seqno = seqno;
81it = opts.find("roothash");
82if (it == opts.end()) {
83return td::Status::Error(ton::ErrorCode::protoviolation, "roothash not set");
84}
85if (it->second.length() != 64) {
86return td::Status::Error(ton::ErrorCode::protoviolation, "roothash bad length");
87}
88auto R = td::hex_decode(td::Slice(it->second));
89if (R.is_error()) {
90return td::Status::Error(ton::ErrorCode::protoviolation, "roothash bad hex");
91}
92block_id.root_hash.as_slice().copy_from(td::as_slice(R.move_as_ok()));
93it = opts.find("filehash");
94if (it == opts.end()) {
95return td::Status::Error(ton::ErrorCode::protoviolation, "filehash not set");
96}
97if (it->second.length() != 64) {
98return td::Status::Error(ton::ErrorCode::protoviolation, "filehash bad length");
99}
100R = td::hex_decode(td::Slice(it->second));
101if (R.is_error()) {
102return td::Status::Error(ton::ErrorCode::protoviolation, "filehash bad hex");
103}
104block_id.file_hash.as_slice().copy_from(td::as_slice(R.move_as_ok()));
105return block_id;
106} catch (...) {
107return td::Status::Error(ton::ErrorCode::protoviolation, "cannot parse int");
108}
109}
110
111td::Result<ton::AccountIdPrefixFull> parse_account_prefix(std::map<std::string, std::string> &opts, bool allow_empty) {
112if (allow_empty) {
113if (opts.count("workchain") == 0 && opts.count("shard") == 0 && opts.count("account") == 0) {
114return ton::AccountIdPrefixFull{ton::masterchainId, 0};
115}
116}
117try {
118ton::AccountIdPrefixFull account_id;
119auto it = opts.find("workchain");
120if (it == opts.end()) {
121return td::Status::Error(ton::ErrorCode::protoviolation, "workchain not set");
122}
123account_id.workchain = std::stoi(it->second);
124it = opts.find("shard");
125if (it == opts.end()) {
126it = opts.find("account");
127if (it == opts.end()) {
128return td::Status::Error(ton::ErrorCode::protoviolation, "shard/account not set");
129}
130}
131account_id.account_id_prefix = std::stoull(it->second, nullptr, 16);
132return account_id;
133} catch (...) {
134return td::Status::Error(ton::ErrorCode::protoviolation, "cannot parse int");
135}
136}
137
138td::Result<block::StdAddress> parse_account_addr(std::map<std::string, std::string> &opts) {
139auto it = opts.find("account");
140if (it == opts.end()) {
141return td::Status::Error(ton::ErrorCode::error, "no account id");
142}
143std::string acc_string = it->second;
144block::StdAddress a;
145if (a.parse_addr(td::Slice(acc_string))) {
146return a;
147}
148ton::WorkchainId workchain_id;
149it = opts.find("accountworkchain");
150if (it == opts.end()) {
151it = opts.find("workchain");
152if (it == opts.end()) {
153return td::Status::Error(ton::ErrorCode::error, "no account workchain id");
154}
155}
156try {
157workchain_id = std::stoi(it->second);
158} catch (...) {
159return td::Status::Error(ton::ErrorCode::error, "bad account workchain id");
160}
161if (acc_string.size() == 64) {
162TRY_RESULT(R, td::hex_decode(acc_string));
163a.addr.as_slice().copy_from(td::Slice(R));
164a.workchain = workchain_id;
165return a;
166}
167return td::Status::Error(ton::ErrorCode::error, "bad account id");
168}
169
170void HttpQueryCommon::abort_query(td::Status error) {
171if (promise_) {
172HttpAnswer A{"error", prefix_};
173A.abort(std::move(error));
174auto page = A.finish();
175auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
176MHD_add_response_header(R, "Content-Type", "text/html");
177promise_.set_value(std::move(R));
178}
179stop();
180}
181
182HttpQueryBlockData::HttpQueryBlockData(ton::BlockIdExt block_id, std::string prefix,
183td::Promise<MHD_Response *> promise)
184: HttpQueryCommon(std::move(prefix), std::move(promise)), block_id_(block_id) {
185}
186
187HttpQueryBlockData::HttpQueryBlockData(std::map<std::string, std::string> opts, std::string prefix,
188td::Promise<MHD_Response *> promise)
189: HttpQueryCommon(std::move(prefix), std::move(promise)) {
190auto R = parse_block_id(opts);
191if (R.is_ok()) {
192block_id_ = R.move_as_ok();
193} else {
194error_ = R.move_as_error();
195}
196}
197
198void HttpQueryBlockData::abort_query(td::Status error) {
199if (promise_) {
200promise_.set_result(nullptr);
201}
202stop();
203}
204
205void HttpQueryBlockData::finish_query() {
206if (promise_) {
207auto response = MHD_create_response_from_buffer(data_.length(), data_.as_slice().begin(), MHD_RESPMEM_MUST_COPY);
208promise_.set_result(response);
209}
210stop();
211}
212
213void HttpQueryBlockData::start_up() {
214auto query = ton::serialize_tl_object(
215ton::create_tl_object<ton::lite_api::liteServer_getBlock>(ton::create_tl_lite_block_id(block_id_)), true);
216
217auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
218if (R.is_error()) {
219td::actor::send_closure(SelfId, &HttpQueryBlockData::abort_query, R.move_as_error_prefix("litequery failed: "));
220} else {
221td::actor::send_closure(SelfId, &HttpQueryBlockData::got_block_data, R.move_as_ok());
222}
223});
224
225td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
226std::move(query), std::move(P));
227}
228
229void HttpQueryBlockData::got_block_data(td::BufferSlice data) {
230auto F = ton::fetch_tl_object<ton::lite_api::liteServer_blockData>(std::move(data), true);
231if (F.is_error()) {
232abort_query(F.move_as_error());
233return;
234}
235data_ = std::move(F.move_as_ok()->data_);
236finish_query();
237}
238
239HttpQueryBlockView::HttpQueryBlockView(ton::BlockIdExt block_id, std::string prefix,
240td::Promise<MHD_Response *> promise)
241: HttpQueryCommon(std::move(prefix), std::move(promise)), block_id_(block_id) {
242}
243
244HttpQueryBlockView::HttpQueryBlockView(std::map<std::string, std::string> opts, std::string prefix,
245td::Promise<MHD_Response *> promise)
246: HttpQueryCommon(std::move(prefix), std::move(promise)) {
247auto R = parse_block_id(opts);
248if (R.is_ok()) {
249block_id_ = R.move_as_ok();
250} else {
251error_ = R.move_as_error();
252}
253}
254
255void HttpQueryBlockView::finish_query() {
256if (promise_) {
257auto page = [&]() -> std::string {
258HttpAnswer A{"viewblock", prefix_};
259A.set_block_id(block_id_);
260auto res = vm::std_boc_deserialize(data_.clone());
261if (res.is_error()) {
262return A.abort(PSTRING() << "cannot deserialize block: " << res.move_as_error());
263}
264create_header(A);
265auto root = res.move_as_ok();
266A << HttpAnswer::RawData<block::gen::Block>{root};
267return A.finish();
268}();
269auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
270MHD_add_response_header(R, "Content-Type", "text/html");
271promise_.set_value(std::move(R));
272}
273stop();
274}
275
276void HttpQueryBlockView::start_up_query() {
277auto query = ton::serialize_tl_object(
278ton::create_tl_object<ton::lite_api::liteServer_getBlock>(ton::create_tl_lite_block_id(block_id_)), true);
279
280auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
281if (R.is_error()) {
282td::actor::send_closure(SelfId, &HttpQueryBlockView::abort_query, R.move_as_error_prefix("litequery failed: "));
283} else {
284td::actor::send_closure(SelfId, &HttpQueryBlockView::got_block_data, R.move_as_ok());
285}
286});
287
288td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
289std::move(query), std::move(P));
290}
291
292void HttpQueryBlockView::got_block_data(td::BufferSlice data) {
293auto F = ton::fetch_tl_object<ton::lite_api::liteServer_blockData>(std::move(data), true);
294if (F.is_error()) {
295abort_query(F.move_as_error());
296}
297data_ = std::move(F.move_as_ok()->data_);
298finish_query();
299}
300
301HttpQueryBlockInfo::HttpQueryBlockInfo(ton::BlockIdExt block_id, std::string prefix,
302td::Promise<MHD_Response *> promise)
303: HttpQueryCommon(std::move(prefix), std::move(promise)), block_id_(block_id) {
304}
305
306HttpQueryBlockInfo::HttpQueryBlockInfo(std::map<std::string, std::string> opts, std::string prefix,
307td::Promise<MHD_Response *> promise)
308: HttpQueryCommon(std::move(prefix), std::move(promise)) {
309auto R = parse_block_id(opts);
310if (R.is_ok()) {
311block_id_ = R.move_as_ok();
312} else {
313error_ = R.move_as_error();
314}
315}
316
317void HttpQueryBlockInfo::start_up_query() {
318auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
319if (R.is_error()) {
320td::actor::send_closure(SelfId, &HttpQueryBlockInfo::abort_query, R.move_as_error_prefix("litequery failed: "));
321} else {
322td::actor::send_closure(SelfId, &HttpQueryBlockInfo::got_block_header, R.move_as_ok());
323}
324});
325auto query = ton::serialize_tl_object(
326ton::create_tl_object<ton::lite_api::liteServer_getBlockHeader>(ton::create_tl_lite_block_id(block_id_), 0),
327true);
328td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
329std::move(query), std::move(P));
330pending_queries_ = 1;
331
332if (block_id_.is_masterchain()) {
333auto P_2 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
334if (R.is_error()) {
335td::actor::send_closure(SelfId, &HttpQueryBlockInfo::failed_to_get_shard_info,
336R.move_as_error_prefix("litequery failed: "));
337} else {
338td::actor::send_closure(SelfId, &HttpQueryBlockInfo::got_shard_info, R.move_as_ok());
339}
340});
341auto query_2 = ton::serialize_tl_object(
342ton::create_tl_object<ton::lite_api::liteServer_getAllShardsInfo>(ton::create_tl_lite_block_id(block_id_)),
343true);
344td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
345std::move(query_2), std::move(P_2));
346pending_queries_++;
347}
348auto query_3 = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_listBlockTransactions>(
349ton::create_tl_lite_block_id(block_id_), 7, 1024, nullptr, false, false),
350true);
351auto P_3 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
352if (R.is_error()) {
353td::actor::send_closure(SelfId, &HttpQueryBlockInfo::abort_query, R.move_as_error_prefix("litequery failed: "));
354} else {
355td::actor::send_closure(SelfId, &HttpQueryBlockInfo::got_transactions, R.move_as_ok());
356}
357});
358td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
359std::move(query_3), std::move(P_3));
360pending_queries_++;
361}
362
363void HttpQueryBlockInfo::got_block_header(td::BufferSlice data) {
364auto F = ton::fetch_tl_object<ton::lite_api::liteServer_blockHeader>(std::move(data), true);
365if (F.is_error()) {
366abort_query(F.move_as_error());
367return;
368}
369data_ = std::move(F.move_as_ok()->header_proof_);
370
371if (!--pending_queries_) {
372finish_query();
373}
374}
375
376void HttpQueryBlockInfo::got_shard_info(td::BufferSlice data) {
377auto F = ton::fetch_tl_object<ton::lite_api::liteServer_allShardsInfo>(std::move(data), true);
378if (F.is_error()) {
379abort_query(F.move_as_error());
380return;
381}
382shard_data_ = std::move(F.move_as_ok()->data_);
383
384if (!--pending_queries_) {
385finish_query();
386}
387}
388
389void HttpQueryBlockInfo::failed_to_get_shard_info(td::Status error) {
390shard_data_error_ = std::move(error);
391if (!--pending_queries_) {
392finish_query();
393}
394}
395
396void HttpQueryBlockInfo::got_transactions(td::BufferSlice data) {
397auto F = ton::fetch_tl_object<ton::lite_api::liteServer_blockTransactions>(std::move(data), true);
398if (F.is_error()) {
399abort_query(F.move_as_error());
400return;
401}
402auto f = F.move_as_ok();
403trans_req_count_ = f->req_count_;
404
405for (auto &T : f->ids_) {
406transactions_.emplace_back(block::StdAddress{block_id_.id.workchain, T->account_},
407static_cast<ton::LogicalTime>(T->lt_), T->hash_);
408}
409
410if (f->incomplete_ && transactions_.size() > 0) {
411const auto &T = *transactions_.rbegin();
412auto query_3 = ton::serialize_tl_object(
413ton::create_tl_object<ton::lite_api::liteServer_listBlockTransactions>(
414ton::create_tl_lite_block_id(block_id_), 7 + 128, 1024,
415ton::create_tl_object<ton::lite_api::liteServer_transactionId3>(T.addr.addr, T.lt), false, false),
416true);
417auto P_3 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
418if (R.is_error()) {
419td::actor::send_closure(SelfId, &HttpQueryBlockInfo::abort_query, R.move_as_error_prefix("litequery failed: "));
420} else {
421td::actor::send_closure(SelfId, &HttpQueryBlockInfo::got_transactions, R.move_as_ok());
422}
423});
424td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
425std::move(query_3), std::move(P_3));
426} else {
427if (!--pending_queries_) {
428finish_query();
429}
430}
431}
432
433void HttpQueryBlockInfo::finish_query() {
434if (promise_) {
435auto page = [&]() -> std::string {
436HttpAnswer A{"blockinfo", prefix_};
437A.set_block_id(block_id_);
438create_header(A);
439auto res = vm::std_boc_deserialize(data_.clone());
440if (res.is_error()) {
441return A.abort(PSTRING() << "cannot deserialize block header data: " << res.move_as_error());
442}
443A << HttpAnswer::BlockHeaderCell{block_id_, res.move_as_ok()};
444
445if (shard_data_.size() > 0) {
446auto R = vm::std_boc_deserialize(shard_data_.clone());
447if (R.is_error()) {
448return A.abort(PSTRING() << "cannot deserialize shard configuration: " << R.move_as_error());
449} else {
450A << HttpAnswer::BlockShardsCell{block_id_, R.move_as_ok()};
451}
452}
453if (shard_data_error_.is_error()) {
454A << HttpAnswer::Error{shard_data_error_.clone()};
455}
456
457HttpAnswer::TransactionList I;
458I.block_id = block_id_;
459I.req_count_ = trans_req_count_;
460for (auto &T : transactions_) {
461I.vec.emplace_back(T.addr, T.lt, T.hash);
462}
463A << I;
464
465return A.finish();
466}();
467auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
468MHD_add_response_header(R, "Content-Type", "text/html");
469promise_.set_value(std::move(R));
470}
471stop();
472}
473
474HttpQueryBlockSearch::HttpQueryBlockSearch(ton::WorkchainId workchain, ton::AccountIdPrefix account,
475ton::BlockSeqno seqno, std::string prefix,
476td::Promise<MHD_Response *> promise)
477: HttpQueryCommon(std::move(prefix), std::move(promise))
478, account_prefix_{workchain, account}
479, mode_(1)
480, seqno_(seqno) {
481}
482HttpQueryBlockSearch::HttpQueryBlockSearch(ton::WorkchainId workchain, ton::AccountIdPrefix account,
483ton::LogicalTime lt, std::string prefix, td::Promise<MHD_Response *> promise)
484: HttpQueryCommon(std::move(prefix), std::move(promise)), account_prefix_{workchain, account}, mode_(2), lt_(lt) {
485}
486HttpQueryBlockSearch::HttpQueryBlockSearch(ton::WorkchainId workchain, ton::AccountIdPrefix account, bool dummy,
487ton::UnixTime utime, std::string prefix, td::Promise<MHD_Response *> promise)
488: HttpQueryCommon(std::move(prefix), std::move(promise))
489, account_prefix_{workchain, account}
490, mode_(4)
491, utime_(utime) {
492}
493
494HttpQueryBlockSearch::HttpQueryBlockSearch(std::map<std::string, std::string> opts, std::string prefix,
495td::Promise<MHD_Response *> promise)
496: HttpQueryCommon(std::move(prefix), std::move(promise)) {
497auto R2 = parse_account_prefix(opts, false);
498if (R2.is_ok()) {
499account_prefix_ = R2.move_as_ok();
500} else {
501error_ = R2.move_as_error();
502return;
503}
504if (opts.count("seqno") + opts.count("lt") + opts.count("utime") != 1) {
505error_ = td::Status::Error(ton::ErrorCode::protoviolation, "exactly one of seqno/lt/utime must be set");
506return;
507}
508if (opts.count("seqno") == 1) {
509try {
510seqno_ = static_cast<td::uint32>(std::stoull(opts["seqno"]));
511mode_ = 1;
512} catch (...) {
513error_ = td::Status::Error("cannot parse seqno");
514return;
515}
516}
517if (opts.count("lt") == 1) {
518try {
519lt_ = std::stoull(opts["lt"]);
520mode_ = 2;
521} catch (...) {
522error_ = td::Status::Error("cannot parse lt");
523return;
524}
525}
526if (opts.count("utime") == 1) {
527try {
528seqno_ = static_cast<td::uint32>(std::stoull(opts["utime"]));
529mode_ = 1;
530} catch (...) {
531error_ = td::Status::Error("cannot parse utime");
532return;
533}
534}
535}
536
537void HttpQueryBlockSearch::start_up_query() {
538auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
539if (R.is_error()) {
540td::actor::send_closure(SelfId, &HttpQueryBlockSearch::abort_query, R.move_as_error_prefix("litequery failed: "));
541} else {
542td::actor::send_closure(SelfId, &HttpQueryBlockSearch::got_block_header, R.move_as_ok());
543}
544});
545auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_lookupBlock>(
546mode_,
547ton::create_tl_lite_block_id_simple(ton::BlockId{
548account_prefix_.workchain, account_prefix_.account_id_prefix, seqno_}),
549lt_, utime_),
550true);
551td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
552std::move(query), std::move(P));
553}
554
555void HttpQueryBlockSearch::got_block_header(td::BufferSlice data) {
556auto F = ton::fetch_tl_object<ton::lite_api::liteServer_blockHeader>(std::move(data), true);
557if (F.is_error()) {
558abort_query(F.move_as_error());
559return;
560}
561auto f = F.move_as_ok();
562data_ = std::move(f->header_proof_);
563block_id_ = ton::create_block_id(f->id_);
564
565if (block_id_.is_masterchain()) {
566auto P_2 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
567if (R.is_error()) {
568td::actor::send_closure(SelfId, &HttpQueryBlockSearch::failed_to_get_shard_info,
569R.move_as_error_prefix("litequery failed: "));
570} else {
571td::actor::send_closure(SelfId, &HttpQueryBlockSearch::got_shard_info, R.move_as_ok());
572}
573});
574auto query_2 = ton::serialize_tl_object(
575ton::create_tl_object<ton::lite_api::liteServer_getAllShardsInfo>(ton::create_tl_lite_block_id(block_id_)),
576true);
577td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
578std::move(query_2), std::move(P_2));
579pending_queries_++;
580}
581
582auto query_3 = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_listBlockTransactions>(
583ton::create_tl_lite_block_id(block_id_), 7, 1024, nullptr, false, false),
584true);
585auto P_3 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
586if (R.is_error()) {
587td::actor::send_closure(SelfId, &HttpQueryBlockSearch::abort_query, R.move_as_error_prefix("litequery failed: "));
588} else {
589td::actor::send_closure(SelfId, &HttpQueryBlockSearch::got_transactions, R.move_as_ok());
590}
591});
592td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
593std::move(query_3), std::move(P_3));
594pending_queries_++;
595}
596
597void HttpQueryBlockSearch::got_shard_info(td::BufferSlice data) {
598auto F = ton::fetch_tl_object<ton::lite_api::liteServer_allShardsInfo>(std::move(data), true);
599if (F.is_error()) {
600abort_query(F.move_as_error());
601return;
602}
603shard_data_ = std::move(F.move_as_ok()->data_);
604
605if (!--pending_queries_) {
606finish_query();
607}
608}
609
610void HttpQueryBlockSearch::failed_to_get_shard_info(td::Status error) {
611shard_data_error_ = std::move(error);
612if (!--pending_queries_) {
613finish_query();
614}
615}
616
617void HttpQueryBlockSearch::got_transactions(td::BufferSlice data) {
618auto F = ton::fetch_tl_object<ton::lite_api::liteServer_blockTransactions>(std::move(data), true);
619if (F.is_error()) {
620abort_query(F.move_as_error());
621return;
622}
623auto f = F.move_as_ok();
624trans_req_count_ = f->req_count_;
625
626for (auto &T : f->ids_) {
627transactions_.emplace_back(block::StdAddress{block_id_.id.workchain, T->account_},
628static_cast<ton::LogicalTime>(T->lt_), T->hash_);
629}
630
631if (f->incomplete_ && transactions_.size() > 0) {
632const auto &T = *transactions_.rbegin();
633auto query_3 = ton::serialize_tl_object(
634ton::create_tl_object<ton::lite_api::liteServer_listBlockTransactions>(
635ton::create_tl_lite_block_id(block_id_), 7 + 128, 1024,
636ton::create_tl_object<ton::lite_api::liteServer_transactionId3>(T.addr.addr, T.lt), false, false),
637true);
638auto P_3 = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
639if (R.is_error()) {
640td::actor::send_closure(SelfId, &HttpQueryBlockSearch::abort_query,
641R.move_as_error_prefix("litequery failed: "));
642} else {
643td::actor::send_closure(SelfId, &HttpQueryBlockSearch::got_transactions, R.move_as_ok());
644}
645});
646td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
647std::move(query_3), std::move(P_3));
648} else {
649if (!--pending_queries_) {
650finish_query();
651}
652}
653}
654
655void HttpQueryBlockSearch::finish_query() {
656if (promise_) {
657auto page = [&]() -> std::string {
658HttpAnswer A{"blockinfo", prefix_};
659A.set_block_id(block_id_);
660create_header(A);
661auto res = vm::std_boc_deserialize(data_.clone());
662if (res.is_error()) {
663return A.abort(PSTRING() << "cannot deserialize block header data: " << res.move_as_error());
664}
665A << HttpAnswer::BlockHeaderCell{block_id_, res.move_as_ok()};
666
667if (shard_data_.size() > 0) {
668auto R = vm::std_boc_deserialize(shard_data_.clone());
669if (R.is_error()) {
670return A.abort(PSTRING() << "cannot deserialize shard configuration: " << R.move_as_error());
671} else {
672A << HttpAnswer::BlockShardsCell{block_id_, R.move_as_ok()};
673}
674}
675if (shard_data_error_.is_error()) {
676A << HttpAnswer::Error{shard_data_error_.clone()};
677}
678
679HttpAnswer::TransactionList I;
680I.block_id = block_id_;
681I.req_count_ = trans_req_count_;
682for (auto &T : transactions_) {
683I.vec.emplace_back(T.addr, T.lt, T.hash);
684}
685A << I;
686
687return A.finish();
688}();
689auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
690MHD_add_response_header(R, "Content-Type", "text/html");
691promise_.set_value(std::move(R));
692}
693stop();
694}
695HttpQueryViewAccount::HttpQueryViewAccount(ton::BlockIdExt block_id, block::StdAddress addr, std::string prefix,
696td::Promise<MHD_Response *> promise)
697: HttpQueryCommon(std::move(prefix), std::move(promise)), block_id_(block_id), addr_(addr) {
698}
699
700HttpQueryViewAccount::HttpQueryViewAccount(std::map<std::string, std::string> opts, std::string prefix,
701td::Promise<MHD_Response *> promise)
702: HttpQueryCommon(std::move(prefix), std::move(promise)) {
703auto R = parse_block_id(opts, true);
704if (R.is_ok()) {
705block_id_ = R.move_as_ok();
706if (!block_id_.is_valid()) {
707block_id_.id.workchain = ton::masterchainId;
708block_id_.id.shard = ton::shardIdAll;
709block_id_.id.seqno = static_cast<td::uint32>(0xffffffff);
710block_id_.root_hash.set_zero();
711block_id_.file_hash.set_zero();
712}
713} else {
714error_ = R.move_as_error();
715return;
716}
717auto R2 = parse_account_addr(opts);
718if (R2.is_ok()) {
719addr_ = R2.move_as_ok();
720} else {
721error_ = R2.move_as_error();
722return;
723}
724}
725
726void HttpQueryViewAccount::start_up_query() {
727auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
728if (R.is_error()) {
729td::actor::send_closure(SelfId, &HttpQueryViewAccount::abort_query, R.move_as_error_prefix("litequery failed: "));
730} else {
731td::actor::send_closure(SelfId, &HttpQueryViewAccount::got_account, R.move_as_ok());
732}
733});
734auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(addr_.workchain, addr_.addr);
735auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getAccountState>(
736ton::create_tl_lite_block_id(block_id_), std::move(a)),
737true);
738td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
739std::move(query), std::move(P));
740}
741
742void HttpQueryViewAccount::got_account(td::BufferSlice data) {
743auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(std::move(data), true);
744if (F.is_error()) {
745abort_query(F.move_as_error());
746return;
747}
748
749auto f = F.move_as_ok();
750data_ = std::move(f->state_);
751proof_ = std::move(f->proof_);
752res_block_id_ = ton::create_block_id(f->shardblk_);
753
754finish_query();
755}
756
757void HttpQueryViewAccount::finish_query() {
758if (promise_) {
759auto page = [&]() -> std::string {
760HttpAnswer A{"account", prefix_};
761A.set_account_id(addr_);
762A.set_block_id(res_block_id_);
763auto R = vm::std_boc_deserialize(data_.clone());
764if (R.is_error()) {
765return A.abort(PSTRING() << "FATAL: cannot deserialize account state" << R.move_as_error());
766}
767auto Q = vm::std_boc_deserialize_multi(proof_.clone());
768if (Q.is_error()) {
769return A.abort(PSTRING() << "FATAL: cannot deserialize account proof" << Q.move_as_error());
770}
771auto Q_roots = Q.move_as_ok();
772auto root = R.move_as_ok();
773A << HttpAnswer::AccountCell{addr_, res_block_id_, root, Q_roots};
774return A.finish();
775}();
776auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
777MHD_add_response_header(R, "Content-Type", "text/html");
778promise_.set_value(std::move(R));
779}
780stop();
781}
782
783HttpQueryViewTransaction::HttpQueryViewTransaction(block::StdAddress addr, ton::LogicalTime lt, ton::Bits256 hash,
784std::string prefix, td::Promise<MHD_Response *> promise)
785: HttpQueryCommon(std::move(prefix), std::move(promise)), addr_(addr), lt_(lt), hash_(hash) {
786}
787
788HttpQueryViewTransaction::HttpQueryViewTransaction(std::map<std::string, std::string> opts, std::string prefix,
789td::Promise<MHD_Response *> promise)
790: HttpQueryCommon(std::move(prefix), std::move(promise)) {
791auto R2 = parse_account_addr(opts);
792if (R2.is_ok()) {
793addr_ = R2.move_as_ok();
794} else {
795error_ = R2.move_as_error();
796return;
797}
798try {
799lt_ = std::stoull(opts["lt"]);
800} catch (...) {
801error_ = td::Status::Error("cannot trans parse lt");
802return;
803}
804try {
805auto h = opts["hash"];
806if (h.length() != 64) {
807error_ = td::Status::Error("cannot trans parse hash");
808return;
809}
810auto R = td::hex_decode(td::Slice(h));
811if (R.is_error()) {
812error_ = td::Status::Error("cannot trans parse hash");
813return;
814}
815hash_.as_slice().copy_from(R.move_as_ok());
816} catch (...) {
817error_ = td::Status::Error("cannot trans parse hash");
818return;
819}
820}
821
822void HttpQueryViewTransaction::start_up_query() {
823auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
824if (R.is_error()) {
825td::actor::send_closure(SelfId, &HttpQueryViewTransaction::abort_query,
826R.move_as_error_prefix("litequery failed: "));
827} else {
828td::actor::send_closure(SelfId, &HttpQueryViewTransaction::got_transaction, R.move_as_ok());
829}
830});
831auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(addr_.workchain, addr_.addr);
832auto query = ton::serialize_tl_object(
833ton::create_tl_object<ton::lite_api::liteServer_getTransactions>(1, std::move(a), lt_, hash_), true);
834td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
835std::move(query), std::move(P));
836}
837
838void HttpQueryViewTransaction::got_transaction(td::BufferSlice data) {
839auto F = ton::fetch_tl_object<ton::lite_api::liteServer_transactionList>(std::move(data), true);
840if (F.is_error()) {
841abort_query(F.move_as_error());
842return;
843}
844
845auto f = F.move_as_ok();
846data_ = std::move(f->transactions_);
847if (f->ids_.size() == 0) {
848abort_query(td::Status::Error("no transactions found"));
849return;
850}
851res_block_id_ = ton::create_block_id(f->ids_[0]);
852
853finish_query();
854}
855
856void HttpQueryViewTransaction::finish_query() {
857if (promise_) {
858auto page = [&]() -> std::string {
859HttpAnswer A{"transaction", prefix_};
860A.set_block_id(res_block_id_);
861A.set_account_id(addr_);
862auto R = vm::std_boc_deserialize_multi(std::move(data_));
863if (R.is_error()) {
864return A.abort(PSTRING() << "FATAL: cannot deserialize transactions BoC");
865}
866auto list = R.move_as_ok();
867auto n = list.size();
868if (n != 1) {
869return A.abort(PSTRING() << "obtained " << n << " transaction, but only 1 have been requested");
870} else {
871A << HttpAnswer::TransactionCell{addr_, res_block_id_, list[0]};
872}
873return A.finish();
874}();
875auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
876MHD_add_response_header(R, "Content-Type", "text/html");
877promise_.set_value(std::move(R));
878}
879stop();
880}
881
882HttpQueryViewTransaction2::HttpQueryViewTransaction2(ton::BlockIdExt block_id, block::StdAddress addr,
883ton::LogicalTime lt, std::string prefix,
884td::Promise<MHD_Response *> promise)
885: HttpQueryCommon(std::move(prefix), std::move(promise)), block_id_(block_id), addr_(addr), lt_(lt) {
886}
887
888HttpQueryViewTransaction2::HttpQueryViewTransaction2(std::map<std::string, std::string> opts, std::string prefix,
889td::Promise<MHD_Response *> promise)
890: HttpQueryCommon(std::move(prefix), std::move(promise)) {
891auto R = parse_block_id(opts);
892if (R.is_ok()) {
893block_id_ = R.move_as_ok();
894} else {
895error_ = R.move_as_error();
896return;
897}
898auto R2 = parse_account_addr(opts);
899if (R2.is_ok()) {
900addr_ = R2.move_as_ok();
901} else {
902error_ = R2.move_as_error();
903return;
904}
905try {
906lt_ = std::stoull(opts["lt"]);
907} catch (...) {
908error_ = td::Status::Error("cannot trans parse lt");
909return;
910}
911}
912
913void HttpQueryViewTransaction2::start_up_query() {
914auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
915if (R.is_error()) {
916td::actor::send_closure(SelfId, &HttpQueryViewTransaction2::abort_query,
917R.move_as_error_prefix("litequery failed: "));
918} else {
919td::actor::send_closure(SelfId, &HttpQueryViewTransaction2::got_transaction, R.move_as_ok());
920}
921});
922auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(addr_.workchain, addr_.addr);
923auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getOneTransaction>(
924ton::create_tl_lite_block_id(block_id_), std::move(a), lt_),
925true);
926td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
927std::move(query), std::move(P));
928}
929
930void HttpQueryViewTransaction2::got_transaction(td::BufferSlice data) {
931auto F = ton::fetch_tl_object<ton::lite_api::liteServer_transactionInfo>(std::move(data), true);
932if (F.is_error()) {
933abort_query(F.move_as_error());
934return;
935}
936
937auto f = F.move_as_ok();
938data_ = std::move(f->transaction_);
939
940finish_query();
941}
942
943void HttpQueryViewTransaction2::finish_query() {
944if (promise_) {
945auto page = [&]() -> std::string {
946HttpAnswer A{"transaction", prefix_};
947A.set_block_id(block_id_);
948A.set_account_id(addr_);
949auto R = vm::std_boc_deserialize(std::move(data_));
950if (R.is_error()) {
951return A.abort(PSTRING() << "FATAL: cannot deserialize transactions BoC");
952}
953auto list = R.move_as_ok();
954A << HttpAnswer::TransactionCell{addr_, block_id_, list};
955return A.finish();
956}();
957auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
958MHD_add_response_header(R, "Content-Type", "text/html");
959promise_.set_value(std::move(R));
960}
961stop();
962}
963
964HttpQueryViewLastBlock::HttpQueryViewLastBlock(std::string prefix, td::Promise<MHD_Response *> promise)
965: HttpQueryCommon(std::move(prefix), std::move(promise)) {
966}
967
968HttpQueryViewLastBlock::HttpQueryViewLastBlock(std::map<std::string, std::string> opts, std::string prefix,
969td::Promise<MHD_Response *> promise)
970: HttpQueryCommon(std::move(prefix), std::move(promise)) {
971}
972
973void HttpQueryViewLastBlock::start_up() {
974if (error_.is_error()) {
975abort_query(std::move(error_));
976return;
977}
978auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
979if (R.is_error()) {
980td::actor::send_closure(SelfId, &HttpQueryViewLastBlock::abort_query, R.move_as_error());
981} else {
982td::actor::send_closure(SelfId, &HttpQueryViewLastBlock::got_result, R.move_as_ok());
983}
984});
985
986auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfo>(), true);
987td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
988std::move(query), std::move(P));
989}
990
991void HttpQueryViewLastBlock::got_result(td::BufferSlice data) {
992auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfo>(std::move(data), true);
993if (F.is_error()) {
994abort_query(F.move_as_error());
995return;
996}
997auto f = F.move_as_ok();
998res_block_id_ = ton::create_block_id(f->last_);
999
1000finish_query();
1001}
1002
1003void HttpQueryViewLastBlock::finish_query() {
1004if (promise_) {
1005td::actor::create_actor<HttpQueryBlockInfo>("blockinfo", res_block_id_, prefix_, std::move(promise_)).release();
1006}
1007stop();
1008}
1009
1010HttpQueryConfig::HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector<td::int32> params,
1011td::Promise<MHD_Response *> promise)
1012: HttpQueryCommon(prefix, std::move(promise)), block_id_(block_id), params_(std::move(params)) {
1013}
1014
1015HttpQueryConfig::HttpQueryConfig(std::map<std::string, std::string> opts, std::string prefix,
1016td::Promise<MHD_Response *> promise)
1017: HttpQueryCommon(prefix, std::move(promise)) {
1018auto R = parse_block_id(opts, true);
1019if (R.is_error()) {
1020error_ = R.move_as_error();
1021return;
1022}
1023block_id_ = R.move_as_ok();
1024
1025auto it = opts.find("param");
1026if (it != opts.end()) {
1027auto R2 = td::to_integer_safe<int>(it->second);
1028if (R2.is_error()) {
1029error_ = R2.move_as_error();
1030return;
1031}
1032params_.push_back(R2.move_as_ok());
1033}
1034}
1035
1036void HttpQueryConfig::start_up() {
1037if (error_.is_error()) {
1038abort_query(std::move(error_));
1039return;
1040}
1041if (block_id_.is_valid()) {
1042send_main_query();
1043} else {
1044auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
1045if (R.is_error()) {
1046td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error());
1047} else {
1048td::actor::send_closure(SelfId, &HttpQueryConfig::got_block, R.move_as_ok());
1049}
1050});
1051
1052auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfo>(), true);
1053td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
1054std::move(query), std::move(P));
1055}
1056}
1057
1058void HttpQueryConfig::got_block(td::BufferSlice data) {
1059auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfo>(std::move(data), true);
1060if (F.is_error()) {
1061abort_query(F.move_as_error());
1062return;
1063}
1064auto f = F.move_as_ok();
1065block_id_ = ton::create_block_id(f->last_);
1066
1067send_main_query();
1068}
1069
1070void HttpQueryConfig::send_main_query() {
1071auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
1072if (R.is_error()) {
1073td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error());
1074} else {
1075td::actor::send_closure(SelfId, &HttpQueryConfig::got_result, R.move_as_ok());
1076}
1077});
1078auto query =
1079params_.size() > 0
1080? ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigParams>(
10810, ton::create_tl_lite_block_id(block_id_), std::vector<int>(params_)),
1082true)
1083: ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigAll>(
10840, ton::create_tl_lite_block_id(block_id_)),
1085true);
1086td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
1087std::move(query), std::move(P));
1088}
1089
1090void HttpQueryConfig::got_result(td::BufferSlice data) {
1091auto F = ton::fetch_tl_object<ton::lite_api::liteServer_configInfo>(std::move(data), true);
1092if (F.is_error()) {
1093abort_query(F.move_as_error());
1094return;
1095}
1096auto f = F.move_as_ok();
1097
1098state_proof_ = std::move(f->state_proof_);
1099config_proof_ = std::move(f->config_proof_);
1100
1101finish_query();
1102}
1103
1104void HttpQueryConfig::finish_query() {
1105if (promise_) {
1106auto page = [&]() -> std::string {
1107HttpAnswer A{"config", prefix_};
1108A.set_block_id(block_id_);
1109auto R = block::check_extract_state_proof(block_id_, state_proof_.as_slice(), config_proof_.as_slice());
1110if (R.is_error()) {
1111A.abort(PSTRING() << "masterchain state proof for " << block_id_.to_str()
1112<< " is invalid : " << R.move_as_error());
1113return A.finish();
1114}
1115try {
1116auto res = block::Config::extract_from_state(R.move_as_ok(), 0);
1117if (res.is_error()) {
1118A.abort(PSTRING() << "cannot unpack configuration: " << res.move_as_error());
1119return A.finish();
1120}
1121auto config = res.move_as_ok();
1122if (params_.size() > 0) {
1123A << "<p>params: ";
1124for (int i : params_) {
1125auto value = config->get_config_param(i);
1126if (value.not_null()) {
1127A << "<a href=\"#configparam" << i << "\">" << i << "</a> ";
1128}
1129}
1130A << "</p>";
1131for (int i : params_) {
1132auto value = config->get_config_param(i);
1133if (value.not_null()) {
1134A << HttpAnswer::ConfigParam{i, value};
1135} else {
1136A << HttpAnswer::Error{td::Status::Error(404, PSTRING() << "empty param " << i)};
1137}
1138}
1139} else {
1140A << "<p>params: ";
1141config->foreach_config_param([&](int i, td::Ref<vm::Cell> value) {
1142if (value.not_null()) {
1143A << "<a href=\"#configparam" << i << "\">" << i << "</a> ";
1144}
1145return true;
1146});
1147A << "</p>";
1148config->foreach_config_param([&](int i, td::Ref<vm::Cell> value) {
1149if (value.not_null()) {
1150A << HttpAnswer::ConfigParam{i, value};
1151}
1152return true;
1153});
1154}
1155} catch (vm::VmError &err) {
1156A.abort(PSTRING() << "error while traversing configuration: " << err.get_msg());
1157} catch (vm::VmVirtError &err) {
1158A.abort(PSTRING() << "virtualization error while traversing configuration: " << err.get_msg());
1159}
1160return A.finish();
1161}();
1162auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
1163MHD_add_response_header(R, "Content-Type", "text/html");
1164promise_.set_value(std::move(R));
1165}
1166stop();
1167}
1168
1169HttpQuerySendForm::HttpQuerySendForm(std::string prefix, td::Promise<MHD_Response *> promise)
1170: HttpQueryCommon(prefix, std::move(promise)) {
1171}
1172
1173HttpQuerySendForm::HttpQuerySendForm(std::map<std::string, std::string> opts, std::string prefix,
1174td::Promise<MHD_Response *> promise)
1175: HttpQueryCommon(prefix, std::move(promise)) {
1176}
1177
1178void HttpQuerySendForm::start_up() {
1179finish_query();
1180}
1181
1182void HttpQuerySendForm::finish_query() {
1183if (promise_) {
1184auto page = [&]() -> std::string {
1185HttpAnswer A{"send", prefix_};
1186A << "<div class=\"row\"><form action=\"" << prefix_
1187<< "send\" method=\"post\" enctype=\"multipart/form-data\"><div class=\"form-group-row\">"
1188<< "<label for=\"filedata\">bag of cells</label>"
1189<< "<input type=\"file\" class=\"form-control-file\" id=\"filedata\" name=\"filedata\">"
1190<< "<button type=\"submit\" class=\"btn btn-primary\">send</button>"
1191<< "</div></form></div>";
1192return A.finish();
1193}();
1194auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
1195MHD_add_response_header(R, "Content-Type", "text/html");
1196promise_.set_value(std::move(R));
1197}
1198stop();
1199}
1200
1201HttpQuerySend::HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise<MHD_Response *> promise)
1202: HttpQueryCommon(prefix, std::move(promise)), data_(std::move(data)) {
1203}
1204
1205HttpQuerySend::HttpQuerySend(std::map<std::string, std::string> opts, std::string prefix,
1206td::Promise<MHD_Response *> promise)
1207: HttpQueryCommon(prefix, std::move(promise)) {
1208auto it = opts.find("filedata");
1209if (it != opts.end()) {
1210data_ = td::BufferSlice{it->second};
1211} else {
1212error_ = td::Status::Error("no file data");
1213return;
1214}
1215}
1216
1217void HttpQuerySend::start_up() {
1218if (error_.is_error()) {
1219abort_query(std::move(error_));
1220return;
1221}
1222auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
1223if (R.is_error()) {
1224td::actor::send_closure(SelfId, &HttpQuerySend::abort_query, R.move_as_error());
1225} else {
1226td::actor::send_closure(SelfId, &HttpQuerySend::got_result, R.move_as_ok());
1227}
1228});
1229auto query =
1230ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_sendMessage>(std::move(data_)), true);
1231td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
1232std::move(query), std::move(P));
1233}
1234
1235void HttpQuerySend::got_result(td::BufferSlice data) {
1236auto F = ton::fetch_tl_object<ton::lite_api::liteServer_sendMsgStatus>(std::move(data), true);
1237if (F.is_error()) {
1238abort_query(F.move_as_error());
1239} else {
1240status_ = F.move_as_ok()->status_;
1241}
1242finish_query();
1243}
1244
1245void HttpQuerySend::finish_query() {
1246if (promise_) {
1247auto page = [&]() -> std::string {
1248HttpAnswer A{"send", prefix_};
1249if (status_ >= 0) {
1250A << HttpAnswer::Notification{"success"};
1251} else {
1252A << HttpAnswer::Error{td::Status::Error(status_, "failed")};
1253}
1254return A.finish();
1255}();
1256auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
1257MHD_add_response_header(R, "Content-Type", "text/html");
1258promise_.set_value(std::move(R));
1259}
1260stop();
1261}
1262
1263HttpQueryRunMethod::HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name,
1264std::vector<vm::StackEntry> params, std::string prefix,
1265td::Promise<MHD_Response *> promise)
1266: HttpQueryCommon(std::move(prefix), std::move(promise))
1267, block_id_(block_id)
1268, addr_(addr)
1269, method_name_(std::move(method_name))
1270, params_(std::move(params)) {
1271}
1272
1273HttpQueryRunMethod::HttpQueryRunMethod(std::map<std::string, std::string> opts, std::string prefix,
1274td::Promise<MHD_Response *> promise)
1275: HttpQueryCommon(std::move(prefix), std::move(promise)) {
1276auto R = parse_block_id(opts, true);
1277if (R.is_ok()) {
1278block_id_ = R.move_as_ok();
1279if (!block_id_.is_valid()) {
1280block_id_.id.workchain = ton::masterchainId;
1281block_id_.id.shard = ton::shardIdAll;
1282block_id_.id.seqno = static_cast<td::uint32>(0xffffffff);
1283block_id_.root_hash.set_zero();
1284block_id_.file_hash.set_zero();
1285}
1286} else {
1287error_ = R.move_as_error();
1288return;
1289}
1290auto R2 = parse_account_addr(opts);
1291if (R2.is_ok()) {
1292addr_ = R2.move_as_ok();
1293} else {
1294error_ = R2.move_as_error();
1295return;
1296}
1297auto it = opts.find("method");
1298if (it == opts.end()) {
1299error_ = td::Status::Error("no method");
1300return;
1301} else {
1302method_name_ = it->second;
1303}
1304it = opts.find("params");
1305if (it != opts.end()) {
1306auto R3 = vm::parse_stack_entries(it->second);
1307if (R3.is_error()) {
1308error_ = R3.move_as_error();
1309return;
1310}
1311params_ = R3.move_as_ok();
1312}
1313}
1314
1315void HttpQueryRunMethod::start_up_query() {
1316auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
1317if (R.is_error()) {
1318td::actor::send_closure(SelfId, &HttpQueryRunMethod::abort_query, R.move_as_error_prefix("litequery failed: "));
1319} else {
1320td::actor::send_closure(SelfId, &HttpQueryRunMethod::got_result, R.move_as_ok());
1321}
1322});
1323
1324auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(addr_.workchain, addr_.addr);
1325td::int64 method_id = (td::crc16(td::Slice{method_name_}) & 0xffff) | 0x10000;
1326
1327// serialize params
1328vm::CellBuilder cb;
1329td::Ref<vm::Cell> cell;
1330if (!(vm::Stack{params_}.serialize(cb) && cb.finalize_to(cell))) {
1331return abort_query(td::Status::Error("cannot serialize stack with get-method parameters"));
1332}
1333auto params_serialized = vm::std_boc_serialize(std::move(cell));
1334if (params_serialized.is_error()) {
1335return abort_query(params_serialized.move_as_error_prefix("cannot serialize stack with get-method parameters : "));
1336}
1337
1338auto query = ton::serialize_tl_object(
1339ton::create_tl_object<ton::lite_api::liteServer_runSmcMethod>(
13400x17, ton::create_tl_lite_block_id(block_id_), std::move(a), method_id, params_serialized.move_as_ok()),
1341true);
1342td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
1343std::move(query), std::move(P));
1344}
1345
1346void HttpQueryRunMethod::got_result(td::BufferSlice data) {
1347auto F = ton::fetch_tl_object<ton::lite_api::liteServer_runMethodResult>(std::move(data), true);
1348if (F.is_error()) {
1349return abort_query(F.move_as_error());
1350}
1351auto f = F.move_as_ok();
1352auto page = [&]() -> std::string {
1353HttpAnswer A{"account", prefix_};
1354A.set_account_id(addr_);
1355A.set_block_id(ton::create_block_id(f->id_));
1356if (f->exit_code_ != 0) {
1357A.abort(PSTRING() << "VM terminated with error code " << f->exit_code_);
1358return A.finish();
1359}
1360
1361std::ostringstream os;
1362os << "result: ";
1363if (f->result_.empty()) {
1364os << "<none>";
1365} else {
1366auto r_cell = vm::std_boc_deserialize(f->result_);
1367if (r_cell.is_error()) {
1368A.abort(PSTRING() << "cannot deserialize VM result boc: " << r_cell.move_as_error());
1369return A.finish();
1370}
1371auto cs = vm::load_cell_slice(r_cell.move_as_ok());
1372td::Ref<vm::Stack> stack;
1373if (!(vm::Stack::deserialize_to(cs, stack, 0) && cs.empty_ext())) {
1374A.abort("VM result boc cannot be deserialized");
1375return A.finish();
1376}
1377stack->dump(os, 3);
1378}
1379A << HttpAnswer::CodeBlock{os.str()};
1380return A.finish();
1381}();
1382auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
1383MHD_add_response_header(R, "Content-Type", "text/html");
1384promise_.set_value(std::move(R));
1385stop();
1386}
1387HttpQueryStatus::HttpQueryStatus(std::string prefix, td::Promise<MHD_Response *> promise)
1388: HttpQueryCommon(std::move(prefix), std::move(promise)) {
1389}
1390
1391HttpQueryStatus::HttpQueryStatus(std::map<std::string, std::string> opts, std::string prefix,
1392td::Promise<MHD_Response *> promise)
1393: HttpQueryCommon(std::move(prefix), std::move(promise)) {
1394}
1395
1396void HttpQueryStatus::start_up() {
1397if (error_.is_error()) {
1398abort_query(std::move(error_));
1399return;
1400}
1401
1402auto P =
1403td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<CoreActorInterface::RemoteNodeStatusList> R) {
1404if (R.is_error()) {
1405td::actor::send_closure(SelfId, &HttpQueryStatus::abort_query, R.move_as_error());
1406} else {
1407td::actor::send_closure(SelfId, &HttpQueryStatus::got_results, R.move_as_ok());
1408}
1409});
1410td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::get_results, 60, std::move(P));
1411}
1412
1413void HttpQueryStatus::got_results(CoreActorInterface::RemoteNodeStatusList results) {
1414results_ = std::move(results);
1415
1416finish_query();
1417}
1418
1419void HttpQueryStatus::finish_query() {
1420if (promise_) {
1421auto page = [&]() -> std::string {
1422std::map<td::uint32, std::set<td::uint32>> m;
1423
1424HttpAnswer A{"status", prefix_};
1425A << "<div class=\"table-responsive my-3\">\n"
1426<< "<table class=\"table-sm\">\n"
1427<< "<tr><td>ip</td>";
1428for (auto &x : results_.results) {
1429A << "<td>" << static_cast<td::int32>(x->ts_.at_unix()) << "</td>";
1430}
1431A << "</tr>\n";
1432for (td::uint32 i = 0; i < results_.ips.size(); i++) {
1433A << "<tr>";
1434if (results_.ips[i].is_valid()) {
1435A << "<td>" << results_.ips[i] << "</td>";
1436} else {
1437A << "<td>hidden</td>";
1438}
1439td::uint32 j = 0;
1440for (auto &X : results_.results) {
1441if (!X->values_[i].is_valid()) {
1442A << "<td class=\"table-danger\">FAIL</td>";
1443} else {
1444if (m[j].count(X->values_[i].id.seqno) == 0) {
1445m[j].insert(X->values_[i].id.seqno);
1446A << "<td><a href=\"" << HttpAnswer::BlockLink{X->values_[i]} << "\">" << X->values_[i].id.seqno
1447<< "</a></td>";
1448} else {
1449A << "<td>" << X->values_[i].id.seqno << "</td>";
1450}
1451}
1452j++;
1453}
1454A << "</tr>\n";
1455}
1456A << "</table></div>";
1457return A.finish();
1458}();
1459auto R = MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY);
1460MHD_add_response_header(R, "Content-Type", "text/html");
1461promise_.set_value(std::move(R));
1462}
1463stop();
1464}
1465