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

Extra currencies (#1122)

* Support extra currencies in tonlib, blockchain-explorer, getAccountPrunned

* Fix dict_combine_with with non-zero mode
This commit is contained in:
SpyCheese 2024-10-01 10:22:49 +03:00 committed by GitHub
parent 257cd8cd9c
commit f94d1bee0c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 182 additions and 57 deletions

View file

@ -122,9 +122,8 @@ HttpAnswer& HttpAnswer::operator<<(MessageCell msg) {
abort("cannot unpack internal message");
return *this;
}
td::RefInt256 value;
td::Ref<vm::Cell> extra;
if (!block::unpack_CurrencyCollection(info.value, value, extra)) {
block::CurrencyCollection currency_collection;
if (!currency_collection.unpack(info.value)) {
abort("cannot unpack message value");
return *this;
}
@ -133,7 +132,7 @@ HttpAnswer& HttpAnswer::operator<<(MessageCell msg) {
<< "<tr><th>destination</th><td>" << AddressCell{info.dest} << "</td></tr>\n"
<< "<tr><th>lt</th><td>" << info.created_lt << "</td></tr>\n"
<< "<tr><th>time</th><td>" << info.created_at << " (" << time_to_human(info.created_at) << ")</td></tr>\n"
<< "<tr><th>value</th><td>" << value << "</td></tr>\n";
<< "<tr><th>value</th><td>" << currency_collection.to_str()<< "</td></tr>\n";
break;
}
default:
@ -365,6 +364,7 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) {
ton::LogicalTime last_trans_lt = 0;
ton::Bits256 last_trans_hash;
last_trans_hash.set_zero();
block::CurrencyCollection balance = block::CurrencyCollection::zero();
try {
auto state_root = vm::MerkleProof::virtualize(acc_c.q_roots[1], 1);
if (state_root.is_null()) {
@ -397,6 +397,20 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) {
}
last_trans_hash = acc_info.last_trans_hash;
last_trans_lt = acc_info.last_trans_lt;
block::gen::Account::Record_account acc;
block::gen::AccountStorage::Record storage_rec;
if (!tlb::unpack_cell(acc_c.root, acc)) {
abort("cannot unpack Account");
return *this;
}
if (!tlb::csr_unpack(acc.storage, storage_rec)) {
abort("cannot unpack AccountStorage");
return *this;
}
if (!balance.unpack(storage_rec.balance)) {
abort("cannot unpack account balance");
return *this;
}
} else if (acc_c.root.not_null()) {
abort(PSTRING() << "account state proof shows that account state for " << acc_c.addr.workchain << ":"
<< acc_c.addr.addr.to_hex() << " must be empty, but it is not");
@ -434,6 +448,7 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) {
*this << "<tr><th>workchain</th><td>" << acc_c.addr.workchain << "</td></tr>";
*this << "<tr><th>account hex</th><td>" << acc_c.addr.addr.to_hex() << "</td></tr>";
*this << "<tr><th>account</th><td>" << acc_c.addr.rserialize(true) << "</td></tr>";
*this << "<tr><th>balance</th><td>" << balance.to_str() << "</td></tr>";
if (last_trans_lt > 0) {
*this << "<tr><th>last transaction</th><td>"
<< "<a href=\"" << TransactionLink{acc_c.addr, last_trans_lt, last_trans_hash} << "\">lt=" << last_trans_lt

View file

@ -61,7 +61,8 @@ block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id,
return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/);
}
void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms) {
void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms,
td::Ref<vm::Cell> extra_currencies) {
td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice());
cb.store_zeroes(1)
@ -73,7 +74,8 @@ void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddr
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
cb.store_zeroes(9 + 64 + 32);
cb.store_maybe_ref(extra_currencies);
cb.store_zeroes(8 + 64 + 32);
}
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,

View file

@ -36,7 +36,8 @@ class GenericAccount {
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) noexcept;
static td::Ref<vm::Cell> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) noexcept;
static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms);
static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms,
td::Ref<vm::Cell> extra_currencies);
static td::Result<td::Ed25519::PublicKey> get_public_key(const SmartContract& sc);
static td::Result<td::uint32> get_seqno(const SmartContract& sc);

View file

@ -48,7 +48,7 @@ td::Result<td::Ref<vm::Cell>> WalletInterface::get_init_message(const td::Ed2551
td::Ref<vm::Cell> WalletInterface::create_int_message(const Gift &gift) {
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms);
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms, gift.extra_currencies);
if (gift.init_state.not_null()) {
cbi.store_ones(2);
cbi.store_ref(gift.init_state);

View file

@ -37,6 +37,7 @@ class WalletInterface : public SmartContract {
struct Gift {
block::StdAddress destination;
td::int64 gramms;
td::Ref<vm::Cell> extra_currencies;
td::int32 send_mode{-1};
bool is_encrypted{false};

View file

@ -1779,7 +1779,7 @@ Ref<Cell> DictionaryFixed::dict_combine_with(Ref<Cell> dict1, Ref<Cell> dict2, t
int mode, int skip1, int skip2) const {
if (dict1.is_null()) {
assert(!skip2);
if ((mode & 1) && dict2.is_null()) {
if ((mode & 1) && dict2.not_null()) {
throw CombineError{};
}
return dict2;
@ -1854,11 +1854,11 @@ Ref<Cell> DictionaryFixed::dict_combine_with(Ref<Cell> dict1, Ref<Cell> dict2, t
key_buffer[-1] = 0;
// combine left subtrees
auto c1 = dict_combine_with(label1.remainder->prefetch_ref(0), label2.remainder->prefetch_ref(0), key_buffer,
n - c - 1, total_key_len, combine_func);
n - c - 1, total_key_len, combine_func, mode);
key_buffer[-1] = 1;
// combine right subtrees
auto c2 = dict_combine_with(label1.remainder->prefetch_ref(1), label2.remainder->prefetch_ref(1), key_buffer,
n - c - 1, total_key_len, combine_func);
n - c - 1, total_key_len, combine_func, mode);
label1.remainder.clear();
label2.remainder.clear();
// c1 and c2 are merged left and right children of dict1 and dict2

View file

@ -50,8 +50,10 @@ internal.transactionId lt:int64 hash:bytes = internal.TransactionId;
ton.blockId workchain:int32 shard:int64 seqno:int32 = internal.BlockId;
ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt;
raw.fullAccountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState;
raw.message source:accountAddress destination:accountAddress value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message;
extraCurrency id:int32 amount:int64 = ExtraCurrency;
raw.fullAccountState balance:int64 extra_currencies:vector<extraCurrency> code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState;
raw.message source:accountAddress destination:accountAddress value:int64 extra_currencies:vector<extraCurrency> fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes msg_data:msg.Data = raw.Message;
raw.transaction address:accountAddress utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
@ -87,7 +89,7 @@ pchan.statePayout A:int64 B:int64 = pchan.State;
pchan.accountState config:pchan.config state:pchan.State description:string = AccountState;
uninited.accountState frozen_hash:bytes = AccountState;
fullAccountState address:accountAddress balance:int64 last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState revision:int32 = FullAccountState;
fullAccountState address:accountAddress balance:int64 extra_currencies:vector<extraCurrency> last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState revision:int32 = FullAccountState;
accountRevisionList revisions:vector<fullAccountState> = AccountRevisionList;
accountList accounts:vector<fullAccountState> = AccountList;
@ -110,7 +112,7 @@ msg.dataDecrypted proof:bytes data:msg.Data = msg.DataDecrypted;
msg.dataEncryptedArray elements:vector<msg.dataEncrypted> = msg.DataEncryptedArray;
msg.dataDecryptedArray elements:vector<msg.dataDecrypted> = msg.DataDecryptedArray;
msg.message destination:accountAddress public_key:string amount:int64 data:msg.Data send_mode:int32 = msg.Message;
msg.message destination:accountAddress public_key:string amount:int64 extra_currencies:vector<extraCurrency> data:msg.Data send_mode:int32 = msg.Message;
//
// DNS

Binary file not shown.

View file

@ -264,7 +264,8 @@ td::Result<QueryId> create_send_grams_query(Client& client, const Wallet& source
data = tonlib_api::make_object<tonlib_api::msg_dataRaw>(message.raw.unwrap(), message.init_state.unwrap());
}
msgs.push_back(tonlib_api::make_object<tonlib_api::msg_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(destination), "", amount, std::move(data), -1));
tonlib_api::make_object<tonlib_api::accountAddress>(destination), "", amount,
std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>{}, std::move(data), -1));
auto r_id =
sync_send(client, tonlib_api::make_object<tonlib_api::createQuery>(
@ -566,7 +567,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) {
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);
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(wallet_id, -1 - i, icb.finalize());

View file

@ -178,6 +178,7 @@ static block::AccountState create_account_state(ton::tl_object_ptr<ton::lite_api
}
struct RawAccountState {
td::int64 balance = -1;
td::Ref<vm::Cell> extra_currencies;
ton::UnixTime storage_last_paid{0};
vm::CellStorageStat storage_stat;
@ -206,6 +207,74 @@ std::string to_bytes(td::Ref<vm::Cell> cell) {
return vm::std_boc_serialize(cell, vm::BagOfCells::Mode::WithCRC32C).move_as_ok().as_slice().str();
}
td::Result<std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>> parse_extra_currencies_or_throw(
const td::Ref<vm::Cell> dict_root) {
std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>> result;
vm::Dictionary dict{dict_root, 32};
if (!dict.check_for_each([&](td::Ref<vm::CellSlice> value, td::ConstBitPtr key, int n) {
CHECK(n == 32);
int id = (int)key.get_int(n);
auto amount_ref = block::tlb::t_VarUIntegerPos_32.as_integer_skip(value.write());
if (amount_ref.is_null() || !value->empty_ext()) {
return false;
}
td::int64 amount = amount_ref->to_long();
if (amount == td::int64(~0ULL << 63)) {
return false;
}
result.push_back(tonlib_api::make_object<tonlib_api::extraCurrency>(id, amount));
return true;
})) {
return td::Status::Error("Failed to parse extra currencies dict");
}
return result;
}
td::Result<std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>> parse_extra_currencies(
const td::Ref<vm::Cell>& dict_root) {
return TRY_VM(parse_extra_currencies_or_throw(dict_root));
}
td::Result<td::Ref<vm::Cell>> to_extra_currenctes_dict(
const std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>& extra_currencies) {
vm::Dictionary dict{32};
for (const auto &f : extra_currencies) {
if (f->amount_ == 0) {
continue;
}
if (f->amount_ < 0) {
return td::Status::Error("Negative extra currency amount");
}
vm::CellBuilder cb2;
block::tlb::t_VarUInteger_32.store_integer_value(cb2, *td::make_refint(f->amount_));
if (!dict.set_builder(td::BitArray<32>(f->id_), cb2, vm::DictionaryBase::SetMode::Add)) {
return td::Status::Error("Duplicate extra currency id");
}
}
return std::move(dict).extract_root_cell();
}
td::Status check_enough_extra_currencies(const td::Ref<vm::Cell> &balance, const td::Ref<vm::Cell> &amount) {
block::CurrencyCollection c1{td::zero_refint(), balance};
block::CurrencyCollection c2{td::zero_refint(), amount};
auto res = TRY_VM(td::Result<bool>{c1 >= c2});
TRY_RESULT(v, std::move(res));
if (!v) {
return TonlibError::NotEnoughFunds();
}
return td::Status::OK();
}
td::Result<td::Ref<vm::Cell>> add_extra_currencies(const td::Ref<vm::Cell> &e1, const td::Ref<vm::Cell> &e2) {
block::CurrencyCollection c1{td::zero_refint(), e1};
block::CurrencyCollection c2{td::zero_refint(), e2};
TRY_RESULT_ASSIGN(c1, TRY_VM(td::Result<block::CurrencyCollection>{c1 + c2}));
if (c1.is_valid()) {
return td::Status::Error("Failed to add extra currencies");
}
return c1.extra;
}
td::Result<block::PublicKey> get_public_key(td::Slice public_key) {
TRY_RESULT_PREFIX(address, block::PublicKey::parse(public_key), TonlibError::InvalidPublicKey());
return address;
@ -313,9 +382,10 @@ class AccountState {
if (state.data.not_null()) {
data = to_bytes(state.data);
}
TRY_RESULT(extra_currencies, parse_extra_currencies(get_extra_currencies()));
return tonlib_api::make_object<tonlib_api::raw_fullAccountState>(
get_balance(), std::move(code), std::move(data), to_transaction_id(raw().info), to_tonlib_api(raw().block_id),
raw().frozen_hash, get_sync_time());
get_balance(), std::move(extra_currencies), std::move(code), std::move(data), to_transaction_id(raw().info),
to_tonlib_api(raw().block_id), raw().frozen_hash, get_sync_time());
}
td::Result<tonlib_api::object_ptr<tonlib_api::wallet_v3_accountState>> to_wallet_v3_accountState() const {
@ -447,10 +517,11 @@ class AccountState {
td::Result<tonlib_api::object_ptr<tonlib_api::fullAccountState>> to_fullAccountState() const {
TRY_RESULT(account_state, to_accountState());
TRY_RESULT(extra_currencies, parse_extra_currencies(get_extra_currencies()));
return tonlib_api::make_object<tonlib_api::fullAccountState>(
tonlib_api::make_object<tonlib_api::accountAddress>(get_address().rserialize(true)), get_balance(),
to_transaction_id(raw().info), to_tonlib_api(raw().block_id), get_sync_time(), std::move(account_state),
get_wallet_revision());
std::move(extra_currencies), to_transaction_id(raw().info), to_tonlib_api(raw().block_id), get_sync_time(),
std::move(account_state), get_wallet_revision());
}
td::Result<tonlib_api::object_ptr<tonlib_api::tvm_cell>> to_shardAccountCell() const {
@ -550,6 +621,10 @@ class AccountState {
return raw_.balance;
}
td::Ref<vm::Cell> get_extra_currencies() const {
return raw_.extra_currencies;
}
const RawAccountState& raw() const {
return raw_;
}
@ -1342,6 +1417,7 @@ class GetRawAccountState : public td::actor::Actor {
}
TRY_RESULT(balance, to_balance(storage.balance));
res.balance = balance;
res.extra_currencies = storage.balance->prefetch_ref();
auto state_tag = block::gen::t_AccountState.get_tag(*storage.state);
if (state_tag < 0) {
return td::Status::Error("Failed to parse AccountState tag");
@ -2008,7 +2084,9 @@ class RunEmulator : public TonlibQueryActor {
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();
block::CurrencyCollection balance = account.get_balance();
raw.balance = balance.grams->to_long();
raw.extra_currencies = balance.extra;
raw.storage_last_paid = std::move(account.last_paid);
raw.storage_stat = std::move(account.storage_stat);
raw.code = std::move(account.code);
@ -3029,6 +3107,7 @@ struct ToRawTransactions {
}
TRY_RESULT(balance, to_balance(msg_info.value));
TRY_RESULT(extra_currencies, parse_extra_currencies(msg_info.value->prefetch_ref()));
TRY_RESULT(src, to_std_address(msg_info.src));
TRY_RESULT(dest, to_std_address(msg_info.dest));
TRY_RESULT(fwd_fee, to_balance(msg_info.fwd_fee));
@ -3037,8 +3116,9 @@ struct ToRawTransactions {
return tonlib_api::make_object<tonlib_api::raw_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(src),
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(dest)), balance, fwd_fee, ihr_fee, created_lt,
std::move(body_hash), get_data(src));
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(dest)), balance,
std::move(extra_currencies), fwd_fee, ihr_fee, created_lt, std::move(body_hash),
get_data(src));
}
case block::gen::CommonMsgInfo::ext_in_msg_info: {
block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info;
@ -3048,7 +3128,8 @@ struct ToRawTransactions {
TRY_RESULT(dest, to_std_address(msg_info.dest));
return tonlib_api::make_object<tonlib_api::raw_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(),
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(dest)), 0, 0, 0, 0, std::move(body_hash),
tonlib_api::make_object<tonlib_api::accountAddress>(std::move(dest)), 0,
std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>{}, 0, 0, 0, std::move(body_hash),
get_data(""));
}
case block::gen::CommonMsgInfo::ext_out_msg_info: {
@ -3060,7 +3141,9 @@ struct ToRawTransactions {
auto created_lt = static_cast<td::int64>(msg_info.created_lt);
return tonlib_api::make_object<tonlib_api::raw_message>(
tonlib_api::make_object<tonlib_api::accountAddress>(src),
tonlib_api::make_object<tonlib_api::accountAddress>(), 0, 0, 0, created_lt, std::move(body_hash), get_data(src));
tonlib_api::make_object<tonlib_api::accountAddress>(), 0,
std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>{}, 0, 0, created_lt, std::move(body_hash),
get_data(src));
}
}
@ -3516,6 +3599,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
struct Action {
block::StdAddress destination;
td::int64 amount;
td::Ref<vm::Cell> extra_currencies;
td::int32 send_mode{-1};
bool is_encrypted{false};
@ -3563,6 +3647,7 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return TonlibError::InvalidField("amount", "can't be negative");
}
res.amount = message.amount_;
TRY_RESULT_ASSIGN(res.extra_currencies, to_extra_currenctes_dict(message.extra_currencies_));
if (!message.public_key_.empty()) {
TRY_RESULT(public_key, get_public_key(message.public_key_));
auto key = td::Ed25519::PublicKey(td::SecureString(public_key.key));
@ -3973,8 +4058,10 @@ class GenericCreateSendGrams : public TonlibQueryActor {
}
td::int64 amount = 0;
td::Ref<vm::Cell> extra_currencies;
for (auto& action : actions_) {
amount += action.amount;
TRY_RESULT_ASSIGN(extra_currencies, add_extra_currencies(extra_currencies, action.extra_currencies));
}
if (amount > source_->get_balance()) {
@ -3986,6 +4073,8 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return TonlibError::NotEnoughFunds();
}
TRY_STATUS(check_enough_extra_currencies(source_->get_extra_currencies(), extra_currencies));
if (source_->get_wallet_type() == AccountState::RestrictedWallet) {
auto r_unlocked_balance = ton::RestrictedWallet::create(source_->get_smc_state())
->get_balance(source_->get_balance(), source_->get_sync_time());
@ -4003,12 +4092,13 @@ class GenericCreateSendGrams : public TonlibQueryActor {
auto& destination = destinations_[i];
gift.destination = destinations_[i]->get_address();
gift.gramms = action.amount;
gift.extra_currencies = action.extra_currencies;
gift.send_mode = action.send_mode;
// Temporary turn off this dangerous transfer
if (false && action.amount == source_->get_balance()) {
gift.gramms = -1;
}
// if (action.amount == source_->get_balance()) {
// gift.gramms = -1;
// }
if (action.body.not_null()) {
gift.body = action.body;

View file

@ -1108,9 +1108,9 @@ class TonlibCli : public td::actor::Actor {
void pchan_init_2(Address addr, td::int32 pchan_id, td::int64 value,
tonlib_api::object_ptr<tonlib_api::query_info> query, td::Promise<td::Unit> promise) {
std::vector<tonlib_api::object_ptr<tonlib_api::msg_message>> messages;
messages.push_back(
make_object<tonlib_api::msg_message>(channels_[pchan_id].to_address(), "", value,
make_object<tonlib_api::msg_dataRaw>(query->body_, query->init_state_), -1));
messages.push_back(make_object<tonlib_api::msg_message>(
channels_[pchan_id].to_address(), "", value, std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>{},
make_object<tonlib_api::msg_dataRaw>(query->body_, query->init_state_), -1));
auto action = make_object<tonlib_api::actionMsg>(std::move(messages), true);
send_query(
make_object<tonlib_api::createQuery>(addr.input_key(), std::move(addr.address), 60, std::move(action), nullptr),
@ -2058,6 +2058,19 @@ class TonlibCli : public td::actor::Actor {
});
}
static void print_full_account_state(const ton::tl_object_ptr<ton::tonlib_api::fullAccountState>& state) {
td::StringBuilder balance_str;
balance_str << "Balance: " << Grams{td::narrow_cast<td::uint64>(state->balance_ * (state->balance_ > 0))};
for (const auto& extra : state->extra_currencies_) {
balance_str << " + " << extra->amount_ << ".$" << extra->id_;
}
td::TerminalIO::out() << balance_str.as_cslice() << "\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_);
}
void get_state(td::Slice key, td::Promise<td::Unit> promise) {
TRY_RESULT_PROMISE(promise, address, to_account_address(key, false));
@ -2066,14 +2079,7 @@ class TonlibCli : public td::actor::Actor {
ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address))),
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_);
print_full_account_state(state);
return td::Unit();
}));
}
@ -2090,14 +2096,7 @@ class TonlibCli : public td::actor::Actor {
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_);
print_full_account_state(state);
return td::Unit();
}));
}
@ -2225,15 +2224,29 @@ class TonlibCli : public td::actor::Actor {
td::StringBuilder sb;
for (tonlib_api::object_ptr<tonlib_api::raw_transaction>& t : res->transactions_) {
td::int64 balance = 0;
std::map<td::int32, td::int64> extra_currencies;
balance += t->in_msg_->value_;
for (const auto& extra : t->in_msg_->extra_currencies_) {
extra_currencies[extra->id_] += extra->amount_;
}
for (auto& ot : t->out_msgs_) {
balance -= ot->value_;
for (const auto& extra : ot->extra_currencies_) {
extra_currencies[extra->id_] -= extra->amount_;
}
}
if (balance >= 0) {
sb << Grams{td::uint64(balance)};
} else {
sb << "-" << Grams{td::uint64(-balance)};
}
for (const auto& [id, amount] : extra_currencies) {
if (amount > 0) {
sb << " + " << amount << ".$" << id;
} else if (amount < 0) {
sb << " - " << -amount << ".$" << id;
}
}
sb << " Fee: " << Grams{td::uint64(t->fee_)};
if (t->in_msg_->source_->account_address_.empty()) {
sb << " External ";
@ -2263,6 +2276,9 @@ class TonlibCli : public td::actor::Actor {
sb << " To " << ot->destination_->account_address_;
}
sb << " " << Grams{td::uint64(ot->value_)};
for (const auto& extra : ot->extra_currencies_) {
sb << " + " << extra->amount_ << ".$" << extra->id_;
}
print_msg_data(sb, ot->msg_data_);
}
sb << "\n";
@ -2327,8 +2343,9 @@ class TonlibCli : public td::actor::Actor {
} else {
data = make_object<tonlib_api::msg_dataText>(message.str());
}
messages.push_back(
make_object<tonlib_api::msg_message>(std::move(address.address), "", amount.nano, std::move(data), -1));
messages.push_back(make_object<tonlib_api::msg_message>(
std::move(address.address), "", amount.nano, std::vector<tonlib_api::object_ptr<tonlib_api::extraCurrency>>{},
std::move(data), -1));
return td::Status::OK();
};

View file

@ -1377,13 +1377,9 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
if (acc_root.not_null()) {
if (mode_ & 0x40000000) {
vm::MerkleProofBuilder mpb{acc_root};
// account_none$0 = Account;
// account$1 addr:MsgAddressInt storage_stat:StorageInfo storage:AccountStorage = Account;
// account_storage$_ last_trans_lt:uint64 balance:CurrencyCollection state:AccountState = AccountStorage;
// account_active$1 _:StateInit = AccountState;
auto S = mpb.root()->load_cell();
if (S.is_error()) {
fatal_error(S.move_as_error_prefix("Failed to load account: "));
// This does not include code, data and libs into proof, but it includes extra currencies
if (!block::gen::t_Account.validate_ref(mpb.root())) {
fatal_error("failed to validate Account");
return;
}
if (!mpb.extract_proof_to(acc_root)) {