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 22:14:52 +04:00
parent 3002321eb7
commit 11bd640ee0
12 changed files with 455 additions and 22 deletions

View file

@ -207,6 +207,7 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/TestGiver.cpp
smc-envelope/TestWallet.cpp
smc-envelope/Wallet.cpp
smc-envelope/WalletV3.cpp
smc-envelope/GenericAccount.h
smc-envelope/MultisigWallet.h
@ -215,6 +216,7 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/TestGiver.h
smc-envelope/TestWallet.h
smc-envelope/Wallet.h
smc-envelope/WalletV3.h
)
set(ED25519_TEST_SOURCE

View file

@ -0,0 +1,50 @@
#!/usr/bin/fift -s
"TonUtil.fif" include
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <subwallet_id> <seqno> <amount> [-B <body-boc>] [<savefile>]" cr
."Creates a request to advanced wallet created by new-wallet-v3.fif, with private key loaded from file <filename-base>.pk "
."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
} : usage
def? $7 { @' $6 "-B" $= { @' $7 =: body-boc-file [forget] $7 def? $8 { @' $8 =: $6 [forget] $8 } { [forget] $6 } cond
@' $# 2- =: $# } if } if
$# dup 5 < swap 6 > or ' usage if
true constant bounce
$1 =: file-base
$2 bounce parse-load-address =: bounce 2=: dest_addr
$3 parse-int =: subwallet_id
$4 parse-int =: seqno
$5 $>GR =: amount
def? $6 { @' $6 } { "wallet-query" } cond constant savefile
3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
60 constant timeout // external message expires in 60 seconds
file-base +".addr" load-address
2dup 2constant wallet_addr
."Source wallet address = " 2dup .addr cr 6 .Addr cr
file-base +".pk" load-keypair nip constant wallet_pk
def? body-boc-file { @' body-boc-file file>B B>boc } { <b 0 32 u, "TESTv3" $, b> } cond
constant body-cell
."Transferring " amount .GR ."to account "
dest_addr 2dup bounce 7 + .Addr ." = " .addr
."subwallet_id=0x" subwallet_id x.
."seqno=0x" seqno x. ."bounce=" bounce . cr
."Body of transfer message is " body-cell <s csr. cr
// create a message
<b b{01} s, bounce 1 i, b{000100} s, dest_addr addr, amount Gram, 0 9 64 32 + + 1+ u,
body-cell <s 2dup s-fits? not rot over 1 i, -rot { drop body-cell ref, } { s, } cond
b>
<b subwallet_id 32 u, now timeout + 32 u, seqno 32 u, send-mode 8 u, swap ref, b>
dup ."signing message: " <s csr. cr
dup hashu wallet_pk ed25519_sign_uint
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
savefile +".boc" tuck B>file
."Query expires in " timeout . ."seconds" cr
."(Saved to file " type .")" cr

View file

@ -0,0 +1,130 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "WalletV3.h"
#include "GenericAccount.h"
#include "vm/boc.h"
#include "vm/cells/CellString.h"
#include "td/utils/base64.h"
#include <limits>
namespace ton {
td::Ref<vm::Cell> WalletV3::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept {
auto code = get_init_code();
auto data = get_init_data(public_key, wallet_id);
return GenericAccount::get_init_state(std::move(code), std::move(data));
}
td::Ref<vm::Cell> WalletV3::get_init_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 wallet_id) noexcept {
td::uint32 seqno = 0;
td::uint32 valid_until = std::numeric_limits<td::uint32>::max();
auto signature = private_key
.sign(vm::CellBuilder()
.store_long(wallet_id, 32)
.store_long(valid_until, 32)
.store_long(seqno, 32)
.finalize()
->get_hash()
.as_slice())
.move_as_ok();
return vm::CellBuilder()
.store_bytes(signature)
.store_long(wallet_id, 32)
.store_long(valid_until, 32)
.store_long(seqno, 32)
.finalize();
}
td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until, td::int64 gramms,
td::Slice message, const block::StdAddress& dest_address) noexcept {
td::int32 send_mode = 3;
if (gramms == -1) {
gramms = 0;
send_mode += 128;
}
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, dest_address, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, message, 35 * 8).ensure();
auto message_inner = cb.finalize();
auto message_outer = vm::CellBuilder()
.store_long(wallet_id, 32)
.store_long(valid_until, 32)
.store_long(seqno, 32)
.store_long(send_mode, 8)
.store_ref(message_inner)
.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> WalletV3::get_init_code() noexcept {
static auto res = [] {
auto serialized_code = td::base64_decode(
"te6ccgEBAQEAYgAAwP8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/T/"
"9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA==")
.move_as_ok();
return vm::std_boc_deserialize(serialized_code).move_as_ok();
}();
return res;
}
vm::CellHash WalletV3::get_init_code_hash() noexcept {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> WalletV3::get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept {
return vm::CellBuilder()
.store_long(0, 32)
.store_long(wallet_id, 32)
.store_bytes(public_key.as_octet_string())
.finalize();
}
td::Result<td::uint32> WalletV3::get_seqno() const {
return TRY_VM(get_seqno_or_throw());
}
td::Result<td::uint32> WalletV3::get_seqno_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
return static_cast<td::uint32>(vm::load_cell_slice(state_.data).fetch_ulong(32));
}
td::Result<td::uint32> WalletV3::get_wallet_id() const {
return TRY_VM(get_wallet_id_or_throw());
}
td::Result<td::uint32> WalletV3::get_wallet_id_or_throw() const {
if (state_.data.is_null()) {
return 0;
}
//FIXME use get method
auto cs = vm::load_cell_slice(state_.data);
cs.skip_first(32);
return static_cast<td::uint32>(cs.fetch_ulong(32));
}
} // namespace ton

View file

@ -0,0 +1,50 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
*/
#pragma once
#include "smc-envelope/SmartContract.h"
#include "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
#include "vm/cells/CellString.h"
namespace ton {
class WalletV3 : ton::SmartContract {
public:
explicit WalletV3(State state) : ton::SmartContract(std::move(state)) {
}
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id) noexcept;
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until, td::int64 gramms,
td::Slice message, const block::StdAddress& dest_address) noexcept;
static td::Ref<vm::Cell> get_init_code() noexcept;
static vm::CellHash get_init_code_hash() noexcept;
static td::Ref<vm::Cell> get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept;
td::Result<td::uint32> get_seqno() const;
td::Result<td::uint32> get_wallet_id() const;
private:
td::Result<td::uint32> get_seqno_or_throw() const;
td::Result<td::uint32> get_wallet_id_or_throw() const;
};
} // namespace ton

View file

@ -34,6 +34,7 @@
#include "smc-envelope/TestGiver.h"
#include "smc-envelope/TestWallet.h"
#include "smc-envelope/Wallet.h"
#include "smc-envelope/WalletV3.h"
#include "td/utils/base64.h"
#include "td/utils/crypto.h"
@ -114,6 +115,33 @@ SETCP0 DUP IFNOTRET // return if recv_internal
)ABCD";
return fift::compile_asm(code).move_as_ok();
}
td::Ref<vm::Cell> get_wallet_v3_source() {
std::string code = R"ABCD(
SETCP0 DUP IFNOTRET // return if recv_internal
DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method
DROP c4 PUSHCTR CTOS 32 PLDU // cnt
}>
INC 32 THROWIF // fail unless recv_external
9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU 32 LDU // signature in_msg subwallet_id valid_until msg_seqno cs
NOW s1 s3 XCHG LEQ 35 THROWIF // signature in_msg subwallet_id cs msg_seqno
c4 PUSH CTOS 32 LDU 32 LDU 256 LDU ENDS // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key
s3 s2 XCPU EQUAL 33 THROWIFNOT // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet
s4 s4 XCPU EQUAL 34 THROWIFNOT // signature in_msg stored_subwallet cs public_key stored_seqno
s0 s4 XCHG HASHSU // signature stored_seqno stored_subwallet cs public_key msg_hash
s0 s5 s5 XC2PU // public_key stored_seqno stored_subwallet cs msg_hash signature public_key
CHKSIGNU 35 THROWIFNOT // public_key stored_seqno stored_subwallet cs
ACCEPT
WHILE:<{
DUP SREFS // public_key stored_seqno stored_subwallet cs _51
}>DO<{ // public_key stored_seqno stored_subwallet cs
8 LDU LDREF s0 s2 XCHG // public_key stored_seqno stored_subwallet cs _56 mode
SENDRAWMSG
}> // public_key stored_seqno stored_subwallet cs
ENDS SWAP INC // public_key stored_subwallet seqno'
NEWC 32 STU 32 STU 256 STU ENDC c4 POP
)ABCD";
return fift::compile_asm(code).move_as_ok();
}
TEST(Tonlib, TestWallet) {
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_test_wallet_source()).move_as_ok());
@ -209,6 +237,55 @@ TEST(Tonlib, Wallet) {
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
}
TEST(Tonlib, WalletV3) {
LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_v3_source()).move_as_ok());
CHECK(get_wallet_v3_source()->get_hash() == ton::WalletV3::get_init_code()->get_hash());
auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v3.fif"), {"aba", "0", "239"}).move_as_ok();
auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data;
auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data;
auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data;
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
auto pub_key = priv_key.get_public_key().move_as_ok();
auto init_state = ton::WalletV3::get_init_state(pub_key, 239);
auto init_message = ton::WalletV3::get_init_message(priv_key, 239);
auto address = ton::GenericAccount::get_address(0, init_state);
CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
td::Ref<vm::Cell> res = ton::GenericAccount::create_ext_message(address, init_state, init_message);
LOG(ERROR) << "-------";
vm::load_cell_slice(res).print_rec(std::cerr);
LOG(ERROR) << "-------";
vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v3.fif")).ensure();
class ZeroOsTime : public fift::OsTime {
public:
td::uint32 now() override {
return 0;
}
};
fift_output.source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
fift_output =
fift::mem_run_fift(std::move(fift_output.source_lookup),
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "239", "123", "321"})
.move_as_ok();
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto gift_message = ton::GenericAccount::create_ext_message(
address, {}, ton::WalletV3::make_a_gift_message(priv_key, 239, 123, 60, 321000000000ll, "TESTv3", dest));
LOG(ERROR) << "-------";
vm::load_cell_slice(gift_message).print_rec(std::cerr);
LOG(ERROR) << "-------";
vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
}
TEST(Tonlib, TestGiver) {
auto address =
block::StdAddress::parse("-1:60c04141c6a7b96d68615e7a91d265ad0f3a9a922e9ae9c901d4fa83f5d3c0d0").move_as_ok();

View file

@ -22,6 +22,7 @@ keyStoreTypeInMemory = KeyStoreType;
config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config;
options config:config keystore_type:KeyStoreType = Options;
options.configInfo default_wallet_id:int53 = options.ConfigInfo;
key public_key:string secret:secureBytes = Key;
inputKeyRegular key:key local_password:secureBytes = InputKey;
@ -50,6 +51,9 @@ testWallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.t
wallet.initialAccountState public_key:string = wallet.InitialAccountState;
wallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = wallet.AccountState;
wallet.v3.initialAccountState public_key:string wallet_id:int53 = wallet.v3.InitialAccountState;
wallet.v3.accountState balance:int64 wallet_id:int53 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = wallet.v3.AccountState;
testGiver.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53= testGiver.AccountState;
uninited.accountState balance:int64 last_transaction_id:internal.transactionId frozen_hash:bytes sync_utime:int53 = uninited.AccountState;
@ -61,6 +65,7 @@ uninited.accountState balance:int64 last_transaction_id:internal.transactionId f
generic.accountStateRaw account_state:raw.accountState = generic.AccountState;
generic.accountStateTestWallet account_state:testWallet.accountState = generic.AccountState;
generic.accountStateWallet account_state:wallet.accountState = generic.AccountState;
generic.accountStateWalletV3 account_state:wallet.v3.accountState = generic.AccountState;
generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState;
generic.accountStateUninited account_state:uninited.accountState = generic.AccountState;
@ -120,7 +125,7 @@ init options:options = Ok;
close = Ok;
options.setConfig config:config = Ok;
options.validateConfig config:config = Ok;
options.validateConfig config:config = options.ConfigInfo;
createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key;
deleteKey key:key = Ok;
@ -159,6 +164,8 @@ wallet.getAccountAddress initital_account_state:wallet.initialAccountState = Acc
wallet.getAccountState account_address:accountAddress = wallet.AccountState;
wallet.sendGrams private_key:InputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult;
wallet.v3.getAccountAddress initital_account_state:wallet.v3.initialAccountState = AccountAddress;
testGiver.getAccountState = testGiver.AccountState;
testGiver.getAccountAddress = AccountAddress;
testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult;

Binary file not shown.

View file

@ -133,9 +133,11 @@ void sync(Client& client) {
sync_send(client, make_object<tonlib_api::sync>()).ensure();
}
static td::uint32 default_wallet_id{0};
std::string wallet_address(Client& client, const Key& key) {
return sync_send(client, make_object<tonlib_api::wallet_getAccountAddress>(
make_object<tonlib_api::wallet_initialAccountState>(key.public_key)))
return sync_send(client,
make_object<tonlib_api::wallet_v3_getAccountAddress>(
make_object<tonlib_api::wallet_v3_initialAccountState>(key.public_key, default_wallet_id)))
.move_as_ok()
->account_address_;
}
@ -171,6 +173,7 @@ AccountState get_account_state(Client& client, std::string address) {
case tonlib_api::generic_accountStateUninited::ID:
res.type = AccountState::Empty;
break;
case tonlib_api::generic_accountStateWalletV3::ID:
case tonlib_api::generic_accountStateWallet::ID:
res.type = AccountState::Wallet;
break;
@ -358,8 +361,9 @@ Wallet create_empty_wallet(Client& client) {
Wallet wallet{"", {key->public_key_, std::move(key->secret_)}};
auto account_address =
sync_send(client, make_object<tonlib_api::wallet_getAccountAddress>(
make_object<tonlib_api::wallet_initialAccountState>(wallet.key.public_key)))
sync_send(client,
make_object<tonlib_api::wallet_v3_getAccountAddress>(
make_object<tonlib_api::wallet_v3_initialAccountState>(wallet.key.public_key, default_wallet_id)))
.move_as_ok();
wallet.address = account_address->account_address_;
@ -529,6 +533,10 @@ int main(int argc, char* argv[]) {
Client client;
{
auto info = sync_send(client, make_object<tonlib_api::options_validateConfig>(
make_object<tonlib_api::config>(global_config_str, "", false, false)))
.move_as_ok();
default_wallet_id = static_cast<td::uint32>(info->default_wallet_id_);
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
make_object<tonlib_api::config>(global_config_str, "", false, false),
make_object<tonlib_api::keyStoreTypeDirectory>(keystore_dir))))

View file

@ -31,6 +31,7 @@
#include "smc-envelope/GenericAccount.h"
#include "smc-envelope/TestWallet.h"
#include "smc-envelope/Wallet.h"
#include "smc-envelope/WalletV3.h"
#include "smc-envelope/TestGiver.h"
#include "auto/tl/tonlib_api.hpp"
@ -41,6 +42,7 @@
#include "vm/boc.h"
#include "td/utils/as.h"
#include "td/utils/Random.h"
#include "td/utils/optional.h"
#include "td/utils/overloaded.h"
@ -121,7 +123,8 @@ std::string to_bytes(td::Ref<vm::Cell> cell) {
class AccountState {
public:
AccountState(block::StdAddress address, RawAccountState&& raw) : address_(std::move(address)), raw_(std::move(raw)) {
AccountState(block::StdAddress address, RawAccountState&& raw, td::uint32 wallet_id)
: address_(std::move(address)), raw_(std::move(raw)), wallet_id_(wallet_id) {
wallet_type_ = guess_type();
}
@ -162,6 +165,17 @@ class AccountState {
return tonlib_api::make_object<tonlib_api::wallet_accountState>(get_balance(), static_cast<td::uint32>(seqno),
to_transaction_id(raw().info), get_sync_time());
}
td::Result<tonlib_api::object_ptr<tonlib_api::wallet_v3_accountState>> to_wallet_v3_accountState() const {
if (wallet_type_ != WalletV3) {
return TonlibError::AccountTypeUnexpected("WalletV3");
}
auto wallet = ton::WalletV3(get_smc_state());
TRY_RESULT(seqno, wallet.get_seqno());
TRY_RESULT(wallet_id, wallet.get_wallet_id());
return tonlib_api::make_object<tonlib_api::wallet_v3_accountState>(
get_balance(), static_cast<td::uint32>(wallet_id), static_cast<td::uint32>(seqno),
to_transaction_id(raw().info), get_sync_time());
}
td::Result<tonlib_api::object_ptr<tonlib_api::testGiver_accountState>> to_testGiver_accountState() const {
if (wallet_type_ != Giver) {
@ -191,11 +205,15 @@ class AccountState {
TRY_RESULT(res, to_wallet_accountState());
return tonlib_api::make_object<tonlib_api::generic_accountStateWallet>(std::move(res));
}
case WalletV3: {
TRY_RESULT(res, to_wallet_v3_accountState());
return tonlib_api::make_object<tonlib_api::generic_accountStateWalletV3>(std::move(res));
}
}
UNREACHABLE();
}
enum WalletType { Empty, Unknown, Giver, SimpleWallet, Wallet };
enum WalletType { Empty, Unknown, Giver, SimpleWallet, Wallet, WalletV3 };
WalletType get_wallet_type() const {
return wallet_type_;
}
@ -235,6 +253,10 @@ class AccountState {
address_.addr) {
set_new_state({ton::Wallet::get_init_code(), ton::Wallet::get_init_data(key)});
wallet_type_ = WalletType::Wallet;
} else if (ton::GenericAccount::get_address(address_.workchain, ton::WalletV3::get_init_state(key, wallet_id_))
.addr == address_.addr) {
set_new_state({ton::WalletV3::get_init_code(), ton::WalletV3::get_init_data(key, wallet_id_)});
wallet_type_ = WalletType::WalletV3;
}
return wallet_type_;
}
@ -243,8 +265,8 @@ class AccountState {
if (wallet_type_ != WalletType::Empty) {
return wallet_type_;
}
set_new_state({ton::Wallet::get_init_code(), ton::Wallet::get_init_data(key)});
wallet_type_ = WalletType::Wallet;
set_new_state({ton::WalletV3::get_init_code(), ton::WalletV3::get_init_data(key, wallet_id_)});
wallet_type_ = WalletType::WalletV3;
return wallet_type_;
}
@ -274,6 +296,7 @@ class AccountState {
block::StdAddress address_;
RawAccountState raw_;
WalletType wallet_type_{Unknown};
td::uint32 wallet_id_{0};
bool has_new_state_{false};
WalletType guess_type() const {
@ -290,6 +313,9 @@ class AccountState {
if (code_hash == ton::Wallet::get_init_code_hash()) {
return WalletType::Wallet;
}
if (code_hash == ton::WalletV3::get_init_code_hash()) {
return WalletType::WalletV3;
}
LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice());
return WalletType::Unknown;
}
@ -999,6 +1025,7 @@ bool TonlibClient::is_static_request(td::int32 id) {
case tonlib_api::raw_getAccountAddress::ID:
case tonlib_api::testWallet_getAccountAddress::ID:
case tonlib_api::wallet_getAccountAddress::ID:
case tonlib_api::wallet_v3_getAccountAddress::ID:
case tonlib_api::testGiver_getAccountAddress::ID:
case tonlib_api::packAccountAddress::ID:
case tonlib_api::unpackAccountAddress::ID:
@ -1062,6 +1089,12 @@ td::Result<block::StdAddress> get_account_address(const tonlib_api::wallet_initi
auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key));
return ton::GenericAccount::get_address(0 /*zerochain*/, ton::Wallet::get_init_state(key));
}
td::Result<block::StdAddress> get_account_address(const tonlib_api::wallet_v3_initialAccountState& test_wallet_state) {
TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_));
auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key));
return ton::GenericAccount::get_address(
0 /*zerochain*/, ton::WalletV3::get_init_state(key, static_cast<td::uint32>(test_wallet_state.wallet_id_)));
}
td::Result<block::StdAddress> get_account_address(td::Slice account_address) {
TRY_RESULT_PREFIX(address, block::StdAddress::parse(account_address), TonlibError::InvalidAccountAddress());
@ -1070,6 +1103,9 @@ td::Result<block::StdAddress> get_account_address(td::Slice account_address) {
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
const tonlib_api::raw_getAccountAddress& request) {
if (!request.initital_account_state_) {
return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state"));
}
auto r_account_address = get_account_address(*request.initital_account_state_);
if (r_account_address.is_error()) {
return status_to_tonlib_api(r_account_address.error());
@ -1079,6 +1115,12 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
const tonlib_api::testWallet_getAccountAddress& request) {
if (!request.initital_account_state_) {
return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state"));
}
if (!request.initital_account_state_) {
return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state"));
}
auto r_account_address = get_account_address(*request.initital_account_state_);
if (r_account_address.is_error()) {
return status_to_tonlib_api(r_account_address.error());
@ -1088,6 +1130,20 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
const tonlib_api::wallet_getAccountAddress& request) {
if (!request.initital_account_state_) {
return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state"));
}
auto r_account_address = get_account_address(*request.initital_account_state_);
if (r_account_address.is_error()) {
return status_to_tonlib_api(r_account_address.error());
}
return tonlib_api::make_object<tonlib_api::accountAddress>(r_account_address.ok().rserialize(true));
}
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
const tonlib_api::wallet_v3_getAccountAddress& request) {
if (!request.initital_account_state_) {
return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state"));
}
auto r_account_address = get_account_address(*request.initital_account_state_);
if (r_account_address.is_error()) {
return status_to_tonlib_api(r_account_address.error());
@ -1249,12 +1305,14 @@ td::Result<TonlibClient::FullConfig> TonlibClient::validate_config(tonlib_api::o
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_;
res.wallet_id = td::as<td::uint32>(res.config.zero_state_id.root_hash.as_slice().data());
return std::move(res);
}
void TonlibClient::set_config(FullConfig full_config) {
config_ = std::move(full_config.config);
config_generation_++;
wallet_id_ = full_config.wallet_id;
blockchain_name_ = config_.zero_state_id.root_hash.as_slice().str();
use_callbacks_for_network_ = full_config.use_callbacks_for_network;
@ -1280,7 +1338,7 @@ tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
if (r_config.is_error()) {
return status_to_tonlib_api(r_config.move_as_error());
}
return tonlib_api::make_object<tonlib_api::ok>();
return tonlib_api::make_object<tonlib_api::options_configInfo>(r_config.ok().wallet_id);
}
td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request,
@ -1893,6 +1951,23 @@ class GenericCreateSendGrams : public TonlibQueryActor {
destination_->get_address());
break;
}
case AccountState::WalletV3: {
if (!private_key_) {
return TonlibError::EmptyField("private_key");
}
if (message.size() > ton::WalletV3::max_message_size) {
return TonlibError::MessageTooLong();
}
auto wallet = ton::WalletV3(source_->get_smc_state());
TRY_RESULT(seqno, wallet.get_seqno());
TRY_RESULT(wallet_id, wallet.get_wallet_id());
auto valid_until = source_->get_sync_time();
valid_until += send_grams_.timeout_ == 0 ? 60 : send_grams_.timeout_;
raw.valid_until = valid_until;
raw.message_body = ton::WalletV3::make_a_gift_message(private_key_.unwrap(), wallet_id, seqno, valid_until,
amount, message, destination_->get_address());
break;
}
}
raw.new_state = source_->get_new_state();
@ -2433,8 +2508,8 @@ td::Status TonlibClient::do_request(int_api::GetAccountState request,
auto actor_id = actor_id_++;
actors_[actor_id] = td::actor::create_actor<GetRawAccountState>(
"GetAccountState", client_.get_client(), request.address, actor_shared(this, actor_id),
promise.wrap([address = request.address](auto&& state) mutable {
return td::make_unique<AccountState>(std::move(address), std::move(state));
promise.wrap([address = request.address, wallet_id = wallet_id_](auto&& state) mutable {
return td::make_unique<AccountState>(std::move(address), std::move(state), wallet_id);
}));
return td::Status::OK();
}
@ -2481,6 +2556,11 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_getAccountAddress&
return TonlibError::Internal();
}
template <class P>
td::Status TonlibClient::do_request(const tonlib_api::wallet_v3_getAccountAddress& request, P&&) {
UNREACHABLE();
return TonlibError::Internal();
}
template <class P>
td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&) {
UNREACHABLE();
return TonlibError::Internal();

View file

@ -65,6 +65,7 @@ class TonlibClient : public td::actor::Actor {
// Config
Config config_;
td::uint32 config_generation_{0};
td::uint32 wallet_id_;
std::string blockchain_name_;
bool ignore_cache_{false};
bool use_callbacks_for_network_{false};
@ -123,6 +124,7 @@ class TonlibClient : public td::actor::Actor {
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::raw_getAccountAddress& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::testWallet_getAccountAddress& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::wallet_getAccountAddress& request);
static object_ptr<tonlib_api::Object> do_static_request(const tonlib_api::wallet_v3_getAccountAddress& request);
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);
@ -151,6 +153,8 @@ class TonlibClient : public td::actor::Actor {
template <class P>
td::Status do_request(const tonlib_api::wallet_getAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::wallet_v3_getAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&);
template <class P>
td::Status do_request(const tonlib_api::packAccountAddress& request, P&&);
@ -198,6 +202,7 @@ class TonlibClient : public td::actor::Actor {
td::optional<Config> o_master_config;
bool use_callbacks_for_network;
bool ignore_cache;
td::uint32 wallet_id;
};
static td::Result<FullConfig> validate_config(tonlib_api::object_ptr<tonlib_api::config> config);
void set_config(FullConfig config);

View file

@ -79,6 +79,9 @@ struct TonlibError {
static td::Status InvalidPemKey() {
return td::Status::Error(400, "INVALID_PEM_KEY");
}
static td::Status NeedConfig() {
return td::Status::Error(400, "NeedConfig");
}
static td::Status MessageTooLong() {
return td::Status::Error(400, "MESSAGE_TOO_LONG");
}

View file

@ -81,7 +81,7 @@ class TonlibCli : public td::actor::Actor {
std::string key_dir{"."};
bool in_memory{false};
bool use_callbacks_for_network{false};
bool use_simple_wallet{false};
td::int32 wallet_version = 2;
bool ignore_cache{false};
bool one_shot{false};
@ -96,6 +96,7 @@ class TonlibCli : public td::actor::Actor {
td::actor::ActorOwn<tonlib::TonlibClient> client_;
std::uint64_t next_query_id_{1};
td::Promise<td::Slice> cont_;
td::uint32 wallet_id_;
struct KeyInfo {
std::string public_key;
@ -176,6 +177,10 @@ class TonlibCli : public td::actor::Actor {
? make_object<tonlib_api::config>(options_.config, options_.name,
options_.use_callbacks_for_network, options_.ignore_cache)
: nullptr;
auto config2 = !options_.config.empty()
? make_object<tonlib_api::config>(options_.config, options_.name,
options_.use_callbacks_for_network, options_.ignore_cache)
: nullptr;
tonlib_api::object_ptr<tonlib_api::KeyStoreType> ks_type;
if (options_.in_memory) {
@ -183,6 +188,14 @@ class TonlibCli : public td::actor::Actor {
} else {
ks_type = make_object<tonlib_api::keyStoreTypeDirectory>(options_.key_dir);
}
auto obj =
tonlib::TonlibClient::static_request(make_object<tonlib_api::options_validateConfig>(std::move(config2)));
if (obj->get_id() != tonlib_api::error::ID) {
auto info = ton::move_tl_object_as<tonlib_api::options_configInfo>(obj);
wallet_id_ = static_cast<td::uint32>(info->default_wallet_id_);
} else {
LOG(ERROR) << "Invalid config";
}
send_query(make_object<tonlib_api::init>(make_object<tonlib_api::options>(std::move(config), std::move(ks_type))),
[](auto r_ok) {
LOG_IF(ERROR, r_ok.is_error()) << r_ok.error();
@ -519,7 +532,7 @@ class TonlibCli : public td::actor::Actor {
}));
} else {
send_query(make_object<tonlib_api::options_validateConfig>(std::move(config)), promise.wrap([](auto&& info) {
td::TerminalIO::out() << "Config is valid\n";
td::TerminalIO::out() << "Config is valid: " << to_string(info) << "\n";
return td::Unit();
}));
}
@ -824,11 +837,19 @@ class TonlibCli : public td::actor::Actor {
auto r_key_i = to_key_i(key);
using tonlib_api::make_object;
if (r_key_i.is_ok()) {
auto obj = options_.use_simple_wallet
? tonlib::TonlibClient::static_request(make_object<tonlib_api::testWallet_getAccountAddress>(
make_object<tonlib_api::testWallet_initialAccountState>(keys_[r_key_i.ok()].public_key)))
: tonlib::TonlibClient::static_request(make_object<tonlib_api::wallet_getAccountAddress>(
make_object<tonlib_api::wallet_initialAccountState>(keys_[r_key_i.ok()].public_key)));
auto obj = [&](td::int32 version) {
if (version == 1) {
return tonlib::TonlibClient::static_request(make_object<tonlib_api::testWallet_getAccountAddress>(
make_object<tonlib_api::testWallet_initialAccountState>(keys_[r_key_i.ok()].public_key)));
}
if (version == 2) {
return tonlib::TonlibClient::static_request(make_object<tonlib_api::wallet_getAccountAddress>(
make_object<tonlib_api::wallet_initialAccountState>(keys_[r_key_i.ok()].public_key)));
}
return tonlib::TonlibClient::static_request(make_object<tonlib_api::wallet_v3_getAccountAddress>(
make_object<tonlib_api::wallet_v3_initialAccountState>(keys_[r_key_i.ok()].public_key, wallet_id_)));
UNREACHABLE();
}(options_.wallet_version);
if (obj->get_id() != tonlib_api::error::ID) {
Address res;
res.address = ton::move_tl_object_as<tonlib_api::accountAddress>(obj);
@ -1166,7 +1187,7 @@ class TonlibCli : public td::actor::Actor {
void init_simple_wallet(std::string key, size_t key_i, td::Slice password) {
using tonlib_api::make_object;
if (options_.use_simple_wallet) {
if (options_.wallet_version == 1) {
send_query(make_object<tonlib_api::testWallet_init>(make_object<tonlib_api::inputKeyRegular>(
make_object<tonlib_api::key>(keys_[key_i].public_key, keys_[key_i].secret.copy()),
td::SecureString(password))),
@ -1259,8 +1280,8 @@ int main(int argc, char* argv[]) {
options.use_callbacks_for_network = true;
return td::Status::OK();
});
p.add_option('S', "use-simple-wallet", "do not use this", [&]() {
options.use_simple_wallet = true;
p.add_option('W', "wallet-version", "do not use this", [&](td::Slice arg) {
options.wallet_version = td::to_integer<td::int32>(arg);
return td::Status::OK();
});