diff --git a/blockchain-explorer/blockchain-explorer-http.cpp b/blockchain-explorer/blockchain-explorer-http.cpp index e2322ff7..e5203cb7 100644 --- a/blockchain-explorer/blockchain-explorer-http.cpp +++ b/blockchain-explorer/blockchain-explorer-http.cpp @@ -122,9 +122,8 @@ HttpAnswer& HttpAnswer::operator<<(MessageCell msg) { abort("cannot unpack internal message"); return *this; } - td::RefInt256 value; - td::Ref 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) { << "destination" << AddressCell{info.dest} << "\n" << "lt" << info.created_lt << "\n" << "time" << info.created_at << " (" << time_to_human(info.created_at) << ")\n" - << "value" << value << "\n"; + << "value" << currency_collection.to_str()<< "\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 << "workchain" << acc_c.addr.workchain << ""; *this << "account hex" << acc_c.addr.addr.to_hex() << ""; *this << "account" << acc_c.addr.rserialize(true) << ""; + *this << "balance" << balance.to_str() << ""; if (last_trans_lt > 0) { *this << "last transaction" << "lt=" << last_trans_lt diff --git a/crypto/smc-envelope/GenericAccount.cpp b/crypto/smc-envelope/GenericAccount.cpp index 04249699..4c9bd165 100644 --- a/crypto/smc-envelope/GenericAccount.cpp +++ b/crypto/smc-envelope/GenericAccount.cpp @@ -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 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 GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref new_state, diff --git a/crypto/smc-envelope/GenericAccount.h b/crypto/smc-envelope/GenericAccount.h index 285553c7..93a059f9 100644 --- a/crypto/smc-envelope/GenericAccount.h +++ b/crypto/smc-envelope/GenericAccount.h @@ -36,7 +36,8 @@ class GenericAccount { static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) noexcept; static td::Ref create_ext_message(const block::StdAddress& address, td::Ref new_state, td::Ref 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 extra_currencies); static td::Result get_public_key(const SmartContract& sc); static td::Result get_seqno(const SmartContract& sc); diff --git a/crypto/smc-envelope/WalletInterface.cpp b/crypto/smc-envelope/WalletInterface.cpp index e02759b2..eecec435 100644 --- a/crypto/smc-envelope/WalletInterface.cpp +++ b/crypto/smc-envelope/WalletInterface.cpp @@ -48,7 +48,7 @@ td::Result> WalletInterface::get_init_message(const td::Ed2551 td::Ref 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); diff --git a/crypto/smc-envelope/WalletInterface.h b/crypto/smc-envelope/WalletInterface.h index c4e1f270..e88ca0a6 100644 --- a/crypto/smc-envelope/WalletInterface.h +++ b/crypto/smc-envelope/WalletInterface.h @@ -37,6 +37,7 @@ class WalletInterface : public SmartContract { struct Gift { block::StdAddress destination; td::int64 gramms; + td::Ref extra_currencies; td::int32 send_mode{-1}; bool is_encrypted{false}; diff --git a/crypto/vm/dict.cpp b/crypto/vm/dict.cpp index c79924d0..41f9c339 100644 --- a/crypto/vm/dict.cpp +++ b/crypto/vm/dict.cpp @@ -1779,7 +1779,7 @@ Ref DictionaryFixed::dict_combine_with(Ref dict1, Ref 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 DictionaryFixed::dict_combine_with(Ref dict1, Ref 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 diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 6cf40d00..5110d6ec 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -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 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 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.Transaction; raw.transactions transactions:vector 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 last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState revision:int32 = FullAccountState; accountRevisionList revisions:vector = AccountRevisionList; accountList accounts:vector = AccountList; @@ -110,7 +112,7 @@ msg.dataDecrypted proof:bytes data:msg.Data = msg.DataDecrypted; msg.dataEncryptedArray elements:vector = msg.DataEncryptedArray; msg.dataDecryptedArray elements:vector = 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 data:msg.Data send_mode:int32 = msg.Message; // // DNS diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 686bd918..70c08459 100644 Binary files a/tl/generate/scheme/tonlib_api.tlo and b/tl/generate/scheme/tonlib_api.tlo differ diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 52f51720..cf892c29 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -264,7 +264,8 @@ td::Result create_send_grams_query(Client& client, const Wallet& source data = tonlib_api::make_object(message.raw.unwrap(), message.init_state.unwrap()); } msgs.push_back(tonlib_api::make_object( - tonlib_api::make_object(destination), "", amount, std::move(data), -1)); + tonlib_api::make_object(destination), "", amount, + std::vector>{}, std::move(data), -1)); auto r_id = sync_send(client, tonlib_api::make_object( @@ -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()); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 5b799b30..7b223839 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -178,6 +178,7 @@ static block::AccountState create_account_state(ton::tl_object_ptr extra_currencies; ton::UnixTime storage_last_paid{0}; vm::CellStorageStat storage_stat; @@ -206,6 +207,74 @@ std::string to_bytes(td::Ref cell) { return vm::std_boc_serialize(cell, vm::BagOfCells::Mode::WithCRC32C).move_as_ok().as_slice().str(); } +td::Result>> parse_extra_currencies_or_throw( + const td::Ref dict_root) { + std::vector> result; + vm::Dictionary dict{dict_root, 32}; + if (!dict.check_for_each([&](td::Ref 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(id, amount)); + return true; + })) { + return td::Status::Error("Failed to parse extra currencies dict"); + } + return result; +} + +td::Result>> parse_extra_currencies( + const td::Ref& dict_root) { + return TRY_VM(parse_extra_currencies_or_throw(dict_root)); +} + +td::Result> to_extra_currenctes_dict( + const std::vector>& 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 &balance, const td::Ref &amount) { + block::CurrencyCollection c1{td::zero_refint(), balance}; + block::CurrencyCollection c2{td::zero_refint(), amount}; + auto res = TRY_VM(td::Result{c1 >= c2}); + TRY_RESULT(v, std::move(res)); + if (!v) { + return TonlibError::NotEnoughFunds(); + } + return td::Status::OK(); +} + +td::Result> add_extra_currencies(const td::Ref &e1, const td::Ref &e2) { + block::CurrencyCollection c1{td::zero_refint(), e1}; + block::CurrencyCollection c2{td::zero_refint(), e2}; + TRY_RESULT_ASSIGN(c1, TRY_VM(td::Result{c1 + c2})); + if (c1.is_valid()) { + return td::Status::Error("Failed to add extra currencies"); + } + return c1.extra; +} + td::Result 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( - 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> to_wallet_v3_accountState() const { @@ -447,10 +517,11 @@ class AccountState { td::Result> 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::make_object(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> to_shardAccountCell() const { @@ -550,6 +621,10 @@ class AccountState { return raw_.balance; } + td::Ref 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::make_object(src), - tonlib_api::make_object(std::move(dest)), balance, fwd_fee, ihr_fee, created_lt, - std::move(body_hash), get_data(src)); + tonlib_api::make_object(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::make_object(), - tonlib_api::make_object(std::move(dest)), 0, 0, 0, 0, std::move(body_hash), + tonlib_api::make_object(std::move(dest)), 0, + std::vector>{}, 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(msg_info.created_lt); return tonlib_api::make_object( tonlib_api::make_object(src), - tonlib_api::make_object(), 0, 0, 0, created_lt, std::move(body_hash), get_data(src)); + tonlib_api::make_object(), 0, + std::vector>{}, 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 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 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; diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index f955adb5..4567478e 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -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 query, td::Promise promise) { std::vector> messages; - messages.push_back( - make_object(channels_[pchan_id].to_address(), "", value, - make_object(query->body_, query->init_state_), -1)); + messages.push_back(make_object( + channels_[pchan_id].to_address(), "", value, std::vector>{}, + make_object(query->body_, query->init_state_), -1)); auto action = make_object(std::move(messages), true); send_query( make_object(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& state) { + td::StringBuilder balance_str; + balance_str << "Balance: " << Grams{td::narrow_cast(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 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(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(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(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(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& t : res->transactions_) { td::int64 balance = 0; + std::map 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(message.str()); } - messages.push_back( - make_object(std::move(address.address), "", amount.nano, std::move(data), -1)); + messages.push_back(make_object( + std::move(address.address), "", amount.nano, std::vector>{}, + std::move(data), -1)); return td::Status::OK(); }; diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 39f9cc9f..6bd4e421 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -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)) {