1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

updated tonlib

This commit is contained in:
ton 2019-10-31 14:26:04 +04:00
parent c860ce3d1e
commit 3002321eb7
22 changed files with 576 additions and 109 deletions

View file

@ -19,6 +19,7 @@
#include "block/block.h"
#include "block/block-auto.h"
#include "block/mc-config.h"
#include "vm/cells.h"
#include "vm/boc.h"
@ -246,6 +247,17 @@ TEST(Tonlib, ParseAddres) {
ASSERT_EQ("Uf9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfdyS", addr_str2->account_address_);
}
TEST(Tonlib, ConfigParseBug) {
td::Slice literal =
"D1000000000000006400000000000186A0DE0000000003E8000000000000000F424000000000000F42400000000000002710000000000098"
"96800000000005F5E100000000003B9ACA00";
unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), literal.begin(), literal.end());
CHECK(bits >= 0);
auto slice = vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize();
block::Config::do_get_gas_limits_prices(std::move(slice), 21).ensure();
}
TEST(Tonlib, EncryptionApi) {
using tonlib_api::make_object;
Client client;

View file

@ -462,29 +462,31 @@ void test_multisig(Client& client, const Wallet& giver_wallet) {
transfer_grams(client, giver_wallet, address, 1 * Gramm).ensure();
auto init_state = ms->get_init_state();
// Just transfer all (some) money back in one query
vm::CellBuilder icb;
ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(),
5 * Gramm / 10);
icb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure();
ton::MultisigWallet::QueryBuilder qb(-1, icb.finalize());
for (int i = 0; i < k - 1; i++) {
qb.sign(i, private_keys[i]);
for (int i = 0; i < 2; i++) {
// Just transfer all (some) money back in one query
vm::CellBuilder icb;
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());
for (int i = 0; i < k - 1; i++) {
qb.sign(i, private_keys[i]);
}
auto query_id =
create_raw_query(client, address,
i == 0 ? vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str() : "",
i == 0 ? vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str() : "",
vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str())
.move_as_ok();
auto fees = query_estimate_fees(client, query_id);
LOG(INFO) << "Expected src fees: " << fees.first;
LOG(INFO) << "Expected dst fees: " << fees.second;
auto a_state = get_account_state(client, address);
query_send(client, query_id);
auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok();
}
auto query_id =
create_raw_query(client, address, vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str(),
vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str(),
vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str())
.move_as_ok();
auto fees = query_estimate_fees(client, query_id);
LOG(INFO) << "Expected src fees: " << fees.first;
LOG(INFO) << "Expected dst fees: " << fees.second;
auto a_state = get_account_state(client, address);
query_send(client, query_id);
auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok();
}
int main(int argc, char* argv[]) {
@ -515,8 +517,8 @@ int main(int argc, char* argv[]) {
if (reset_keystore_dir) {
td::rmrf(keystore_dir).ignore();
td::mkdir(keystore_dir).ensure();
}
td::mkdir(keystore_dir).ensure();
SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO));
static_send(make_object<tonlib_api::setLogTagVerbosityLevel>("tonlib_query", 4)).ensure();

View file

@ -42,6 +42,7 @@ class ExtClientOutboundImp : public ExtClientOutbound {
auto it = queries_.find(id);
if (it == queries_.end()) {
promise.set_error(TonlibError::Internal("Unknown query id"));
return;
}
it->second.set_result(std::move(r_data));
queries_.erase(it);

View file

@ -25,6 +25,8 @@
#include "lite-client/lite-client-common.h"
#include "td/utils/JsonBuilder.h"
namespace tonlib {
// init_state <-> last_key_block
@ -313,6 +315,18 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) {
state_.last_key_block_id = mc_key_block_id;
VLOG(last_block) << "Update masterchain key block id: " << state_.last_key_block_id.to_str();
if (true) {
td::JsonBuilder jb;
auto jo = jb.enter_object();
jo("workchain", state_.last_key_block_id.id.workchain);
jo("shard", static_cast<td::int64>(state_.last_key_block_id.id.shard));
jo("seqno", static_cast<td::int32>(state_.last_key_block_id.id.seqno));
jo("root_hash", td::base64_encode(as_slice(state_.last_key_block_id.root_hash)));
jo("file_hash", td::base64_encode(as_slice(state_.last_key_block_id.file_hash)));
jo.leave();
LOG(INFO) << jb.string_builder().as_cslice();
}
//LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " "
//<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " "
//<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice());

View file

@ -798,18 +798,22 @@ void TonlibClient::init_ext_client() {
if (use_callbacks_for_network_) {
class Callback : public ExtClientOutbound::Callback {
public:
explicit Callback(td::actor::ActorShared<TonlibClient> parent) : parent_(std::move(parent)) {
explicit Callback(td::actor::ActorShared<TonlibClient> parent, td::uint32 config_generation)
: parent_(std::move(parent)), config_generation_(config_generation) {
}
void request(td::int64 id, std::string data) override {
send_closure(parent_, &TonlibClient::proxy_request, id, std::move(data));
send_closure(parent_, &TonlibClient::proxy_request, (id << 16) | (config_generation_ & 0xffff),
std::move(data));
}
private:
td::actor::ActorShared<TonlibClient> parent_;
td::uint32 config_generation_;
};
ref_cnt_++;
auto client = ExtClientOutbound::create(td::make_unique<Callback>(td::actor::actor_shared(this)));
auto client =
ExtClientOutbound::create(td::make_unique<Callback>(td::actor::actor_shared(this), config_generation_));
ext_client_outbound_ = client.get();
raw_client_ = std::move(client);
} else {
@ -836,6 +840,7 @@ void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 conf
if (config_generation != config_generation_) {
return;
}
last_block_storage_.save_state(blockchain_name_, state);
}
@ -858,7 +863,7 @@ void TonlibClient::update_sync_state(LastBlockSyncState state, td::uint32 config
}
}
void TonlibClient::init_last_block() {
void TonlibClient::init_last_block(td::optional<Config> o_master_config) {
ref_cnt_++;
class Callback : public LastBlock::Callback {
public:
@ -893,6 +898,15 @@ void TonlibClient::init_last_block() {
state = r_state.move_as_ok();
}
if (o_master_config) {
auto master_config = o_master_config.unwrap();
if (master_config.init_block_id.is_valid() &&
state.last_key_block_id.id.seqno < master_config.init_block_id.id.seqno) {
state.last_key_block_id = master_config.init_block_id;
LOG(INFO) << "Use init block from MASTER config: " << master_config.init_block_id.to_str();
}
}
raw_last_block_ = td::actor::create_actor<LastBlock>(
td::actor::ActorOptions().with_name("LastBlock").with_poll(false), get_client_ref(), std::move(state), config_,
source_.get_cancellation_token(), td::make_unique<Callback>(td::actor::actor_shared(this), config_generation_));
@ -988,6 +1002,7 @@ bool TonlibClient::is_static_request(td::int32 id) {
case tonlib_api::testGiver_getAccountAddress::ID:
case tonlib_api::packAccountAddress::ID:
case tonlib_api::unpackAccountAddress::ID:
case tonlib_api::options_validateConfig::ID:
case tonlib_api::getBip39Hints::ID:
case tonlib_api::setLogStream::ID:
case tonlib_api::getLogStream::ID:
@ -1142,37 +1157,112 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request,
key_storage_.set_key_value(kv_);
last_block_storage_.set_key_value(kv_);
if (request.options_->config_) {
TRY_STATUS(set_config(std::move(request.options_->config_)));
TRY_RESULT(full_config, validate_config(std::move(request.options_->config_)));
set_config(std::move(full_config));
}
state_ = State::Running;
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
return td::Status::OK();
}
td::Status TonlibClient::set_config(object_ptr<tonlib_api::config> config) {
CHECK(config);
class MasterConfig {
public:
void add_config(std::string name, std::string json) {
auto config = std::make_shared<Config>(Config::parse(json).move_as_ok());
if (!name.empty()) {
by_name_[name] = config;
}
by_root_hash_[config->zero_state_id.root_hash] = config;
}
td::optional<Config> by_name(std::string name) const {
auto it = by_name_.find(name);
if (it == by_name_.end()) {
return {};
}
return *it->second;
}
td::optional<Config> by_root_hash(const ton::RootHash& root_hash) const {
auto it = by_root_hash_.find(root_hash);
if (it == by_root_hash_.end()) {
return {};
}
return *it->second;
}
private:
size_t next_id_{0};
std::map<std::string, std::shared_ptr<const Config>> by_name_;
std::map<ton::RootHash, std::shared_ptr<const Config>> by_root_hash_;
};
const MasterConfig& get_default_master_config() {
static MasterConfig config = [] {
MasterConfig res;
res.add_config("testnet", R"abc({
"liteservers": [
],
"validator": {
"@type": "validator.config.global",
"zero_state": {
"workchain": -1,
"shard": -9223372036854775808,
"seqno": 0,
"root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=",
"file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo="
},
"init_block":
{"workchain":-1,"shard":-9223372036854775808,"seqno":870721,"root_hash":"jYKhSQ1xeSPprzgjqiUOnAWwc2yqs7nCVAU21k922s4=","file_hash":"kHidF02CZpaz2ia9jtXUJLp0AiWMWwfzprTUIsddHSo="}
}
})abc");
return res;
}();
return config;
}
td::Result<TonlibClient::FullConfig> TonlibClient::validate_config(tonlib_api::object_ptr<tonlib_api::config> config) {
if (!config) {
return TonlibError::EmptyField("config");
}
if (config->config_.empty()) {
return TonlibError::InvalidConfig("config is empty");
}
TRY_RESULT_PREFIX(new_config, Config::parse(std::move(config->config_)),
TonlibError::InvalidConfig("can't parse config"));
if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) {
return TonlibError::InvalidConfig("no lite clients");
}
config_ = std::move(new_config);
config_generation_++;
td::optional<Config> o_master_config;
if (config->blockchain_name_.empty()) {
blockchain_name_ = td::sha256(config_.zero_state_id.to_str()).substr(0, 16);
o_master_config = get_default_master_config().by_root_hash(new_config.zero_state_id.root_hash);
} else {
blockchain_name_ = config->blockchain_name_;
o_master_config = get_default_master_config().by_name(config->blockchain_name_);
}
use_callbacks_for_network_ = config->use_callbacks_for_network_;
ignore_cache_ = config->ignore_cache_;
if (o_master_config && o_master_config.value().zero_state_id != new_config.zero_state_id) {
return TonlibError::InvalidConfig("zero_state differs from embedded zero_state");
}
FullConfig res;
res.config = std::move(new_config);
res.o_master_config = std::move(o_master_config);
res.ignore_cache = config->ignore_cache_;
res.use_callbacks_for_network = config->use_callbacks_for_network_;
return std::move(res);
}
void TonlibClient::set_config(FullConfig full_config) {
config_ = std::move(full_config.config);
config_generation_++;
blockchain_name_ = config_.zero_state_id.root_hash.as_slice().str();
use_callbacks_for_network_ = full_config.use_callbacks_for_network;
ignore_cache_ = full_config.ignore_cache;
init_ext_client();
init_last_block();
init_last_block(std::move(full_config.o_master_config));
init_last_config();
client_.set_client(get_client_ref());
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::close& request,
@ -1184,12 +1274,22 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request,
return td::Status::OK();
}
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
tonlib_api::options_validateConfig& request) {
auto r_config = validate_config(std::move(request.config_));
if (r_config.is_error()) {
return status_to_tonlib_api(r_config.move_as_error());
}
return tonlib_api::make_object<tonlib_api::ok>();
}
td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
if (!request.config_) {
return TonlibError::EmptyField("config");
}
TRY_STATUS(set_config(std::move(request.config_)));
TRY_RESULT(config, validate_config(std::move(request.config_)));
set_config(std::move(config));
promise.set_value(tonlib_api::make_object<tonlib_api::ok>());
return td::Status::OK();
}
@ -2020,6 +2120,84 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_getState& request,
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request,
td::Promise<object_ptr<tonlib_api::smc_runResult>>&& promise) {
auto it = smcs_.find(request.id_);
if (it == smcs_.end()) {
return TonlibError::InvalidSmcId();
}
td::Ref<ton::SmartContract> smc(true, it->second->get_smc_state());
ton::SmartContract::Args args;
downcast_call(*request.method_,
td::overloaded([&](tonlib_api::smc_methodIdNumber& number) { args.set_method_id(number.number_); },
[&](tonlib_api::smc_methodIdName& name) { args.set_method_id(name.name_); }));
td::Ref<vm::Stack> stack(true);
td::Status status;
// TODO: error codes
// downcast_call
for (auto& entry : request.stack_) {
downcast_call(*entry, td::overloaded(
[&](tonlib_api::tvm_stackEntryUnsupported& cell) {
status = td::Status::Error("Unsuppored stack entry");
},
[&](tonlib_api::tvm_stackEntrySlice& cell) {
auto r_cell = vm::std_boc_deserialize(cell.slice_->bytes_);
if (r_cell.is_error()) {
status = r_cell.move_as_error();
return;
}
stack.write().push_cell(r_cell.move_as_ok());
},
[&](tonlib_api::tvm_stackEntryCell& cell) {
auto r_cell = vm::std_boc_deserialize(cell.cell_->bytes_);
if (r_cell.is_error()) {
status = r_cell.move_as_error();
return;
}
stack.write().push_cell(r_cell.move_as_ok());
},
[&](tonlib_api::tvm_stackEntryNumber& number) {
[&](tonlib_api::tvm_numberDecimal& dec) {
auto num = td::dec_string_to_int256(dec.number_);
if (num.is_null()) {
status = td::Status::Error("Failed to parse dec string to int256");
return;
}
stack.write().push_int(std::move(num));
}(*number.number_);
}));
}
TRY_STATUS(std::move(status));
args.set_stack(std::move(stack));
auto res = smc->run_get_method(std::move(args));
// smc.runResult gas_used:int53 stack:vector<tvm.StackEntry> exit_code:int32 = smc.RunResult;
std::vector<object_ptr<tonlib_api::tvm_StackEntry>> res_stack;
for (auto& entry : res.stack->as_span()) {
switch (entry.type()) {
case vm::StackEntry::Type::t_int:
res_stack.push_back(tonlib_api::make_object<tonlib_api::tvm_stackEntryNumber>(
tonlib_api::make_object<tonlib_api::tvm_numberDecimal>(dec_string(entry.as_int()))));
break;
case vm::StackEntry::Type::t_slice:
res_stack.push_back(
tonlib_api::make_object<tonlib_api::tvm_stackEntryCell>(tonlib_api::make_object<tonlib_api::tvm_cell>(
to_bytes(vm::CellBuilder().append_cellslice(entry.as_slice()).finalize()))));
break;
case vm::StackEntry::Type::t_cell:
res_stack.push_back(tonlib_api::make_object<tonlib_api::tvm_stackEntryCell>(
tonlib_api::make_object<tonlib_api::tvm_cell>(to_bytes(entry.as_cell()))));
break;
default:
res_stack.push_back(tonlib_api::make_object<tonlib_api::tvm_stackEntryUnsupported>());
break;
}
}
promise.set_value(tonlib_api::make_object<tonlib_api::smc_runResult>(res.gas_used, std::move(res_stack), res.code));
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::sync& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
client_.with_last_block(to_any_promise(std::move(promise)));
return td::Status::OK();
@ -2146,13 +2324,25 @@ td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& reque
td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryResult& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, td::BufferSlice(request.bytes_),
to_any_promise(std::move(promise)));
if (ext_client_outbound_.empty()) {
return TonlibError::InvalidQueryId();
}
if (((request.id_ ^ config_generation_) & 0xffff) != 0) {
return TonlibError::InvalidQueryId();
}
send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_ >> 16,
td::BufferSlice(request.bytes_), to_any_promise(std::move(promise)));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_,
if (ext_client_outbound_.empty()) {
return TonlibError::InvalidQueryId();
}
if (((request.id_ ^ config_generation_) & 0xffff) != 0) {
return TonlibError::InvalidQueryId();
}
send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_ >> 16,
td::Status::Error(request.error_->code_, request.error_->message_)
.move_as_error_prefix(TonlibError::LiteServerNetwork()),
to_any_promise(std::move(promise)));
@ -2306,6 +2496,11 @@ td::Status TonlibClient::do_request(const tonlib_api::unpackAccountAddress& requ
return TonlibError::Internal();
}
template <class P>
td::Status TonlibClient::do_request(const tonlib_api::options_validateConfig& request, P&&) {
UNREACHABLE();
return TonlibError::Internal();
}
template <class P>
td::Status TonlibClient::do_request(tonlib_api::getBip39Hints& request, P&&) {
UNREACHABLE();
return TonlibError::Internal();

View file

@ -30,6 +30,7 @@
#include "td/actor/actor.h"
#include "td/utils/CancellationToken.h"
#include "td/utils/optional.h"
#include <map>
@ -87,7 +88,7 @@ class TonlibClient : public td::actor::Actor {
ExtClientRef get_client_ref();
void init_ext_client();
void init_last_block();
void init_last_block(td::optional<Config> o_master_config);
void init_last_config();
bool is_closing_{false};
@ -125,6 +126,7 @@ class TonlibClient : public td::actor::Actor {
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::testGiver_getAccountAddress& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::packAccountAddress& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::unpackAccountAddress& request);
static object_ptr<tonlib_api::Object> do_static_request(tonlib_api::options_validateConfig& request);
static object_ptr<tonlib_api::Object> do_static_request(tonlib_api::getBip39Hints& request);
static object_ptr<tonlib_api::Object> do_static_request(tonlib_api::setLogStream& request);
@ -155,6 +157,8 @@ class TonlibClient : public td::actor::Actor {
template <class P>
td::Status do_request(const tonlib_api::unpackAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::options_validateConfig& request, P&&);
template <class P>
td::Status do_request(tonlib_api::getBip39Hints& request, P&&);
template <class P>
@ -189,7 +193,14 @@ class TonlibClient : public td::actor::Actor {
}
}
td::Status set_config(object_ptr<tonlib_api::config> config);
struct FullConfig {
Config config;
td::optional<Config> o_master_config;
bool use_callbacks_for_network;
bool ignore_cache;
};
static td::Result<FullConfig> validate_config(tonlib_api::object_ptr<tonlib_api::config> config);
void set_config(FullConfig config);
td::Status do_request(const tonlib_api::init& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::close& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(tonlib_api::options_setConfig& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
@ -288,6 +299,9 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(const tonlib_api::smc_getState& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
td::Status do_request(const tonlib_api::smc_runGetMethod& request,
td::Promise<object_ptr<tonlib_api::smc_runResult>>&& promise);
td::Status do_request(int_api::GetAccountState request, td::Promise<td::unique_ptr<AccountState>>&&);
td::Status do_request(int_api::GetPrivateKey request, td::Promise<KeyStorage::PrivateKey>&&);
td::Status do_request(int_api::SendMessage request, td::Promise<td::Unit>&& promise);

View file

@ -29,20 +29,21 @@
// "help [<command>]\tThis help\n" // TODO: support [<command>]
// "quit\tExit\n";
// "sendfile <filename>\tLoad a serialized message from <filename> and send it to server\n"
//
// "saveaccount[code|data] <filename> <addr> [<block-id-ext>]\tSaves into specified file the most recent state "
// "(StateInit) or just the code or data of specified account; <addr> is in "
// "[<workchain>:]<hex-or-base64-addr> format\n"
//
// "runmethod <addr> <method-id> <params>...\tRuns GET method <method-id> of account <addr> "
// "with specified parameters\n"
//
// "getaccount <addr> [<block-id-ext>]\tLoads the most recent state of specified account; <addr> is in "
// "[<workchain>:]<hex-or-base64-addr> format\n"
//
// WONTSUPPORT
//
// UNSUPPORTED
//"last\tGet last block and state info from server\n"
//"status\tShow connection and local database status\n"
//"getaccount <addr> [<block-id-ext>]\tLoads the most recent state of specified account; <addr> is in "
//"[<workchain>:]<hex-or-base64-addr> format\n"
//"(StateInit) or just the code or data of specified account; <addr> is in "
//"[<workchain>:]<hex-or-base64-addr> format\n"
//"allshards [<block-id-ext>]\tShows shard configuration from the most recent masterchain "
//"state or from masterchain state corresponding to <block-id-ext>\n"
//"getconfig [<param>...]\tShows specified or all configuration parameters from the latest masterchain state\n"
@ -266,6 +267,8 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << "time\tGet server time\n";
td::TerminalIO::out() << "remote-version\tShows server time, version and capabilities\n";
td::TerminalIO::out() << "sendfile <filename>\tLoad a serialized message from <filename> and send it to server\n";
td::TerminalIO::out() << "setconfig|validateconfig <path> [<name>] [<use_callback>] [<force>] - set or validate "
"lite server config\n";
td::TerminalIO::out() << "exit\tExit\n";
td::TerminalIO::out() << "quit\tExit\n";
td::TerminalIO::out()
@ -279,7 +282,6 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n";
td::TerminalIO::out() << "exportkey [<key_id>] - export key\n";
td::TerminalIO::out() << "exportkeypem [<key_id>] - export key\n";
td::TerminalIO::out() << "setconfig <path> [<name>] [<use_callback>] [<force>] - set lite server config\n";
td::TerminalIO::out() << "getstate <key_id> - get state of simple wallet with requested key\n";
td::TerminalIO::out()
<< "gethistory <key_id> - get history fo simple wallet with requested key (last 10 transactions)\n";
@ -305,12 +307,6 @@ class TonlibCli : public td::actor::Actor {
export_key(cmd.str(), parser.read_word());
} else if (cmd == "importkey") {
import_key(parser.read_all());
} else if (cmd == "setconfig") {
auto config = parser.read_word();
auto name = parser.read_word();
auto use_callback = parser.read_word();
auto force = parser.read_word();
set_config(config, name, to_bool(use_callback), to_bool(force));
} else if (cmd == "getstate") {
get_state(parser.read_word());
} else if (cmd == "gethistory") {
@ -346,6 +342,14 @@ class TonlibCli : public td::actor::Actor {
auto path = parser.read_word();
auto address = parser.read_word();
save_account(cmd, path, address, std::move(cmd_promise));
} else if (cmd == "runmethod") {
run_method(parser, std::move(cmd_promise));
} else if (cmd == "setconfig" || cmd == "validateconfig") {
auto config = parser.read_word();
auto name = parser.read_word();
auto use_callback = parser.read_word();
auto force = parser.read_word();
set_validate_config(cmd, config, name, to_bool(use_callback), to_bool(force), std::move(cmd_promise));
} else {
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`"));
}
@ -419,6 +423,107 @@ class TonlibCli : public td::actor::Actor {
return td::Unit();
}));
}
td::Result<tonlib_api::object_ptr<tonlib_api::tvm_StackEntry>> parse_stack_entry(td::Slice str) {
if (str.empty() || str.size() > 65535) {
return td::Status::Error("String is or empty or too big");
}
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("Failed to parse slice");
}
return tonlib_api::make_object<tonlib_api::tvm_stackEntrySlice>(
tonlib_api::make_object<tonlib_api::tvm_slice>(vm::std_boc_serialize(cb.finalize()).ok().as_slice().str()));
}
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 slice");
}
return tonlib_api::make_object<tonlib_api::tvm_stackEntrySlice>(tonlib_api::make_object<tonlib_api::tvm_slice>(
vm::std_boc_serialize(vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize())
.ok()
.as_slice()
.str()));
}
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 a 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 a number");
}
x.negate().normalize();
} else if (!l || x.parse_dec(str.data(), l) != l) {
return td::Status::Error("Failed to parse a number");
}
return tonlib_api::make_object<tonlib_api::tvm_stackEntryNumber>(
tonlib_api::make_object<tonlib_api::tvm_numberDecimal>(dec_string(num)));
}
void run_method(td::ConstParser& parser, td::Promise<td::Unit> promise) {
TRY_RESULT_PROMISE(promise, addr, to_account_address(parser.read_word(), false));
auto method_str = parser.read_word();
tonlib_api::object_ptr<tonlib_api::smc_MethodId> method;
if (std::all_of(method_str.begin(), method_str.end(), [](auto c) { return c >= '0' && c <= '9'; })) {
method = tonlib_api::make_object<tonlib_api::smc_methodIdNumber>(td::to_integer<td::int32>(method_str.str()));
} else {
method = tonlib_api::make_object<tonlib_api::smc_methodIdName>(method_str.str());
}
std::vector<tonlib_api::object_ptr<tonlib_api::tvm_StackEntry>> stack;
while (true) {
auto word = parser.read_word();
if (word.empty()) {
break;
}
TRY_RESULT_PROMISE(promise, stack_entry, parse_stack_entry(word));
stack.push_back(std::move(stack_entry));
}
auto to_run =
tonlib_api::make_object<tonlib_api::smc_runGetMethod>(0 /*fixme*/, std::move(method), std::move(stack));
send_query(tonlib_api::make_object<tonlib_api::smc_load>(std::move(addr.address)),
promise.send_closure(actor_id(this), &TonlibCli::run_method_2, std::move(to_run)));
}
void run_method_2(tonlib_api::object_ptr<tonlib_api::smc_runGetMethod> to_run,
tonlib_api::object_ptr<tonlib_api::smc_info> info, td::Promise<td::Unit> promise) {
to_run->id_ = info->id_;
send_query(std::move(to_run), promise.send_closure(actor_id(this), &TonlibCli::run_method_3));
}
void run_method_3(tonlib_api::object_ptr<tonlib_api::smc_runResult> info, td::Promise<td::Unit> promise) {
td::TerminalIO::out() << "Got smc result " << to_string(info);
promise.set_value({});
}
void set_validate_config(td::Slice cmd, td::Slice path, td::Slice name, bool use_callback, bool ignore_cache,
td::Promise<td::Unit> promise) {
TRY_RESULT_PROMISE(promise, data, td::read_file_str(path.str()));
using tonlib_api::make_object;
auto config = make_object<tonlib_api::config>(std::move(data), name.str(), use_callback, ignore_cache);
if (cmd == "setconfig") {
send_query(make_object<tonlib_api::options_setConfig>(std::move(config)), promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Config is set\n";
return td::Unit();
}));
} else {
send_query(make_object<tonlib_api::options_validateConfig>(std::move(config)), promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Config is valid\n";
return td::Unit();
}));
}
}
void dump_netstats() {
td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n";
@ -437,6 +542,8 @@ class TonlibCli : public td::actor::Actor {
}
}
td::Timestamp sync_started_;
void on_tonlib_result(std::uint64_t id, tonlib_api::object_ptr<tonlib_api::Object> result) {
if (id == 0) {
switch (result->get_id()) {
@ -455,10 +562,15 @@ class TonlibCli : public td::actor::Actor {
auto update = tonlib_api::move_object_as<tonlib_api::updateSyncState>(std::move(result));
switch (update->sync_state_->get_id()) {
case tonlib_api::syncStateDone::ID: {
td::TerminalIO::out() << "synchronization: DONE\n";
td::TerminalIO::out() << "synchronization: DONE in "
<< td::format::as_time(td::Time::now() - sync_started_.at()) << "\n";
sync_started_ = {};
break;
}
case tonlib_api::syncStateInProgress::ID: {
if (!sync_started_) {
sync_started_ = td::Timestamp::now();
}
auto progress = tonlib_api::move_object_as<tonlib_api::syncStateInProgress>(update->sync_state_);
auto from = progress->from_seqno_;
auto to = progress->to_seqno_;
@ -857,26 +969,6 @@ class TonlibCli : public td::actor::Actor {
});
}
void set_config(td::Slice path, td::Slice name, bool use_callback, bool ignore_cache) {
auto r_data = td::read_file_str(path.str());
if (r_data.is_error()) {
td::TerminalIO::out() << "Can't read file [" << path << "] : " << r_data.error() << "\n";
return;
}
auto data = r_data.move_as_ok();
using tonlib_api::make_object;
send_query(make_object<tonlib_api::options_setConfig>(
make_object<tonlib_api::config>(std::move(data), name.str(), use_callback, ignore_cache)),
[](auto r_res) {
if (r_res.is_error()) {
td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n";
return;
}
td::TerminalIO::out() << to_string(r_res.ok());
});
}
void get_state(td::Slice key) {
if (key.empty()) {
dump_keys();