1
0
Fork 0
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:
ton 2019-11-28 18:44:14 +04:00
parent 7f3a22a217
commit 090e0c16eb
82 changed files with 1852 additions and 391 deletions

View file

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

View file

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

View file

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

View file

@ -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)) {
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View 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

View file

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

View file

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

View file

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

View file

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

View file

@ -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
/*
*

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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++) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -170,4 +170,8 @@ void Package::iterate(std::function<bool(std::string, td::BufferSlice, td::uint6
}
}
Package::~Package() {
fd_.close();
}
} // namespace ton

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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")); }));
}
@ -1067,7 +1072,7 @@ 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 {
@ -1294,7 +1299,7 @@ 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 {
@ -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

View file

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

View file

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

View file

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

View file

@ -107,6 +107,7 @@ struct BlockHandleInterface {
};
using BlockHandle = std::shared_ptr<BlockHandleInterface>;
using ConstBlockHandle = std::shared_ptr<const BlockHandleInterface>;
} // namespace validator

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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