mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	slightly changed block format
- small change in block format - added config in blockchain explorer - bugfixes
This commit is contained in:
		
							parent
							
								
									7f3a22a217
								
							
						
					
					
						commit
						090e0c16eb
					
				
					 82 changed files with 1852 additions and 391 deletions
				
			
		| 
						 | 
				
			
			@ -441,7 +441,7 @@ if (USE_LIBRAPTORQ)
 | 
			
		|||
endif()
 | 
			
		||||
 | 
			
		||||
add_executable(test-hello-world test/test-hello-world.cpp )
 | 
			
		||||
target_link_libraries(test-hello-world tl_api crypto ton_crypto)
 | 
			
		||||
target_link_libraries(test-hello-world tl_api ton_crypto)
 | 
			
		||||
 | 
			
		||||
add_executable(test-adnl test/test-adnl.cpp)
 | 
			
		||||
target_link_libraries(test-adnl adnl adnltest dht tl_api)
 | 
			
		||||
| 
						 | 
				
			
			@ -510,6 +510,12 @@ add_test(test-tonlib-offline test-tonlib-offline)
 | 
			
		|||
 | 
			
		||||
#BEGIN internal
 | 
			
		||||
if (NOT TON_ONLY_TONLIB)
 | 
			
		||||
add_test(test-adnl test-adnl)
 | 
			
		||||
add_test(test-dht test-dht)
 | 
			
		||||
add_test(test-rldp test-rldp)
 | 
			
		||||
add_test(test-validator-session-state test-validator-session-state)
 | 
			
		||||
add_test(test-catchain test-catchain)
 | 
			
		||||
 | 
			
		||||
add_test(test-fec test-fec)
 | 
			
		||||
add_test(test-tddb test-tddb ${TEST_OPTIONS})
 | 
			
		||||
add_test(test-db test-db ${TEST_OPTIONS})
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -379,6 +379,23 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) {
 | 
			
		|||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  *this << "<form class=\"container\" action=\"" << prefix_ << "runmethod\" method=\"get\">"
 | 
			
		||||
        << "<div class=\"row\">"
 | 
			
		||||
        << "<p>Run get method<p>"
 | 
			
		||||
        << "<div class=\"form-group col-lg-3 col-md-4\">"
 | 
			
		||||
        << "<input type=\"text\" class=\"form-control mr-2\" name=\"method\" placeholder=\"method\">"
 | 
			
		||||
        << "</div>\n"
 | 
			
		||||
        << "<div class=\"form-group col-lg-4 col-md-6\">"
 | 
			
		||||
        << "<input type=\"text\" class=\"form-control mr-2\" name=\"params\" placeholder=\"paramerers\"></div>"
 | 
			
		||||
        << "<input type=\"hidden\" name=\"account\" value=\"" << acc_c.addr.rserialize(true) << "\">"
 | 
			
		||||
        << "<input type=\"hidden\" name=\"workchain\" value=\"" << block_id.id.workchain << "\">"
 | 
			
		||||
        << "<input type=\"hidden\" name=\"shard\" value=\"" << ton::shard_to_str(block_id.id.shard) << "\">"
 | 
			
		||||
        << "<input type=\"hidden\" name=\"seqno\" value=\"" << block_id.id.seqno << "\">"
 | 
			
		||||
        << "<input type=\"hidden\" name=\"roothash\" value=\"" << block_id.root_hash.to_hex() << "\">"
 | 
			
		||||
        << "<input type=\"hidden\" name=\"filehash\" value=\"" << block_id.file_hash.to_hex() << "\">"
 | 
			
		||||
        << "<div><button type=\"submit\" class=\"btn btn-primary mr-2\">Run!</button></div>"
 | 
			
		||||
        << "</div></form>\n";
 | 
			
		||||
 | 
			
		||||
  *this << "<div class=\"table-responsive my-3\">\n"
 | 
			
		||||
        << "<table class=\"table-sm table-striped\">\n";
 | 
			
		||||
  *this << "<tr><th>block</th><td><a href=\"" << BlockLink{acc_c.block_id} << "\">" << block_id.id.to_str()
 | 
			
		||||
| 
						 | 
				
			
			@ -480,10 +497,13 @@ HttpAnswer& HttpAnswer::operator<<(BlockHeaderCell head_c) {
 | 
			
		|||
    return *this;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return *this << "<p><a class=\"btn btn-primary mr-2\" href=\"" << BlockDownloadLink{block_id} << "\" download=\""
 | 
			
		||||
  *this << "<p><a class=\"btn btn-primary mr-2\" href=\"" << BlockDownloadLink{block_id} << "\" download=\""
 | 
			
		||||
        << block_id.file_hash << ".boc\">download block</a>"
 | 
			
		||||
               << "<a class=\"btn btn-primary\" href=\"" << BlockViewLink{block_id} << "\">view block</a>\n"
 | 
			
		||||
               << "</p></div>";
 | 
			
		||||
        << "<a class=\"btn btn-primary\" href=\"" << BlockViewLink{block_id} << "\">view block</a>\n";
 | 
			
		||||
  if (block_id.is_masterchain()) {
 | 
			
		||||
    *this << "<a class=\"btn btn-primary\" href=\"" << ConfigViewLink{block_id} << "\">view config</a>\n";
 | 
			
		||||
  }
 | 
			
		||||
  return *this << "</p></div>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpAnswer& HttpAnswer::operator<<(BlockShardsCell shards_c) {
 | 
			
		||||
| 
						 | 
				
			
			@ -571,6 +591,12 @@ HttpAnswer& HttpAnswer::operator<<(BlockViewLink block) {
 | 
			
		|||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpAnswer& HttpAnswer::operator<<(ConfigViewLink block) {
 | 
			
		||||
  *this << prefix_ << "config?";
 | 
			
		||||
  block_id_link(block.block_id);
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpAnswer& HttpAnswer::operator<<(BlockDownloadLink block) {
 | 
			
		||||
  *this << prefix_ << "download?";
 | 
			
		||||
  block_id_link(block.block_id);
 | 
			
		||||
| 
						 | 
				
			
			@ -606,10 +632,26 @@ HttpAnswer& HttpAnswer::operator<<(TransactionList trans) {
 | 
			
		|||
  return *this << "</tbody></table></div>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpAnswer& HttpAnswer::operator<<(ConfigParam conf) {
 | 
			
		||||
  std::ostringstream os;
 | 
			
		||||
  *this << "<div id=\"configparam" << conf.idx << "\"><h3>param " << conf.idx << "</h3>";
 | 
			
		||||
  if (conf.idx >= 0) {
 | 
			
		||||
    *this << RawData<block::gen::ConfigParam>{conf.root, conf.idx};
 | 
			
		||||
  } else {
 | 
			
		||||
    *this << RawData<void>{conf.root};
 | 
			
		||||
  }
 | 
			
		||||
  *this << "</div>\n";
 | 
			
		||||
  return *this;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpAnswer& HttpAnswer::operator<<(Error error) {
 | 
			
		||||
  return *this << "<div class=\"alert alert-danger\">" << error.error.to_string() << "</div>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpAnswer& HttpAnswer::operator<<(Notification n) {
 | 
			
		||||
  return *this << "<div class=\"alert alert-success\">" << n.text << "</div>";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpAnswer::block_id_link(ton::BlockIdExt block_id) {
 | 
			
		||||
  *this << "workchain=" << block_id.id.workchain << "&shard=" << ton::shard_to_str(block_id.id.shard)
 | 
			
		||||
        << "&seqno=" << block_id.id.seqno << "&roothash=" << block_id.root_hash << "&filehash=" << block_id.file_hash;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -84,6 +84,9 @@ class HttpAnswer {
 | 
			
		|||
  struct BlockViewLink {
 | 
			
		||||
    ton::BlockIdExt block_id;
 | 
			
		||||
  };
 | 
			
		||||
  struct ConfigViewLink {
 | 
			
		||||
    ton::BlockIdExt block_id;
 | 
			
		||||
  };
 | 
			
		||||
  struct BlockDownloadLink {
 | 
			
		||||
    ton::BlockIdExt block_id;
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			@ -116,9 +119,16 @@ class HttpAnswer {
 | 
			
		|||
  struct CodeBlock {
 | 
			
		||||
    std::string data;
 | 
			
		||||
  };
 | 
			
		||||
  struct ConfigParam {
 | 
			
		||||
    td::int32 idx;
 | 
			
		||||
    td::Ref<vm::Cell> root;
 | 
			
		||||
  };
 | 
			
		||||
  struct Error {
 | 
			
		||||
    td::Status error;
 | 
			
		||||
  };
 | 
			
		||||
  struct Notification {
 | 
			
		||||
    std::string text;
 | 
			
		||||
  };
 | 
			
		||||
  template <class T>
 | 
			
		||||
  struct RawData {
 | 
			
		||||
    td::Ref<vm::Cell> root;
 | 
			
		||||
| 
						 | 
				
			
			@ -189,14 +199,17 @@ class HttpAnswer {
 | 
			
		|||
  HttpAnswer &operator<<(TransactionLinkShort trans);
 | 
			
		||||
  HttpAnswer &operator<<(BlockLink block);
 | 
			
		||||
  HttpAnswer &operator<<(BlockViewLink block);
 | 
			
		||||
  HttpAnswer &operator<<(ConfigViewLink block);
 | 
			
		||||
  HttpAnswer &operator<<(BlockDownloadLink block);
 | 
			
		||||
 | 
			
		||||
  HttpAnswer &operator<<(Error error);
 | 
			
		||||
  HttpAnswer &operator<<(Notification notification);
 | 
			
		||||
 | 
			
		||||
  HttpAnswer &operator<<(TransactionList trans);
 | 
			
		||||
  HttpAnswer &operator<<(CodeBlock block) {
 | 
			
		||||
    return *this << "<pre><code>" << block.data << "</code></pre>";
 | 
			
		||||
  }
 | 
			
		||||
  HttpAnswer &operator<<(ConfigParam conf);
 | 
			
		||||
 | 
			
		||||
  template <class T>
 | 
			
		||||
  HttpAnswer &operator<<(RawData<T> data) {
 | 
			
		||||
| 
						 | 
				
			
			@ -220,3 +233,16 @@ class HttpAnswer {
 | 
			
		|||
  std::unique_ptr<td::StringBuilder> sb_;
 | 
			
		||||
  td::BufferSlice buf_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <>
 | 
			
		||||
struct HttpAnswer::RawData<void> {
 | 
			
		||||
  td::Ref<vm::Cell> root;
 | 
			
		||||
  RawData(td::Ref<vm::Cell> root) : root(std::move(root)) {
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
template <>
 | 
			
		||||
inline HttpAnswer &HttpAnswer::operator<<(RawData<void> data) {
 | 
			
		||||
  std::ostringstream outp;
 | 
			
		||||
  vm::load_cell_slice(data.root).print_rec(outp);
 | 
			
		||||
  return *this << CodeBlock{outp.str()};
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,6 +28,8 @@
 | 
			
		|||
*/
 | 
			
		||||
#include "blockchain-explorer-query.hpp"
 | 
			
		||||
#include "blockchain-explorer-http.hpp"
 | 
			
		||||
#include "block/mc-config.h"
 | 
			
		||||
#include "crypto/block/check-proof.h"
 | 
			
		||||
 | 
			
		||||
#include "auto/tl/lite_api.h"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -39,6 +41,40 @@
 | 
			
		|||
 | 
			
		||||
#include "common/errorcode.h"
 | 
			
		||||
#include "block/block-auto.h"
 | 
			
		||||
#include "crypto/vm/utils.h"
 | 
			
		||||
#include "td/utils/crypto.h"
 | 
			
		||||
 | 
			
		||||
#include "vm/boc.h"
 | 
			
		||||
#include "vm/cellops.h"
 | 
			
		||||
#include "vm/cells/MerkleProof.h"
 | 
			
		||||
#include "vm/continuation.h"
 | 
			
		||||
#include "vm/cp0.h"
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
td::Ref<vm::Tuple> prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref<vm::CellSlice> my_addr,
 | 
			
		||||
                                 const block::CurrencyCollection &balance) {
 | 
			
		||||
  td::BitArray<256> rand_seed;
 | 
			
		||||
  td::RefInt256 rand_seed_int{true};
 | 
			
		||||
  td::Random::secure_bytes(rand_seed.as_slice());
 | 
			
		||||
  if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea),  // [ magic:0x076ef1ea
 | 
			
		||||
                                  td::make_refint(0),           //   actions:Integer
 | 
			
		||||
                                  td::make_refint(0),           //   msgs_sent:Integer
 | 
			
		||||
                                  td::make_refint(now),         //   unixtime:Integer
 | 
			
		||||
                                  td::make_refint(lt),          //   block_lt:Integer
 | 
			
		||||
                                  td::make_refint(lt),          //   trans_lt:Integer
 | 
			
		||||
                                  std::move(rand_seed_int),     //   rand_seed:Integer
 | 
			
		||||
                                  balance.as_vm_tuple(),        //   balance_remaining:[Integer (Maybe Cell)]
 | 
			
		||||
                                  my_addr,                      //  myself:MsgAddressInt
 | 
			
		||||
                                  vm::StackEntry());            //  global_config:(Maybe Cell) ] = SmartContractInfo;
 | 
			
		||||
  LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
 | 
			
		||||
  return vm::make_tuple_ref(std::move(tuple));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
td::Result<ton::BlockIdExt> parse_block_id(std::map<std::string, std::string> &opts, bool allow_empty) {
 | 
			
		||||
  if (allow_empty) {
 | 
			
		||||
| 
						 | 
				
			
			@ -990,6 +1026,419 @@ void HttpQueryViewLastBlock::finish_query() {
 | 
			
		|||
  stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQueryConfig::HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector<td::int32> params,
 | 
			
		||||
                                 td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(prefix, std::move(promise)), block_id_(block_id), params_(std::move(params)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQueryConfig::HttpQueryConfig(std::map<std::string, std::string> opts, std::string prefix,
 | 
			
		||||
                                 td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(prefix, std::move(promise)) {
 | 
			
		||||
  auto R = parse_block_id(opts, true);
 | 
			
		||||
  if (R.is_error()) {
 | 
			
		||||
    error_ = R.move_as_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  block_id_ = R.move_as_ok();
 | 
			
		||||
 | 
			
		||||
  auto it = opts.find("param");
 | 
			
		||||
  if (it != opts.end()) {
 | 
			
		||||
    auto R2 = td::to_integer_safe<int>(it->second);
 | 
			
		||||
    if (R2.is_error()) {
 | 
			
		||||
      error_ = R2.move_as_error();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    params_.push_back(R2.move_as_ok());
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryConfig::start_up() {
 | 
			
		||||
  if (error_.is_error()) {
 | 
			
		||||
    abort_query(std::move(error_));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (block_id_.is_valid()) {
 | 
			
		||||
    send_main_query();
 | 
			
		||||
  } else {
 | 
			
		||||
    auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
 | 
			
		||||
      if (R.is_error()) {
 | 
			
		||||
        td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error());
 | 
			
		||||
      } else {
 | 
			
		||||
        td::actor::send_closure(SelfId, &HttpQueryConfig::got_block, R.move_as_ok());
 | 
			
		||||
      }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getMasterchainInfo>(), true);
 | 
			
		||||
    td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
 | 
			
		||||
                            std::move(query), std::move(P));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryConfig::got_block(td::BufferSlice data) {
 | 
			
		||||
  auto F = ton::fetch_tl_object<ton::lite_api::liteServer_masterchainInfo>(std::move(data), true);
 | 
			
		||||
  if (F.is_error()) {
 | 
			
		||||
    abort_query(F.move_as_error());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto f = F.move_as_ok();
 | 
			
		||||
  block_id_ = ton::create_block_id(f->last_);
 | 
			
		||||
 | 
			
		||||
  send_main_query();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryConfig::send_main_query() {
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
 | 
			
		||||
    if (R.is_error()) {
 | 
			
		||||
      td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      td::actor::send_closure(SelfId, &HttpQueryConfig::got_result, R.move_as_ok());
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  auto query =
 | 
			
		||||
      params_.size() > 0
 | 
			
		||||
          ? ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigParams>(
 | 
			
		||||
                                         0, ton::create_tl_lite_block_id(block_id_), std::vector<int>(params_)),
 | 
			
		||||
                                     true)
 | 
			
		||||
          : ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getConfigAll>(
 | 
			
		||||
                                         0, ton::create_tl_lite_block_id(block_id_)),
 | 
			
		||||
                                     true);
 | 
			
		||||
  td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
 | 
			
		||||
                          std::move(query), std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryConfig::got_result(td::BufferSlice data) {
 | 
			
		||||
  auto F = ton::fetch_tl_object<ton::lite_api::liteServer_configInfo>(std::move(data), true);
 | 
			
		||||
  if (F.is_error()) {
 | 
			
		||||
    abort_query(F.move_as_error());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto f = F.move_as_ok();
 | 
			
		||||
 | 
			
		||||
  state_proof_ = std::move(f->state_proof_);
 | 
			
		||||
  config_proof_ = std::move(f->config_proof_);
 | 
			
		||||
 | 
			
		||||
  finish_query();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryConfig::finish_query() {
 | 
			
		||||
  if (promise_) {
 | 
			
		||||
    auto page = [&]() -> std::string {
 | 
			
		||||
      HttpAnswer A{"config", prefix_};
 | 
			
		||||
      A.set_block_id(block_id_);
 | 
			
		||||
      auto R = block::check_extract_state_proof(block_id_, state_proof_.as_slice(), config_proof_.as_slice());
 | 
			
		||||
      if (R.is_error()) {
 | 
			
		||||
        A.abort(PSTRING() << "masterchain state proof for " << block_id_.to_str()
 | 
			
		||||
                          << " is invalid : " << R.move_as_error());
 | 
			
		||||
        return A.finish();
 | 
			
		||||
      }
 | 
			
		||||
      try {
 | 
			
		||||
        auto res = block::Config::extract_from_state(R.move_as_ok(), 0);
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          A.abort(PSTRING() << "cannot unpack configuration: " << res.move_as_error());
 | 
			
		||||
          return A.finish();
 | 
			
		||||
        }
 | 
			
		||||
        auto config = res.move_as_ok();
 | 
			
		||||
        if (params_.size() > 0) {
 | 
			
		||||
          A << "<p>params: ";
 | 
			
		||||
          for (int i : params_) {
 | 
			
		||||
            auto value = config->get_config_param(i);
 | 
			
		||||
            if (value.not_null()) {
 | 
			
		||||
              A << "<a href=\"#configparam" << i << "\">" << i << "</a> ";
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          A << "</p>";
 | 
			
		||||
          for (int i : params_) {
 | 
			
		||||
            auto value = config->get_config_param(i);
 | 
			
		||||
            if (value.not_null()) {
 | 
			
		||||
              A << HttpAnswer::ConfigParam{i, value};
 | 
			
		||||
            } else {
 | 
			
		||||
              A << HttpAnswer::Error{td::Status::Error(404, PSTRING() << "empty param " << i)};
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          A << "<p>params: ";
 | 
			
		||||
          config->foreach_config_param([&](int i, td::Ref<vm::Cell> value) {
 | 
			
		||||
            if (value.not_null()) {
 | 
			
		||||
              A << "<a href=\"#configparam" << i << "\">" << i << "</a> ";
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
          });
 | 
			
		||||
          A << "</p>";
 | 
			
		||||
          config->foreach_config_param([&](int i, td::Ref<vm::Cell> value) {
 | 
			
		||||
            if (value.not_null()) {
 | 
			
		||||
              A << HttpAnswer::ConfigParam{i, value};
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
          });
 | 
			
		||||
        }
 | 
			
		||||
      } catch (vm::VmError &err) {
 | 
			
		||||
        A.abort(PSTRING() << "error while traversing configuration: " << err.get_msg());
 | 
			
		||||
      } catch (vm::VmVirtError &err) {
 | 
			
		||||
        A.abort(PSTRING() << "virtualization error while traversing configuration: " << err.get_msg());
 | 
			
		||||
      }
 | 
			
		||||
      return A.finish();
 | 
			
		||||
    }();
 | 
			
		||||
    promise_.set_value(
 | 
			
		||||
        MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
 | 
			
		||||
  }
 | 
			
		||||
  stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQuerySendForm::HttpQuerySendForm(std::string prefix, td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(prefix, std::move(promise)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQuerySendForm::HttpQuerySendForm(std::map<std::string, std::string> opts, std::string prefix,
 | 
			
		||||
                                     td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(prefix, std::move(promise)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQuerySendForm::start_up() {
 | 
			
		||||
  finish_query();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQuerySendForm::finish_query() {
 | 
			
		||||
  if (promise_) {
 | 
			
		||||
    auto page = [&]() -> std::string {
 | 
			
		||||
      HttpAnswer A{"send", prefix_};
 | 
			
		||||
      A << "<div class=\"row\"><form action=\"" << prefix_
 | 
			
		||||
        << "send\" method=\"post\" enctype=\"multipart/form-data\"><div class=\"form-group-row\">"
 | 
			
		||||
        << "<label for=\"filedata\">bag of cells</label>"
 | 
			
		||||
        << "<input type=\"file\" class=\"form-control-file\" id=\"filedata\" name=\"filedata\">"
 | 
			
		||||
        << "<button type=\"submit\" class=\"btn btn-primary\">send</button>"
 | 
			
		||||
        << "</div></form></div>";
 | 
			
		||||
      return A.finish();
 | 
			
		||||
    }();
 | 
			
		||||
    promise_.set_value(
 | 
			
		||||
        MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
 | 
			
		||||
  }
 | 
			
		||||
  stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQuerySend::HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(prefix, std::move(promise)), data_(std::move(data)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQuerySend::HttpQuerySend(std::map<std::string, std::string> opts, std::string prefix,
 | 
			
		||||
                             td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(prefix, std::move(promise)) {
 | 
			
		||||
  auto it = opts.find("filedata");
 | 
			
		||||
  if (it != opts.end()) {
 | 
			
		||||
    data_ = td::BufferSlice{it->second};
 | 
			
		||||
  } else {
 | 
			
		||||
    error_ = td::Status::Error("no file data");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQuerySend::start_up() {
 | 
			
		||||
  if (error_.is_error()) {
 | 
			
		||||
    abort_query(std::move(error_));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
 | 
			
		||||
    if (R.is_error()) {
 | 
			
		||||
      td::actor::send_closure(SelfId, &HttpQuerySend::abort_query, R.move_as_error());
 | 
			
		||||
    } else {
 | 
			
		||||
      td::actor::send_closure(SelfId, &HttpQuerySend::got_result, R.move_as_ok());
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  auto query =
 | 
			
		||||
      ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_sendMessage>(std::move(data_)), true);
 | 
			
		||||
  td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
 | 
			
		||||
                          std::move(query), std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQuerySend::got_result(td::BufferSlice data) {
 | 
			
		||||
  auto F = ton::fetch_tl_object<ton::lite_api::liteServer_sendMsgStatus>(std::move(data), true);
 | 
			
		||||
  if (F.is_error()) {
 | 
			
		||||
    abort_query(F.move_as_error());
 | 
			
		||||
  } else {
 | 
			
		||||
    status_ = F.move_as_ok()->status_;
 | 
			
		||||
  }
 | 
			
		||||
  finish_query();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQuerySend::finish_query() {
 | 
			
		||||
  if (promise_) {
 | 
			
		||||
    auto page = [&]() -> std::string {
 | 
			
		||||
      HttpAnswer A{"send", prefix_};
 | 
			
		||||
      if (status_ >= 0) {
 | 
			
		||||
        A << HttpAnswer::Notification{"success"};
 | 
			
		||||
      } else {
 | 
			
		||||
        A << HttpAnswer::Error{td::Status::Error(status_, "failed")};
 | 
			
		||||
      }
 | 
			
		||||
      return A.finish();
 | 
			
		||||
    }();
 | 
			
		||||
    promise_.set_value(
 | 
			
		||||
        MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
 | 
			
		||||
  }
 | 
			
		||||
  stop();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQueryRunMethod::HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name,
 | 
			
		||||
                                       std::vector<vm::StackEntry> params, std::string prefix,
 | 
			
		||||
                                       td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(std::move(prefix), std::move(promise))
 | 
			
		||||
    , block_id_(block_id)
 | 
			
		||||
    , addr_(addr)
 | 
			
		||||
    , method_name_(std::move(method_name))
 | 
			
		||||
    , params_(std::move(params)) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HttpQueryRunMethod::HttpQueryRunMethod(std::map<std::string, std::string> opts, std::string prefix,
 | 
			
		||||
                                       td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(std::move(prefix), std::move(promise)) {
 | 
			
		||||
  auto R = parse_block_id(opts, true);
 | 
			
		||||
  if (R.is_ok()) {
 | 
			
		||||
    block_id_ = R.move_as_ok();
 | 
			
		||||
    if (!block_id_.is_valid()) {
 | 
			
		||||
      block_id_.id.workchain = ton::masterchainId;
 | 
			
		||||
      block_id_.id.shard = ton::shardIdAll;
 | 
			
		||||
      block_id_.id.seqno = static_cast<td::uint32>(0xffffffff);
 | 
			
		||||
      block_id_.root_hash.set_zero();
 | 
			
		||||
      block_id_.file_hash.set_zero();
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    error_ = R.move_as_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto R2 = parse_account_addr(opts);
 | 
			
		||||
  if (R2.is_ok()) {
 | 
			
		||||
    addr_ = R2.move_as_ok();
 | 
			
		||||
  } else {
 | 
			
		||||
    error_ = R2.move_as_error();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto it = opts.find("method");
 | 
			
		||||
  if (it == opts.end()) {
 | 
			
		||||
    error_ = td::Status::Error("no method");
 | 
			
		||||
    return;
 | 
			
		||||
  } else {
 | 
			
		||||
    method_name_ = it->second;
 | 
			
		||||
  }
 | 
			
		||||
  it = opts.find("params");
 | 
			
		||||
  if (it != opts.end()) {
 | 
			
		||||
    auto R3 = vm::parse_stack_entries(it->second);
 | 
			
		||||
    if (R3.is_error()) {
 | 
			
		||||
      error_ = R3.move_as_error();
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    params_ = R3.move_as_ok();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryRunMethod::start_up_query() {
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::BufferSlice> R) {
 | 
			
		||||
    if (R.is_error()) {
 | 
			
		||||
      td::actor::send_closure(SelfId, &HttpQueryRunMethod::abort_query, R.move_as_error_prefix("litequery failed: "));
 | 
			
		||||
    } else {
 | 
			
		||||
      td::actor::send_closure(SelfId, &HttpQueryRunMethod::got_account, R.move_as_ok());
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
  auto a = ton::create_tl_object<ton::lite_api::liteServer_accountId>(addr_.workchain, addr_.addr);
 | 
			
		||||
  auto query = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getAccountState>(
 | 
			
		||||
                                            ton::create_tl_lite_block_id(block_id_), std::move(a)),
 | 
			
		||||
                                        true);
 | 
			
		||||
  td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query,
 | 
			
		||||
                          std::move(query), std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryRunMethod::got_account(td::BufferSlice data) {
 | 
			
		||||
  auto F = ton::fetch_tl_object<ton::lite_api::liteServer_accountState>(std::move(data), true);
 | 
			
		||||
  if (F.is_error()) {
 | 
			
		||||
    abort_query(F.move_as_error());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto f = F.move_as_ok();
 | 
			
		||||
  data_ = std::move(f->state_);
 | 
			
		||||
  proof_ = std::move(f->proof_);
 | 
			
		||||
  shard_proof_ = std::move(f->shard_proof_);
 | 
			
		||||
  block_id_ = ton::create_block_id(f->id_);
 | 
			
		||||
  res_block_id_ = ton::create_block_id(f->shardblk_);
 | 
			
		||||
 | 
			
		||||
  finish_query();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HttpQueryRunMethod::finish_query() {
 | 
			
		||||
  if (promise_) {
 | 
			
		||||
    auto page = [&]() -> std::string {
 | 
			
		||||
      HttpAnswer A{"account", prefix_};
 | 
			
		||||
      A.set_account_id(addr_);
 | 
			
		||||
      A.set_block_id(res_block_id_);
 | 
			
		||||
 | 
			
		||||
      block::AccountState account_state;
 | 
			
		||||
      account_state.blk = block_id_;
 | 
			
		||||
      account_state.shard_blk = res_block_id_;
 | 
			
		||||
      account_state.shard_proof = std::move(shard_proof_);
 | 
			
		||||
      account_state.proof = std::move(proof_);
 | 
			
		||||
      account_state.state = std::move(data_);
 | 
			
		||||
      auto r_info = account_state.validate(block_id_, addr_);
 | 
			
		||||
      if (r_info.is_error()) {
 | 
			
		||||
        A.abort(r_info.move_as_error());
 | 
			
		||||
        return A.finish();
 | 
			
		||||
      }
 | 
			
		||||
      auto info = r_info.move_as_ok();
 | 
			
		||||
      if (info.root.is_null()) {
 | 
			
		||||
        A.abort(PSTRING() << "account state of " << addr_ << " is empty (cannot run method `" << method_name_ << "`)");
 | 
			
		||||
        return A.finish();
 | 
			
		||||
      }
 | 
			
		||||
      block::gen::Account::Record_account acc;
 | 
			
		||||
      block::gen::AccountStorage::Record store;
 | 
			
		||||
      block::CurrencyCollection balance;
 | 
			
		||||
      if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) &&
 | 
			
		||||
            balance.validate_unpack(store.balance))) {
 | 
			
		||||
        A.abort("error unpacking account state");
 | 
			
		||||
        return A.finish();
 | 
			
		||||
      }
 | 
			
		||||
      int tag = block::gen::t_AccountState.get_tag(*store.state);
 | 
			
		||||
      switch (tag) {
 | 
			
		||||
        case block::gen::AccountState::account_uninit:
 | 
			
		||||
          A.abort(PSTRING() << "account " << addr_ << " not initialized yet (cannot run any methods)");
 | 
			
		||||
          return A.finish();
 | 
			
		||||
        case block::gen::AccountState::account_frozen:
 | 
			
		||||
          A.abort(PSTRING() << "account " << addr_ << " frozen (cannot run any methods)");
 | 
			
		||||
          return A.finish();
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      CHECK(store.state.write().fetch_ulong(1) == 1);  // account_init$1 _:StateInit = AccountState;
 | 
			
		||||
      block::gen::StateInit::Record state_init;
 | 
			
		||||
      CHECK(tlb::csr_unpack(store.state, state_init));
 | 
			
		||||
      auto code = state_init.code->prefetch_ref();
 | 
			
		||||
      auto data = state_init.data->prefetch_ref();
 | 
			
		||||
      auto stack = td::make_ref<vm::Stack>(std::move(params_));
 | 
			
		||||
      td::int64 method_id = (td::crc16(td::Slice{method_name_}) & 0xffff) | 0x10000;
 | 
			
		||||
      stack.write().push_smallint(method_id);
 | 
			
		||||
      long long gas_limit = vm::GasLimits::infty;
 | 
			
		||||
      // OstreamLogger ostream_logger(ctx.error_stream);
 | 
			
		||||
      // auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
 | 
			
		||||
      vm::GasLimits gas{gas_limit};
 | 
			
		||||
      LOG(DEBUG) << "creating VM";
 | 
			
		||||
      vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()};
 | 
			
		||||
      vm.set_c7(prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance));  // tuple with SmartContractInfo
 | 
			
		||||
      // vm.incr_stack_trace(1);    // enable stack dump after each step
 | 
			
		||||
      int exit_code = ~vm.run();
 | 
			
		||||
      if (exit_code != 0) {
 | 
			
		||||
        A.abort(PSTRING() << "VM terminated with error code " << exit_code);
 | 
			
		||||
        return A.finish();
 | 
			
		||||
      }
 | 
			
		||||
      stack = vm.get_stack_ref();
 | 
			
		||||
      {
 | 
			
		||||
        std::ostringstream os;
 | 
			
		||||
        os << "result: ";
 | 
			
		||||
        stack->dump(os, 3);
 | 
			
		||||
 | 
			
		||||
        A << HttpAnswer::CodeBlock{os.str()};
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      return A.finish();
 | 
			
		||||
    }();
 | 
			
		||||
    promise_.set_value(
 | 
			
		||||
        MHD_create_response_from_buffer(page.length(), const_cast<char *>(page.c_str()), MHD_RESPMEM_MUST_COPY));
 | 
			
		||||
  }
 | 
			
		||||
  stop();
 | 
			
		||||
}
 | 
			
		||||
HttpQueryStatus::HttpQueryStatus(std::string prefix, td::Promise<MHD_Response *> promise)
 | 
			
		||||
    : HttpQueryCommon(std::move(prefix), std::move(promise)) {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -258,6 +258,77 @@ class HttpQueryViewLastBlock : public HttpQueryCommon {
 | 
			
		|||
  ton::BlockIdExt res_block_id_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HttpQueryConfig : public HttpQueryCommon {
 | 
			
		||||
 public:
 | 
			
		||||
  HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector<td::int32> params,
 | 
			
		||||
                  td::Promise<MHD_Response *> promise);
 | 
			
		||||
  HttpQueryConfig(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
 | 
			
		||||
 | 
			
		||||
  void finish_query();
 | 
			
		||||
 | 
			
		||||
  void start_up() override;
 | 
			
		||||
  void got_block(td::BufferSlice result);
 | 
			
		||||
  void send_main_query();
 | 
			
		||||
  void got_result(td::BufferSlice result);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  ton::BlockIdExt block_id_;
 | 
			
		||||
  std::vector<td::int32> params_;
 | 
			
		||||
 | 
			
		||||
  td::BufferSlice state_proof_;
 | 
			
		||||
  td::BufferSlice config_proof_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HttpQuerySendForm : public HttpQueryCommon {
 | 
			
		||||
 public:
 | 
			
		||||
  HttpQuerySendForm(std::string prefix, td::Promise<MHD_Response *> promise);
 | 
			
		||||
  HttpQuerySendForm(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
 | 
			
		||||
 | 
			
		||||
  void start_up() override;
 | 
			
		||||
  void finish_query();
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HttpQuerySend : public HttpQueryCommon {
 | 
			
		||||
 public:
 | 
			
		||||
  HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise<MHD_Response *> promise);
 | 
			
		||||
  HttpQuerySend(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
 | 
			
		||||
 | 
			
		||||
  void finish_query();
 | 
			
		||||
 | 
			
		||||
  void start_up() override;
 | 
			
		||||
  void got_result(td::BufferSlice result);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  td::BufferSlice data_;
 | 
			
		||||
  td::int32 status_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HttpQueryRunMethod : public HttpQueryCommon {
 | 
			
		||||
 public:
 | 
			
		||||
  HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name,
 | 
			
		||||
                     std::vector<vm::StackEntry> params, std::string prefix, td::Promise<MHD_Response *> promise);
 | 
			
		||||
  HttpQueryRunMethod(std::map<std::string, std::string> opts, std::string prefix, td::Promise<MHD_Response *> promise);
 | 
			
		||||
 | 
			
		||||
  void finish_query();
 | 
			
		||||
 | 
			
		||||
  void start_up_query() override;
 | 
			
		||||
  void got_account(td::BufferSlice result);
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  ton::BlockIdExt block_id_;
 | 
			
		||||
  block::StdAddress addr_;
 | 
			
		||||
 | 
			
		||||
  std::string method_name_;
 | 
			
		||||
  std::vector<vm::StackEntry> params_;
 | 
			
		||||
 | 
			
		||||
  td::BufferSlice data_;
 | 
			
		||||
  td::BufferSlice proof_;
 | 
			
		||||
  td::BufferSlice shard_proof_;
 | 
			
		||||
  ton::BlockIdExt res_block_id_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class HttpQueryStatus : public HttpQueryCommon {
 | 
			
		||||
 public:
 | 
			
		||||
  HttpQueryStatus(std::string prefix, td::Promise<MHD_Response *> promise);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,6 +49,12 @@
 | 
			
		|||
#include "blockchain-explorer-http.hpp"
 | 
			
		||||
#include "blockchain-explorer-query.hpp"
 | 
			
		||||
 | 
			
		||||
#include "vm/boc.h"
 | 
			
		||||
#include "vm/cellops.h"
 | 
			
		||||
#include "vm/cells/MerkleProof.h"
 | 
			
		||||
#include "vm/continuation.h"
 | 
			
		||||
#include "vm/cp0.h"
 | 
			
		||||
 | 
			
		||||
#include "auto/tl/lite_api.h"
 | 
			
		||||
#include "ton/lite-tl.hpp"
 | 
			
		||||
#include "tl-utils/lite-utils.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -263,22 +269,80 @@ class CoreActor : public CoreActorInterface {
 | 
			
		|||
    return MHD_YES;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  struct HttpRequestExtra {
 | 
			
		||||
    HttpRequestExtra(MHD_Connection* connection, bool is_post) {
 | 
			
		||||
      if (is_post) {
 | 
			
		||||
        postprocessor = MHD_create_post_processor(connection, 1 << 14, iterate_post, static_cast<void*>(this));
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    ~HttpRequestExtra() {
 | 
			
		||||
      MHD_destroy_post_processor(postprocessor);
 | 
			
		||||
    }
 | 
			
		||||
    static int iterate_post(void* coninfo_cls, enum MHD_ValueKind kind, const char* key, const char* filename,
 | 
			
		||||
                            const char* content_type, const char* transfer_encoding, const char* data, uint64_t off,
 | 
			
		||||
                            size_t size) {
 | 
			
		||||
      auto ptr = static_cast<HttpRequestExtra*>(coninfo_cls);
 | 
			
		||||
      ptr->total_size += strlen(key) + size;
 | 
			
		||||
      if (ptr->total_size > MAX_POST_SIZE) {
 | 
			
		||||
        return MHD_NO;
 | 
			
		||||
      }
 | 
			
		||||
      std::string k = key;
 | 
			
		||||
      if (ptr->opts[k].size() < off + size) {
 | 
			
		||||
        ptr->opts[k].resize(off + size);
 | 
			
		||||
      }
 | 
			
		||||
      td::MutableSlice(ptr->opts[k]).remove_prefix(off).copy_from(td::Slice(data, size));
 | 
			
		||||
      return MHD_YES;
 | 
			
		||||
    }
 | 
			
		||||
    MHD_PostProcessor* postprocessor;
 | 
			
		||||
    std::map<std::string, std::string> opts;
 | 
			
		||||
    td::uint64 total_size = 0;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static void request_completed(void* cls, struct MHD_Connection* connection, void** ptr,
 | 
			
		||||
                                enum MHD_RequestTerminationCode toe) {
 | 
			
		||||
    auto e = static_cast<HttpRequestExtra*>(*ptr);
 | 
			
		||||
    if (e) {
 | 
			
		||||
      delete e;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static int process_http_request(void* cls, struct MHD_Connection* connection, const char* url, const char* method,
 | 
			
		||||
                                  const char* version, const char* upload_data, size_t* upload_data_size, void** ptr) {
 | 
			
		||||
    static int dummy;
 | 
			
		||||
    struct MHD_Response* response = nullptr;
 | 
			
		||||
    int ret;
 | 
			
		||||
 | 
			
		||||
    if (0 != std::strcmp(method, "GET"))
 | 
			
		||||
    bool is_post = false;
 | 
			
		||||
    if (std::strcmp(method, "GET") == 0) {
 | 
			
		||||
      is_post = false;
 | 
			
		||||
    } else if (std::strcmp(method, "POST") == 0) {
 | 
			
		||||
      is_post = true;
 | 
			
		||||
    } else {
 | 
			
		||||
      return MHD_NO; /* unexpected method */
 | 
			
		||||
    if (&dummy != *ptr) {
 | 
			
		||||
      /* The first time only the headers are valid,
 | 
			
		||||
         do not respond in the first round... */
 | 
			
		||||
      *ptr = &dummy;
 | 
			
		||||
    }
 | 
			
		||||
    std::map<std::string, std::string> opts;
 | 
			
		||||
    if (!is_post) {
 | 
			
		||||
      if (!*ptr) {
 | 
			
		||||
        *ptr = static_cast<void*>(new HttpRequestExtra{connection, false});
 | 
			
		||||
        return MHD_YES;
 | 
			
		||||
      }
 | 
			
		||||
      if (0 != *upload_data_size)
 | 
			
		||||
        return MHD_NO; /* upload data in a GET!? */
 | 
			
		||||
    } else {
 | 
			
		||||
      if (!*ptr) {
 | 
			
		||||
        *ptr = static_cast<void*>(new HttpRequestExtra{connection, true});
 | 
			
		||||
        return MHD_YES;
 | 
			
		||||
      }
 | 
			
		||||
      auto e = static_cast<HttpRequestExtra*>(*ptr);
 | 
			
		||||
      if (0 != *upload_data_size) {
 | 
			
		||||
        CHECK(e->postprocessor);
 | 
			
		||||
        MHD_post_process(e->postprocessor, upload_data, *upload_data_size);
 | 
			
		||||
        *upload_data_size = 0;
 | 
			
		||||
        return MHD_YES;
 | 
			
		||||
      }
 | 
			
		||||
      for (auto& o : e->opts) {
 | 
			
		||||
        opts[o.first] = std::move(o.second);
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::string url_s = url;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -295,7 +359,6 @@ class CoreActor : public CoreActorInterface {
 | 
			
		|||
      command = url_s.substr(pos + 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::map<std::string, std::string> opts;
 | 
			
		||||
    MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, get_arg_iterate, static_cast<void*>(&opts));
 | 
			
		||||
 | 
			
		||||
    if (command == "status") {
 | 
			
		||||
| 
						 | 
				
			
			@ -352,6 +415,26 @@ class CoreActor : public CoreActorInterface {
 | 
			
		|||
            .release();
 | 
			
		||||
      }};
 | 
			
		||||
      response = g.wait();
 | 
			
		||||
    } else if (command == "config") {
 | 
			
		||||
      HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
 | 
			
		||||
        td::actor::create_actor<HttpQueryConfig>("getconfig", opts, prefix, std::move(promise)).release();
 | 
			
		||||
      }};
 | 
			
		||||
      response = g.wait();
 | 
			
		||||
    } else if (command == "send") {
 | 
			
		||||
      HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
 | 
			
		||||
        td::actor::create_actor<HttpQuerySend>("send", opts, prefix, std::move(promise)).release();
 | 
			
		||||
      }};
 | 
			
		||||
      response = g.wait();
 | 
			
		||||
    } else if (command == "sendform") {
 | 
			
		||||
      HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
 | 
			
		||||
        td::actor::create_actor<HttpQuerySendForm>("sendform", opts, prefix, std::move(promise)).release();
 | 
			
		||||
      }};
 | 
			
		||||
      response = g.wait();
 | 
			
		||||
    } else if (command == "runmethod") {
 | 
			
		||||
      HttpQueryRunner g{[&](td::Promise<MHD_Response*> promise) {
 | 
			
		||||
        td::actor::create_actor<HttpQueryRunMethod>("runmethod", opts, prefix, std::move(promise)).release();
 | 
			
		||||
      }};
 | 
			
		||||
      response = g.wait();
 | 
			
		||||
    } else {
 | 
			
		||||
      ret = MHD_NO;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -394,7 +477,8 @@ class CoreActor : public CoreActorInterface {
 | 
			
		|||
                                                             remote_addr_, make_callback(0)));
 | 
			
		||||
    }
 | 
			
		||||
    daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast<td::uint16>(http_port_), nullptr, nullptr,
 | 
			
		||||
                               &process_http_request, nullptr, MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END);
 | 
			
		||||
                               &process_http_request, nullptr, MHD_OPTION_NOTIFY_COMPLETED, request_completed, nullptr,
 | 
			
		||||
                               MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END);
 | 
			
		||||
    CHECK(daemon_ != nullptr);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -567,6 +651,8 @@ int main(int argc, char* argv[]) {
 | 
			
		|||
  });
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
  vm::init_op_cp0();
 | 
			
		||||
 | 
			
		||||
  td::actor::Scheduler scheduler({2});
 | 
			
		||||
  scheduler_ptr = &scheduler;
 | 
			
		||||
  scheduler.run_in_context([&] { x = td::actor::create_actor<CoreActor>("testnode"); });
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,8 @@
 | 
			
		|||
#include "ton/ton-types.h"
 | 
			
		||||
#include "td/utils/port/IPAddress.h"
 | 
			
		||||
 | 
			
		||||
#define MAX_POST_SIZE (64 << 10)
 | 
			
		||||
 | 
			
		||||
class CoreActorInterface : public td::actor::Actor {
 | 
			
		||||
 public:
 | 
			
		||||
  struct RemoteNodeStatus {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ set(TON_CRYPTO_SOURCE
 | 
			
		|||
  vm/debugops.cpp
 | 
			
		||||
  vm/tonops.cpp
 | 
			
		||||
  vm/boc.cpp
 | 
			
		||||
  vm/utils.cpp
 | 
			
		||||
  tl/tlblib.cpp
 | 
			
		||||
 | 
			
		||||
  Ed25519.h
 | 
			
		||||
| 
						 | 
				
			
			@ -80,6 +81,7 @@ set(TON_CRYPTO_SOURCE
 | 
			
		|||
  vm/tupleops.h
 | 
			
		||||
  vm/tonops.h
 | 
			
		||||
  vm/vmstate.h
 | 
			
		||||
  vm/utils.h
 | 
			
		||||
 | 
			
		||||
  vm/cells.h
 | 
			
		||||
  vm/cellslice.h
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -789,10 +789,11 @@ td::Status ShardState::unpack_state(ton::BlockIdExt blkid, Ref<vm::Cell> prev_st
 | 
			
		|||
      return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid global_balance");
 | 
			
		||||
    }
 | 
			
		||||
    if (extra.r1.flags & 1) {
 | 
			
		||||
      if (extra.r1.block_create_stats->prefetch_ulong(8) != 0x17) {
 | 
			
		||||
      if (extra.r1.block_create_stats->prefetch_ulong(8) == 0x17) {
 | 
			
		||||
        block_create_stats_ = std::make_unique<vm::Dictionary>(extra.r1.block_create_stats->prefetch_ref(), 256);
 | 
			
		||||
      } else {
 | 
			
		||||
        return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid BlockCreateStats");
 | 
			
		||||
      }
 | 
			
		||||
      block_create_stats_ = std::make_unique<vm::Dictionary>(extra.r1.block_create_stats->prefetch_ref(), 256);
 | 
			
		||||
    } else {
 | 
			
		||||
      block_create_stats_ = std::make_unique<vm::Dictionary>(256);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1846,6 +1847,18 @@ td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& i
 | 
			
		|||
  return td::Status::OK();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<vm::Dictionary> get_block_create_stats_dict(Ref<vm::Cell> state_root) {
 | 
			
		||||
  block::gen::ShardStateUnsplit::Record info;
 | 
			
		||||
  block::gen::McStateExtra::Record extra;
 | 
			
		||||
  block::gen::BlockCreateStats::Record_block_create_stats cstats;
 | 
			
		||||
  if (!(::tlb::unpack_cell(std::move(state_root), info) && info.custom->size_refs() &&
 | 
			
		||||
        ::tlb::unpack_cell(info.custom->prefetch_ref(), extra) && (extra.r1.flags & 1) &&
 | 
			
		||||
        ::tlb::csr_unpack(std::move(extra.r1.block_create_stats), cstats))) {
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  return std::make_unique<vm::Dictionary>(std::move(cstats.counters), 256);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root) {
 | 
			
		||||
  block::gen::ShardStateUnsplit::Record info;
 | 
			
		||||
  block::gen::McStateExtra::Record extra_info;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -163,12 +163,12 @@ struct MsgProcessedUpto {
 | 
			
		|||
  MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
 | 
			
		||||
      : shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
 | 
			
		||||
  }
 | 
			
		||||
  bool operator<(const MsgProcessedUpto& other) const& {
 | 
			
		||||
  bool operator<(const MsgProcessedUpto& other) const & {
 | 
			
		||||
    return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
 | 
			
		||||
  }
 | 
			
		||||
  bool contains(const MsgProcessedUpto& other) const&;
 | 
			
		||||
  bool contains(const MsgProcessedUpto& other) const &;
 | 
			
		||||
  bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
 | 
			
		||||
                ton::BlockSeqno other_mc_seqno) const&;
 | 
			
		||||
                ton::BlockSeqno other_mc_seqno) const &;
 | 
			
		||||
  // NB: this is for checking whether we have already imported an internal message
 | 
			
		||||
  bool already_processed(const EnqueuedMsgDescr& msg) const;
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -514,6 +514,9 @@ struct DiscountedCounter {
 | 
			
		|||
    return last_updated == other.last_updated && total == other.total && cnt2048 <= other.cnt2048 + 1 &&
 | 
			
		||||
           other.cnt2048 <= cnt2048 + 1 && cnt65536 <= other.cnt65536 + 1 && other.cnt65536 <= cnt65536 + 1;
 | 
			
		||||
  }
 | 
			
		||||
  bool modified_since(ton::UnixTime utime) const {
 | 
			
		||||
    return last_updated >= utime;
 | 
			
		||||
  }
 | 
			
		||||
  bool validate();
 | 
			
		||||
  bool increase_by(unsigned count, ton::UnixTime now);
 | 
			
		||||
  bool fetch(vm::CellSlice& cs);
 | 
			
		||||
| 
						 | 
				
			
			@ -629,6 +632,8 @@ td::Status unpack_block_prev_blk_try(Ref<vm::Cell> block_root, const ton::BlockI
 | 
			
		|||
td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& id,
 | 
			
		||||
                              ton::Bits256* store_shard_hash_to = nullptr);
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<vm::Dictionary> get_block_create_stats_dict(Ref<vm::Cell> state_root);
 | 
			
		||||
 | 
			
		||||
std::unique_ptr<vm::AugmentedDictionary> get_prev_blocks_dict(Ref<vm::Cell> state_root);
 | 
			
		||||
bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
 | 
			
		||||
                         ton::LogicalTime* end_lt = nullptr);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -366,6 +366,10 @@ action_send_msg#0ec3c86d mode:(## 8)
 | 
			
		|||
action_set_code#ad4de08e new_code:^Cell = OutAction;
 | 
			
		||||
action_reserve_currency#36e6b809 mode:(## 8)
 | 
			
		||||
  currency:CurrencyCollection = OutAction;
 | 
			
		||||
libref_hash$0 lib_hash:bits256 = LibRef;
 | 
			
		||||
libref_ref$1 library:^Cell = LibRef;
 | 
			
		||||
action_change_library#26fa1dd4 mode:(## 7) { mode <= 2 }
 | 
			
		||||
  libref:LibRef = OutAction;
 | 
			
		||||
 | 
			
		||||
out_list_node$_ prev:^Cell action:OutAction = OutListNode;
 | 
			
		||||
//
 | 
			
		||||
| 
						 | 
				
			
			@ -505,6 +509,7 @@ _ (HashmapAugE 32 KeyExtBlkRef KeyMaxLt) = OldMcBlocksInfo;
 | 
			
		|||
counters#_ last_updated:uint32 total:uint64 cnt2048:uint64 cnt65536:uint64 = Counters; 
 | 
			
		||||
creator_info#4 mc_blocks:Counters shard_blocks:Counters = CreatorStats; 
 | 
			
		||||
block_create_stats#17 counters:(HashmapE 256 CreatorStats) = BlockCreateStats;
 | 
			
		||||
block_create_stats_ext#34 counters:(HashmapAugE 256 CreatorStats uint32) = BlockCreateStats;
 | 
			
		||||
 | 
			
		||||
masterchain_state_extra#cc26
 | 
			
		||||
  shard_hashes:ShardHashes
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1710,6 +1710,30 @@ std::vector<ton::ValidatorDescr> Config::compute_total_validator_set(int next) c
 | 
			
		|||
  return res.move_as_ok()->export_validator_set();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<std::pair<ton::UnixTime, ton::UnixTime>> Config::unpack_validator_set_start_stop(Ref<vm::Cell> vset_root) {
 | 
			
		||||
  if (vset_root.is_null()) {
 | 
			
		||||
    return td::Status::Error("validator set absent");
 | 
			
		||||
  }
 | 
			
		||||
  gen::ValidatorSet::Record_validators_ext rec;
 | 
			
		||||
  if (tlb::unpack_cell(vset_root, rec)) {
 | 
			
		||||
    return std::pair<ton::UnixTime, ton::UnixTime>(rec.utime_since, rec.utime_until);
 | 
			
		||||
  }
 | 
			
		||||
  gen::ValidatorSet::Record_validators rec0;
 | 
			
		||||
  if (tlb::unpack_cell(std::move(vset_root), rec0)) {
 | 
			
		||||
    return std::pair<ton::UnixTime, ton::UnixTime>(rec0.utime_since, rec0.utime_until);
 | 
			
		||||
  }
 | 
			
		||||
  return td::Status::Error("validator set is invalid");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<ton::UnixTime, ton::UnixTime> Config::get_validator_set_start_stop(int next) const {
 | 
			
		||||
  auto res = unpack_validator_set_start_stop(get_config_param(next < 0 ? 32 : (next ? 36 : 34)));
 | 
			
		||||
  if (res.is_error()) {
 | 
			
		||||
    return {0, 0};
 | 
			
		||||
  } else {
 | 
			
		||||
    return res.move_as_ok();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool WorkchainInfo::unpack(ton::WorkchainId wc, vm::CellSlice& cs) {
 | 
			
		||||
  workchain = ton::workchainInvalid;
 | 
			
		||||
  if (wc == ton::workchainInvalid) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,7 +50,7 @@ struct ValidatorDescr {
 | 
			
		|||
      : pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) {
 | 
			
		||||
    adnl_addr.set_zero();
 | 
			
		||||
  }
 | 
			
		||||
  bool operator<(td::uint64 wt_pos) const& {
 | 
			
		||||
  bool operator<(td::uint64 wt_pos) const & {
 | 
			
		||||
    return cum_weight < wt_pos;
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -558,6 +558,7 @@ class Config {
 | 
			
		|||
  const ValidatorSet* get_cur_validator_set() const {
 | 
			
		||||
    return cur_validators_.get();
 | 
			
		||||
  }
 | 
			
		||||
  std::pair<ton::UnixTime, ton::UnixTime> get_validator_set_start_stop(int next = 0) const;
 | 
			
		||||
  ton::ValidatorSessionConfig get_consensus_config() const;
 | 
			
		||||
  bool foreach_config_param(std::function<bool(int, Ref<vm::Cell>)> scan_func) const;
 | 
			
		||||
  Ref<WorkchainInfo> get_workchain_info(ton::WorkchainId workchain_id) const;
 | 
			
		||||
| 
						 | 
				
			
			@ -577,6 +578,7 @@ class Config {
 | 
			
		|||
  static td::Result<std::unique_ptr<Config>> unpack_config(Ref<vm::CellSlice> config_csr, int mode = 0);
 | 
			
		||||
  static td::Result<std::unique_ptr<Config>> extract_from_state(Ref<vm::Cell> mc_state_root, int mode = 0);
 | 
			
		||||
  static td::Result<std::unique_ptr<Config>> extract_from_key_block(Ref<vm::Cell> key_block_root, int mode = 0);
 | 
			
		||||
  static td::Result<std::pair<ton::UnixTime, ton::UnixTime>> unpack_validator_set_start_stop(Ref<vm::Cell> root);
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  Config(int _mode) : mode(_mode) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1098,6 +1098,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
 | 
			
		|||
      case block::gen::OutAction::action_reserve_currency:
 | 
			
		||||
        err_code = try_action_reserve_currency(cs, ap, cfg);
 | 
			
		||||
        break;
 | 
			
		||||
      case block::gen::OutAction::action_change_library:
 | 
			
		||||
        err_code = try_action_change_library(cs, ap, cfg);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    if (err_code) {
 | 
			
		||||
      ap.result_code = (err_code == -1 ? 34 : err_code);
 | 
			
		||||
| 
						 | 
				
			
			@ -1148,6 +1151,56 @@ int Transaction::try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const A
 | 
			
		|||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int Transaction::try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) {
 | 
			
		||||
  block::gen::OutAction::Record_action_change_library rec;
 | 
			
		||||
  if (!tlb::unpack_exact(cs, rec)) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  }
 | 
			
		||||
  // mode: +0 = remove library, +1 = add private library, +2 = add public library
 | 
			
		||||
  Ref<vm::Cell> lib_ref = rec.libref->prefetch_ref();
 | 
			
		||||
  ton::Bits256 hash;
 | 
			
		||||
  if (lib_ref.not_null()) {
 | 
			
		||||
    hash = lib_ref->get_hash().bits();
 | 
			
		||||
  } else {
 | 
			
		||||
    CHECK(rec.libref.write().fetch_ulong(1) == 0 && rec.libref.write().fetch_bits_to(hash));
 | 
			
		||||
  }
 | 
			
		||||
  try {
 | 
			
		||||
    vm::Dictionary dict{new_library, 256};
 | 
			
		||||
    if (!rec.mode) {
 | 
			
		||||
      // remove library
 | 
			
		||||
      dict.lookup_delete(hash);
 | 
			
		||||
      LOG(DEBUG) << "removed " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex();
 | 
			
		||||
    } else {
 | 
			
		||||
      auto val = dict.lookup(hash);
 | 
			
		||||
      if (val.not_null()) {
 | 
			
		||||
        bool is_public = val->prefetch_ulong(1);
 | 
			
		||||
        auto ref = val->prefetch_ref();
 | 
			
		||||
        if (hash == ref->get_hash().bits()) {
 | 
			
		||||
          lib_ref = ref;
 | 
			
		||||
          if (is_public == (rec.mode >> 1)) {
 | 
			
		||||
            // library already in required state
 | 
			
		||||
            ap.spec_actions++;
 | 
			
		||||
            return 0;
 | 
			
		||||
          }
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      if (lib_ref.is_null()) {
 | 
			
		||||
        // library code not found
 | 
			
		||||
        return 41;
 | 
			
		||||
      }
 | 
			
		||||
      vm::CellBuilder cb;
 | 
			
		||||
      CHECK(cb.store_bool_bool(rec.mode >> 1) && cb.store_ref_bool(std::move(lib_ref)));
 | 
			
		||||
      CHECK(dict.set_builder(hash, cb));
 | 
			
		||||
      LOG(DEBUG) << "added " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex();
 | 
			
		||||
    }
 | 
			
		||||
    new_library = std::move(dict).extract_root_cell();
 | 
			
		||||
  } catch (vm::VmError& vme) {
 | 
			
		||||
    return 42;
 | 
			
		||||
  }
 | 
			
		||||
  ap.spec_actions++;
 | 
			
		||||
  return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
 | 
			
		||||
// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
 | 
			
		||||
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,10 +65,10 @@ struct NewOutMsg {
 | 
			
		|||
  NewOutMsg(ton::LogicalTime _lt, Ref<vm::Cell> _msg, Ref<vm::Cell> _trans)
 | 
			
		||||
      : lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) {
 | 
			
		||||
  }
 | 
			
		||||
  bool operator<(const NewOutMsg& other) const& {
 | 
			
		||||
  bool operator<(const NewOutMsg& other) const & {
 | 
			
		||||
    return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash());
 | 
			
		||||
  }
 | 
			
		||||
  bool operator>(const NewOutMsg& other) const& {
 | 
			
		||||
  bool operator>(const NewOutMsg& other) const & {
 | 
			
		||||
    return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash());
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
| 
						 | 
				
			
			@ -371,6 +371,7 @@ struct Transaction {
 | 
			
		|||
  Ref<vm::Tuple> prepare_vm_c7(const ComputePhaseConfig& cfg) const;
 | 
			
		||||
  bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const;
 | 
			
		||||
  int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
 | 
			
		||||
  int try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
 | 
			
		||||
  int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0);
 | 
			
		||||
  int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg);
 | 
			
		||||
  bool check_replace_src_addr(Ref<vm::CellSlice>& src_addr) const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1013,6 +1013,8 @@ x{FB00} @Defop SENDRAWMSG
 | 
			
		|||
x{FB02} @Defop RAWRESERVE
 | 
			
		||||
x{FB03} @Defop RAWRESERVEX
 | 
			
		||||
x{FB04} @Defop SETCODE
 | 
			
		||||
x{FB06} @Defop SETLIBCODE
 | 
			
		||||
x{FB07} @Defop CHANGELIB
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// debug primitives
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -111,3 +111,4 @@ variable base
 | 
			
		|||
{ true (atom) drop } : atom
 | 
			
		||||
{ bl word atom 1 'nop } ::_ `
 | 
			
		||||
{ hole dup 1 { @ execute } does create } : recursive
 | 
			
		||||
{ 0 { 1+ dup 1 ' $() does over (.) "$" swap $+ 0 (create) } rot times drop } : :$1..n
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										92
									
								
								crypto/fift/lib/GetOpt.fif
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								crypto/fift/lib/GetOpt.fif
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,92 @@
 | 
			
		|||
library GetOpt  // Simple command-line options parser
 | 
			
		||||
"Lists.fif" include
 | 
			
		||||
 | 
			
		||||
// May be used as follows:
 | 
			
		||||
// begin-options
 | 
			
		||||
//   "h" { ."Help Message" 0 halt } short-option
 | 
			
		||||
//   "v" { parse-int =: verbosity } short-option-arg
 | 
			
		||||
//   "i" "--interactive" { true =: interactive } short-long-option
 | 
			
		||||
// parse-options
 | 
			
		||||
 | 
			
		||||
// ( l -- l')  computes tail of list l if non-empty; else ()
 | 
			
		||||
{ dup null? ' cdr ifnot } : safe-cdr
 | 
			
		||||
// ( l c -- l')  deletes first c elements from list l
 | 
			
		||||
{ ' safe-cdr swap times } : list-delete-first
 | 
			
		||||
// ( l n c -- l' )  deletes c elements starting from n-th in list l
 | 
			
		||||
recursive list-delete-range {
 | 
			
		||||
  dup 0<= { 2drop } {
 | 
			
		||||
  over 0<= { nip list-delete-first } {
 | 
			
		||||
  swap 1- swap rot uncons 2swap list-delete-range cons
 | 
			
		||||
  } cond } cond
 | 
			
		||||
} swap !
 | 
			
		||||
// ( n c -- )  deletes $n .. $(n+c-1) from the argument list $*
 | 
			
		||||
{ swap 1- $* @ swap rot list-delete-range $* ! } : $*del.. 
 | 
			
		||||
// ( s s' -- ? )  checks whether s' is a prefix of s
 | 
			
		||||
{ tuck $len over $len over >= { $| drop $= } { 2drop drop false } cond
 | 
			
		||||
} : $pfx?
 | 
			
		||||
// ( s -- ? )  checks whether s is an option (a string beginning with '-')
 | 
			
		||||
{ dup $len 1 > { "-" $pfx? } { drop false } cond } : is-opt?
 | 
			
		||||
// ( l -- s i or 0 )  finds first string in l beginning with '-'
 | 
			
		||||
{ 0 { 1+ over null? { 2drop 0 true } {
 | 
			
		||||
  swap uncons over is-opt? { drop swap true } { nip swap false } cond
 | 
			
		||||
  } cond } until
 | 
			
		||||
} : list-find-opt
 | 
			
		||||
// ( -- s i or 0 )  finds first option in cmdline args
 | 
			
		||||
{ $* @ list-find-opt } : first-opt
 | 
			
		||||
// ( s t -- ? )  checks whether short/long option s matches description t
 | 
			
		||||
{ third $= } : short-option-matches
 | 
			
		||||
' second : get-opt-flags
 | 
			
		||||
' first : get-opt-exec
 | 
			
		||||
{ dup get-opt-flags 4 and 0= 3 + [] $=
 | 
			
		||||
} : long-option-matches
 | 
			
		||||
// ( s l -- t -1 or 0 )  finds short/long option s in list l
 | 
			
		||||
{ swap 1 { swap short-option-matches } does assoc-gen
 | 
			
		||||
} : lookup-short-option
 | 
			
		||||
{ swap 1 { swap long-option-matches } does assoc-gen
 | 
			
		||||
} : lookup-long-option
 | 
			
		||||
// ( s -- s' null or s' s'' )  Splits long option --opt=arg at '='
 | 
			
		||||
{ dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond
 | 
			
		||||
} : split-longopt
 | 
			
		||||
// ( l -- i or 0 ) 
 | 
			
		||||
// parses command line arguments according to option description list l
 | 
			
		||||
// and returns index i of first incorrect option
 | 
			
		||||
{ { first-opt dup 0= { true } {
 | 
			
		||||
    swap dup "--" $pfx? {  // l i s
 | 
			
		||||
      dup $len 2 = { drop dup 1 $*del.. 0 true } {
 | 
			
		||||
      split-longopt swap 3 pick
 | 
			
		||||
      lookup-long-option not { drop true } { // l i s' t f
 | 
			
		||||
      dup get-opt-exec swap get-opt-flags 3 and // l i s' e f'
 | 
			
		||||
      2 pick null? { dup 1 = } { dup 0= negate } cond  // l i s' e f' f''
 | 
			
		||||
      dup 1 = { 2drop 2drop true } {
 | 
			
		||||
      { drop nip over 1+ $() swap execute 2 $*del.. false } {
 | 
			
		||||
      ' nip ifnot execute 1 $*del.. false
 | 
			
		||||
    } cond } cond } cond } cond } { // l i s
 | 
			
		||||
      1 $| nip {
 | 
			
		||||
        dup $len 0= { drop 1 $*del.. false true } {
 | 
			
		||||
        1 $| swap 3 pick  // l i s' s l
 | 
			
		||||
        lookup-short-option not { drop true true } { // l i s' t
 | 
			
		||||
        dup get-opt-exec swap get-opt-flags 3 and  // l i s' e f'
 | 
			
		||||
        ?dup 0= { execute false } {
 | 
			
		||||
        2 pick $len { drop execute "" false } {
 | 
			
		||||
        2 = { nip null swap execute "" false } {  // l i e
 | 
			
		||||
        nip over 1+ $() swap execute 2 $*del.. false true
 | 
			
		||||
      } cond } cond } cond } cond } cond } until
 | 
			
		||||
    } cond
 | 
			
		||||
  } cond } until nip
 | 
			
		||||
} : getopt
 | 
			
		||||
// ( l -- )  Parses options and throws an error on failure
 | 
			
		||||
{ getopt ?dup { $() "cannot parse command line options near `" swap $+ +"`" abort } if
 | 
			
		||||
} : run-getopt
 | 
			
		||||
 | 
			
		||||
anon constant opt-list-marker
 | 
			
		||||
' opt-list-marker : begin-options
 | 
			
		||||
{ opt-list-marker list-until-marker } : end-options
 | 
			
		||||
{ end-options run-getopt } : parse-options
 | 
			
		||||
// ( s e -- o )  Creates short/long option s with execution token e
 | 
			
		||||
{ 0 rot triple } dup : short-option : long-option
 | 
			
		||||
// ( s s' e -- o ) Creates a combined short option s and long option s' with execution token e
 | 
			
		||||
{ 4 2swap 4 tuple } : short-long-option
 | 
			
		||||
{ 1 rot triple } dup : short-option-arg : long-option-arg
 | 
			
		||||
{ 2 rot triple } dup : short-option-?arg : long-option-?arg
 | 
			
		||||
{ 5 2swap 4 tuple } : short-long-option-arg
 | 
			
		||||
{ 6 2swap 4 tuple } : short-long-option-?arg
 | 
			
		||||
| 
						 | 
				
			
			@ -46,6 +46,9 @@ td::Result<std::string> load_Lists_fif(std::string dir = "") {
 | 
			
		|||
td::Result<std::string> load_Lisp_fif(std::string dir = "") {
 | 
			
		||||
  return load_source("Lisp.fif", dir);
 | 
			
		||||
}
 | 
			
		||||
td::Result<std::string> load_GetOpt_fif(std::string dir = "") {
 | 
			
		||||
  return load_source("GetOpt.fif", dir);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class MemoryFileLoader : public fift::FileLoader {
 | 
			
		||||
 public:
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +118,10 @@ td::Result<fift::SourceLookup> create_source_lookup(std::string main, bool need_
 | 
			
		|||
      TRY_RESULT(f, load_TonUtil_fif(dir));
 | 
			
		||||
      loader->add_file("/TonUtil.fif", std::move(f));
 | 
			
		||||
    }
 | 
			
		||||
    {
 | 
			
		||||
      TRY_RESULT(f, load_GetOpt_fif(dir));
 | 
			
		||||
      loader->add_file("/GetOpt.fif", std::move(f));
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (need_lisp) {
 | 
			
		||||
    TRY_RESULT(f, load_Lisp_fif(dir));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -604,6 +604,12 @@ void interpret_str_split(vm::Stack& stack) {
 | 
			
		|||
  stack.push_string(std::string{str, sz});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void interpret_str_pos(vm::Stack& stack) {
 | 
			
		||||
  auto s2 = stack.pop_string(), s1 = stack.pop_string();
 | 
			
		||||
  auto pos = s1.find(s2);
 | 
			
		||||
  stack.push_smallint(pos == std::string::npos ? -1 : pos);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void interpret_str_reverse(vm::Stack& stack) {
 | 
			
		||||
  std::string s = stack.pop_string();
 | 
			
		||||
  auto it = s.begin();
 | 
			
		||||
| 
						 | 
				
			
			@ -659,6 +665,20 @@ void interpret_utf8_str_split(vm::Stack& stack) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void interpret_utf8_str_pos(vm::Stack& stack) {
 | 
			
		||||
  auto s2 = stack.pop_string(), s1 = stack.pop_string();
 | 
			
		||||
  auto pos = s1.find(s2);
 | 
			
		||||
  if (pos == std::string::npos) {
 | 
			
		||||
    stack.push_smallint(-1);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  int cnt = 0;
 | 
			
		||||
  for (std::size_t i = 0; i < pos; i++) {
 | 
			
		||||
    cnt += ((s1[i] & 0xc0) != 0x80);
 | 
			
		||||
  }
 | 
			
		||||
  stack.push_smallint(cnt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void interpret_str_remove_trailing_int(vm::Stack& stack, int arg) {
 | 
			
		||||
  char x = (char)(arg ? arg : stack.pop_long_range(127));
 | 
			
		||||
  std::string s = stack.pop_string();
 | 
			
		||||
| 
						 | 
				
			
			@ -2336,12 +2356,38 @@ void interpret_db_run_vm_parallel(IntCtx& ctx) {
 | 
			
		|||
  do_interpret_db_run_vm_parallel(ctx.error_stream, ctx.stack, ctx.ton_db, threads_n, tasks_n);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Ref<vm::Box> cmdline_args{true};
 | 
			
		||||
 | 
			
		||||
void interpret_get_fixed_cmdline_arg(vm::Stack& stack, int n) {
 | 
			
		||||
  if (!n) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto v = cmdline_args->get();
 | 
			
		||||
  while (true) {
 | 
			
		||||
    if (v.empty()) {
 | 
			
		||||
      stack.push(vm::StackEntry{});
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    auto t = v.as_tuple_range(2, 2);
 | 
			
		||||
    if (t.is_null()) {
 | 
			
		||||
      throw IntError{"invalid cmdline arg list"};
 | 
			
		||||
    }
 | 
			
		||||
    if (!--n) {
 | 
			
		||||
      stack.push(t->at(0));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    v = t->at(1);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// n -- executes $n
 | 
			
		||||
void interpret_get_cmdline_arg(IntCtx& ctx) {
 | 
			
		||||
  int n = ctx.stack.pop_smallint_range(999999);
 | 
			
		||||
  char buffer[14];
 | 
			
		||||
  sprintf(buffer, "$%d ", n);
 | 
			
		||||
  auto entry = ctx.dictionary->lookup(std::string{buffer});
 | 
			
		||||
  if (n) {
 | 
			
		||||
    interpret_get_fixed_cmdline_arg(ctx.stack, n);
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto entry = ctx.dictionary->lookup("$0 ");
 | 
			
		||||
  if (!entry) {
 | 
			
		||||
    throw IntError{"-?"};
 | 
			
		||||
  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -2349,6 +2395,19 @@ void interpret_get_cmdline_arg(IntCtx& ctx) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void interpret_get_cmdline_arg_count(vm::Stack& stack) {
 | 
			
		||||
  auto v = cmdline_args->get();
 | 
			
		||||
  int cnt;
 | 
			
		||||
  for (cnt = 0; !v.empty(); cnt++) {
 | 
			
		||||
    auto t = v.as_tuple_range(2, 2);
 | 
			
		||||
    if (t.is_null()) {
 | 
			
		||||
      throw IntError{"invalid cmdline arg list"};
 | 
			
		||||
    }
 | 
			
		||||
    v = t->at(1);
 | 
			
		||||
  }
 | 
			
		||||
  stack.push_smallint(cnt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void interpret_getenv(vm::Stack& stack) {
 | 
			
		||||
  auto str = stack.pop_string();
 | 
			
		||||
  auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr;
 | 
			
		||||
| 
						 | 
				
			
			@ -2568,6 +2627,7 @@ void init_words_common(Dictionary& d) {
 | 
			
		|||
  d.def_stack_word("$= ", interpret_str_equal);
 | 
			
		||||
  d.def_stack_word("$cmp ", interpret_str_cmp);
 | 
			
		||||
  d.def_stack_word("$reverse ", interpret_str_reverse);
 | 
			
		||||
  d.def_stack_word("$pos ", interpret_str_pos);
 | 
			
		||||
  d.def_stack_word("(-trailing) ", std::bind(interpret_str_remove_trailing_int, _1, 0));
 | 
			
		||||
  d.def_stack_word("-trailing ", std::bind(interpret_str_remove_trailing_int, _1, ' '));
 | 
			
		||||
  d.def_stack_word("-trailing0 ", std::bind(interpret_str_remove_trailing_int, _1, '0'));
 | 
			
		||||
| 
						 | 
				
			
			@ -2575,6 +2635,7 @@ void init_words_common(Dictionary& d) {
 | 
			
		|||
  d.def_stack_word("Blen ", interpret_bytes_len);
 | 
			
		||||
  d.def_stack_word("$Len ", interpret_utf8_str_len);
 | 
			
		||||
  d.def_stack_word("$Split ", interpret_utf8_str_split);
 | 
			
		||||
  d.def_stack_word("$Pos ", interpret_utf8_str_pos);
 | 
			
		||||
  d.def_ctx_word("Bx. ", std::bind(interpret_bytes_hex_print_raw, _1, true));
 | 
			
		||||
  d.def_stack_word("B>X ", std::bind(interpret_bytes_to_hex, _1, true));
 | 
			
		||||
  d.def_stack_word("B>x ", std::bind(interpret_bytes_to_hex, _1, false));
 | 
			
		||||
| 
						 | 
				
			
			@ -2766,6 +2827,10 @@ void init_words_common(Dictionary& d) {
 | 
			
		|||
  d.def_ctx_word("quit ", interpret_quit);
 | 
			
		||||
  d.def_ctx_word("bye ", interpret_bye);
 | 
			
		||||
  d.def_stack_word("halt ", interpret_halt);
 | 
			
		||||
  // cmdline args
 | 
			
		||||
  d.def_stack_word("$* ", std::bind(interpret_literal, _1, vm::StackEntry{cmdline_args}));
 | 
			
		||||
  d.def_stack_word("$# ", interpret_get_cmdline_arg_count);
 | 
			
		||||
  d.def_ctx_word("$() ", interpret_get_cmdline_arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void init_words_ton(Dictionary& d) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2799,13 +2864,16 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con
 | 
			
		|||
  using namespace std::placeholders;
 | 
			
		||||
  LOG(DEBUG) << "import_cmdlist_args(" << arg0 << "," << n << ")";
 | 
			
		||||
  d.def_stack_word("$0 ", std::bind(interpret_literal, _1, vm::StackEntry{arg0}));
 | 
			
		||||
  for (int i = 0; i < n; i++) {
 | 
			
		||||
    char buffer[14];
 | 
			
		||||
    sprintf(buffer, "$%d ", i + 1);
 | 
			
		||||
    d.def_stack_word(buffer, std::bind(interpret_literal, _1, vm::StackEntry{argv[i]}));
 | 
			
		||||
  vm::StackEntry list;
 | 
			
		||||
  for (int i = n - 1; i >= 0; i--) {
 | 
			
		||||
    list = vm::StackEntry::cons(vm::StackEntry{argv[i]}, list);
 | 
			
		||||
  }
 | 
			
		||||
  cmdline_args->set(std::move(list));
 | 
			
		||||
  for (int i = 1; i <= n; i++) {
 | 
			
		||||
    char buffer[14];
 | 
			
		||||
    sprintf(buffer, "$%d ", i);
 | 
			
		||||
    d.def_stack_word(buffer, std::bind(interpret_get_fixed_cmdline_arg, _1, i));
 | 
			
		||||
  }
 | 
			
		||||
  d.def_stack_word("$# ", std::bind(interpret_const, _1, n));
 | 
			
		||||
  d.def_ctx_word("$() ", interpret_get_cmdline_arg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<td::RefInt256, td::RefInt256> numeric_value_ext(std::string s, bool allow_frac = true) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,10 +9,9 @@
 | 
			
		|||
 | 
			
		||||
(cell, int, int, cell) load_data() inline {
 | 
			
		||||
  var cs = get_data().begin_parse();
 | 
			
		||||
  var (cfg_dict, stored_seqno, public_key) = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256));
 | 
			
		||||
  var vote_dict = cs.slice_empty?() ? new_dict() : cs~load_dict(); 
 | 
			
		||||
  var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict());
 | 
			
		||||
  cs.end_parse();
 | 
			
		||||
  return (cfg_dict, stored_seqno, public_key, vote_dict);
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -796,7 +796,7 @@ _ participant_list() method_id {
 | 
			
		|||
_ participant_list_extended() method_id {
 | 
			
		||||
  var elect = get_data().begin_parse().preload_dict();
 | 
			
		||||
  if (elect.null?()) {
 | 
			
		||||
    return nil;
 | 
			
		||||
    return (0, 0, 0, 0, nil, 0, 0);
 | 
			
		||||
  }
 | 
			
		||||
  var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
 | 
			
		||||
  var l = nil;
 | 
			
		||||
| 
						 | 
				
			
			@ -809,7 +809,7 @@ _ participant_list_extended() method_id {
 | 
			
		|||
      l = cons(pair(id, tuple4(stake, max_factor, addr, adnl_addr)), l);
 | 
			
		||||
    }
 | 
			
		||||
  } until (~ f);
 | 
			
		||||
  return l;
 | 
			
		||||
  return (elect_at, elect_close, min_stake, total_stake, l, failed, finished);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
;; computes the return stake
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -160,7 +160,7 @@ Masterchain swap
 | 
			
		|||
// 9 4 1 config.validator_num!
 | 
			
		||||
1000 100 13 config.validator_num!
 | 
			
		||||
// min-stake max-stake min-total-stake max-factor
 | 
			
		||||
GR$10000 GR$10000000 GR$1000000 sg~10 config.validator_stake_limits!
 | 
			
		||||
GR$10000 GR$10000000 GR$500000 sg~10 config.validator_stake_limits!
 | 
			
		||||
// elected-for elect-start-before elect-end-before stakes-frozen-for
 | 
			
		||||
// 400000 200000 4000 400000 config.election_params!
 | 
			
		||||
// 4000 2000 500 1000 config.election_params!  // DEBUG
 | 
			
		||||
| 
						 | 
				
			
			@ -219,7 +219,7 @@ now dup orig_vset_valid_for + 0 config.validators!
 | 
			
		|||
   0 32 u,          // seqno
 | 
			
		||||
   "config-master" +suffix +".pk" load-generate-keypair drop
 | 
			
		||||
   B,
 | 
			
		||||
   newdict dict,   // vote dict
 | 
			
		||||
   dictnew dict,   // vote dict
 | 
			
		||||
b> // data
 | 
			
		||||
empty_cell  // libraries
 | 
			
		||||
GR$10  // balance
 | 
			
		||||
| 
						 | 
				
			
			@ -237,8 +237,8 @@ Masterchain swap
 | 
			
		|||
 */
 | 
			
		||||
 
 | 
			
		||||
// pubkey amount `create-wallet1` or pubkey amount `create-wallet2`
 | 
			
		||||
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100 create-wallet1
 | 
			
		||||
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$170 create-wallet0
 | 
			
		||||
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$1000000000 create-wallet1
 | 
			
		||||
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,21 +2,32 @@
 | 
			
		|||
 | 
			
		||||
_ unpack_state() inline_ref {
 | 
			
		||||
  var ds = begin_parse(get_data());
 | 
			
		||||
  var res = (ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict());
 | 
			
		||||
  var res = (ds~load_uint(32), ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict());
 | 
			
		||||
  ds.end_parse();
 | 
			
		||||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, int n) inline_ref {
 | 
			
		||||
_ pack_state(cell pending_queries, cell owner_infos, int last_cleaned, int k, int n, int wallet_id) inline_ref {
 | 
			
		||||
  return begin_cell()
 | 
			
		||||
    .store_uint(wallet_id, 32)
 | 
			
		||||
    .store_uint(n, 8)
 | 
			
		||||
    .store_uint(k, 8)
 | 
			
		||||
    .store_uint(last_cleaned, 64)
 | 
			
		||||
    .store_dict(public_keys)
 | 
			
		||||
    .store_dict(owner_infos)
 | 
			
		||||
    .store_dict(pending_queries)
 | 
			
		||||
  .end_cell();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_ pack_owner_info(int public_key, int flood) inline_ref {
 | 
			
		||||
  return begin_cell()
 | 
			
		||||
    .store_uint(public_key, 256)
 | 
			
		||||
    .store_uint(flood, 8);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
_ unpack_owner_info(slice cs) inline_ref {
 | 
			
		||||
  return (cs~load_uint(256), cs~load_uint(8));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
(int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref {
 | 
			
		||||
  int cnt = 0;
 | 
			
		||||
  
 | 
			
		||||
| 
						 | 
				
			
			@ -41,44 +52,60 @@ _ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, in
 | 
			
		|||
  return (cnt, cnt_bits);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
() recv_internal(slice in_msg) impure {
 | 
			
		||||
  ;; do nothing for internal messages
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
(int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?) inline_ref {
 | 
			
		||||
(int, int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?, int root_i) inline_ref {
 | 
			
		||||
  if (found?) {
 | 
			
		||||
    throw_unless(35, query~load_int(1));
 | 
			
		||||
    (int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(n), query);
 | 
			
		||||
    (int creator_i, int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(8), query~load_uint(n), query);
 | 
			
		||||
    throw_unless(36, slice_hash(msg) == slice_hash(in_msg));
 | 
			
		||||
    return (cnt, cnt_bits, msg);
 | 
			
		||||
    return (creator_i, cnt, cnt_bits, msg);
 | 
			
		||||
  }
 | 
			
		||||
  return (0, 0, in_msg);
 | 
			
		||||
  return (root_i, 0, 0, in_msg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
() try_init() impure inline_ref {
 | 
			
		||||
  ;; first query without signatures is always accepted
 | 
			
		||||
  (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state();
 | 
			
		||||
  (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state();
 | 
			
		||||
  throw_if(37, last_cleaned);
 | 
			
		||||
  accept_message();
 | 
			
		||||
  set_data(pack_state(pending_queries, public_keys, 1, k, n));
 | 
			
		||||
  set_data(pack_state(pending_queries, owner_infos, 1, k, n, wallet_id));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell update_pending_queries(cell pending_queries, slice msg, int query_id, int cnt, int cnt_bits, int n, int k) impure inline_ref {
 | 
			
		||||
(cell, cell) update_pending_queries(cell pending_queries, cell owner_infos, slice msg, int query_id, int creator_i, int cnt, int cnt_bits, int n, int k) impure inline_ref {
 | 
			
		||||
  if (cnt >= k) {
 | 
			
		||||
    accept_message();
 | 
			
		||||
    while (msg.slice_refs()) {
 | 
			
		||||
      var mode = msg~load_uint(8);
 | 
			
		||||
      send_raw_message(msg~load_ref(), mode);
 | 
			
		||||
    }
 | 
			
		||||
    pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1));
 | 
			
		||||
 | 
			
		||||
    (slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
 | 
			
		||||
    (int public_key, int flood) = unpack_owner_info(owner_info);
 | 
			
		||||
    owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
 | 
			
		||||
  } else {
 | 
			
		||||
    pending_queries~udict_set_builder(64, query_id, begin_cell()
 | 
			
		||||
      .store_uint(1, 1)
 | 
			
		||||
      .store_uint(creator_i, 8)
 | 
			
		||||
      .store_uint(cnt, 8)
 | 
			
		||||
      .store_uint(cnt_bits, n)
 | 
			
		||||
      .store_slice(msg));
 | 
			
		||||
  }
 | 
			
		||||
  return pending_queries;
 | 
			
		||||
  return (pending_queries, owner_infos);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
(int, int) calc_boc_size(int cells, int bits, slice root) {
 | 
			
		||||
  cells += 1;
 | 
			
		||||
  bits += root.slice_bits();
 | 
			
		||||
 | 
			
		||||
  while (root.slice_refs()) {
 | 
			
		||||
    (cells, bits) = calc_boc_size(cells, bits, root~load_ref().begin_parse());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return (cells, bits);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
() recv_external(slice in_msg) impure {
 | 
			
		||||
| 
						 | 
				
			
			@ -92,44 +119,62 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c
 | 
			
		|||
  int root_hash = slice_hash(in_msg);
 | 
			
		||||
  int root_i = in_msg~load_uint(8);
 | 
			
		||||
 | 
			
		||||
  (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state();
 | 
			
		||||
  (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state();
 | 
			
		||||
  last_cleaned -= last_cleaned == 0;
 | 
			
		||||
 | 
			
		||||
  (slice public_key, var found?) = public_keys.udict_get?(8, root_i);
 | 
			
		||||
  (slice owner_info, var found?) = owner_infos.udict_get?(8, root_i);
 | 
			
		||||
  (int public_key, int flood) = unpack_owner_info(owner_info);
 | 
			
		||||
  throw_unless(31, found?);
 | 
			
		||||
  throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256)));
 | 
			
		||||
  throw_unless(32, check_signature(root_hash, root_signature, public_key));
 | 
			
		||||
 | 
			
		||||
  cell signatures = in_msg~load_dict();
 | 
			
		||||
 | 
			
		||||
  var hash = slice_hash(in_msg);
 | 
			
		||||
  int query_wallet_id = in_msg~load_uint(32);
 | 
			
		||||
  throw_unless(42, query_wallet_id == wallet_id);
 | 
			
		||||
 | 
			
		||||
  int query_id = in_msg~load_uint(64);
 | 
			
		||||
 | 
			
		||||
  (int cnt, int bits) = calc_boc_size(0, 0, in_msg);
 | 
			
		||||
  throw_if(40, (cnt > 8) | (bits > 2048));
 | 
			
		||||
 | 
			
		||||
  (slice query, var found?) = pending_queries.udict_get?(64, query_id);
 | 
			
		||||
 | 
			
		||||
  ifnot (found?) {
 | 
			
		||||
    flood += 1;
 | 
			
		||||
    throw_if(39, flood > 10);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  var bound = (now() << 32);
 | 
			
		||||
  throw_if(33, query_id < bound);
 | 
			
		||||
 | 
			
		||||
  (slice query, var found?) = pending_queries.udict_get?(64, query_id);
 | 
			
		||||
  (int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?);
 | 
			
		||||
  (int creator_i, int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?, root_i);
 | 
			
		||||
  int mask = 1 << root_i;
 | 
			
		||||
  throw_if(34, cnt_bits & mask);
 | 
			
		||||
  cnt_bits |= mask;
 | 
			
		||||
  cnt += 1;
 | 
			
		||||
 | 
			
		||||
  ;; TODO: reserve some gas or FAIL
 | 
			
		||||
  accept_message();
 | 
			
		||||
  set_gas_limit(100000);
 | 
			
		||||
 | 
			
		||||
  pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k);
 | 
			
		||||
  set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n));
 | 
			
		||||
  ifnot (found?) {
 | 
			
		||||
    owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood));
 | 
			
		||||
    throw_if(41, (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
 | 
			
		||||
  set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id));
 | 
			
		||||
 | 
			
		||||
  commit();
 | 
			
		||||
  
 | 
			
		||||
  int need_save = 0;
 | 
			
		||||
  ifnot (cell_null?(signatures) | (cnt >= k)) {
 | 
			
		||||
    (int new_cnt, cnt_bits) = check_signatures(public_keys, signatures, hash, cnt_bits);
 | 
			
		||||
    (int new_cnt, cnt_bits) = check_signatures(owner_infos, signatures, hash, cnt_bits);
 | 
			
		||||
    cnt += new_cnt;
 | 
			
		||||
    pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k);
 | 
			
		||||
    (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
 | 
			
		||||
    need_save = -1;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  accept_message();
 | 
			
		||||
  bound -= (64 << 32);   ;; clean up records expired more than 64 seconds ago
 | 
			
		||||
  int old_last_cleaned = last_cleaned;
 | 
			
		||||
  do {
 | 
			
		||||
| 
						 | 
				
			
			@ -146,18 +191,18 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c
 | 
			
		|||
  } until (~ f);
 | 
			
		||||
 | 
			
		||||
  if (need_save) {
 | 
			
		||||
    set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n));
 | 
			
		||||
    set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
;; Get methods
 | 
			
		||||
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
 | 
			
		||||
(int, int) get_query_state(int query_id) method_id {
 | 
			
		||||
  (int n, _, int last_cleaned, _, cell pending_queries) = unpack_state();
 | 
			
		||||
  (_, int n, _, int last_cleaned, _, cell pending_queries) = unpack_state();
 | 
			
		||||
  (slice cs, var found) = pending_queries.udict_get?(64, query_id);
 | 
			
		||||
  if (found) {
 | 
			
		||||
    if (cs~load_int(1)) {
 | 
			
		||||
      cs~load_uint(8);
 | 
			
		||||
      cs~load_uint(8 + 8);
 | 
			
		||||
      return (0, cs~load_uint(n));
 | 
			
		||||
    } else {
 | 
			
		||||
      return (-1, 0);
 | 
			
		||||
| 
						 | 
				
			
			@ -172,8 +217,8 @@ int processed?(int query_id) method_id {
 | 
			
		|||
  return x;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell create_init_state(int n, int k, cell public_keys) method_id {
 | 
			
		||||
  return pack_state(new_dict(), public_keys, 0, k, n);
 | 
			
		||||
cell create_init_state(int wallet_id, int n, int k, cell owners_info) method_id {
 | 
			
		||||
  return pack_state(new_dict(), owners_info, 0, k, n, wallet_id);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cell merge_list(cell a, cell b) {
 | 
			
		||||
| 
						 | 
				
			
			@ -196,7 +241,7 @@ cell merge_list(cell a, cell b) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
cell get_public_keys() method_id {
 | 
			
		||||
  (_, _, _, cell public_keys, _) = unpack_state();
 | 
			
		||||
  (_, _, _, _, cell public_keys, _) = unpack_state();
 | 
			
		||||
  return public_keys;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -222,14 +267,14 @@ cell get_public_keys() method_id {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
cell messages_by_mask(int mask) method_id {
 | 
			
		||||
  (int n, _, _, _, cell pending_queries) = unpack_state();
 | 
			
		||||
  (_, int n, _, _, _, cell pending_queries) = unpack_state();
 | 
			
		||||
  int i = -1;
 | 
			
		||||
  cell a = new_dict();
 | 
			
		||||
  do {
 | 
			
		||||
    (i, var cs, var f) = pending_queries.udict_get_next?(64, i);
 | 
			
		||||
    if (f) {
 | 
			
		||||
      if (cs~load_int(1)) {
 | 
			
		||||
        int cnt_bits = cs.skip_bits(8).preload_uint(n);
 | 
			
		||||
        int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n);
 | 
			
		||||
        if (cnt_bits & mask) {
 | 
			
		||||
          a~udict_set_builder(64, i, begin_cell().store_slice(cs));
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +293,7 @@ cell get_messages_unsigned() method_id {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
(int, int) get_n_k() method_id {
 | 
			
		||||
  (int n, int k, _, _, _) = unpack_state();
 | 
			
		||||
  (_, int n, int k, _, _, _) = unpack_state();
 | 
			
		||||
  return (n, k);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,13 @@
 | 
			
		|||
#!/usr/bin/fift -s
 | 
			
		||||
"TonUtil.fif" include
 | 
			
		||||
 | 
			
		||||
{ ."usage: " @' $0 type ." <filename-base>" cr
 | 
			
		||||
{ ."usage: " $0 type ." <filename-base>" cr
 | 
			
		||||
  ."Shows the address of a simple wallet created by new-wallet.fif, with address in <filename-base>.addr "
 | 
			
		||||
  ."and private key in file <filename-base>.pk" cr 1 halt
 | 
			
		||||
} : usage
 | 
			
		||||
def? $# { @' $# 1 > ' usage if } if
 | 
			
		||||
def? $1 { @' $1 } { "new-wallet" } cond constant file-base
 | 
			
		||||
$# 1 > ' usage if
 | 
			
		||||
1 :$1..n
 | 
			
		||||
$1 dup null? { drop "new-wallet" } if =: file-base
 | 
			
		||||
 | 
			
		||||
file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B| 
 | 
			
		||||
dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -40,6 +40,7 @@ cont get_c3() impure asm "c3 PUSH";
 | 
			
		|||
cont bless(slice s) impure asm "BLESS";
 | 
			
		||||
 | 
			
		||||
() accept_message() impure asm "ACCEPT";
 | 
			
		||||
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
 | 
			
		||||
() commit() impure asm "COMMIT";
 | 
			
		||||
 | 
			
		||||
int min(int x, int y) asm "MIN";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,35 +1,32 @@
 | 
			
		|||
#!/usr/bin/fift -s
 | 
			
		||||
"TonUtil.fif" include
 | 
			
		||||
"GetOpt.fif" include
 | 
			
		||||
 | 
			
		||||
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-n] [-B <body-boc>] [-C <transfer-comment>] [<savefile>]" cr
 | 
			
		||||
  ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file <filename-base>.pk "
 | 
			
		||||
  ."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
 | 
			
		||||
} : usage
 | 
			
		||||
 | 
			
		||||
"" =: comment  // comment for simple transfers
 | 
			
		||||
true =: allow-bounce
 | 
			
		||||
def? $5 { @' $5 "-n" $= { false =: allow-bounce [forget] $5
 | 
			
		||||
  def? $6 { @' $6 =: $5 [forget] $6 } if
 | 
			
		||||
  def? $7 { @' $7 =: $6 [forget] $7 } if
 | 
			
		||||
  @' $# 1- =: $#
 | 
			
		||||
  } if
 | 
			
		||||
} if
 | 
			
		||||
3 =: send-mode  // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
 | 
			
		||||
 | 
			
		||||
begin-options
 | 
			
		||||
  "n" "--no-bounce" { false =: allow-bounce } short-long-option
 | 
			
		||||
  "B" "--body" { =: body-boc-file } short-long-option-arg
 | 
			
		||||
  "C" "--comment" { =: comment } short-long-option-arg
 | 
			
		||||
  "m" "--mode" { parse-int =: send-mode } short-long-option-arg
 | 
			
		||||
  "h" "--help" { usage } short-long-option
 | 
			
		||||
parse-options
 | 
			
		||||
 | 
			
		||||
def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or
 | 
			
		||||
  { @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6
 | 
			
		||||
    def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
 | 
			
		||||
  @' $# 2- =: $#
 | 
			
		||||
  } if
 | 
			
		||||
} if
 | 
			
		||||
$# dup 4 < swap 5 > or ' usage if
 | 
			
		||||
 | 
			
		||||
true constant bounce
 | 
			
		||||
 | 
			
		||||
5 :$1..n
 | 
			
		||||
true =: bounce
 | 
			
		||||
$1 =: file-base
 | 
			
		||||
$2 bounce parse-load-address allow-bounce and =: bounce 2=: dest_addr
 | 
			
		||||
$3 parse-int =: seqno
 | 
			
		||||
$4 $>GR =: amount
 | 
			
		||||
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
 | 
			
		||||
3 constant send-mode  // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
 | 
			
		||||
$5 dup null? { drop "wallet-query" } if =: savefile
 | 
			
		||||
// "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment
 | 
			
		||||
 | 
			
		||||
file-base +".addr" load-address
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -8,8 +8,13 @@
 | 
			
		|||
 | 
			
		||||
namespace ton {
 | 
			
		||||
 | 
			
		||||
MultisigWallet::QueryBuilder::QueryBuilder(td::int64 query_id, td::Ref<vm::Cell> msg, int mode) {
 | 
			
		||||
  msg_ = vm::CellBuilder().store_long(query_id, 64).store_long(mode, 8).store_ref(std::move(msg)).finalize();
 | 
			
		||||
MultisigWallet::QueryBuilder::QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref<vm::Cell> msg, int mode) {
 | 
			
		||||
  msg_ = vm::CellBuilder()
 | 
			
		||||
             .store_long(wallet_id, 32)
 | 
			
		||||
             .store_long(query_id, 64)
 | 
			
		||||
             .store_long(mode, 8)
 | 
			
		||||
             .store_ref(std::move(msg))
 | 
			
		||||
             .finalize();
 | 
			
		||||
}
 | 
			
		||||
void MultisigWallet::QueryBuilder::sign(td::int32 id, td::Ed25519::PrivateKey& pk) {
 | 
			
		||||
  CHECK(id < td::narrow_cast<td::int32>(mask_.size()));
 | 
			
		||||
| 
						 | 
				
			
			@ -87,26 +92,29 @@ std::vector<td::SecureString> MultisigWallet::get_public_keys() const {
 | 
			
		|||
  return res;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Ref<vm::Cell> MultisigWallet::create_init_data(std::vector<td::SecureString> public_keys, int k) const {
 | 
			
		||||
td::Ref<vm::Cell> MultisigWallet::create_init_data(td::uint32 wallet_id, std::vector<td::SecureString> public_keys,
 | 
			
		||||
                                                   int k) const {
 | 
			
		||||
  vm::Dictionary pk(8);
 | 
			
		||||
  for (size_t i = 0; i < public_keys.size(); i++) {
 | 
			
		||||
    auto key = pk.integer_key(td::make_refint(i), 8, false);
 | 
			
		||||
    pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()));
 | 
			
		||||
    pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8));
 | 
			
		||||
  }
 | 
			
		||||
  auto res = run_get_method("create_init_state",
 | 
			
		||||
                            {td::make_refint(public_keys.size()), td::make_refint(k), pk.get_root_cell()});
 | 
			
		||||
  auto res = run_get_method("create_init_state", {td::make_refint(wallet_id), td::make_refint(public_keys.size()),
 | 
			
		||||
                                                  td::make_refint(k), pk.get_root_cell()});
 | 
			
		||||
  CHECK(res.code == 0);
 | 
			
		||||
  return res.stack.write().pop_cell();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Ref<vm::Cell> MultisigWallet::create_init_data_fast(std::vector<td::SecureString> public_keys, int k) {
 | 
			
		||||
td::Ref<vm::Cell> MultisigWallet::create_init_data_fast(td::uint32 wallet_id, std::vector<td::SecureString> public_keys,
 | 
			
		||||
                                                        int k) {
 | 
			
		||||
  vm::Dictionary pk(8);
 | 
			
		||||
  for (size_t i = 0; i < public_keys.size(); i++) {
 | 
			
		||||
    auto key = pk.integer_key(td::make_refint(i), 8, false);
 | 
			
		||||
    pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()));
 | 
			
		||||
    pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  vm::CellBuilder cb;
 | 
			
		||||
  cb.store_long(wallet_id, 32);
 | 
			
		||||
  cb.store_long(public_keys.size(), 8).store_long(k, 8).store_long(0, 64);
 | 
			
		||||
  cb.ensure_throw(cb.store_maybe_ref(pk.get_root_cell()));
 | 
			
		||||
  cb.ensure_throw(cb.store_maybe_ref({}));
 | 
			
		||||
| 
						 | 
				
			
			@ -156,7 +164,7 @@ std::vector<MultisigWallet::Message> MultisigWallet::get_unsigned_messaged(int i
 | 
			
		|||
  vm::Dictionary dict(std::move(cell), 64);
 | 
			
		||||
  std::vector<Message> res;
 | 
			
		||||
  dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) {
 | 
			
		||||
    cs.write().skip_first(8);
 | 
			
		||||
    cs.write().skip_first(8 + 8);
 | 
			
		||||
    Message message;
 | 
			
		||||
    td::BigInt256 query_id;
 | 
			
		||||
    query_id.import_bits(ptr, ptr_bits, false);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,7 +20,7 @@ class MultisigWallet : public ton::SmartContract {
 | 
			
		|||
 | 
			
		||||
  class QueryBuilder {
 | 
			
		||||
   public:
 | 
			
		||||
    QueryBuilder(td::int64 query_id, td::Ref<vm::Cell> msg, int mode = 3);
 | 
			
		||||
    QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref<vm::Cell> msg, int mode = 3);
 | 
			
		||||
    void sign(td::int32 id, td::Ed25519::PrivateKey& pk);
 | 
			
		||||
 | 
			
		||||
    td::Ref<vm::Cell> create_inner() const;
 | 
			
		||||
| 
						 | 
				
			
			@ -42,8 +42,9 @@ class MultisigWallet : public ton::SmartContract {
 | 
			
		|||
  // creation
 | 
			
		||||
  static td::Ref<MultisigWallet> create(td::Ref<vm::Cell> data = {});
 | 
			
		||||
 | 
			
		||||
  td::Ref<vm::Cell> create_init_data(std::vector<td::SecureString> public_keys, int k) const;
 | 
			
		||||
  static td::Ref<vm::Cell> create_init_data_fast(std::vector<td::SecureString> public_keys, int k);
 | 
			
		||||
  td::Ref<vm::Cell> create_init_data(td::uint32 wallet_id, std::vector<td::SecureString> public_keys, int k) const;
 | 
			
		||||
  static td::Ref<vm::Cell> create_init_data_fast(td::uint32 wallet_id, std::vector<td::SecureString> public_keys,
 | 
			
		||||
                                                 int k);
 | 
			
		||||
 | 
			
		||||
  // get methods
 | 
			
		||||
  int processed(td::uint64 query_id) const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,10 +30,6 @@
 | 
			
		|||
 | 
			
		||||
namespace ton {
 | 
			
		||||
namespace {
 | 
			
		||||
td::int32 get_method_id(td::Slice method_name) {
 | 
			
		||||
  unsigned crc = td::crc16(method_name);
 | 
			
		||||
  return (crc & 0xffff) | 0x10000;
 | 
			
		||||
}
 | 
			
		||||
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
 | 
			
		||||
  td::Ref<vm::Stack> stack_ref{true};
 | 
			
		||||
  td::RefInt256 acc_addr{true};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,11 +27,7 @@ namespace ton {
 | 
			
		|||
namespace {
 | 
			
		||||
const auto& get_map() {
 | 
			
		||||
  static auto map = [] {
 | 
			
		||||
    class Cmp : public std::less<> {
 | 
			
		||||
     public:
 | 
			
		||||
      using is_transparent = void;
 | 
			
		||||
    };
 | 
			
		||||
    std::map<std::string, td::Ref<vm::Cell>, Cmp> map;
 | 
			
		||||
    std::map<std::string, td::Ref<vm::Cell>, std::less<>> map;
 | 
			
		||||
    auto with_tvm_code = [&](auto name, td::Slice code_str) {
 | 
			
		||||
      map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok();
 | 
			
		||||
    };
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -455,16 +455,17 @@ TEST(Smartcon, Multisig) {
 | 
			
		|||
 | 
			
		||||
  int n = 100;
 | 
			
		||||
  int k = 99;
 | 
			
		||||
  td::uint32 wallet_id = std::numeric_limits<td::uint32>::max() - 3;
 | 
			
		||||
  std::vector<td::Ed25519::PrivateKey> keys;
 | 
			
		||||
  for (int i = 0; i < n; i++) {
 | 
			
		||||
    keys.push_back(td::Ed25519::generate_private_key().move_as_ok());
 | 
			
		||||
  }
 | 
			
		||||
  auto init_state = ms_lib->create_init_data(
 | 
			
		||||
      td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k);
 | 
			
		||||
      wallet_id, td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k);
 | 
			
		||||
  auto ms = ton::MultisigWallet::create(init_state);
 | 
			
		||||
 | 
			
		||||
  td::uint64 query_id = 123;
 | 
			
		||||
  ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize());
 | 
			
		||||
  td::uint64 query_id = 123 | ((100 * 60ull) << 32);
 | 
			
		||||
  ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
 | 
			
		||||
  // first empty query (init)
 | 
			
		||||
  CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0);
 | 
			
		||||
  // first empty query
 | 
			
		||||
| 
						 | 
				
			
			@ -491,7 +492,7 @@ TEST(Smartcon, Multisig) {
 | 
			
		|||
  ASSERT_EQ(0, ms->processed(query_id));
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize());
 | 
			
		||||
    ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
 | 
			
		||||
    for (int i = 50; i + 1 < 100; i++) {
 | 
			
		||||
      qb.sign(i, keys[i]);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -507,6 +508,7 @@ TEST(Smartcon, Multisig) {
 | 
			
		|||
TEST(Smartcont, MultisigStress) {
 | 
			
		||||
  int n = 10;
 | 
			
		||||
  int k = 5;
 | 
			
		||||
  td::uint32 wallet_id = std::numeric_limits<td::uint32>::max() - 3;
 | 
			
		||||
 | 
			
		||||
  std::vector<td::Ed25519::PrivateKey> keys;
 | 
			
		||||
  for (int i = 0; i < n; i++) {
 | 
			
		||||
| 
						 | 
				
			
			@ -515,13 +517,14 @@ TEST(Smartcont, MultisigStress) {
 | 
			
		|||
  auto public_keys = td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); });
 | 
			
		||||
  auto ms_lib = ton::MultisigWallet::create();
 | 
			
		||||
  auto init_state_old =
 | 
			
		||||
      ms_lib->create_init_data_fast(td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
 | 
			
		||||
  auto init_state = ms_lib->create_init_data(td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
 | 
			
		||||
      ms_lib->create_init_data_fast(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
 | 
			
		||||
  auto init_state =
 | 
			
		||||
      ms_lib->create_init_data(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k);
 | 
			
		||||
  CHECK(init_state_old->get_hash() == init_state->get_hash());
 | 
			
		||||
  auto ms = ton::MultisigWallet::create(init_state);
 | 
			
		||||
  CHECK(ms->get_public_keys() == public_keys);
 | 
			
		||||
 | 
			
		||||
  td::int32 now = 0;
 | 
			
		||||
  td::int32 now = 100 * 60;
 | 
			
		||||
  td::int32 qid = 1;
 | 
			
		||||
  using Mask = std::bitset<128>;
 | 
			
		||||
  struct Query {
 | 
			
		||||
| 
						 | 
				
			
			@ -566,7 +569,7 @@ TEST(Smartcont, MultisigStress) {
 | 
			
		|||
  };
 | 
			
		||||
 | 
			
		||||
  auto sign_query = [&](Query& query, Mask mask) {
 | 
			
		||||
    auto qb = ton::MultisigWallet::QueryBuilder(query.id, query.message);
 | 
			
		||||
    auto qb = ton::MultisigWallet::QueryBuilder(wallet_id, query.id, query.message);
 | 
			
		||||
    int first_i = -1;
 | 
			
		||||
    for (int i = 0; i < (int)mask.size(); i++) {
 | 
			
		||||
      if (mask.test(i)) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -708,6 +708,9 @@ int VmState::step() {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
int VmState::run() {
 | 
			
		||||
  if (code.is_null()) {
 | 
			
		||||
    throw VmError{Excno::fatal, "cannot run an uninitialized VM"};
 | 
			
		||||
  }
 | 
			
		||||
  int res;
 | 
			
		||||
  Guard guard(this);
 | 
			
		||||
  do {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -237,6 +237,11 @@ class DictionaryFixed : public DictionaryBase {
 | 
			
		|||
  Ref<CellSlice> get_minmax_key(T& key_buffer, bool fetch_max = false, bool invert_first = false) {
 | 
			
		||||
    return get_minmax_key(key_buffer.bits(), key_buffer.size(), fetch_max, invert_first);
 | 
			
		||||
  }
 | 
			
		||||
  template <typename T>
 | 
			
		||||
  Ref<CellSlice> lookup_nearest_key(T& key_buffer, bool fetch_next = false, bool allow_eq = false,
 | 
			
		||||
                                    bool invert_first = false) {
 | 
			
		||||
    return lookup_nearest_key(key_buffer.bits(), key_buffer.size(), fetch_next, allow_eq, invert_first);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 protected:
 | 
			
		||||
  virtual int label_mode() const {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -57,6 +57,11 @@ class Atom;
 | 
			
		|||
 | 
			
		||||
using Tuple = td::Cnt<std::vector<StackEntry>>;
 | 
			
		||||
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
Ref<Tuple> make_tuple_ref(Args&&... args) {
 | 
			
		||||
  return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::vector<vm::StackEntry>{std::forward<Args>(args)...});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct from_object_t {};
 | 
			
		||||
constexpr from_object_t from_object{};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -192,6 +197,10 @@ class StackEntry {
 | 
			
		|||
 public:
 | 
			
		||||
  static StackEntry make_list(std::vector<StackEntry>&& elems);
 | 
			
		||||
  static StackEntry make_list(const std::vector<StackEntry>& elems);
 | 
			
		||||
  template <typename T1, typename T2>
 | 
			
		||||
  static StackEntry cons(T1&& x, T2&& y) {
 | 
			
		||||
    return StackEntry{make_tuple_ref(std::forward<T1>(x), std::forward<T2>(y))};
 | 
			
		||||
  }
 | 
			
		||||
  template <typename T>
 | 
			
		||||
  static StackEntry maybe(Ref<T> ref) {
 | 
			
		||||
    if (ref.is_null()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -268,11 +277,6 @@ inline void swap(StackEntry& se1, StackEntry& se2) {
 | 
			
		|||
  se1.swap(se2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename... Args>
 | 
			
		||||
Ref<Tuple> make_tuple_ref(Args&&... args) {
 | 
			
		||||
  return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::vector<vm::StackEntry>{std::forward<Args>(args)...});
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const StackEntry& tuple_index(const Tuple& tup, unsigned idx);
 | 
			
		||||
StackEntry tuple_extend_index(const Ref<Tuple>& tup, unsigned idx);
 | 
			
		||||
unsigned tuple_extend_set_index(Ref<Tuple>& tup, unsigned idx, StackEntry&& value, bool force = false);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -662,12 +662,49 @@ int exec_set_code(VmState* st) {
 | 
			
		|||
  return install_output_action(st, cb.finalize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int exec_set_lib_code(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute SETLIBCODE";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(2);
 | 
			
		||||
  int mode = stack.pop_smallint_range(2);
 | 
			
		||||
  auto code = stack.pop_cell();
 | 
			
		||||
  CellBuilder cb;
 | 
			
		||||
  if (!(cb.store_ref_bool(get_actions(st))         // out_list$_ {n:#} prev:^(OutList n)
 | 
			
		||||
        && cb.store_long_bool(0x26fa1dd4, 32)      // action_change_library#26fa1dd4
 | 
			
		||||
        && cb.store_long_bool(mode * 2 + 1, 8)     // mode:(## 7) { mode <= 2 }
 | 
			
		||||
        && cb.store_ref_bool(std::move(code)))) {  // libref:LibRef = OutAction;
 | 
			
		||||
    throw VmError{Excno::cell_ov, "cannot serialize new library code into an output action cell"};
 | 
			
		||||
  }
 | 
			
		||||
  return install_output_action(st, cb.finalize());
 | 
			
		||||
}
 | 
			
		||||
  
 | 
			
		||||
int exec_change_lib(VmState* st) {
 | 
			
		||||
  VM_LOG(st) << "execute CHANGELIB";
 | 
			
		||||
  Stack& stack = st->get_stack();
 | 
			
		||||
  stack.check_underflow(2);
 | 
			
		||||
  int mode = stack.pop_smallint_range(2);
 | 
			
		||||
  auto hash = stack.pop_int_finite();
 | 
			
		||||
  if (!hash->unsigned_fits_bits(256)) {
 | 
			
		||||
    throw VmError{Excno::range_chk, "library hash must be non-negative"};
 | 
			
		||||
  }
 | 
			
		||||
  CellBuilder cb;
 | 
			
		||||
  if (!(cb.store_ref_bool(get_actions(st))         // out_list$_ {n:#} prev:^(OutList n)
 | 
			
		||||
        && cb.store_long_bool(0x26fa1dd4, 32)      // action_change_library#26fa1dd4
 | 
			
		||||
        && cb.store_long_bool(mode * 2, 8)         // mode:(## 7) { mode <= 2 }
 | 
			
		||||
        && cb.store_int256_bool(hash, 256, false))) {  // libref:LibRef = OutAction;
 | 
			
		||||
    throw VmError{Excno::cell_ov, "cannot serialize library hash into an output action cell"};
 | 
			
		||||
  }
 | 
			
		||||
  return install_output_action(st, cb.finalize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void register_ton_message_ops(OpcodeTable& cp0) {
 | 
			
		||||
  using namespace std::placeholders;
 | 
			
		||||
  cp0.insert(OpcodeInstr::mksimple(0xfb00, 16, "SENDRAWMSG", exec_send_raw_message))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xfb02, 16, "RESERVERAW", std::bind(exec_reserve_raw, _1, 0)))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xfb03, 16, "RESERVERAWX", std::bind(exec_reserve_raw, _1, 1)))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code));
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xfb06, 16, "SETLIBCODE", exec_set_lib_code))
 | 
			
		||||
      .insert(OpcodeInstr::mksimple(0xfb07, 16, "CHANGELIB", exec_change_lib));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void register_ton_ops(OpcodeTable& cp0) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										134
									
								
								crypto/vm/utils.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								crypto/vm/utils.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,134 @@
 | 
			
		|||
#include "utils.h"
 | 
			
		||||
 | 
			
		||||
namespace vm {
 | 
			
		||||
 | 
			
		||||
td::Result<vm::StackEntry> convert_stack_entry(td::Slice word);
 | 
			
		||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries_in(td::Slice& str, bool prefix_only = false);
 | 
			
		||||
td::Result<vm::StackEntry> parse_stack_entry_in(td::Slice& str, bool prefix_only = false);
 | 
			
		||||
 | 
			
		||||
namespace {
 | 
			
		||||
 | 
			
		||||
td::Slice& skip_spaces(td::Slice& str, const char* delims) {
 | 
			
		||||
  while (str.size() > 0 && strchr(delims, str[0])) {
 | 
			
		||||
    str.remove_prefix(1);
 | 
			
		||||
  }
 | 
			
		||||
  return str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Slice get_word(td::Slice& str, const char* delims, const char* specials) {
 | 
			
		||||
  skip_spaces(str, delims);
 | 
			
		||||
 | 
			
		||||
  size_t p = 0;
 | 
			
		||||
  while (p < str.size() && !strchr(delims, str[p])) {
 | 
			
		||||
    if (specials && strchr(specials, str[p])) {
 | 
			
		||||
      if (!p) {
 | 
			
		||||
        p++;
 | 
			
		||||
      }
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    p++;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::Slice ret = str.copy().truncate(p);
 | 
			
		||||
  str.remove_prefix(p);
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
 | 
			
		||||
td::Result<vm::StackEntry> parse_stack_entry_in(td::Slice& str, bool prefix_only) {
 | 
			
		||||
  auto word = get_word(str, " \t", "[()]");
 | 
			
		||||
  if (word.empty()) {
 | 
			
		||||
    return td::Status::Error("stack value expected instead of end-of-line");
 | 
			
		||||
  }
 | 
			
		||||
  if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) {
 | 
			
		||||
    int expected = (word[0] == '(' ? ')' : ']');
 | 
			
		||||
    TRY_RESULT(values, parse_stack_entries_in(str, true));
 | 
			
		||||
    word = get_word(str, " \t", "[()]");
 | 
			
		||||
    if (word.size() != 1 || word[0] != expected) {
 | 
			
		||||
      return td::Status::Error("closing bracket expected");
 | 
			
		||||
    }
 | 
			
		||||
    vm::StackEntry value;
 | 
			
		||||
    if (expected == ']') {
 | 
			
		||||
      value = vm::StackEntry{std::move(values)};
 | 
			
		||||
    } else {
 | 
			
		||||
      value = vm::StackEntry::make_list(std::move(values));
 | 
			
		||||
    }
 | 
			
		||||
    if (prefix_only || (skip_spaces(str, " \t").size() == 0)) {
 | 
			
		||||
      return value;
 | 
			
		||||
    } else {
 | 
			
		||||
      return td::Status::Error("extra data at the end");
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
    return convert_stack_entry(word);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<vm::StackEntry> convert_stack_entry(td::Slice str) {
 | 
			
		||||
  if (str.empty() || str.size() > 65535) {
 | 
			
		||||
    return td::Status::Error("too long string");
 | 
			
		||||
  }
 | 
			
		||||
  int l = (int)str.size();
 | 
			
		||||
  if (str[0] == '"') {
 | 
			
		||||
    vm::CellBuilder cb;
 | 
			
		||||
    if (l == 1 || str.back() != '"' || l >= 127 + 2 || !cb.store_bytes_bool(str.data() + 1, l - 2)) {
 | 
			
		||||
      return td::Status::Error("incomplete (or too long) string");
 | 
			
		||||
    }
 | 
			
		||||
    return vm::StackEntry{vm::load_cell_slice_ref(cb.finalize())};
 | 
			
		||||
  }
 | 
			
		||||
  if (l >= 3 && (str[0] == 'x' || str[0] == 'b') && str[1] == '{' && str.back() == '}') {
 | 
			
		||||
    unsigned char buff[128];
 | 
			
		||||
    int bits =
 | 
			
		||||
        (str[0] == 'x')
 | 
			
		||||
            ? (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1)
 | 
			
		||||
            : (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1);
 | 
			
		||||
    if (bits < 0) {
 | 
			
		||||
      return td::Status::Error("failed to parse raw b{...}/x{...} number");
 | 
			
		||||
    }
 | 
			
		||||
    return vm::StackEntry{
 | 
			
		||||
        Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}};
 | 
			
		||||
  }
 | 
			
		||||
  auto num = td::RefInt256{true};
 | 
			
		||||
  auto& x = num.unique_write();
 | 
			
		||||
  if (l >= 3 && str[0] == '0' && str[1] == 'x') {
 | 
			
		||||
    if (x.parse_hex(str.data() + 2, l - 2) != l - 2) {
 | 
			
		||||
      return td::Status::Error("failed to parse 0x... hex number");
 | 
			
		||||
    }
 | 
			
		||||
  } else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') {
 | 
			
		||||
    if (x.parse_hex(str.data() + 3, l - 3) != l - 3) {
 | 
			
		||||
      return td::Status::Error("failed to parse -0x... hex number");
 | 
			
		||||
    }
 | 
			
		||||
    x.negate().normalize();
 | 
			
		||||
  } else if (!l || x.parse_dec(str.data(), l) != l) {
 | 
			
		||||
    return td::Status::Error("failed to parse dec number");
 | 
			
		||||
  }
 | 
			
		||||
  return vm::StackEntry{std::move(num)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries_in(td::Slice& str, bool prefix_only) {
 | 
			
		||||
  std::vector<vm::StackEntry> ret;
 | 
			
		||||
  while (!skip_spaces(str, " \t").empty()) {
 | 
			
		||||
    auto c = str.copy();
 | 
			
		||||
    auto word = get_word(c, " \t", "[()]");
 | 
			
		||||
    if (word == "]" || word == ")") {
 | 
			
		||||
      if (prefix_only) {
 | 
			
		||||
        return ret;
 | 
			
		||||
      } else {
 | 
			
		||||
        return td::Status::Error("not paired closing bracket");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    TRY_RESULT(value, parse_stack_entry_in(str, true));
 | 
			
		||||
    ret.push_back(std::move(value));
 | 
			
		||||
  }
 | 
			
		||||
  return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries(td::Slice str, bool prefix_only) {
 | 
			
		||||
  return parse_stack_entries_in(str, prefix_only);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<vm::StackEntry> parse_stack_entry(td::Slice str, bool prefix_only) {
 | 
			
		||||
  return parse_stack_entry_in(str, prefix_only);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace vm
 | 
			
		||||
							
								
								
									
										11
									
								
								crypto/vm/utils.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								crypto/vm/utils.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,11 @@
 | 
			
		|||
#pragma once
 | 
			
		||||
#include "stack.hpp"
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace vm {
 | 
			
		||||
 | 
			
		||||
td::Result<std::vector<vm::StackEntry>> parse_stack_entries(td::Slice str, bool prefix_only = false);
 | 
			
		||||
td::Result<vm::StackEntry> parse_stack_entry(td::Slice str, bool prefix_only = false);
 | 
			
		||||
 | 
			
		||||
}  // namespace vm
 | 
			
		||||
| 
						 | 
				
			
			@ -1848,6 +1848,7 @@ For example, the active prefix word {\tt B\{}, used for defining {\em Bytes\/} l
 | 
			
		|||
\item {\tt \$@?+} ($s$ $x$ -- $S$ $s'$ $-1$ or $s$ $0$), similar to {\tt \$@+}, but uses a flag to indicate failure instead of throwing an exception, cf.~\ptref{p:slice.ops}.
 | 
			
		||||
\item {\tt \$cmp} ($S$ $S'$ -- $x$), returns $0$ if strings $S$ and $S'$ are equal, $-1$ if $S$ is lexicographically less than $S'$, and $1$ if $S$ is lexicographically greater than $S'$, cf.~\ptref{p:string.cmp.ops}.
 | 
			
		||||
\item {\tt \$len} ($S$ -- $x$), computes the byte length (not the UTF-8 character length!) of a string, cf.~\ptref{p:string.ops}.
 | 
			
		||||
\item {\tt \$pos} ($S$ $S'$ -- $x$ or $-1$), returns the position (byte offset)~$x$ of the first occurence of substring $S'$ in string~$S$ or $-1$.
 | 
			
		||||
\item {\tt \$reverse} ($S$ -- $S'$), reverses the order of UTF-8 characters in {\em String\/}~$S$. If $S$ is not a valid UTF-8 string, the return value is undefined and may be also invalid.
 | 
			
		||||
\item {\tt \%1<{<}} ($x$ $y$ -- $z$), computes $z:=x\bmod 2^y=x\&(2^y-1)$ for two {\em Integer\/}s $x$ and $0\leq y\leq 256$.
 | 
			
		||||
\item {\tt \underline{'} $\langle\textit{word-name}\rangle$} ( -- $e$), returns the execution token equal to the current (compile-time) definition of $\langle\textit{word-name}\rangle$, cf.~\ptref{p:blocks}. If the specified word is not found, throws an exception.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2267,7 +2267,9 @@ The following primitives, which use the above conventions, are defined:
 | 
			
		|||
\item {\tt FB02} --- {\tt RAWRESERVE} ($x$ $y$ -- ), creates an output action which would reserve exactly $x$ nanograms (if $y=0$), at most $x$ nanograms (if $y=2$), or all but $x$ nanograms (if $y=1$ or $y=3$), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying $x$ nanograms (or $b-x$ nanograms, where $b$ is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit $+2$ in $y$ means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Currently $x$ must be a non-negative integer, and $y$ must be in the range $0\ldots 3$.
 | 
			
		||||
\item {\tt FB03} --- {\tt RAWRESERVEX} ($s$ $y$ -- ), similar to {\tt RAWRESERVE}, but accepts a {\em Slice $s$} with a {\em CurrencyCollection\/} as an argument. In this way currencies other than Grams can be reserved.
 | 
			
		||||
\item {\tt FB04} --- {\tt SETCODE} ($c$ -- ), creates an output action that would change this smart contract code to that given by {\em Cell\/}~$c$. Notice that this change will take effect only after the successful termination of the current run of the smart contract.
 | 
			
		||||
\item {\tt FB05}--{\tt FB3F} --- Reserved for output action primitives.
 | 
			
		||||
\item {\tt FB06} --- {\tt SETLIBCODE} ($c$ $x$ -- ), creates an output action that would modify the collection of this smart contract libraries by adding or removing library with code given in {\em Cell\/}~$c$. If $x=0$, the library is actually removed if it was previously present in the collection (if not, this action does nothing). If $x=1$, the library is added as a private library, and if $x=2$, the library is added as a public library (and becomes available to all smart contracts if the current smart contract resides in the masterchain); if the library was present in the collection before, its public/private status is changed according to $x$. Values of $x$ other than $0\ldots 2$ are invalid.
 | 
			
		||||
\item {\tt FB07} --- {\tt CHANGELIB} ($h$ $x$ -- ), creates an output action similarly to {\tt SETLIBCODE}, but instead of the library code accepts its hash as an unsigned 256-bit integer $h$. If $x\neq0$ and the library with hash $h$ is absent from the library collection of this smart contract, this output action will fail.
 | 
			
		||||
\item {\tt FB08}--{\tt FB3F} --- Reserved for output action primitives.
 | 
			
		||||
\end{itemize}
 | 
			
		||||
 | 
			
		||||
\mysubsection{Debug primitives}\label{p:prim.debug}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,7 @@
 | 
			
		|||
#include "vm/cp0.h"
 | 
			
		||||
#include "ton/ton-shard.h"
 | 
			
		||||
#include "openssl/rand.hpp"
 | 
			
		||||
#include "crypto/vm/utils.h"
 | 
			
		||||
 | 
			
		||||
#if TD_DARWIN || TD_LINUX
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
| 
						 | 
				
			
			@ -756,91 +757,6 @@ bool TestNode::parse_shard_id(ton::ShardIdFull& shard) {
 | 
			
		|||
  return convert_shard_id(get_word(), shard) || set_error("cannot parse full shard identifier or prefix");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TestNode::parse_stack_value(td::Slice str, vm::StackEntry& value) {
 | 
			
		||||
  if (str.empty() || str.size() > 65535) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  int l = (int)str.size();
 | 
			
		||||
  if (str[0] == '"') {
 | 
			
		||||
    vm::CellBuilder cb;
 | 
			
		||||
    if (l == 1 || str.back() != '"' || l >= 127 + 2 || !cb.store_bytes_bool(str.data() + 1, l - 2)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    value = vm::StackEntry{vm::load_cell_slice_ref(cb.finalize())};
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  if (l >= 3 && (str[0] == 'x' || str[0] == 'b') && str[1] == '{' && str.back() == '}') {
 | 
			
		||||
    unsigned char buff[128];
 | 
			
		||||
    int bits =
 | 
			
		||||
        (str[0] == 'x')
 | 
			
		||||
            ? (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1)
 | 
			
		||||
            : (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1);
 | 
			
		||||
    if (bits < 0) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    value =
 | 
			
		||||
        vm::StackEntry{Ref<vm::CellSlice>{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}};
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
  auto num = td::RefInt256{true};
 | 
			
		||||
  auto& x = num.unique_write();
 | 
			
		||||
  if (l >= 3 && str[0] == '0' && str[1] == 'x') {
 | 
			
		||||
    if (x.parse_hex(str.data() + 2, l - 2) != l - 2) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  } else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') {
 | 
			
		||||
    if (x.parse_hex(str.data() + 3, l - 3) != l - 3) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    x.negate().normalize();
 | 
			
		||||
  } else if (!l || x.parse_dec(str.data(), l) != l) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  value = vm::StackEntry{std::move(num)};
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TestNode::parse_stack_value(vm::StackEntry& value) {
 | 
			
		||||
  auto word = get_word_ext(" \t", "[()]");
 | 
			
		||||
  if (word.empty()) {
 | 
			
		||||
    return set_error("stack value expected instead of end-of-line");
 | 
			
		||||
  }
 | 
			
		||||
  if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) {
 | 
			
		||||
    int expected = (word[0] == '(' ? ')' : ']');
 | 
			
		||||
    std::vector<vm::StackEntry> values;
 | 
			
		||||
    if (!parse_stack_values(values)) {
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    word = get_word_ext(" \t", "[()]");
 | 
			
		||||
    if (word.size() != 1 || word[0] != expected) {
 | 
			
		||||
      return set_error("closing bracket expected");
 | 
			
		||||
    }
 | 
			
		||||
    if (expected == ']') {
 | 
			
		||||
      value = vm::StackEntry{std::move(values)};
 | 
			
		||||
    } else {
 | 
			
		||||
      value = vm::StackEntry::make_list(std::move(values));
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  } else {
 | 
			
		||||
    return parse_stack_value(word, value) || set_error("invalid vm stack value");
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TestNode::parse_stack_values(std::vector<vm::StackEntry>& values) {
 | 
			
		||||
  values.clear();
 | 
			
		||||
  while (!seekeoln()) {
 | 
			
		||||
    if (cur() == ']' || cur() == ')') {
 | 
			
		||||
      break;
 | 
			
		||||
    }
 | 
			
		||||
    values.emplace_back();
 | 
			
		||||
    if (!parse_stack_value(values.back())) {
 | 
			
		||||
      values.pop_back();
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TestNode::set_error(std::string err_msg) {
 | 
			
		||||
  return set_error(td::Status::Error(-1, err_msg));
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -912,6 +828,11 @@ bool TestNode::show_help(std::string command) {
 | 
			
		|||
         "header\n"
 | 
			
		||||
         "byutime <workchain> <shard-prefix> <utime>\tLooks up a block by workchain, shard and creation time, and "
 | 
			
		||||
         "shows its header\n"
 | 
			
		||||
         "creatorstats <block-id-ext> [<count> [<start-pubkey>]]\tLists block creator statistics by validator public "
 | 
			
		||||
         "key\n"
 | 
			
		||||
         "recentcreatorstats <block-id-ext> <start-utime> [<count> [<start-pubkey>]]\tLists block creator statistics "
 | 
			
		||||
         "updated after <start-utime> by validator public "
 | 
			
		||||
         "key\n"
 | 
			
		||||
         "known\tShows the list of all known block ids\n"
 | 
			
		||||
         "privkey <filename>\tLoads a private key from file\n"
 | 
			
		||||
         "help [<command>]\tThis help\n"
 | 
			
		||||
| 
						 | 
				
			
			@ -999,6 +920,12 @@ bool TestNode::do_parse_line() {
 | 
			
		|||
    return parse_shard_id(shard) && parse_uint32(utime) && seekeoln() && lookup_block(shard, 4, utime);
 | 
			
		||||
  } else if (word == "bylt") {
 | 
			
		||||
    return parse_shard_id(shard) && parse_lt(lt) && seekeoln() && lookup_block(shard, 2, lt);
 | 
			
		||||
  } else if (word == "creatorstats" || word == "recentcreatorstats") {
 | 
			
		||||
    count = 1000;
 | 
			
		||||
    int mode = (word == "recentcreatorstats" ? 4 : 0);
 | 
			
		||||
    return parse_block_id_ext(blkid) && (!mode || parse_uint32(utime)) && (seekeoln() || parse_uint32(count)) &&
 | 
			
		||||
           (seekeoln() || (parse_hash(hash) && (mode |= 1))) && seekeoln() &&
 | 
			
		||||
           get_creator_stats(blkid, mode, count, hash, utime);
 | 
			
		||||
  } else if (word == "known") {
 | 
			
		||||
    return eoln() && show_new_blkids(true);
 | 
			
		||||
  } else if (word == "quit" && eoln()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -1090,13 +1017,12 @@ bool TestNode::get_account_state(ton::WorkchainId workchain, ton::StdSmcAddress
 | 
			
		|||
 | 
			
		||||
bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid,
 | 
			
		||||
                                std::string method_name) {
 | 
			
		||||
  std::vector<vm::StackEntry> params;
 | 
			
		||||
  if (!parse_stack_values(params)) {
 | 
			
		||||
    return set_error("cannot parse list of TVM stack values");
 | 
			
		||||
  }
 | 
			
		||||
  if (!seekeoln()) {
 | 
			
		||||
    return set_error("extra characters after a list of TVM stack values");
 | 
			
		||||
  auto R = vm::parse_stack_entries(td::Slice(parse_ptr_, parse_end_));
 | 
			
		||||
  if (R.is_error()) {
 | 
			
		||||
    return set_error(R.move_as_error().to_string());
 | 
			
		||||
  }
 | 
			
		||||
  parse_ptr_ = parse_end_;
 | 
			
		||||
  auto params = R.move_as_ok();
 | 
			
		||||
  if (!ref_blkid.is_valid()) {
 | 
			
		||||
    return set_error("must obtain last block information before making other queries");
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -2236,6 +2162,107 @@ void TestNode::got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mod
 | 
			
		|||
  show_new_blkids();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool TestNode::get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after,
 | 
			
		||||
                                 ton::UnixTime min_utime) {
 | 
			
		||||
  if (!(ready_ && !client_.empty())) {
 | 
			
		||||
    return set_error("server connection not ready");
 | 
			
		||||
  }
 | 
			
		||||
  if (!blkid.is_masterchain_ext()) {
 | 
			
		||||
    return set_error("only masterchain blocks contain block creator statistics");
 | 
			
		||||
  }
 | 
			
		||||
  if (!(mode & 1)) {
 | 
			
		||||
    start_after.set_zero();
 | 
			
		||||
  }
 | 
			
		||||
  auto b = ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_getValidatorStats>(
 | 
			
		||||
                                        mode, ton::create_tl_lite_block_id(blkid), req_count, start_after, min_utime),
 | 
			
		||||
                                    true);
 | 
			
		||||
  LOG(INFO) << "requesting up to " << req_count << " block creator stats records with respect to masterchain block "
 | 
			
		||||
            << blkid.to_str() << " starting from validator public key " << start_after.to_hex() << " created after "
 | 
			
		||||
            << min_utime << " (mode=" << mode << ")";
 | 
			
		||||
  return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, blkid, req_count, start_after,
 | 
			
		||||
                                             min_utime ](td::Result<td::BufferSlice> R) {
 | 
			
		||||
    if (R.is_error()) {
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    auto F = ton::fetch_tl_object<ton::lite_api::liteServer_validatorStats>(R.move_as_ok(), true);
 | 
			
		||||
    if (F.is_error()) {
 | 
			
		||||
      LOG(ERROR) << "cannot parse answer to liteServer.getValidatorStats";
 | 
			
		||||
    } else {
 | 
			
		||||
      auto f = F.move_as_ok();
 | 
			
		||||
      td::actor::send_closure_later(Self, &TestNode::got_creator_stats, blkid, ton::create_block_id(f->id_), mode,
 | 
			
		||||
                                    f->mode_, start_after, min_utime, std::move(f->state_proof_),
 | 
			
		||||
                                    std::move(f->data_proof_), f->count_, req_count, f->complete_);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void TestNode::got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode,
 | 
			
		||||
                                 td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof,
 | 
			
		||||
                                 td::BufferSlice data_proof, int count, int req_count, bool complete) {
 | 
			
		||||
  LOG(INFO) << "got answer to getValidatorStats query: " << count << " records out of " << req_count << ", "
 | 
			
		||||
            << (complete ? "complete" : "incomplete");
 | 
			
		||||
  if (!blkid.is_masterchain_ext()) {
 | 
			
		||||
    LOG(ERROR) << "reference block " << blkid.to_str()
 | 
			
		||||
               << " for block creator statistics is not a valid masterchain block";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (count > req_count) {
 | 
			
		||||
    LOG(ERROR) << "obtained " << count << " answers to getValidatorStats query, but only " << req_count
 | 
			
		||||
               << " were requested";
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if (blkid != req_blkid) {
 | 
			
		||||
    LOG(ERROR) << "answer to getValidatorStats refers to masterchain block " << blkid.to_str()
 | 
			
		||||
               << " different from requested " << req_blkid.to_str();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto R = block::check_extract_state_proof(blkid, state_proof.as_slice(), data_proof.as_slice());
 | 
			
		||||
  if (R.is_error()) {
 | 
			
		||||
    LOG(ERROR) << "masterchain state proof for " << blkid.to_str() << " is invalid : " << R.move_as_error().to_string();
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  bool allow_eq = (mode & 3) != 1;
 | 
			
		||||
  ton::Bits256 key{start_after};
 | 
			
		||||
  std::ostringstream os;
 | 
			
		||||
  try {
 | 
			
		||||
    auto dict = block::get_block_create_stats_dict(R.move_as_ok());
 | 
			
		||||
    if (!dict) {
 | 
			
		||||
      LOG(ERROR) << "cannot extract BlockCreateStats from mc state";
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    for (int i = 0; i < count + (int)complete; i++) {
 | 
			
		||||
      auto v = dict->lookup_nearest_key(key, true, allow_eq);
 | 
			
		||||
      if (v.is_null()) {
 | 
			
		||||
        if (i != count) {
 | 
			
		||||
          LOG(ERROR) << "could fetch only " << i << " CreatorStats entries out of " << count
 | 
			
		||||
                     << " declared in answer to getValidatorStats";
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      block::DiscountedCounter mc_cnt, shard_cnt;
 | 
			
		||||
      if (!block::unpack_CreatorStats(std::move(v), mc_cnt, shard_cnt)) {
 | 
			
		||||
        LOG(ERROR) << "invalid CreatorStats record with key " << key.to_hex();
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      if (mc_cnt.modified_since(min_utime) || shard_cnt.modified_since(min_utime)) {
 | 
			
		||||
        os << key.to_hex() << " mc_cnt:" << mc_cnt << " shard_cnt:" << shard_cnt << std::endl;
 | 
			
		||||
      }
 | 
			
		||||
      allow_eq = false;
 | 
			
		||||
    }
 | 
			
		||||
    if (complete) {
 | 
			
		||||
      os << "(complete)" << std::endl;
 | 
			
		||||
    } else {
 | 
			
		||||
      os << "(incomplete, repeat query from " << key.to_hex() << " )" << std::endl;
 | 
			
		||||
    }
 | 
			
		||||
    td::TerminalIO::out() << os.str();
 | 
			
		||||
  } catch (vm::VmError& err) {
 | 
			
		||||
    LOG(ERROR) << "error while traversing block creator stats: " << err.get_msg();
 | 
			
		||||
  } catch (vm::VmVirtError& err) {
 | 
			
		||||
    LOG(ERROR) << "virtualization error while traversing block creator stats: " << err.get_msg();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char* argv[]) {
 | 
			
		||||
  SET_VERBOSITY_LEVEL(verbosity_INFO);
 | 
			
		||||
  td::set_default_failure_signal_handler();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -153,6 +153,12 @@ class TestNode : public td::actor::Actor {
 | 
			
		|||
                              std::vector<TransId> trans, td::BufferSlice proof);
 | 
			
		||||
  bool get_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode);
 | 
			
		||||
  void got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, td::BufferSlice res);
 | 
			
		||||
  bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after,
 | 
			
		||||
                         ton::UnixTime min_utime);
 | 
			
		||||
  void got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode,
 | 
			
		||||
                         td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof,
 | 
			
		||||
                         td::BufferSlice data_proof, int count, int req_count, bool complete);
 | 
			
		||||
  // parser
 | 
			
		||||
  bool do_parse_line();
 | 
			
		||||
  bool show_help(std::string command);
 | 
			
		||||
  td::Slice get_word(char delim = ' ');
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,11 +38,7 @@ class MemoryKeyValue : public KeyValue {
 | 
			
		|||
  std::string stats() const override;
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  class Cmp : public std::less<> {
 | 
			
		||||
   public:
 | 
			
		||||
    using is_transparent = void;
 | 
			
		||||
  };
 | 
			
		||||
  std::map<std::string, std::string, Cmp> map_;
 | 
			
		||||
  std::map<std::string, std::string, std::less<>> map_;
 | 
			
		||||
  int64 get_count_{0};
 | 
			
		||||
};
 | 
			
		||||
}  // namespace td
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ int main() {
 | 
			
		|||
  SET_VERBOSITY_LEVEL(verbosity_INFO);
 | 
			
		||||
 | 
			
		||||
  std::string db_root_ = "tmp-ee";
 | 
			
		||||
  td::rmrf(db_root_).ignore();
 | 
			
		||||
  td::mkdir(db_root_).ensure();
 | 
			
		||||
 | 
			
		||||
  td::set_default_failure_signal_handler().ensure();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -217,6 +217,7 @@ int main(int argc, char *argv[]) {
 | 
			
		|||
  td::set_default_failure_signal_handler().ensure();
 | 
			
		||||
 | 
			
		||||
  std::string db_root_ = "tmp-ee";
 | 
			
		||||
  td::rmrf(db_root_).ignore();
 | 
			
		||||
  td::mkdir(db_root_).ensure();
 | 
			
		||||
 | 
			
		||||
  td::set_default_failure_signal_handler().ensure();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,7 @@ int main() {
 | 
			
		|||
  SET_VERBOSITY_LEVEL(verbosity_INFO);
 | 
			
		||||
 | 
			
		||||
  std::string db_root_ = "tmp-ee";
 | 
			
		||||
  td::rmrf(db_root_).ignore();
 | 
			
		||||
  td::mkdir(db_root_).ensure();
 | 
			
		||||
 | 
			
		||||
  td::set_default_failure_signal_handler().ensure();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,7 @@ int main() {
 | 
			
		|||
  SET_VERBOSITY_LEVEL(verbosity_INFO);
 | 
			
		||||
 | 
			
		||||
  std::string db_root_ = "tmp-ee";
 | 
			
		||||
  td::rmrf(db_root_).ignore();
 | 
			
		||||
  td::mkdir(db_root_).ensure();
 | 
			
		||||
 | 
			
		||||
  td::set_default_failure_signal_handler().ensure();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -318,8 +318,8 @@ int main() {
 | 
			
		|||
      CHECK(!found);
 | 
			
		||||
      auto vec = s->choose_blocks_to_approve(desc, i);
 | 
			
		||||
      CHECK(vec.size() == 1);
 | 
			
		||||
      CHECK(vec[1] == nullptr);
 | 
			
		||||
      CHECK(ton::validatorsession::SentBlock::get_block_id(vec[1]) == ton::validatorsession::skip_round_candidate_id());
 | 
			
		||||
      CHECK(vec[0] == nullptr);
 | 
			
		||||
      CHECK(ton::validatorsession::SentBlock::get_block_id(vec[0]) == ton::validatorsession::skip_round_candidate_id());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (td::uint32 i = 0; i < total_nodes; i++) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,6 +48,7 @@ liteServer.blockLinkBack to_key_block:Bool from:tonNode.blockIdExt to:tonNode.bl
 | 
			
		|||
liteServer.blockLinkForward to_key_block:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt dest_proof:bytes config_proof:bytes signatures:liteServer.SignatureSet = liteServer.BlockLink;
 | 
			
		||||
liteServer.partialBlockProof complete:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt steps:(vector liteServer.BlockLink) = liteServer.PartialBlockProof;
 | 
			
		||||
liteServer.configInfo mode:# id:tonNode.blockIdExt state_proof:bytes config_proof:bytes = liteServer.ConfigInfo;
 | 
			
		||||
liteServer.validatorStats mode:# id:tonNode.blockIdExt count:int complete:Bool state_proof:bytes data_proof:bytes = liteServer.ValidatorStats;
 | 
			
		||||
 | 
			
		||||
liteServer.debug.verbosity value:int = liteServer.debug.Verbosity;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,6 +72,7 @@ liteServer.listBlockTransactions id:tonNode.blockIdExt mode:# count:# after:mode
 | 
			
		|||
liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
 | 
			
		||||
liteServer.getConfigAll mode:# id:tonNode.blockIdExt = liteServer.ConfigInfo;
 | 
			
		||||
liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) = liteServer.ConfigInfo;
 | 
			
		||||
liteServer.getValidatorStats#091a58bc mode:# id:tonNode.blockIdExt limit:int start_after:mode.0?int256 modified_after:mode.2?int = liteServer.ValidatorStats;
 | 
			
		||||
 | 
			
		||||
liteServer.queryPrefix = Object; 
 | 
			
		||||
liteServer.query data:bytes = Object;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -25,6 +25,8 @@
 | 
			
		|||
#include "td/utils/UInt.h"
 | 
			
		||||
#include "td/utils/misc.h"
 | 
			
		||||
 | 
			
		||||
#include <cinttypes>
 | 
			
		||||
 | 
			
		||||
namespace ton {
 | 
			
		||||
 | 
			
		||||
using WorkchainId = td::int32;
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +143,7 @@ struct AccountIdPrefixFull {
 | 
			
		|||
  std::string to_str() const {
 | 
			
		||||
    char buffer[64];
 | 
			
		||||
    return std::string{
 | 
			
		||||
        buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx)", workchain, (unsigned long long)account_id_prefix)};
 | 
			
		||||
        buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx)", workchain, static_cast<long long>(account_id_prefix))};
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -204,8 +206,8 @@ struct BlockId {
 | 
			
		|||
  }
 | 
			
		||||
  std::string to_str() const {
 | 
			
		||||
    char buffer[64];
 | 
			
		||||
    return std::string{buffer,
 | 
			
		||||
                       (unsigned)snprintf(buffer, 63, "(%d,%016llx,%u)", workchain, (unsigned long long)shard, seqno)};
 | 
			
		||||
    return std::string{buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx,%u)", workchain,
 | 
			
		||||
                                                  static_cast<unsigned long long>(shard), seqno)};
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -282,7 +284,7 @@ struct BlockIdExt {
 | 
			
		|||
    BlockIdExt v;
 | 
			
		||||
    char rh[65];
 | 
			
		||||
    char fh[65];
 | 
			
		||||
    auto r = sscanf(s.begin(), "(%d,%lx,%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh);
 | 
			
		||||
    auto r = sscanf(s.begin(), "(%d,%" SCNu64 ",%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh);
 | 
			
		||||
    if (r < 5) {
 | 
			
		||||
      return td::Status::Error("failed to parse block id");
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -451,6 +451,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) {
 | 
			
		|||
 | 
			
		||||
  int n = 16;
 | 
			
		||||
  int k = 10;
 | 
			
		||||
  td::uint32 wallet_id = 7;
 | 
			
		||||
  std::vector<td::Ed25519::PrivateKey> private_keys;
 | 
			
		||||
  for (int i = 0; i < n; i++) {
 | 
			
		||||
    private_keys.push_back(td::Ed25519::generate_private_key().move_as_ok());
 | 
			
		||||
| 
						 | 
				
			
			@ -458,6 +459,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) {
 | 
			
		|||
 | 
			
		||||
  auto ms = ton::MultisigWallet::create();
 | 
			
		||||
  auto init_data = ms->create_init_data(
 | 
			
		||||
      wallet_id,
 | 
			
		||||
      td::transform(private_keys, [](const auto& pk) { return pk.get_public_key().move_as_ok().as_octet_string(); }),
 | 
			
		||||
      k);
 | 
			
		||||
  ms = ton::MultisigWallet::create(init_data);
 | 
			
		||||
| 
						 | 
				
			
			@ -472,7 +474,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) {
 | 
			
		|||
    ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), 1);
 | 
			
		||||
    icb.store_bytes("\0\0\0\0", 4);
 | 
			
		||||
    vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure();
 | 
			
		||||
    ton::MultisigWallet::QueryBuilder qb(-1 - i, icb.finalize());
 | 
			
		||||
    ton::MultisigWallet::QueryBuilder qb(wallet_id, -1 - i, icb.finalize());
 | 
			
		||||
    for (int i = 0; i < k - 1; i++) {
 | 
			
		||||
      qb.sign(i, private_keys[i]);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -106,11 +106,7 @@ class KeyValueInmemory : public KeyValue {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  class Cmp : public std::less<> {
 | 
			
		||||
   public:
 | 
			
		||||
    using is_transparent = void;
 | 
			
		||||
  };
 | 
			
		||||
  std::map<std::string, td::SecureString, Cmp> map_;
 | 
			
		||||
  std::map<std::string, td::SecureString, std::less<>> map_;
 | 
			
		||||
};
 | 
			
		||||
}  // namespace detail
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -648,12 +648,13 @@ td::Result<bool> Config::config_del_gc(ton::PublicKeyHash key) {
 | 
			
		|||
class ValidatorElectionBidCreator : public td::actor::Actor {
 | 
			
		||||
 public:
 | 
			
		||||
  ValidatorElectionBidCreator(td::uint32 date, std::string addr, std::string wallet, std::string dir,
 | 
			
		||||
                              td::actor::ActorId<ValidatorEngine> engine,
 | 
			
		||||
                              std::vector<ton::PublicKeyHash> old_keys, td::actor::ActorId<ValidatorEngine> engine,
 | 
			
		||||
                              td::actor::ActorId<ton::keyring::Keyring> keyring, td::Promise<td::BufferSlice> promise)
 | 
			
		||||
      : date_(date)
 | 
			
		||||
      , addr_(addr)
 | 
			
		||||
      , wallet_(wallet)
 | 
			
		||||
      , dir_(dir)
 | 
			
		||||
      , old_keys_(std::move(old_keys))
 | 
			
		||||
      , engine_(engine)
 | 
			
		||||
      , keyring_(keyring)
 | 
			
		||||
      , promise_(std::move(promise)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -661,6 +662,22 @@ class ValidatorElectionBidCreator : public td::actor::Actor {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  void start_up() override {
 | 
			
		||||
    if (old_keys_.size() > 0) {
 | 
			
		||||
      CHECK(old_keys_.size() == 3);
 | 
			
		||||
 | 
			
		||||
      adnl_addr_ = ton::adnl::AdnlNodeIdShort{old_keys_[2]};
 | 
			
		||||
      perm_key_ = old_keys_[0];
 | 
			
		||||
 | 
			
		||||
      auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<ton::PublicKey> R) {
 | 
			
		||||
        if (R.is_error()) {
 | 
			
		||||
          td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::abort_query, R.move_as_error());
 | 
			
		||||
        } else {
 | 
			
		||||
          td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::got_perm_public_key, R.move_as_ok());
 | 
			
		||||
        }
 | 
			
		||||
      });
 | 
			
		||||
      td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, perm_key_, std::move(P));
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    auto pk1 = ton::PrivateKey{ton::privkeys::Ed25519::random()};
 | 
			
		||||
    perm_key_full_ = pk1.compute_public_key();
 | 
			
		||||
    perm_key_ = perm_key_full_.compute_short_id();
 | 
			
		||||
| 
						 | 
				
			
			@ -712,6 +729,11 @@ class ValidatorElectionBidCreator : public td::actor::Actor {
 | 
			
		|||
                            ttl_, ig.get_promise());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void got_perm_public_key(ton::PublicKey pub) {
 | 
			
		||||
    perm_key_full_ = pub;
 | 
			
		||||
    updated_config();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void updated_config() {
 | 
			
		||||
    auto codeR = td::read_file_str(dir_ + "/validator-elect-req.fif");
 | 
			
		||||
    if (codeR.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -792,6 +814,7 @@ class ValidatorElectionBidCreator : public td::actor::Actor {
 | 
			
		|||
  std::string addr_;
 | 
			
		||||
  std::string wallet_;
 | 
			
		||||
  std::string dir_;
 | 
			
		||||
  std::vector<ton::PublicKeyHash> old_keys_;
 | 
			
		||||
  td::actor::ActorId<ValidatorEngine> engine_;
 | 
			
		||||
  td::actor::ActorId<ton::keyring::Keyring> keyring_;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -899,28 +922,24 @@ void ValidatorEngine::alarm() {
 | 
			
		|||
    if (state_.not_null()) {
 | 
			
		||||
      bool need_write = false;
 | 
			
		||||
 | 
			
		||||
      auto configR = state_->get_config_holder();
 | 
			
		||||
      configR.ensure();
 | 
			
		||||
      auto config = configR.move_as_ok();
 | 
			
		||||
      auto cur_t = config->get_validator_set_start_stop(0);
 | 
			
		||||
      CHECK(cur_t.first > 0);
 | 
			
		||||
      LOG(ERROR) << "curt: " << cur_t.first << " " << cur_t.second;
 | 
			
		||||
 | 
			
		||||
      auto val_set = state_->get_total_validator_set(0);
 | 
			
		||||
      auto e = val_set->export_vector();
 | 
			
		||||
      std::set<ton::PublicKeyHash> adnl_ids;
 | 
			
		||||
      for (auto &el : e) {
 | 
			
		||||
        adnl_ids.insert(ton::PublicKeyHash{el.addr});
 | 
			
		||||
      }
 | 
			
		||||
      std::set<ton::PublicKeyHash> to_del;
 | 
			
		||||
      for (auto &val : config_.validators) {
 | 
			
		||||
        if (val.second.expire_at < state_->get_unix_time() &&
 | 
			
		||||
            !val_set->is_validator(ton::NodeIdShort{val.first.bits256_value()})) {
 | 
			
		||||
        bool is_validator = false;
 | 
			
		||||
        if (val_set->is_validator(ton::NodeIdShort{val.first.bits256_value()})) {
 | 
			
		||||
          is_validator = true;
 | 
			
		||||
        }
 | 
			
		||||
        if (!is_validator && val.second.election_date < cur_t.first && cur_t.first + 600 < state_->get_unix_time()) {
 | 
			
		||||
          to_del.insert(val.first);
 | 
			
		||||
        } else {
 | 
			
		||||
          std::set<ton::PublicKeyHash> to_del_2;
 | 
			
		||||
          for (auto &x : val.second.adnl_ids) {
 | 
			
		||||
            if (x.second < state_->get_unix_time() && !adnl_ids.count(x.first)) {
 | 
			
		||||
              to_del_2.insert(x.first);
 | 
			
		||||
            }
 | 
			
		||||
          }
 | 
			
		||||
          for (auto &x : to_del_2) {
 | 
			
		||||
            config_.config_del_validator_temp_key(val.first, x);
 | 
			
		||||
            need_write = true;
 | 
			
		||||
          }
 | 
			
		||||
          continue;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      for (auto &x : to_del) {
 | 
			
		||||
| 
						 | 
				
			
			@ -2699,9 +2718,23 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_createEle
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  std::vector<ton::PublicKeyHash> v;
 | 
			
		||||
  for (auto &x : config_.validators) {
 | 
			
		||||
    if (x.second.election_date == static_cast<ton::UnixTime>(query.election_date_)) {
 | 
			
		||||
      if (x.second.temp_keys.size() == 0 || x.second.adnl_ids.size() == 0) {
 | 
			
		||||
        promise.set_value(
 | 
			
		||||
            create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "prev bid is partial")));
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      v.push_back(x.first);
 | 
			
		||||
      v.push_back(x.second.temp_keys.begin()->first);
 | 
			
		||||
      v.push_back(x.second.adnl_ids.begin()->first);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  td::actor::create_actor<ValidatorElectionBidCreator>("bidcreate", query.election_date_, query.election_addr_,
 | 
			
		||||
                                                       query.wallet_, fift_dir_, actor_id(this), keyring_.get(),
 | 
			
		||||
                                                       std::move(promise))
 | 
			
		||||
                                                       query.wallet_, fift_dir_, std::move(v), actor_id(this),
 | 
			
		||||
                                                       keyring_.get(), std::move(promise))
 | 
			
		||||
      .release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,9 +45,11 @@ void ArchiveManager::add_handle(BlockHandle handle, td::Promise<td::Unit> promis
 | 
			
		|||
    update_handle(std::move(handle), std::move(promise));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto p = get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(),
 | 
			
		||||
  auto p = handle->id().is_masterchain()
 | 
			
		||||
               ? get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(),
 | 
			
		||||
                                      handle->unix_time(), handle->logical_time(),
 | 
			
		||||
                                handle->inited_is_key_block() && handle->is_key_block());
 | 
			
		||||
                                      handle->inited_is_key_block() && handle->is_key_block())
 | 
			
		||||
               : get_package_id(handle->masterchain_ref_block());
 | 
			
		||||
  auto f = get_file_desc(handle->id().shard_full(), p, handle->id().seqno(), handle->unix_time(),
 | 
			
		||||
                         handle->logical_time(), true);
 | 
			
		||||
  td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_handle, std::move(handle), std::move(promise));
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +250,7 @@ void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td
 | 
			
		|||
  td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::get_file(BlockHandle handle, FileReference ref_id, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
void ArchiveManager::get_file(ConstBlockHandle handle, FileReference ref_id, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
  if (handle->moved_to_archive()) {
 | 
			
		||||
    auto f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), 0, 0, 0, false);
 | 
			
		||||
    if (f) {
 | 
			
		||||
| 
						 | 
				
			
			@ -368,27 +370,53 @@ void ArchiveManager::check_persistent_state(BlockIdExt block_id, BlockIdExt mast
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts,
 | 
			
		||||
                                            td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                            td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  auto f = get_file_desc_by_unix_time(account_id, ts, false);
 | 
			
		||||
  if (f) {
 | 
			
		||||
    td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts,
 | 
			
		||||
                            std::move(promise));
 | 
			
		||||
    auto n = get_next_file_desc(f);
 | 
			
		||||
    td::actor::ActorId<ArchiveSlice> aid;
 | 
			
		||||
    if (n) {
 | 
			
		||||
      aid = n->file_actor_id();
 | 
			
		||||
    }
 | 
			
		||||
    auto P = td::PromiseCreator::lambda(
 | 
			
		||||
        [aid, account_id, ts, promise = std::move(promise)](td::Result<ConstBlockHandle> R) mutable {
 | 
			
		||||
          if (R.is_ok() || R.error().code() != ErrorCode::notready || aid.empty()) {
 | 
			
		||||
            promise.set_result(std::move(R));
 | 
			
		||||
          } else {
 | 
			
		||||
            td::actor::send_closure(aid, &ArchiveSlice::get_block_by_unix_time, account_id, ts, std::move(promise));
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
    td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts, std::move(P));
 | 
			
		||||
  } else {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "ts not in db"));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise) {
 | 
			
		||||
void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt,
 | 
			
		||||
                                     td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  auto f = get_file_desc_by_lt(account_id, lt, false);
 | 
			
		||||
  if (f) {
 | 
			
		||||
    td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise));
 | 
			
		||||
    auto n = get_next_file_desc(f);
 | 
			
		||||
    td::actor::ActorId<ArchiveSlice> aid;
 | 
			
		||||
    if (n) {
 | 
			
		||||
      aid = n->file_actor_id();
 | 
			
		||||
    }
 | 
			
		||||
    auto P = td::PromiseCreator::lambda(
 | 
			
		||||
        [aid, account_id, lt, promise = std::move(promise)](td::Result<ConstBlockHandle> R) mutable {
 | 
			
		||||
          if (R.is_ok() || R.error().code() != ErrorCode::notready || aid.empty()) {
 | 
			
		||||
            promise.set_result(std::move(R));
 | 
			
		||||
          } else {
 | 
			
		||||
            td::actor::send_closure(aid, &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise));
 | 
			
		||||
          }
 | 
			
		||||
        });
 | 
			
		||||
    td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(P));
 | 
			
		||||
  } else {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "lt not in db"));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno,
 | 
			
		||||
                                        td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                        td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  auto f = get_file_desc_by_seqno(account_id, seqno, false);
 | 
			
		||||
  if (f) {
 | 
			
		||||
    td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_seqno, account_id, seqno,
 | 
			
		||||
| 
						 | 
				
			
			@ -398,7 +426,7 @@ void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeq
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::delete_package(PackageId id) {
 | 
			
		||||
void ArchiveManager::delete_package(PackageId id, td::Promise<td::Unit> promise) {
 | 
			
		||||
  auto key = create_serialize_tl_object<ton_api::db_files_package_key>(id.id, id.key, id.temp);
 | 
			
		||||
 | 
			
		||||
  std::string value;
 | 
			
		||||
| 
						 | 
				
			
			@ -411,24 +439,27 @@ void ArchiveManager::delete_package(PackageId id) {
 | 
			
		|||
  auto x = R.move_as_ok();
 | 
			
		||||
 | 
			
		||||
  if (x->deleted_) {
 | 
			
		||||
    promise.set_value(td::Unit());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto &m = get_file_map(id);
 | 
			
		||||
  auto it = m.find(id);
 | 
			
		||||
  if (it == m.end() || it->second.deleted) {
 | 
			
		||||
    promise.set_value(td::Unit());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  it->second.deleted = true;
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id](td::Result<td::Unit> R) {
 | 
			
		||||
  auto P = td::PromiseCreator::lambda(
 | 
			
		||||
      [SelfId = actor_id(this), id, promise = std::move(promise)](td::Result<td::Unit> R) mutable {
 | 
			
		||||
        R.ensure();
 | 
			
		||||
    td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id);
 | 
			
		||||
        td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id, std::move(promise));
 | 
			
		||||
      });
 | 
			
		||||
  td::actor::send_closure(it->second.file_actor_id(), &ArchiveSlice::destroy, std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::deleted_package(PackageId id) {
 | 
			
		||||
void ArchiveManager::deleted_package(PackageId id, td::Promise<td::Unit> promise) {
 | 
			
		||||
  auto key = create_serialize_tl_object<ton_api::db_files_package_key>(id.id, id.key, id.temp);
 | 
			
		||||
 | 
			
		||||
  std::string value;
 | 
			
		||||
| 
						 | 
				
			
			@ -441,6 +472,7 @@ void ArchiveManager::deleted_package(PackageId id) {
 | 
			
		|||
  auto x = R.move_as_ok();
 | 
			
		||||
 | 
			
		||||
  if (x->deleted_) {
 | 
			
		||||
    promise.set_value(td::Unit());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  x->deleted_ = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -453,6 +485,7 @@ void ArchiveManager::deleted_package(PackageId id) {
 | 
			
		|||
  CHECK(it != m.end());
 | 
			
		||||
  CHECK(it->second.deleted);
 | 
			
		||||
  it->second.clear_actor_id();
 | 
			
		||||
  promise.set_value(td::Unit());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::load_package(PackageId id) {
 | 
			
		||||
| 
						 | 
				
			
			@ -690,6 +723,18 @@ ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(AccountIdPr
 | 
			
		|||
  return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ArchiveManager::FileDescription *ArchiveManager::get_next_file_desc(FileDescription *f) {
 | 
			
		||||
  auto &m = get_file_map(f->id);
 | 
			
		||||
  auto it = m.find(f->id);
 | 
			
		||||
  CHECK(it != m.end());
 | 
			
		||||
  it++;
 | 
			
		||||
  if (it == m.end()) {
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  } else {
 | 
			
		||||
    return &it->second;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ArchiveManager::FileDescription *ArchiveManager::get_temp_file_desc_by_idx(PackageId idx) {
 | 
			
		||||
  auto it = temp_files_.find(idx);
 | 
			
		||||
  if (it != temp_files_.end()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -797,7 +842,7 @@ void ArchiveManager::run_gc(UnixTime ts) {
 | 
			
		|||
  vec.resize(vec.size() - 1, PackageId::empty(false, true));
 | 
			
		||||
 | 
			
		||||
  for (auto &x : vec) {
 | 
			
		||||
    delete_package(x);
 | 
			
		||||
    delete_package(x, [](td::Unit) {});
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -841,7 +886,7 @@ void ArchiveManager::persistent_state_gc(FileHash last) {
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result<BlockHandle> R) {
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result<ConstBlockHandle> R) {
 | 
			
		||||
    if (R.is_error()) {
 | 
			
		||||
      td::actor::send_closure(SelfId, &ArchiveManager::got_gc_masterchain_handle, nullptr, hash);
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -852,7 +897,7 @@ void ArchiveManager::persistent_state_gc(FileHash last) {
 | 
			
		|||
  get_block_by_seqno(AccountIdPrefixFull{masterchainId, 0}, seqno, std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveManager::got_gc_masterchain_handle(BlockHandle handle, FileHash hash) {
 | 
			
		||||
void ArchiveManager::got_gc_masterchain_handle(ConstBlockHandle handle, FileHash hash) {
 | 
			
		||||
  bool to_del = false;
 | 
			
		||||
  if (!handle || !handle->inited_unix_time() || !handle->unix_time()) {
 | 
			
		||||
    to_del = true;
 | 
			
		||||
| 
						 | 
				
			
			@ -881,7 +926,7 @@ PackageId ArchiveManager::get_temp_package_id_by_unixtime(UnixTime ts) const {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
PackageId ArchiveManager::get_key_package_id(BlockSeqno seqno) const {
 | 
			
		||||
  return PackageId{seqno - seqno % 200000, true, false};
 | 
			
		||||
  return PackageId{seqno - seqno % key_archive_size(), true, false};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
PackageId ArchiveManager::get_package_id(BlockSeqno seqno) const {
 | 
			
		||||
| 
						 | 
				
			
			@ -896,7 +941,7 @@ PackageId ArchiveManager::get_package_id_force(BlockSeqno masterchain_seqno, Sha
 | 
			
		|||
  PackageId p = PackageId::empty(false, false);
 | 
			
		||||
  if (!is_key) {
 | 
			
		||||
    auto it = files_.upper_bound(PackageId{masterchain_seqno, false, false});
 | 
			
		||||
    p = PackageId{masterchain_seqno - (masterchain_seqno % 20000), false, false};
 | 
			
		||||
    p = PackageId{masterchain_seqno - (masterchain_seqno % archive_size()), false, false};
 | 
			
		||||
    if (it != files_.begin()) {
 | 
			
		||||
      it--;
 | 
			
		||||
      if (p < it->first) {
 | 
			
		||||
| 
						 | 
				
			
			@ -980,6 +1025,7 @@ void ArchiveManager::set_async_mode(bool mode, td::Promise<td::Unit> promise) {
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace validator
 | 
			
		||||
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -48,7 +48,7 @@ class ArchiveManager : public td::actor::Actor {
 | 
			
		|||
  void get_key_block_proof(FileReference ref_id, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
  void get_temp_file_short(FileReference ref_id, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
  void get_file_short(FileReference ref_id, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
  void get_file(BlockHandle handle, FileReference ref_id, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
  void get_file(ConstBlockHandle handle, FileReference ref_id, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
 | 
			
		||||
  void add_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise<td::Unit> promise);
 | 
			
		||||
  void add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data,
 | 
			
		||||
| 
						 | 
				
			
			@ -60,12 +60,15 @@ class ArchiveManager : public td::actor::Actor {
 | 
			
		|||
  void check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise<bool> promise);
 | 
			
		||||
  void check_zero_state(BlockIdExt block_id, td::Promise<bool> promise);
 | 
			
		||||
 | 
			
		||||
  //void truncate(BlockSeqno masterchain_seqno, td::Promise<td::Unit> promise);
 | 
			
		||||
  //void truncate_continue(BlockSeqno masterchain_seqno, td::Promise<td::Unit> promise);
 | 
			
		||||
 | 
			
		||||
  void run_gc(UnixTime ts);
 | 
			
		||||
 | 
			
		||||
  /* from LTDB */
 | 
			
		||||
  void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
  void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
  void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
 | 
			
		||||
  void get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise);
 | 
			
		||||
  void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit,
 | 
			
		||||
| 
						 | 
				
			
			@ -77,6 +80,13 @@ class ArchiveManager : public td::actor::Actor {
 | 
			
		|||
  void commit_transaction();
 | 
			
		||||
  void set_async_mode(bool mode, td::Promise<td::Unit> promise);
 | 
			
		||||
 | 
			
		||||
  static constexpr td::uint32 archive_size() {
 | 
			
		||||
    return 20000;
 | 
			
		||||
  }
 | 
			
		||||
  static constexpr td::uint32 key_archive_size() {
 | 
			
		||||
    return 200000;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  struct FileDescription {
 | 
			
		||||
    struct Desc {
 | 
			
		||||
| 
						 | 
				
			
			@ -113,8 +123,8 @@ class ArchiveManager : public td::actor::Actor {
 | 
			
		|||
  std::map<FileHash, FileReferenceShort> perm_states_;
 | 
			
		||||
 | 
			
		||||
  void load_package(PackageId seqno);
 | 
			
		||||
  void delete_package(PackageId seqno);
 | 
			
		||||
  void deleted_package(PackageId seqno);
 | 
			
		||||
  void delete_package(PackageId seqno, td::Promise<td::Unit> promise);
 | 
			
		||||
  void deleted_package(PackageId seqno, td::Promise<td::Unit> promise);
 | 
			
		||||
  void get_handle_cont(BlockIdExt block_id, PackageId id, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_handle_finish(BlockHandle handle, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +139,7 @@ class ArchiveManager : public td::actor::Actor {
 | 
			
		|||
  FileDescription *get_file_desc_by_seqno(AccountIdPrefixFull shard, BlockSeqno seqno, bool key_block);
 | 
			
		||||
  FileDescription *get_file_desc_by_lt(AccountIdPrefixFull shard, LogicalTime lt, bool key_block);
 | 
			
		||||
  FileDescription *get_file_desc_by_unix_time(AccountIdPrefixFull shard, UnixTime ts, bool key_block);
 | 
			
		||||
  FileDescription *get_next_file_desc(FileDescription *f);
 | 
			
		||||
  FileDescription *get_temp_file_desc_by_idx(PackageId idx);
 | 
			
		||||
  PackageId get_max_temp_file_desc_idx();
 | 
			
		||||
  PackageId get_prev_temp_file_desc_idx(PackageId id);
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +147,7 @@ class ArchiveManager : public td::actor::Actor {
 | 
			
		|||
  void written_perm_state(FileReferenceShort id);
 | 
			
		||||
 | 
			
		||||
  void persistent_state_gc(FileHash last);
 | 
			
		||||
  void got_gc_masterchain_handle(BlockHandle handle, FileHash hash);
 | 
			
		||||
  void got_gc_masterchain_handle(ConstBlockHandle handle, FileHash hash);
 | 
			
		||||
 | 
			
		||||
  std::string db_root_;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -204,6 +204,28 @@ void ArchiveSlice::get_handle(BlockIdExt block_id, td::Promise<BlockHandle> prom
 | 
			
		|||
  promise.set_value(std::move(handle));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveSlice::get_temp_handle(BlockIdExt block_id, td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  if (destroyed_) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  CHECK(!key_blocks_only_);
 | 
			
		||||
  std::string value;
 | 
			
		||||
  auto R = kv_->get(get_db_key_block_info(block_id), value);
 | 
			
		||||
  R.ensure();
 | 
			
		||||
  if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "handle not in archive slice"));
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto E = create_block_handle(td::BufferSlice{value});
 | 
			
		||||
  E.ensure();
 | 
			
		||||
  auto handle = E.move_as_ok();
 | 
			
		||||
  if (!temp_) {
 | 
			
		||||
    handle->set_handle_moved_to_archive();
 | 
			
		||||
  }
 | 
			
		||||
  promise.set_value(std::move(handle));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveSlice::get_file(FileReference ref_id, td::Promise<td::BufferSlice> promise) {
 | 
			
		||||
  if (destroyed_) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
 | 
			
		||||
| 
						 | 
				
			
			@ -231,7 +253,7 @@ void ArchiveSlice::get_file(FileReference ref_id, td::Promise<td::BufferSlice> p
 | 
			
		|||
void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id,
 | 
			
		||||
                                    std::function<td::int32(ton_api::db_lt_desc_value &)> compare_desc,
 | 
			
		||||
                                    std::function<td::int32(ton_api::db_lt_el_value &)> compare, bool exact,
 | 
			
		||||
                                    td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                    td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  if (destroyed_) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd"));
 | 
			
		||||
    return;
 | 
			
		||||
| 
						 | 
				
			
			@ -281,7 +303,7 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id,
 | 
			
		|||
        lseq = create_block_id(e->id_);
 | 
			
		||||
        l = x;
 | 
			
		||||
      } else {
 | 
			
		||||
        get_handle(create_block_id(e->id_), std::move(promise));
 | 
			
		||||
        get_temp_handle(create_block_id(e->id_), std::move(promise));
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -299,7 +321,7 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id,
 | 
			
		|||
    }
 | 
			
		||||
    if (block_id.is_valid() && ls + 1 == block_id.id.seqno) {
 | 
			
		||||
      if (!exact) {
 | 
			
		||||
        get_handle(block_id, std::move(promise));
 | 
			
		||||
        get_temp_handle(block_id, std::move(promise));
 | 
			
		||||
      } else {
 | 
			
		||||
        promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found"));
 | 
			
		||||
      }
 | 
			
		||||
| 
						 | 
				
			
			@ -307,13 +329,14 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id,
 | 
			
		|||
    }
 | 
			
		||||
  }
 | 
			
		||||
  if (!exact && block_id.is_valid()) {
 | 
			
		||||
    get_handle(block_id, std::move(promise));
 | 
			
		||||
    get_temp_handle(block_id, std::move(promise));
 | 
			
		||||
  } else {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found"));
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise) {
 | 
			
		||||
void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt,
 | 
			
		||||
                                   td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  return get_block_common(
 | 
			
		||||
      account_id,
 | 
			
		||||
      [lt](ton_api::db_lt_desc_value &w) {
 | 
			
		||||
| 
						 | 
				
			
			@ -326,7 +349,7 @@ void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime l
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno,
 | 
			
		||||
                                      td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                      td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  return get_block_common(
 | 
			
		||||
      account_id,
 | 
			
		||||
      [seqno](ton_api::db_lt_desc_value &w) {
 | 
			
		||||
| 
						 | 
				
			
			@ -343,7 +366,7 @@ void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void ArchiveSlice::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts,
 | 
			
		||||
                                          td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                          td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  return get_block_common(
 | 
			
		||||
      account_id,
 | 
			
		||||
      [ts](ton_api::db_lt_desc_value &w) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,16 +35,17 @@ class ArchiveSlice : public td::actor::Actor {
 | 
			
		|||
  void update_handle(BlockHandle handle, td::Promise<td::Unit> promise);
 | 
			
		||||
  void add_file(FileReference ref_id, td::BufferSlice data, td::Promise<td::Unit> promise);
 | 
			
		||||
  void get_handle(BlockIdExt block_id, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_temp_handle(BlockIdExt block_id, td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
  void get_file(FileReference ref_id, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
 | 
			
		||||
  /* from LTDB */
 | 
			
		||||
  void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<BlockHandle> promise);
 | 
			
		||||
  void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
  void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
  void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
  void get_block_common(AccountIdPrefixFull account_id,
 | 
			
		||||
                        std::function<td::int32(ton_api::db_lt_desc_value &)> compare_desc,
 | 
			
		||||
                        std::function<td::int32(ton_api::db_lt_el_value &)> compare, bool exact,
 | 
			
		||||
                        td::Promise<BlockHandle> promise);
 | 
			
		||||
                        td::Promise<ConstBlockHandle> promise);
 | 
			
		||||
 | 
			
		||||
  void get_slice(td::uint64 offset, td::uint32 limit, td::Promise<td::BufferSlice> promise);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -170,4 +170,8 @@ void Package::iterate(std::function<bool(std::string, td::BufferSlice, td::uint6
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Package::~Package() {
 | 
			
		||||
  fd_.close();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,6 +11,8 @@ class Package {
 | 
			
		|||
  static td::Result<Package> open(std::string path, bool read_only = false, bool create = false);
 | 
			
		||||
 | 
			
		||||
  Package(td::FileFd fd);
 | 
			
		||||
  Package(Package &&p) = default;
 | 
			
		||||
  ~Package();
 | 
			
		||||
 | 
			
		||||
  td::Status truncate(td::uint64 size);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,7 +51,7 @@ void RootDb::store_block_data(BlockHandle handle, td::Ref<BlockData> block, td::
 | 
			
		|||
                          std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_data(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) {
 | 
			
		||||
void RootDb::get_block_data(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) {
 | 
			
		||||
  if (!handle->received()) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "not in db"));
 | 
			
		||||
  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +88,7 @@ void RootDb::store_block_signatures(BlockHandle handle, td::Ref<BlockSignatureSe
 | 
			
		|||
                          data->serialize(), std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_signatures(BlockHandle handle, td::Promise<td::Ref<BlockSignatureSet>> promise) {
 | 
			
		||||
void RootDb::get_block_signatures(ConstBlockHandle handle, td::Promise<td::Ref<BlockSignatureSet>> promise) {
 | 
			
		||||
  if (!handle->inited_signatures() || handle->moved_to_archive()) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "not in db"));
 | 
			
		||||
  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +124,7 @@ void RootDb::store_block_proof(BlockHandle handle, td::Ref<Proof> proof, td::Pro
 | 
			
		|||
                          std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_proof(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) {
 | 
			
		||||
void RootDb::get_block_proof(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) {
 | 
			
		||||
  if (!handle->inited_proof()) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "not in db"));
 | 
			
		||||
  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -159,7 +159,7 @@ void RootDb::store_block_proof_link(BlockHandle handle, td::Ref<ProofLink> proof
 | 
			
		|||
                          proof->data(), std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_proof_link(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) {
 | 
			
		||||
void RootDb::get_block_proof_link(ConstBlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) {
 | 
			
		||||
  if (!handle->inited_proof_link()) {
 | 
			
		||||
    promise.set_error(td::Status::Error(ErrorCode::notready, "not in db"));
 | 
			
		||||
  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -248,7 +248,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref<ShardState> state,
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_state(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) {
 | 
			
		||||
void RootDb::get_block_state(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) {
 | 
			
		||||
  if (handle->inited_state_boc()) {
 | 
			
		||||
    if (handle->deleted_state_boc()) {
 | 
			
		||||
      promise.set_error(td::Status::Error(ErrorCode::error, "state already gc'd"));
 | 
			
		||||
| 
						 | 
				
			
			@ -323,15 +323,15 @@ void RootDb::apply_block(BlockHandle handle, td::Promise<td::Unit> promise) {
 | 
			
		|||
      .release();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockHandle> promise) {
 | 
			
		||||
void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_lt, account, lt, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<BlockHandle> promise) {
 | 
			
		||||
void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_unix_time, account, ts, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<BlockHandle> promise) {
 | 
			
		||||
void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_seqno, account, seqno, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,17 +41,17 @@ class RootDb : public Db {
 | 
			
		|||
  void start_up() override;
 | 
			
		||||
 | 
			
		||||
  void store_block_data(BlockHandle handle, td::Ref<BlockData> block, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_block_data(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
  void get_block_data(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
 | 
			
		||||
  void store_block_signatures(BlockHandle handle, td::Ref<BlockSignatureSet> data,
 | 
			
		||||
                              td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_block_signatures(BlockHandle handle, td::Promise<td::Ref<BlockSignatureSet>> promise) override;
 | 
			
		||||
  void get_block_signatures(ConstBlockHandle handle, td::Promise<td::Ref<BlockSignatureSet>> promise) override;
 | 
			
		||||
 | 
			
		||||
  void store_block_proof(BlockHandle handle, td::Ref<Proof> proof, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_block_proof(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
  void get_block_proof(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
 | 
			
		||||
  void store_block_proof_link(BlockHandle handle, td::Ref<ProofLink> proof, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_block_proof_link(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
  void get_block_proof_link(ConstBlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
 | 
			
		||||
  void store_block_candidate(BlockCandidate candidate, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_block_candidate(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash,
 | 
			
		||||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ class RootDb : public Db {
 | 
			
		|||
 | 
			
		||||
  void store_block_state(BlockHandle handle, td::Ref<ShardState> state,
 | 
			
		||||
                         td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_block_state(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_block_state(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
 | 
			
		||||
  void store_block_handle(BlockHandle handle, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_block_handle(BlockIdExt id, td::Promise<BlockHandle> promise) override;
 | 
			
		||||
| 
						 | 
				
			
			@ -82,9 +82,9 @@ class RootDb : public Db {
 | 
			
		|||
  void try_get_static_file(FileHash file_hash, td::Promise<td::BufferSlice> promise) override;
 | 
			
		||||
 | 
			
		||||
  void apply_block(BlockHandle handle, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<BlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<BlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
 | 
			
		||||
  void update_init_masterchain_block(BlockIdExt block, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void get_init_masterchain_block(td::Promise<BlockIdExt> promise) override;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,6 +38,7 @@ td::Result<td::Ref<BlockSignatureSet>> create_signature_set(td::BufferSlice sig_
 | 
			
		|||
td::Result<td::Ref<ShardState>> create_shard_state(BlockIdExt block_id, td::BufferSlice data);
 | 
			
		||||
td::Result<td::Ref<ShardState>> create_shard_state(BlockIdExt block_id, td::Ref<vm::DataCell> root_cell);
 | 
			
		||||
td::Result<BlockHandle> create_block_handle(td::BufferSlice data);
 | 
			
		||||
td::Result<ConstBlockHandle> create_temp_block_handle(td::BufferSlice data);
 | 
			
		||||
BlockHandle create_empty_block_handle(BlockIdExt id);
 | 
			
		||||
td::Result<td::Ref<ExtMessage>> create_ext_message(td::BufferSlice data);
 | 
			
		||||
td::Result<td::Ref<IhrMessage>> create_ihr_message(td::BufferSlice data);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,6 +53,14 @@ td::Ref<ValidatorSet> ConfigHolderQ::get_validator_set(ShardIdFull shard, UnixTi
 | 
			
		|||
  return Ref<ValidatorSetQ>{true, cc_seqno, shard, std::move(nodes)};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::pair<UnixTime, UnixTime> ConfigHolderQ::get_validator_set_start_stop(int next) const {
 | 
			
		||||
  if (!config_) {
 | 
			
		||||
    LOG(ERROR) << "MasterchainStateQ::get_validator_set_start_stop() : no config";
 | 
			
		||||
    return {};
 | 
			
		||||
  }
 | 
			
		||||
  return config_->get_validator_set_start_stop(next);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace validator
 | 
			
		||||
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ class ConfigHolderQ : public ConfigHolder {
 | 
			
		|||
  // if necessary, add more public methods providing interface to config_->...()
 | 
			
		||||
  td::Ref<ValidatorSet> get_total_validator_set(int next) const override;  // next = -1 -> prev, next = 0 -> cur
 | 
			
		||||
  td::Ref<ValidatorSet> get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const override;
 | 
			
		||||
  std::pair<UnixTime, UnixTime> get_validator_set_start_stop(int next) const override;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace validator
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,6 +96,10 @@ td::Result<BlockHandle> create_block_handle(td::BufferSlice data) {
 | 
			
		|||
  return ton::validator::BlockHandleImpl::create(std::move(data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
td::Result<ConstBlockHandle> create_temp_block_handle(td::BufferSlice data) {
 | 
			
		||||
  return ton::validator::BlockHandleImpl::create(std::move(data));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
BlockHandle create_empty_block_handle(BlockIdExt id) {
 | 
			
		||||
  return ton::validator::BlockHandleImpl::create_empty(id);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -167,6 +167,11 @@ void LiteQuery::start_up() {
 | 
			
		|||
                                        q.mode_ & 1 ? ton::create_block_id(q.target_block_) : ton::BlockIdExt{},
 | 
			
		||||
                                        q.mode_);
 | 
			
		||||
          },
 | 
			
		||||
          [&](lite_api::liteServer_getValidatorStats& q) {
 | 
			
		||||
            this->perform_getValidatorStats(ton::create_block_id(q.id_), q.mode_, q.limit_,
 | 
			
		||||
                                            q.mode_ & 1 ? q.start_after_ : td::Bits256::zero(),
 | 
			
		||||
                                            q.mode_ & 4 ? q.modified_after_ : 0);
 | 
			
		||||
          },
 | 
			
		||||
          [&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -192,7 +197,7 @@ void LiteQuery::perform_getMasterchainInfo(int mode) {
 | 
			
		|||
  }
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
 | 
			
		||||
      [Self = actor_id(this), mode](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res) {
 | 
			
		||||
      [ Self = actor_id(this), mode ](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
        } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -230,7 +235,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) {
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
 | 
			
		||||
                                [Self = actor_id(this), blkid](td::Result<Ref<ton::validator::BlockData>> res) {
 | 
			
		||||
                                [ Self = actor_id(this), blkid ](td::Result<Ref<ton::validator::BlockData>> res) {
 | 
			
		||||
                                  if (res.is_error()) {
 | 
			
		||||
                                    td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
                                  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +261,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) {
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
 | 
			
		||||
                                [Self = actor_id(this), blkid, mode](td::Result<Ref<ton::validator::BlockData>> res) {
 | 
			
		||||
                                [ Self = actor_id(this), blkid, mode ](td::Result<Ref<ton::validator::BlockData>> res) {
 | 
			
		||||
                                  if (res.is_error()) {
 | 
			
		||||
                                    td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
                                  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -371,7 +376,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
 | 
			
		|||
  }
 | 
			
		||||
  if (blkid.id.seqno) {
 | 
			
		||||
    td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
 | 
			
		||||
                                  [Self = actor_id(this), blkid](td::Result<Ref<ton::validator::ShardState>> res) {
 | 
			
		||||
                                  [ Self = actor_id(this), blkid ](td::Result<Ref<ton::validator::ShardState>> res) {
 | 
			
		||||
                                    if (res.is_error()) {
 | 
			
		||||
                                      td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
                                    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -381,7 +386,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) {
 | 
			
		|||
                                  });
 | 
			
		||||
  } else {
 | 
			
		||||
    td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid,
 | 
			
		||||
                                  [Self = actor_id(this), blkid](td::Result<td::BufferSlice> res) {
 | 
			
		||||
                                  [ Self = actor_id(this), blkid ](td::Result<td::BufferSlice> res) {
 | 
			
		||||
                                    if (res.is_error()) {
 | 
			
		||||
                                      td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
                                    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -440,7 +445,7 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) {
 | 
			
		|||
  ++pending_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
 | 
			
		||||
      [Self = actor_id(this), blkid](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
      [ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query,
 | 
			
		||||
                                  res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : "));
 | 
			
		||||
| 
						 | 
				
			
			@ -466,7 +471,7 @@ bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) {
 | 
			
		|||
  ++pending_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_block_proof_from_db_short, blkid,
 | 
			
		||||
      [Self = actor_id(this), blkid, mode](td::Result<Ref<Proof>> res) {
 | 
			
		||||
      [ Self = actor_id(this), blkid, mode ](td::Result<Ref<Proof>> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query,
 | 
			
		||||
                                  res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : "));
 | 
			
		||||
| 
						 | 
				
			
			@ -488,7 +493,7 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) {
 | 
			
		|||
  ++pending_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
 | 
			
		||||
      [Self = actor_id(this), blkid](td::Result<Ref<ShardState>> res) {
 | 
			
		||||
      [ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query,
 | 
			
		||||
                                  res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : "));
 | 
			
		||||
| 
						 | 
				
			
			@ -519,7 +524,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) {
 | 
			
		|||
  ++pending_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_shard_state_from_db_short, blkid,
 | 
			
		||||
      [Self = actor_id(this), blkid](td::Result<Ref<ShardState>> res) {
 | 
			
		||||
      [ Self = actor_id(this), blkid ](td::Result<Ref<ShardState>> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query,
 | 
			
		||||
                                  res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : "));
 | 
			
		||||
| 
						 | 
				
			
			@ -541,7 +546,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) {
 | 
			
		|||
  ++pending_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_block_data_from_db_short, blkid,
 | 
			
		||||
      [Self = actor_id(this), blkid](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
      [ Self = actor_id(this), blkid ](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query,
 | 
			
		||||
                                  res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : "));
 | 
			
		||||
| 
						 | 
				
			
			@ -563,7 +568,7 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) {
 | 
			
		|||
  ++pending_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid,
 | 
			
		||||
      [Self = actor_id(this), blkid](td::Result<Ref<ProofLink>> res) {
 | 
			
		||||
      [ Self = actor_id(this), blkid ](td::Result<Ref<ProofLink>> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query,
 | 
			
		||||
                                  res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : "));
 | 
			
		||||
| 
						 | 
				
			
			@ -588,7 +593,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) {
 | 
			
		|||
  ++pending_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_zero_state, blkid,
 | 
			
		||||
      [Self = actor_id(this), blkid](td::Result<td::BufferSlice> res) {
 | 
			
		||||
      [ Self = actor_id(this), blkid ](td::Result<td::BufferSlice> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query,
 | 
			
		||||
                                  res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : "));
 | 
			
		||||
| 
						 | 
				
			
			@ -632,7 +637,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain,
 | 
			
		|||
    LOG(INFO) << "sending a get_top_masterchain_state_block query to manager";
 | 
			
		||||
    td::actor::send_closure_later(
 | 
			
		||||
        manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
 | 
			
		||||
        [Self = actor_id(this)](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res) -> void {
 | 
			
		||||
        [Self = actor_id(this)](td::Result<std::pair<Ref<ton::validator::MasterchainState>, BlockIdExt>> res)->void {
 | 
			
		||||
          if (res.is_error()) {
 | 
			
		||||
            td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
          } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1067,14 +1072,14 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) {
 | 
			
		|||
             << " " << trans_lt_;
 | 
			
		||||
  td::actor::send_closure_later(
 | 
			
		||||
      manager_, &ValidatorManager::get_block_by_lt_from_db, ton::extract_addr_prefix(acc_workchain_, acc_addr_),
 | 
			
		||||
      trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result<BlockHandle> res) {
 | 
			
		||||
      trans_lt_, [ Self = actor_id(this), remaining, manager = manager_ ](td::Result<ConstBlockHandle> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{});
 | 
			
		||||
        } else {
 | 
			
		||||
          auto handle = res.move_as_ok();
 | 
			
		||||
          LOG(DEBUG) << "requesting data for block " << handle->id().to_str();
 | 
			
		||||
          td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle,
 | 
			
		||||
                                        [Self, blkid = handle->id(), remaining](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
                                        [ Self, blkid = handle->id(), remaining ](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
                                          if (res.is_error()) {
 | 
			
		||||
                                            td::actor::send_closure(Self, &LiteQuery::abort_getTransactions,
 | 
			
		||||
                                                                    res.move_as_error(), blkid);
 | 
			
		||||
| 
						 | 
				
			
			@ -1141,7 +1146,7 @@ void LiteQuery::perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool e
 | 
			
		|||
void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector<int> param_list) {
 | 
			
		||||
  LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", <list of " << param_list.size()
 | 
			
		||||
            << " parameters>) liteserver query";
 | 
			
		||||
  set_continuation([this, mode, param_list = std::move(param_list)]() mutable {
 | 
			
		||||
  set_continuation([ this, mode, param_list = std::move(param_list) ]() mutable {
 | 
			
		||||
    continue_getConfigParams(mode, std::move(param_list));
 | 
			
		||||
  });
 | 
			
		||||
  request_mc_block_data_state(blkid);
 | 
			
		||||
| 
						 | 
				
			
			@ -1294,14 +1299,14 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni
 | 
			
		|||
  LOG(INFO) << "performing a lookupBlock(" << blkid.to_str() << ", " << mode << ", " << lt << ", " << utime
 | 
			
		||||
            << ") query";
 | 
			
		||||
  auto P = td::PromiseCreator::lambda(
 | 
			
		||||
      [Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result<BlockHandle> res) {
 | 
			
		||||
      [ Self = actor_id(this), manager = manager_, mode = (mode >> 4) ](td::Result<ConstBlockHandle> res) {
 | 
			
		||||
        if (res.is_error()) {
 | 
			
		||||
          td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
        } else {
 | 
			
		||||
          auto handle = res.move_as_ok();
 | 
			
		||||
          LOG(DEBUG) << "requesting data for block " << handle->id().to_str();
 | 
			
		||||
          td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle,
 | 
			
		||||
                                        [Self, blkid = handle->id(), mode](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
                                        [ Self, blkid = handle->id(), mode ](td::Result<Ref<BlockData>> res) {
 | 
			
		||||
                                          if (res.is_error()) {
 | 
			
		||||
                                            td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
                                          } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1449,7 +1454,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
 | 
			
		|||
    if (mode & 0x1000) {
 | 
			
		||||
      BlockIdExt bblk = (from.seqno() > to.seqno()) ? from : to;
 | 
			
		||||
      td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, bblk,
 | 
			
		||||
                                    [Self = actor_id(this), from, to, bblk, mode](td::Result<Ref<ShardState>> res) {
 | 
			
		||||
                                    [ Self = actor_id(this), from, to, bblk, mode ](td::Result<Ref<ShardState>> res) {
 | 
			
		||||
                                      if (res.is_error()) {
 | 
			
		||||
                                        td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
                                      } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1461,7 +1466,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
 | 
			
		|||
    } else {
 | 
			
		||||
      td::actor::send_closure_later(
 | 
			
		||||
          manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
 | 
			
		||||
          [Self = actor_id(this), from, to, mode](td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res) {
 | 
			
		||||
          [ Self = actor_id(this), from, to, mode ](td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res) {
 | 
			
		||||
            if (res.is_error()) {
 | 
			
		||||
              td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
            } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1474,7 +1479,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
 | 
			
		|||
  } else if (mode & 2) {
 | 
			
		||||
    td::actor::send_closure_later(
 | 
			
		||||
        manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block,
 | 
			
		||||
        [Self = actor_id(this), from, mode](td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res) {
 | 
			
		||||
        [ Self = actor_id(this), from, mode ](td::Result<std::pair<Ref<MasterchainState>, BlockIdExt>> res) {
 | 
			
		||||
          if (res.is_error()) {
 | 
			
		||||
            td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
          } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1485,7 +1490,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to,
 | 
			
		|||
        });
 | 
			
		||||
  } else {
 | 
			
		||||
    td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false,
 | 
			
		||||
                                  [Self = actor_id(this), from, mode](td::Result<BlockIdExt> res) {
 | 
			
		||||
                                  [ Self = actor_id(this), from, mode ](td::Result<BlockIdExt> res) {
 | 
			
		||||
                                    if (res.is_error()) {
 | 
			
		||||
                                      td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error());
 | 
			
		||||
                                    } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -1873,5 +1878,72 @@ bool LiteQuery::finish_proof_chain(ton::BlockIdExt id) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LiteQuery::perform_getValidatorStats(BlockIdExt blkid, int mode, int count, Bits256 start_after,
 | 
			
		||||
                                          UnixTime min_utime) {
 | 
			
		||||
  LOG(INFO) << "started a getValidatorStats(" << blkid.to_str() << ", " << mode << ", " << count << ", "
 | 
			
		||||
            << start_after.to_hex() << ", " << min_utime << ") liteserver query";
 | 
			
		||||
  if (count <= 0) {
 | 
			
		||||
    fatal_error("requested entry count limit must be positive");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  if ((mode & ~7) != 0) {
 | 
			
		||||
    fatal_error("unknown flags set in mode");
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  set_continuation([this, mode, count, min_utime, start_after]() {
 | 
			
		||||
    continue_getValidatorStats(mode, count, start_after, min_utime);
 | 
			
		||||
  });
 | 
			
		||||
  request_mc_block_data_state(blkid);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LiteQuery::continue_getValidatorStats(int mode, int limit, Bits256 start_after, UnixTime min_utime) {
 | 
			
		||||
  LOG(INFO) << "completing getValidatorStats(" << base_blk_id_.to_str() << ", " << mode << ", " << limit << ", "
 | 
			
		||||
            << start_after.to_hex() << ", " << min_utime << ") liteserver query";
 | 
			
		||||
  Ref<vm::Cell> proof1;
 | 
			
		||||
  if (!make_mc_state_root_proof(proof1)) {
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  vm::MerkleProofBuilder mpb{mc_state_->root_cell()};
 | 
			
		||||
  int count;
 | 
			
		||||
  bool complete = false, allow_eq = (mode & 3) != 1;
 | 
			
		||||
  limit = std::min(limit, 1000);
 | 
			
		||||
  try {
 | 
			
		||||
    auto dict = block::get_block_create_stats_dict(mpb.root());
 | 
			
		||||
    if (!dict) {
 | 
			
		||||
      fatal_error("cannot extract block create stats from mc state");
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
    for (count = 0; count < limit; count++) {
 | 
			
		||||
      auto v = dict->lookup_nearest_key(start_after, true, allow_eq);
 | 
			
		||||
      if (v.is_null()) {
 | 
			
		||||
        complete = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
      if (!block::gen::t_CreatorStats.validate_csr(std::move(v))) {
 | 
			
		||||
        fatal_error("invalid CreatorStats record with key "s + start_after.to_hex());
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      allow_eq = false;
 | 
			
		||||
    }
 | 
			
		||||
  } catch (vm::VmError& err) {
 | 
			
		||||
    fatal_error("error while traversing required block create stats records: "s + err.get_msg());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto res1 = vm::std_boc_serialize(std::move(proof1));
 | 
			
		||||
  if (res1.is_error()) {
 | 
			
		||||
    fatal_error("cannot serialize Merkle proof : "s + res1.move_as_error().to_string());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  auto res2 = mpb.extract_proof_boc();
 | 
			
		||||
  if (res2.is_error()) {
 | 
			
		||||
    fatal_error("cannot serialize Merkle proof : "s + res2.move_as_error().to_string());
 | 
			
		||||
    return;
 | 
			
		||||
  }
 | 
			
		||||
  LOG(INFO) << "getValidatorStats() query completed";
 | 
			
		||||
  auto b = ton::create_serialize_tl_object<ton::lite_api::liteServer_validatorStats>(
 | 
			
		||||
      mode & 0xff, ton::create_tl_lite_block_id(base_blk_id_), count, complete, res1.move_as_ok(), res2.move_as_ok());
 | 
			
		||||
  finish_query(std::move(b));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
}  // namespace validator
 | 
			
		||||
}  // namespace ton
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -114,6 +114,8 @@ class LiteQuery : public td::actor::Actor {
 | 
			
		|||
  void perform_getBlockProof(BlockIdExt from, BlockIdExt to, int mode);
 | 
			
		||||
  void continue_getBlockProof(BlockIdExt from, BlockIdExt to, int mode, BlockIdExt baseblk,
 | 
			
		||||
                              Ref<MasterchainStateQ> state);
 | 
			
		||||
  void perform_getValidatorStats(BlockIdExt blkid, int mode, int count, Bits256 start_after, UnixTime min_utime);
 | 
			
		||||
  void continue_getValidatorStats(int mode, int limit, Bits256 start_after, UnixTime min_utime);
 | 
			
		||||
  bool construct_proof_chain(BlockIdExt id);
 | 
			
		||||
  bool construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdExt next);
 | 
			
		||||
  bool construct_proof_link_forward_cont(ton::BlockIdExt cur, ton::BlockIdExt next);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -139,7 +139,7 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ {
 | 
			
		|||
  std::shared_ptr<block::ConfigInfo> get_config() const {
 | 
			
		||||
    return config_;
 | 
			
		||||
  }
 | 
			
		||||
  td::Result<td::Ref<ConfigHolder>> get_key_block_config() const override {
 | 
			
		||||
  td::Result<td::Ref<ConfigHolder>> get_config_holder() const override {
 | 
			
		||||
    if (!config_) {
 | 
			
		||||
      return td::Status::Error(ErrorCode::notready, "config not found");
 | 
			
		||||
    } else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5086,7 +5086,7 @@ bool ValidateQuery::check_mc_state_extra() {
 | 
			
		|||
  }
 | 
			
		||||
  // block_create_stats:(flags . 0)?BlockCreateStats
 | 
			
		||||
  if (new_extra.r1.flags & 1) {
 | 
			
		||||
    block::gen::BlockCreateStats::Record rec;
 | 
			
		||||
    block::gen::BlockCreateStats::Record_block_create_stats rec;
 | 
			
		||||
    if (!tlb::csr_unpack(new_extra.r1.block_create_stats, rec)) {
 | 
			
		||||
      return reject_query("cannot unpack BlockCreateStats in the new masterchain state");
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,6 +107,7 @@ struct BlockHandleInterface {
 | 
			
		|||
};
 | 
			
		||||
 | 
			
		||||
using BlockHandle = std::shared_ptr<BlockHandleInterface>;
 | 
			
		||||
using ConstBlockHandle = std::shared_ptr<const BlockHandleInterface>;
 | 
			
		||||
 | 
			
		||||
}  // namespace validator
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -35,6 +35,7 @@ class ConfigHolder : public td::CntObject {
 | 
			
		|||
 | 
			
		||||
  virtual td::Ref<ValidatorSet> get_total_validator_set(int next) const = 0;  // next = -1 -> prev, next = 0 -> cur
 | 
			
		||||
  virtual td::Ref<ValidatorSet> get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const = 0;
 | 
			
		||||
  virtual std::pair<UnixTime, UnixTime> get_validator_set_start_stop(int next) const = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace validator
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -31,17 +31,17 @@ class Db : public td::actor::Actor {
 | 
			
		|||
  virtual ~Db() = default;
 | 
			
		||||
 | 
			
		||||
  virtual void store_block_data(BlockHandle handle, td::Ref<BlockData> data, td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
  virtual void get_block_data(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) = 0;
 | 
			
		||||
  virtual void get_block_data(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void store_block_signatures(BlockHandle handle, td::Ref<BlockSignatureSet> data,
 | 
			
		||||
                                      td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
  virtual void get_block_signatures(BlockHandle handle, td::Promise<td::Ref<BlockSignatureSet>> promise) = 0;
 | 
			
		||||
  virtual void get_block_signatures(ConstBlockHandle handle, td::Promise<td::Ref<BlockSignatureSet>> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void store_block_proof(BlockHandle handle, td::Ref<Proof> proof, td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void store_block_proof_link(BlockHandle handle, td::Ref<ProofLink> proof, td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_link(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_link(ConstBlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void store_block_candidate(BlockCandidate candidate, td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
  virtual void get_block_candidate(ton::PublicKey source, BlockIdExt id, FileHash collated_data_file_hash,
 | 
			
		||||
| 
						 | 
				
			
			@ -49,7 +49,7 @@ class Db : public td::actor::Actor {
 | 
			
		|||
 | 
			
		||||
  virtual void store_block_state(BlockHandle handle, td::Ref<ShardState> state,
 | 
			
		||||
                                 td::Promise<td::Ref<ShardState>> promise) = 0;
 | 
			
		||||
  virtual void get_block_state(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) = 0;
 | 
			
		||||
  virtual void get_block_state(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice state,
 | 
			
		||||
                                           td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -72,9 +72,11 @@ class Db : public td::actor::Actor {
 | 
			
		|||
  virtual void get_block_handle(BlockIdExt id, td::Promise<BlockHandle> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void apply_block(BlockHandle handle, td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockHandle> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise<BlockHandle> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise<BlockHandle> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise<ConstBlockHandle> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts,
 | 
			
		||||
                                      td::Promise<ConstBlockHandle> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno,
 | 
			
		||||
                                  td::Promise<ConstBlockHandle> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void update_init_masterchain_block(BlockIdExt block, td::Promise<td::Unit> promise) = 0;
 | 
			
		||||
  virtual void get_init_masterchain_block(td::Promise<BlockIdExt> promise) = 0;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,7 +79,7 @@ class MasterchainState : virtual public ShardState {
 | 
			
		|||
  virtual bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid,
 | 
			
		||||
                                   ton::LogicalTime* end_lt = nullptr) const = 0;
 | 
			
		||||
  virtual bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const = 0;
 | 
			
		||||
  virtual td::Result<td::Ref<ConfigHolder>> get_key_block_config() const = 0;
 | 
			
		||||
  virtual td::Result<td::Ref<ConfigHolder>> get_config_holder() const = 0;
 | 
			
		||||
  virtual td::Status prepare() {
 | 
			
		||||
    return td::Status::OK();
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -541,7 +541,7 @@ void ValidatorManagerImpl::complete_ihr_messages(std::vector<IhrMessage::Hash> t
 | 
			
		|||
                                                 std::vector<IhrMessage::Hash> to_delete) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_data_from_db(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_block_data_from_db(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_data, handle, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -558,7 +558,7 @@ void ValidatorManagerImpl::get_block_data_from_db_short(BlockIdExt block_id, td:
 | 
			
		|||
  get_block_handle(block_id, false, std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_shard_state_from_db(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_state, handle, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -582,7 +582,7 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId
 | 
			
		|||
  td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_from_db(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -599,7 +599,8 @@ void ValidatorManagerImpl::get_block_proof_from_db_short(BlockIdExt block_id, td
 | 
			
		|||
  get_block_handle(block_id, false, std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_link_from_db(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_link_from_db(ConstBlockHandle handle,
 | 
			
		||||
                                                        td::Promise<td::Ref<ProofLink>> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_proof_link, std::move(handle), std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -618,17 +619,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt,
 | 
			
		||||
                                                   td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                                   td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts,
 | 
			
		||||
                                                          td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                                          td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno,
 | 
			
		||||
                                                      td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                                      td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -191,22 +191,23 @@ class ValidatorManagerImpl : public ValidatorManager {
 | 
			
		|||
  //void set_first_block(ZeroStateIdExt state, BlockIdExt block, td::Promise<td::Unit> promise) override;
 | 
			
		||||
  void set_next_block(BlockIdExt prev, BlockIdExt next, td::Promise<td::Unit> promise) override;
 | 
			
		||||
 | 
			
		||||
  void get_block_data_from_db(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
  void get_block_data_from_db(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
  void get_block_data_from_db_short(BlockIdExt block_id, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
  void get_shard_state_from_db(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_shard_state_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash,
 | 
			
		||||
                                   td::Promise<BlockCandidate> promise) override;
 | 
			
		||||
  void get_block_proof_from_db(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
  void get_block_proof_from_db(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
  void get_block_proof_from_db_short(BlockIdExt id, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
  void get_block_proof_link_from_db(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
  void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
  void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
 | 
			
		||||
  void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt,
 | 
			
		||||
                               td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts,
 | 
			
		||||
                                      td::Promise<BlockHandle> promise) override;
 | 
			
		||||
                                      td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno,
 | 
			
		||||
                                  td::Promise<BlockHandle> promise) override;
 | 
			
		||||
                                  td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
 | 
			
		||||
  // get block handle declared in parent class
 | 
			
		||||
  void write_handle(BlockHandle handle, td::Promise<td::Unit> promise) override;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -461,9 +461,9 @@ void ValidatorManagerMasterchainStarter::got_hardforks(std::vector<BlockIdExt> v
 | 
			
		|||
    return;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
 | 
			
		||||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), db = db_](td::Result<ConstBlockHandle> R) {
 | 
			
		||||
    R.ensure();
 | 
			
		||||
    td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_handle, R.move_as_ok());
 | 
			
		||||
    td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_id, R.move_as_ok()->id());
 | 
			
		||||
  });
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_by_seqno, AccountIdPrefixFull{masterchainId, 0}, b.seqno() - 1,
 | 
			
		||||
                          std::move(P));
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -846,7 +846,7 @@ void ValidatorManagerImpl::complete_ihr_messages(std::vector<IhrMessage::Hash> t
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_data_from_db(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_block_data_from_db(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_data, std::move(handle), std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -863,7 +863,7 @@ void ValidatorManagerImpl::get_block_data_from_db_short(BlockIdExt block_id, td:
 | 
			
		|||
  get_block_handle(block_id, false, std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_shard_state_from_db(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_state, handle, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -887,7 +887,7 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId
 | 
			
		|||
  td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_from_db(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -904,7 +904,8 @@ void ValidatorManagerImpl::get_block_proof_from_db_short(BlockIdExt block_id, td
 | 
			
		|||
  get_block_handle(block_id, false, std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_link_from_db(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) {
 | 
			
		||||
void ValidatorManagerImpl::get_block_proof_link_from_db(ConstBlockHandle handle,
 | 
			
		||||
                                                        td::Promise<td::Ref<ProofLink>> promise) {
 | 
			
		||||
  if (handle->inited_proof_link()) {
 | 
			
		||||
    td::actor::send_closure(db_, &Db::get_block_proof_link, std::move(handle), std::move(promise));
 | 
			
		||||
  } else if (handle->inited_proof()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -937,17 +938,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt,
 | 
			
		||||
                                                   td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                                   td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts,
 | 
			
		||||
                                                          td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                                          td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno,
 | 
			
		||||
                                                      td::Promise<BlockHandle> promise) {
 | 
			
		||||
                                                      td::Promise<ConstBlockHandle> promise) {
 | 
			
		||||
  td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1455,6 +1456,8 @@ bool ValidatorManagerImpl::out_of_sync() {
 | 
			
		|||
  if (validator_groups_.size() > 0 && last_known_key_block_handle_->id().seqno() <= last_masterchain_seqno_) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
  LOG(INFO) << "groups=" << validator_groups_.size() << " seqno=" << last_known_key_block_handle_->id().seqno()
 | 
			
		||||
            << " our_seqno=" << last_masterchain_seqno_;
 | 
			
		||||
 | 
			
		||||
  return true;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1475,7 +1478,8 @@ void ValidatorManagerImpl::download_next_archive() {
 | 
			
		|||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<std::string> R) {
 | 
			
		||||
    if (R.is_error()) {
 | 
			
		||||
      LOG(INFO) << "failed to download archive slice: " << R.error();
 | 
			
		||||
      td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive);
 | 
			
		||||
      delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); },
 | 
			
		||||
                   td::Timestamp::in(2.0));
 | 
			
		||||
    } else {
 | 
			
		||||
      td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok());
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1489,7 +1493,8 @@ void ValidatorManagerImpl::downloaded_archive_slice(std::string name) {
 | 
			
		|||
  auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<std::vector<BlockSeqno>> R) {
 | 
			
		||||
    if (R.is_error()) {
 | 
			
		||||
      LOG(INFO) << "failed to check downloaded archive slice: " << R.error();
 | 
			
		||||
      td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive);
 | 
			
		||||
      delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); },
 | 
			
		||||
                   td::Timestamp::in(2.0));
 | 
			
		||||
    } else {
 | 
			
		||||
      td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok());
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -1504,9 +1509,14 @@ void ValidatorManagerImpl::checked_archive_slice(std::vector<BlockSeqno> seqno)
 | 
			
		|||
  CHECK(seqno.size() == 2);
 | 
			
		||||
  LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1];
 | 
			
		||||
  CHECK(seqno[0] <= last_masterchain_seqno_);
 | 
			
		||||
  CHECK(seqno[1] <= seqno[0]);
 | 
			
		||||
 | 
			
		||||
  BlockIdExt b;
 | 
			
		||||
  if (seqno[1] < last_masterchain_seqno_) {
 | 
			
		||||
    CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b));
 | 
			
		||||
  } else {
 | 
			
		||||
    b = last_masterchain_block_id_;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  auto P = td::PromiseCreator::lambda(
 | 
			
		||||
      [SelfId = actor_id(this), db = db_.get(), client = shard_client_.get()](td::Result<BlockHandle> R) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -385,22 +385,23 @@ class ValidatorManagerImpl : public ValidatorManager {
 | 
			
		|||
 | 
			
		||||
  void set_next_block(BlockIdExt prev, BlockIdExt next, td::Promise<td::Unit> promise) override;
 | 
			
		||||
 | 
			
		||||
  void get_block_data_from_db(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
  void get_block_data_from_db(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
  void get_block_data_from_db_short(BlockIdExt block_id, td::Promise<td::Ref<BlockData>> promise) override;
 | 
			
		||||
  void get_shard_state_from_db(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_shard_state_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise<td::Ref<ShardState>> promise) override;
 | 
			
		||||
  void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash,
 | 
			
		||||
                                   td::Promise<BlockCandidate> promise) override;
 | 
			
		||||
  void get_block_proof_from_db(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
  void get_block_proof_from_db(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
  void get_block_proof_from_db_short(BlockIdExt id, td::Promise<td::Ref<Proof>> promise) override;
 | 
			
		||||
  void get_block_proof_link_from_db(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
  void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
  void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise<td::Ref<ProofLink>> promise) override;
 | 
			
		||||
 | 
			
		||||
  void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise<BlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt,
 | 
			
		||||
                               td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts,
 | 
			
		||||
                                      td::Promise<BlockHandle> promise) override;
 | 
			
		||||
                                      td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
  void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno,
 | 
			
		||||
                                  td::Promise<BlockHandle> promise) override;
 | 
			
		||||
                                  td::Promise<ConstBlockHandle> promise) override;
 | 
			
		||||
 | 
			
		||||
  // get block handle declared in parent class
 | 
			
		||||
  void write_handle(BlockHandle handle, td::Promise<td::Unit> promise) override;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -110,7 +110,7 @@ void ValidateBroadcast::start_up() {
 | 
			
		|||
    } else if (key_block_seqno == last_masterchain_state_->get_seqno()) {
 | 
			
		||||
      got_key_block_handle(last_masterchain_block_handle_);
 | 
			
		||||
    } else {
 | 
			
		||||
      auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<BlockHandle> R) {
 | 
			
		||||
      auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<ConstBlockHandle> R) {
 | 
			
		||||
        if (R.is_error()) {
 | 
			
		||||
          td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query,
 | 
			
		||||
                                  R.move_as_error_prefix("cannot find reference key block id: "));
 | 
			
		||||
| 
						 | 
				
			
			@ -138,7 +138,7 @@ void ValidateBroadcast::got_key_block_id(BlockIdExt block_id) {
 | 
			
		|||
  td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id, false, std::move(P));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ValidateBroadcast::got_key_block_handle(BlockHandle handle) {
 | 
			
		||||
void ValidateBroadcast::got_key_block_handle(ConstBlockHandle handle) {
 | 
			
		||||
  if (handle->id().seqno() == 0) {
 | 
			
		||||
    auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result<td::Ref<ShardState>> R) {
 | 
			
		||||
      if (R.is_error()) {
 | 
			
		||||
| 
						 | 
				
			
			@ -183,7 +183,7 @@ void ValidateBroadcast::got_key_block_proof_link(td::Ref<ProofLink> key_proof_li
 | 
			
		|||
 | 
			
		||||
void ValidateBroadcast::got_zero_state(td::Ref<MasterchainState> state) {
 | 
			
		||||
  zero_state_ = state;
 | 
			
		||||
  auto confR = state->get_key_block_config();
 | 
			
		||||
  auto confR = state->get_config_holder();
 | 
			
		||||
  if (confR.is_error()) {
 | 
			
		||||
    abort_query(confR.move_as_error_prefix("failed to extract config from zero state: "));
 | 
			
		||||
    return;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,7 +65,7 @@ class ValidateBroadcast : public td::actor::Actor {
 | 
			
		|||
 | 
			
		||||
  void start_up() override;
 | 
			
		||||
  void got_key_block_id(BlockIdExt block_id);
 | 
			
		||||
  void got_key_block_handle(BlockHandle block_handle);
 | 
			
		||||
  void got_key_block_handle(ConstBlockHandle block_handle);
 | 
			
		||||
  void got_key_block_proof_link(td::Ref<ProofLink> proof_link);
 | 
			
		||||
  void got_zero_state(td::Ref<MasterchainState> state);
 | 
			
		||||
  void check_signatures_common(td::Ref<ConfigHolder> conf);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -176,23 +176,23 @@ class ValidatorManagerInterface : public td::actor::Actor {
 | 
			
		|||
  virtual void get_download_token(size_t download_size, td::uint32 priority, td::Timestamp timeout,
 | 
			
		||||
                                  td::Promise<std::unique_ptr<DownloadToken>> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void get_block_data_from_db(BlockHandle handle, td::Promise<td::Ref<BlockData>> promise) = 0;
 | 
			
		||||
  virtual void get_block_data_from_db(ConstBlockHandle handle, td::Promise<td::Ref<BlockData>> promise) = 0;
 | 
			
		||||
  virtual void get_block_data_from_db_short(BlockIdExt block_id, td::Promise<td::Ref<BlockData>> promise) = 0;
 | 
			
		||||
  virtual void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash,
 | 
			
		||||
                                           td::Promise<BlockCandidate> promise) = 0;
 | 
			
		||||
  virtual void get_shard_state_from_db(BlockHandle handle, td::Promise<td::Ref<ShardState>> promise) = 0;
 | 
			
		||||
  virtual void get_shard_state_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ShardState>> promise) = 0;
 | 
			
		||||
  virtual void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise<td::Ref<ShardState>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_from_db(BlockHandle handle, td::Promise<td::Ref<Proof>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_from_db(ConstBlockHandle handle, td::Promise<td::Ref<Proof>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_from_db_short(BlockIdExt id, td::Promise<td::Ref<Proof>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_link_from_db(BlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise<td::Ref<ProofLink>> promise) = 0;
 | 
			
		||||
  virtual void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise<td::Ref<ProofLink>> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt,
 | 
			
		||||
                                       td::Promise<BlockHandle> promise) = 0;
 | 
			
		||||
                                       td::Promise<ConstBlockHandle> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts,
 | 
			
		||||
                                              td::Promise<BlockHandle> promise) = 0;
 | 
			
		||||
                                              td::Promise<ConstBlockHandle> promise) = 0;
 | 
			
		||||
  virtual void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno,
 | 
			
		||||
                                          td::Promise<BlockHandle> promise) = 0;
 | 
			
		||||
                                          td::Promise<ConstBlockHandle> promise) = 0;
 | 
			
		||||
 | 
			
		||||
  virtual void get_archive_id(BlockSeqno masterchain_seqno, td::Promise<td::uint64> promise) = 0;
 | 
			
		||||
  virtual void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue