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

Add account state by transaction and emulator (extended) (#592)

* account_state_by_transaction

* Correct time calculation

* Bug fixes

* Refactor

* namespace block::transaction

* smc.forget

* RunEmulator: remove wallet_id

* Refactor & fixes

* AccountStateByTransaction: use shardchain block instead of masterchain block

* transaction emulator core

* refactor

* tx emulator major functionality

* small format changes

* readme

* clean

* return json, add support for init messages

* tx emulator readme

* refactor getConfigParam and getConfigAll

* add shardchain_libs_boc parameter

* option to change verbosity level of transaction emulator

* fix deserializing ShardAccount with account_none

* add mode needSpecialSmc when unpack config

* emulator: block::Transaction -> block::transaction::Transaction

* Refactor

* emulator: Fix bug

* emulator: Support for emulator-extern

* emulator: Refactor

* Return vm log and vm exit code.

* fix build on macos, emulator_static added

* adjust documentation

* ignore_chksig for external messages

* tvm emulator, run get method

* Added more params for transaction emulator

* Added setters for optional transaction emulator params, moved libs to a new setter

* Added actions cell output to transaction emulator

* fix tonlib build

* refactoring, rand seed as hex size 64, tvm emulator send message

* tvm send message, small refactoring

* fix config decoding, rename

* improve documentation

* macos export symbols

* Added run_get_method to transaction emulator emscipten wrapper

* Fixed empty action list serialization

* Changed actions list cell to serialize as json null instead of empty string in transaction emulator

* stack as boc

* log gas remaining

* Fix prev_block_id

* fix build errors

* Refactor fetch_config_params

* fix failing unwrap of optional rand_seed

* lookup correct shard, choose prev_block based on account shard

* fix tonlib android jni build

---------

Co-authored-by: legaii <jgates.ardux@gmail.com>
Co-authored-by: ms <dungeon666master@protonmail.com>
Co-authored-by: krigga <krigga7@gmail.com>
This commit is contained in:
EmelyanenkoK 2023-02-02 10:03:45 +03:00 committed by GitHub
parent adf67aa869
commit 3b3c25b654
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 2095 additions and 158 deletions

View file

@ -60,7 +60,7 @@ target_include_directories(tonlib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>/..
$<BUILD_INTERFACE:${OPENSSL_INCLUDE_DIR}>
)
target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common smc-envelope)
target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common smc-envelope emulator_static)
target_link_libraries(tonlib PUBLIC tdutils tl_tonlib_api)
if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android
@ -136,7 +136,7 @@ if (WIN32)
set(WINGETOPT_TARGET wingetopt)
endif()
install(TARGETS tdnet keys crc32c tdactor adnllite tl_api tl-utils tl_lite_api tl-lite-utils ton_crypto ton_block smc-envelope ${WINGETOPT_TARGET}
tdutils tl_tonlib_api tonlib lite-client-common tddb_utils Tonlib EXPORT Tonlib
tdutils tl_tonlib_api tonlib lite-client-common tddb_utils emulator_static Tonlib EXPORT Tonlib
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
RUNTIME DESTINATION bin

View file

@ -36,6 +36,8 @@
#include "smc-envelope/PaymentChannel.h"
#include "smc-envelope/SmartContractCode.h"
#include "emulator/transaction-emulator.h"
#include "auto/tl/tonlib_api.hpp"
#include "block/block-auto.h"
#include "block/check-proof.h"
@ -74,6 +76,14 @@ struct GetAccountState {
using ReturnType = td::unique_ptr<AccountState>;
};
struct GetAccountStateByTransaction {
block::StdAddress address;
std::int64_t lt;
td::Bits256 hash;
//td::optional<td::Ed25519::PublicKey> public_key;
using ReturnType = td::unique_ptr<AccountState>;
};
struct RemoteRunSmcMethod {
block::StdAddress address;
td::optional<ton::BlockIdExt> block_id;
@ -416,6 +426,19 @@ class AccountState {
get_wallet_revision());
}
td::Result<tonlib_api::object_ptr<tonlib_api::tvm_cell>> to_shardAccountCell() const {
auto account_root = raw_.info.root;
if (account_root.is_null()) {
block::gen::Account().cell_pack_account_none(account_root);
}
auto cell = vm::CellBuilder().store_ref(account_root).store_bits(raw_.info.last_trans_hash.as_bitslice()).store_long(raw_.info.last_trans_lt).finalize();
return tonlib_api::make_object<tonlib_api::tvm_cell>(to_bytes(cell));
}
td::Result<td::Ref<vm::CellSlice>> to_shardAccountCellSlice() const {
return vm::CellBuilder().store_ref(raw_.info.root).store_bits(raw_.info.last_trans_hash.as_bitslice()).store_long(raw_.info.last_trans_lt).as_cellslice_ref();
}
//NB: Order is important! Used during guessAccountRevision
enum WalletType {
Empty,
@ -940,7 +963,7 @@ class Query {
} while (list.not_null());
return i;
}
}; // namespace tonlib
};
td::Result<td::int64> to_balance_or_throw(td::Ref<vm::CellSlice> balance_ref) {
vm::CellSlice balance_slice = *balance_ref;
@ -1633,6 +1656,305 @@ class GetShardBlockProof : public td::actor::Actor {
std::vector<std::pair<ton::BlockIdExt, td::BufferSlice>> links_;
};
auto to_lite_api(const tonlib_api::ton_blockIdExt& blk) -> td::Result<lite_api_ptr<ton::lite_api::tonNode_blockIdExt>>;
auto to_tonlib_api(const ton::lite_api::liteServer_transactionId& txid) -> tonlib_api_ptr<tonlib_api::blocks_shortTxId>;
class RunEmulator : public td::actor::Actor {
public:
RunEmulator(ExtClientRef ext_client_ref, int_api::GetAccountStateByTransaction request,
td::actor::ActorShared<> parent, td::Promise<td::unique_ptr<AccountState>>&& promise)
: request_(std::move(request)), parent_(std::move(parent)), promise_(std::move(promise)) {
client_.set_client(ext_client_ref);
}
private:
struct FullBlockId {
ton::BlockIdExt id;
ton::BlockIdExt mc;
ton::BlockIdExt prev;
ton::Bits256 rand_seed;
};
ExtClient client_;
int_api::GetAccountStateByTransaction request_;
td::actor::ActorShared<> parent_;
td::Promise<td::unique_ptr<AccountState>> promise_;
std::map<td::int64, td::actor::ActorOwn<>> actors_;
td::int64 actor_id_{1};
FullBlockId block_id_;
td::Ref<vm::Cell> mc_state_root_; // ^ShardStateUnsplit
td::unique_ptr<AccountState> account_state_;
std::vector<td::Ref<vm::Cell>> transactions_; // std::vector<^Transaction>
size_t count_{0};
size_t count_transactions_{0};
bool incomplete_{true};
bool stopped_{false};
void get_block_id(td::Promise<FullBlockId>&& promise) {
auto shard_id = ton::shard_prefix(request_.address.addr, 60);
auto query = ton::lite_api::liteServer_lookupBlock(0b111111010, ton::create_tl_lite_block_id_simple({request_.address.workchain, shard_id, 0}), request_.lt, 0);
client_.send_query(std::move(query), promise.wrap([self = this, shard_id](td::Result<tonlib_api::object_ptr<ton::lite_api::liteServer_blockHeader>> header_r) -> td::Result<FullBlockId> {
TRY_RESULT(header, std::move(header_r));
ton::BlockIdExt block_id = ton::create_block_id(header->id_);
TRY_RESULT(root, vm::std_boc_deserialize(std::move(header->header_proof_)));
try {
auto virt_root = vm::MerkleProof::virtualize(root, 1);
if (virt_root.is_null()) {
return td::Status::Error("block header proof is not a valid Merkle proof");
}
ton::RootHash vhash{virt_root->get_hash().bits()};
if (ton::RootHash{virt_root->get_hash().bits()} != block_id.root_hash) {
return td::Status::Error("block header has incorrect root hash");
}
std::vector<ton::BlockIdExt> prev_blocks;
ton::BlockIdExt mc_block_id;
bool after_split;
td::Status status = block::unpack_block_prev_blk_ext(virt_root, block_id, prev_blocks, mc_block_id, after_split);
if (status.is_error()) {
return status.move_as_error();
}
ton::BlockIdExt prev_block;
if (prev_blocks.size() == 1 || ton::shard_is_ancestor(prev_blocks[0].id.shard, shard_id)) {
prev_block = std::move(prev_blocks[0]);
} else {
prev_block = std::move(prev_blocks[1]);
}
block::gen::Block::Record block;
block::gen::BlockExtra::Record extra;
if (!tlb::unpack_cell(virt_root, block) || !tlb::unpack_cell(block.extra, extra)) {
return td::Status::Error("cannot unpack block header");
}
return FullBlockId{std::move(block_id), std::move(mc_block_id), std::move(prev_block), std::move(extra.rand_seed)};
} catch (vm::VmError& err) {
return err.as_status("error processing header");
} catch (vm::VmVirtError& err) {
return err.as_status("error processing header");
}
}));
}
void get_mc_state_root(td::Promise<td::Ref<vm::Cell>>&& promise) {
TRY_RESULT_PROMISE(promise, lite_block, to_lite_api(*to_tonlib_api(block_id_.mc)));
auto block = ton::create_block_id(lite_block);
client_.send_query(ton::lite_api::liteServer_getConfigAll(0b11'11111111, std::move(lite_block)), promise.wrap([self = this, block](auto r_config) -> td::Result<td::Ref<vm::Cell>> {
TRY_RESULT(state, block::check_extract_state_proof(block, r_config->state_proof_.as_slice(), r_config->config_proof_.as_slice()));
return std::move(state);
}));
}
void get_account_state(td::Promise<td::unique_ptr<AccountState>>&& promise) {
auto actor_id = actor_id_++;
actors_[actor_id] = td::actor::create_actor<GetRawAccountState>(
"GetAccountState", client_.get_client(), request_.address, block_id_.prev,
actor_shared(this, actor_id),
promise.wrap([address = request_.address](auto&& state) {
return td::make_unique<AccountState>(std::move(address), std::move(state), 0);
}));
}
td::Status get_transactions(std::int64_t lt) {
TRY_RESULT(lite_block, to_lite_api(*to_tonlib_api(block_id_.id)));
auto after = ton::lite_api::make_object<ton::lite_api::liteServer_transactionId3>(request_.address.addr, lt);
auto query = ton::lite_api::liteServer_listBlockTransactions(std::move(lite_block), 0b10100111, 256, std::move(after), false, false);
client_.send_query(std::move(query), [self = this](lite_api_ptr<ton::lite_api::liteServer_blockTransactions>&& bTxes) {
if (!bTxes) {
self->check(td::Status::Error("liteServer.blockTransactions is null"));
return;
}
std::int64_t last_lt = 0;
for (auto& id : bTxes->ids_) {
last_lt = id->lt_;
if (id->account_ != self->request_.address.addr) {
continue;
}
if (id->lt_ == self->request_.lt && id->hash_ == self->request_.hash) {
self->incomplete_ = false;
}
self->transactions_.push_back({});
self->get_transaction(id->lt_, id->hash_, [self, i = self->transactions_.size() - 1](auto transaction) { self->set_transaction(i, std::move(transaction)); });
if (!self->incomplete_) {
return;
}
}
if (bTxes->incomplete_) {
self->check(self->get_transactions(last_lt));
}
});
return td::Status::OK();
}
void get_transaction(std::int64_t lt, td::Bits256 hash, td::Promise<td::Ref<vm::Cell>>&& promise) {
auto actor_id = actor_id_++;
actors_[actor_id] = td::actor::create_actor<GetTransactionHistory>(
"GetTransactionHistory", client_.get_client(), request_.address, lt, hash, 1, actor_shared(this, actor_id),
promise.wrap([](auto&& transactions) mutable {
return std::move(transactions.transactions.front().transaction);
}));
}
void start_up() override {
if (stopped_) {
return;
}
get_block_id([self = this](td::Result<FullBlockId>&& block_id) { self->set_block_id(std::move(block_id)); });
}
void set_block_id(td::Result<FullBlockId>&& block_id) {
if (block_id.is_error()) {
check(block_id.move_as_error());
} else {
block_id_ = block_id.move_as_ok();
get_mc_state_root([self = this](td::Result<td::Ref<vm::Cell>>&& mc_state_root) { self->set_mc_state_root(std::move(mc_state_root)); });
get_account_state([self = this](td::Result<td::unique_ptr<AccountState>>&& state) { self->set_account_state(std::move(state)); });
check(get_transactions(0));
inc();
}
}
void set_mc_state_root(td::Result<td::Ref<vm::Cell>>&& mc_state_root) {
if (mc_state_root.is_error()) {
check(mc_state_root.move_as_error());
} else {
mc_state_root_ = mc_state_root.move_as_ok();
inc();
}
}
void set_account_state(td::Result<td::unique_ptr<AccountState>>&& account_state) {
if (account_state.is_error()) {
check(account_state.move_as_error());
} else {
account_state_ = account_state.move_as_ok();
inc();
}
}
void set_transaction(size_t i, td::Result<td::Ref<vm::Cell>>&& transaction) {
if (transaction.is_error()) {
check(transaction.move_as_error());
} else {
transactions_[i] = transaction.move_as_ok();
inc_transactions();
}
}
void inc_transactions() {
if (stopped_ || ++count_transactions_ != transactions_.size() || incomplete_) {
return;
}
inc();
}
void inc() {
if (stopped_ || ++count_ != 4) { // 4 -- block_id + mc_state_root + account_state + transactions
return;
}
auto r_config = block::Config::extract_from_state(mc_state_root_, 0b11'11111111);
if (r_config.is_error()) {
check(r_config.move_as_error());
return;
}
std::unique_ptr<block::Config> config = r_config.move_as_ok();
block::gen::ShardStateUnsplit::Record shard_state;
if (!tlb::unpack_cell(mc_state_root_, shard_state)) {
check(td::Status::Error("Failed to unpack masterchain state"));
return;
}
vm::Dictionary libraries(shard_state.r1.libraries->prefetch_ref(), 256);
auto r_shard_account = account_state_->to_shardAccountCellSlice();
if (r_shard_account.is_error()) {
check(r_shard_account.move_as_error());
return;
}
td::Ref<vm::CellSlice> shard_account = r_shard_account.move_as_ok();
const block::StdAddress& address = account_state_->get_address();
ton::UnixTime now = account_state_->get_sync_time();
bool is_special = address.workchain == ton::masterchainId && config->is_special_smartcontract(address.addr);
block::Account account(address.workchain, address.addr.bits());
if (!account.unpack(std::move(shard_account), td::Ref<vm::CellSlice>(), now, is_special)) {
check(td::Status::Error("Can't unpack shard account"));
return;
}
emulator::TransactionEmulator trans_emulator(std::move(*config));
trans_emulator.set_libs(std::move(libraries));
trans_emulator.set_rand_seed(block_id_.rand_seed);
td::Result<emulator::TransactionEmulator::EmulationChain> emulation_result = trans_emulator.emulate_transactions_chain(std::move(account), std::move(transactions_));
if (emulation_result.is_error()) {
promise_.set_error(emulation_result.move_as_error());
} else {
account = std::move(emulation_result.move_as_ok().account);
RawAccountState raw = std::move(account_state_->raw());
raw.block_id = block_id_.id;
raw.balance = account.get_balance().grams->to_long();
raw.storage_last_paid = std::move(account.last_paid);
raw.storage_stat = std::move(account.storage_stat);
raw.code = std::move(account.code);
raw.data = std::move(account.data);
raw.state = std::move(account.total_state);
raw.info.last_trans_lt = account.last_trans_lt_;
raw.info.last_trans_hash = account.last_trans_hash_;
raw.info.gen_utime = account.now_;
if (account.status == block::Account::acc_frozen) {
raw.frozen_hash = (char*)account.state_hash.data();
}
promise_.set_value(td::make_unique<AccountState>(address, std::move(raw), 0));
}
stopped_ = true;
try_stop();
}
void check(td::Status status) {
if (status.is_error()) {
promise_.set_error(std::move(status));
stopped_ = true;
try_stop();
}
}
void try_stop() {
if (stopped_ && actors_.empty()) {
stop();
}
}
void hangup_shared() override {
actors_.erase(get_link_token());
try_stop();
}
void hangup() override {
check(TonlibError::Cancelled());
}
};
TonlibClient::TonlibClient(td::unique_ptr<TonlibCallback> callback) : callback_(std::move(callback)) {
}
TonlibClient::~TonlibClient() = default;
@ -2765,6 +3087,27 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getAccountState& request,
return td::Status::OK();
}
td::Status TonlibClient::do_request(tonlib_api::raw_getAccountStateByTransaction& request,
td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise) {
if (!request.account_address_) {
return TonlibError::EmptyField("account_address");
}
if (!request.transaction_id_) {
return TonlibError::EmptyField("transaction_id");
}
TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
auto lt = request.transaction_id_->lt_;
auto hash_str = request.transaction_id_->hash_;
if (hash_str.size() != 32) {
return td::Status::Error(400, "Invalid transaction id hash size");
}
td::Bits256 hash;
hash.as_slice().copy_from(hash_str);
make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
promise.wrap([](auto&& res) { return res->to_raw_fullAccountState(); }));
return td::Status::OK();
}
td::Result<KeyStorage::InputKey> from_tonlib(tonlib_api::inputKeyRegular& input_key) {
if (!input_key.key_) {
return TonlibError::EmptyField("key");
@ -2877,6 +3220,59 @@ td::Status TonlibClient::do_request(const tonlib_api::getAccountState& request,
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::getAccountStateByTransaction& request,
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise) {
if (!request.account_address_) {
return TonlibError::EmptyField("account_address");
}
if (!request.transaction_id_) {
return TonlibError::EmptyField("transaction_id");
}
TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
auto lt = request.transaction_id_->lt_;
auto hash_str = request.transaction_id_->hash_;
if (hash_str.size() != 32) {
return td::Status::Error(400, "Invalid transaction id hash size");
}
td::Bits256 hash;
hash.as_slice().copy_from(hash_str);
make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
promise.wrap([](auto&& res) { return res->to_fullAccountState(); }));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::getShardAccountCell& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise) {
if (!request.account_address_) {
return TonlibError::EmptyField("account_address");
}
TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
make_request(int_api::GetAccountState{std::move(account_address), query_context_.block_id.copy(), {}},
promise.wrap([](auto&& res) { return res->to_shardAccountCell(); }));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::getShardAccountCellByTransaction& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise) {
if (!request.account_address_) {
return TonlibError::EmptyField("account_address");
}
if (!request.transaction_id_) {
return TonlibError::EmptyField("transaction_id");
}
TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
auto lt = request.transaction_id_->lt_;
auto hash_str = request.transaction_id_->hash_;
if (hash_str.size() != 32) {
return td::Status::Error(400, "Invalid transaction id hash size");
}
td::Bits256 hash;
hash.as_slice().copy_from(hash_str);
make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
promise.wrap([](auto&& res) { return res->to_shardAccountCell(); }));
return td::Status::OK();
}
td::Result<ton::ManualDns::EntryData> to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) {
using R = td::Result<ton::ManualDns::EntryData>;
return downcast_call2<R>(
@ -3502,7 +3898,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return with_wallet(*source_->get_wallet());
}
}; // namespace tonlib
};
td::int64 TonlibClient::register_query(td::unique_ptr<Query> query) {
auto query_id = ++next_query_id_;
@ -3713,6 +4109,27 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_load& request,
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::smc_loadByTransaction& request,
td::Promise<object_ptr<tonlib_api::smc_info>>&& promise) {
if (!request.account_address_) {
return TonlibError::EmptyField("account_address");
}
if (!request.transaction_id_) {
return TonlibError::EmptyField("transaction_id");
}
TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_));
auto lt = request.transaction_id_->lt_;
auto hash_str = request.transaction_id_->hash_;
if (hash_str.size() != 32) {
return td::Status::Error(400, "Invalid transaction id hash size");
}
td::Bits256 hash;
hash.as_slice().copy_from(hash_str);
make_request(int_api::GetAccountStateByTransaction{account_address, lt, hash},
promise.send_closure(actor_id(this), &TonlibClient::finish_load_smc));
return td::Status::OK();
}
td::Status TonlibClient::do_request(const tonlib_api::smc_forget& request,
td::Promise<object_ptr<tonlib_api::ok>>&& promise) {
auto it = smcs_.find(request.id_);
@ -4555,6 +4972,17 @@ td::Status TonlibClient::do_request(int_api::GetAccountState request,
return td::Status::OK();
}
td::Status TonlibClient::do_request(int_api::GetAccountStateByTransaction request,
td::Promise<td::unique_ptr<AccountState>>&& promise) {
auto actor_id = actor_id_++;
actors_[actor_id] = td::actor::create_actor<RunEmulator>(
"RunEmulator", client_.get_client(), request, actor_shared(this, actor_id),
promise.wrap([](auto&& state) {
return std::move(state);
}));
return td::Status::OK();
}
td::Status TonlibClient::do_request(int_api::RemoteRunSmcMethod request,
td::Promise<int_api::RemoteRunSmcMethod::ReturnType>&& promise) {
auto actor_id = actor_id_++;
@ -4641,29 +5069,72 @@ td::Result<ton::BlockIdExt> to_block_id(const tonlib_api::ton_blockIdExt& blk) {
return ton::BlockIdExt(blk.workchain_, blk.shard_, blk.seqno_, root_hash, file_hash);
}
td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
TRY_RESULT(lite_block, to_lite_api(*request.id_))
auto block = create_block_id(std::move(lite_block));
auto param = request.param_;
void TonlibClient::get_config_param(int32_t param, int32_t mode, ton::BlockIdExt block, td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
std::vector<int32_t> params = { param };
client_.send_query(ton::lite_api::liteServer_getConfigParams(0, std::move(lite_block), std::move(params)),
promise.wrap([block, param](auto r_config) {
client_.send_query(ton::lite_api::liteServer_getConfigParams(mode, ton::create_tl_lite_block_id(block), std::move(params)),
promise.wrap([param, block](auto r_config) -> td::Result<object_ptr<tonlib_api::configInfo>> {
auto state = block::check_extract_state_proof(block, r_config->state_proof_.as_slice(),
r_config->config_proof_.as_slice());
if (state.is_error()) {
LOG(ERROR) << "block::check_extract_state_proof failed: " << state.error();
return state.move_as_error_prefix(TonlibError::ValidateConfig());
}
auto config = block::Config::extract_from_state(std::move(state.move_as_ok()), 0);
if (config.is_error()) {
LOG(ERROR) << "block::Config::extract_from_state failed: " << config.error();
return config.move_as_error_prefix(TonlibError::ValidateConfig());
}
tonlib_api::configInfo config_result;
config_result.config_ = tonlib_api::make_object<tonlib_api::tvm_cell>(to_bytes(config.move_as_ok()->get_config_param(param)));
return tonlib_api::make_object<tonlib_api::configInfo>(std::move(config_result));
}));
}
td::Status TonlibClient::do_request(const tonlib_api::getConfigParam& request,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
if (query_context_.block_id) {
get_config_param(request.param_, request.mode_, query_context_.block_id.value(), std::move(promise));
} else {
client_.with_last_block([this, promise = std::move(promise), param = request.param_, mode = request.mode_](td::Result<LastBlockState> r_last_block) mutable {
if (r_last_block.is_error()) {
promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed ")));
} else {
this->get_config_param(param, mode, r_last_block.move_as_ok().last_block_id, std::move(promise));
}
});
}
return td::Status::OK();
}
void TonlibClient::get_config_all(int32_t mode, ton::BlockIdExt block, td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
client_.send_query(ton::lite_api::liteServer_getConfigAll(mode, ton::create_tl_lite_block_id(block)),
promise.wrap([block](auto r_config) -> td::Result<object_ptr<tonlib_api::configInfo>> {
auto state = block::check_extract_state_proof(block, r_config->state_proof_.as_slice(),
r_config->config_proof_.as_slice());
if (state.is_error()) {
return state.move_as_error_prefix(TonlibError::ValidateConfig());
}
auto config = block::Config::extract_from_state(std::move(state.move_as_ok()), 0);
if (config.is_error()) {
return config.move_as_error_prefix(TonlibError::ValidateConfig());
}
tonlib_api::configInfo config_result;
config_result.config_ = tonlib_api::make_object<tonlib_api::tvm_cell>(to_bytes(config.move_as_ok()->get_root_cell()));
return tonlib_api::make_object<tonlib_api::configInfo>(std::move(config_result));
}));
}
td::Status TonlibClient::do_request(const tonlib_api::getConfigAll& request,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise) {
if (query_context_.block_id) {
get_config_all(request.mode_, query_context_.block_id.value(), std::move(promise));
} else {
client_.with_last_block([this, promise = std::move(promise), mode = request.mode_](td::Result<LastBlockState> r_last_block) mutable {
if (r_last_block.is_error()) {
promise.set_error(r_last_block.move_as_error_prefix(TonlibError::Internal("get last block failed ")));
} else {
this->get_config_all(mode, r_last_block.move_as_ok().last_block_id, std::move(promise));
}
});
}
return td::Status::OK();
}

View file

@ -39,6 +39,7 @@
namespace tonlib {
namespace int_api {
struct GetAccountState;
struct GetAccountStateByTransaction;
struct GetPrivateKey;
struct GetDnsResolver;
struct SendMessage;
@ -51,6 +52,7 @@ inline std::string to_string(const int_api::SendMessage&) {
} // namespace int_api
class AccountState;
class Query;
class RunEmulator;
td::Result<tonlib_api::object_ptr<tonlib_api::dns_EntryData>> to_tonlib_api(
const ton::ManualDns::EntryData& entry_data);
@ -234,6 +236,8 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(tonlib_api::raw_getAccountState& request,
td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
td::Status do_request(tonlib_api::raw_getAccountStateByTransaction& request,
td::Promise<object_ptr<tonlib_api::raw_fullAccountState>>&& promise);
td::Status do_request(tonlib_api::raw_getTransactions& request,
td::Promise<object_ptr<tonlib_api::raw_transactions>>&& promise);
td::Status do_request(tonlib_api::raw_getTransactionsV2& request,
@ -241,6 +245,12 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(const tonlib_api::getAccountState& request,
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
td::Status do_request(const tonlib_api::getAccountStateByTransaction& request,
td::Promise<object_ptr<tonlib_api::fullAccountState>>&& promise);
td::Status do_request(const tonlib_api::getShardAccountCell& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
td::Status do_request(const tonlib_api::getShardAccountCellByTransaction& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
td::Status do_request(tonlib_api::guessAccountRevision& request,
td::Promise<object_ptr<tonlib_api::accountRevisionList>>&& promise);
td::Status do_request(tonlib_api::guessAccount& request,
@ -305,6 +315,7 @@ class TonlibClient : public td::actor::Actor {
td::Result<tonlib_api::object_ptr<tonlib_api::smc_info>> get_smc_info(td::int64 id);
void finish_load_smc(td::unique_ptr<AccountState> query, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
td::Status do_request(const tonlib_api::smc_load& request, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
td::Status do_request(const tonlib_api::smc_loadByTransaction& request, td::Promise<object_ptr<tonlib_api::smc_info>>&& promise);
td::Status do_request(const tonlib_api::smc_forget& request, td::Promise<object_ptr<tonlib_api::ok>>&& promise);
td::Status do_request(const tonlib_api::smc_getCode& request,
td::Promise<object_ptr<tonlib_api::tvm_cell>>&& promise);
@ -344,6 +355,7 @@ class TonlibClient : public td::actor::Actor {
td::Promise<object_ptr<tonlib_api::dns_resolved>>&& promise);
td::Status do_request(int_api::GetAccountState request, td::Promise<td::unique_ptr<AccountState>>&&);
td::Status do_request(int_api::GetAccountStateByTransaction 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::GetDnsResolver request, td::Promise<block::StdAddress>&&);
td::Status do_request(int_api::RemoteRunSmcMethod request,
@ -370,8 +382,14 @@ class TonlibClient : public td::actor::Actor {
td::Status do_request(const tonlib_api::blocks_getShardBlockProof& request,
td::Promise<object_ptr<tonlib_api::blocks_shardBlockProof>>&& promise);
void get_config_param(int32_t param, int32_t mode, ton::BlockIdExt block,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
td::Status do_request(const tonlib_api::getConfigParam& request,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
void get_config_all(int32_t mode, ton::BlockIdExt block,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
td::Status do_request(const tonlib_api::getConfigAll& request,
td::Promise<object_ptr<tonlib_api::configInfo>>&& promise);
void proxy_request(td::int64 query_id, std::string data);

View file

@ -389,6 +389,7 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << "runmethod <addr> <method-id> <params>...\tRuns GET method <method-id> of account "
"<addr> with specified parameters\n";
td::TerminalIO::out() << "getstate <key_id>\tget state of wallet with requested key\n";
td::TerminalIO::out() << "getstatebytransaction <key_id> <lt> <hash>\tget state of wallet with requested key after transaction with local time <lt> and hash <hash> (base64url)\n";
td::TerminalIO::out() << "guessrevision <key_id>\tsearch of existing accounts corresponding to the given key\n";
td::TerminalIO::out() << "guessaccount <key_id>\tsearch of existing accounts corresponding to the given key\n";
td::TerminalIO::out() << "getaddress <key_id>\tget address of wallet with requested key\n";
@ -489,6 +490,8 @@ class TonlibCli : public td::actor::Actor {
transfer(parser, cmd, std::move(cmd_promise));
} else if (cmd == "getstate") {
get_state(parser.read_word(), std::move(cmd_promise));
} else if (cmd == "getstatebytransaction") {
get_state_by_transaction(parser, std::move(cmd_promise));
} else if (cmd == "getaddress") {
get_address(parser.read_word(), std::move(cmd_promise));
} else if (cmd == "importkeypem") {
@ -2067,6 +2070,30 @@ class TonlibCli : public td::actor::Actor {
}));
}
void get_state_by_transaction(td::ConstParser& parser, td::Promise<td::Unit> promise) {
TRY_RESULT_PROMISE(promise, address, to_account_address(parser.read_word(), false));
TRY_RESULT_PROMISE(promise, lt, td::to_integer_safe<std::int64_t>(parser.read_word()));
TRY_RESULT_PROMISE(promise, hash, td::base64url_decode(parser.read_word()));
auto address_str = address.address->account_address_;
auto transaction_id = std::make_unique<tonlib_api::internal_transactionId>(lt, std::move(hash));
send_query(make_object<tonlib_api::getAccountStateByTransaction>(
ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address)),
ton::move_tl_object_as<tonlib_api::internal_transactionId>(std::move(transaction_id))),
promise.wrap([address_str](auto&& state) {
td::TerminalIO::out() << "Address: " << address_str << "\n";
td::TerminalIO::out() << "Balance: "
<< Grams{td::narrow_cast<td::uint64>(state->balance_ * (state->balance_ > 0))}
<< "\n";
td::TerminalIO::out() << "Sync utime: " << state->sync_utime_ << "\n";
td::TerminalIO::out() << "transaction.LT: " << state->last_transaction_id_->lt_ << "\n";
td::TerminalIO::out() << "transaction.Hash: " << td::base64_encode(state->last_transaction_id_->hash_)
<< "\n";
td::TerminalIO::out() << to_string(state->account_state_);
return td::Unit();
}));
}
void get_address(td::Slice key, td::Promise<td::Unit> promise) {
TRY_RESULT_PROMISE(promise, address, to_account_address(key, false));
promise.set_value(td::Unit());