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();