1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00
new database
fift/func bugfixes
This commit is contained in:
ton 2019-11-15 18:02:37 +04:00
parent 950e292264
commit e30d98eb30
110 changed files with 6102 additions and 2075 deletions

View file

@ -201,6 +201,7 @@ set(BLOCK_SOURCE
set(SMC_ENVELOPE_SOURCE
smc-envelope/GenericAccount.cpp
smc-envelope/HighloadWallet.cpp
smc-envelope/MultisigWallet.cpp
smc-envelope/SmartContract.cpp
smc-envelope/SmartContractCode.cpp
@ -210,6 +211,7 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/WalletV3.cpp
smc-envelope/GenericAccount.h
smc-envelope/HighloadWallet.h
smc-envelope/MultisigWallet.h
smc-envelope/SmartContract.h
smc-envelope/SmartContractCode.h
@ -387,5 +389,13 @@ if (WINGETOPT_FOUND)
target_link_libraries_system(dump-block wingetopt)
endif()
add_executable(adjust-block block/adjust-block.cpp)
target_include_directories(adjust-block PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(adjust-block PUBLIC ton_crypto fift-lib ton_block)
if (WINGETOPT_FOUND)
target_link_libraries_system(dump-block wingetopt)
endif()
install(TARGETS fift func RUNTIME DESTINATION bin)
install(DIRECTORY fift/lib/ DESTINATION lib/fift)

View file

@ -0,0 +1,203 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU 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 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "block/block.h"
#include "vm/boc.h"
#include <iostream>
#include "block-db.h"
#include "block-auto.h"
#include "block-parse.h"
#include "vm/cp0.h"
#include "td/utils/crypto.h"
#include <getopt.h>
using td::Ref;
using namespace std::literals::string_literals;
int verbosity;
struct IntError {
std::string err_msg;
IntError(std::string _msg) : err_msg(_msg) {
}
IntError(const char* _msg) : err_msg(_msg) {
}
};
int fatal(std::string str) {
std::cerr << "fatal error: " << str << std::endl;
std::exit(2);
return 2;
}
static inline void fail_unless(td::Status res) {
if (res.is_error()) {
throw IntError{res.to_string()};
}
}
td::Ref<vm::Cell> load_block(std::string filename, ton::BlockIdExt& id) {
std::cerr << "loading block from bag-of-cell file " << filename << std::endl;
auto bytes_res = block::load_binary_file(filename);
if (bytes_res.is_error()) {
throw IntError{PSTRING() << "cannot load file `" << filename << "` : " << bytes_res.move_as_error()};
}
ton::FileHash fhash;
td::sha256(bytes_res.ok(), fhash.as_slice());
vm::BagOfCells boc;
auto res = boc.deserialize(bytes_res.move_as_ok());
if (res.is_error()) {
throw IntError{PSTRING() << "cannot deserialize bag-of-cells " << res.move_as_error()};
}
if (res.move_as_ok() <= 0 || boc.get_root_cell().is_null()) {
throw IntError{"cannot deserialize bag-of-cells"};
}
auto root = boc.get_root_cell();
std::vector<ton::BlockIdExt> prev;
ton::BlockIdExt mc_blkid;
bool after_split;
fail_unless(block::unpack_block_prev_blk_try(root, id, prev, mc_blkid, after_split, &id));
id.file_hash = fhash;
std::cerr << "loaded block " << id.to_str() << std::endl;
return root;
}
bool save_block(std::string filename, Ref<vm::Cell> root, ton::BlockIdExt& id) {
std::cerr << "saving block into bag-of-cell file " << filename << std::endl;
if (root.is_null()) {
throw IntError{"new block has no root"};
}
id.root_hash = root->get_hash().bits();
auto res = vm::std_boc_serialize(std::move(root), 31);
if (res.is_error()) {
throw IntError{PSTRING() << "cannot serialize modified block as a bag-of-cells: "
<< res.move_as_error().to_string()};
}
auto data = res.move_as_ok();
td::sha256(data, id.file_hash.as_slice());
auto res1 = block::save_binary_file(filename, std::move(data));
if (res1.is_error()) {
throw IntError{PSTRING() << "cannot save file `" << filename << "` : " << res1};
}
return true;
}
Ref<vm::Cell> adjust_block(Ref<vm::Cell> root, int vseqno_incr, const ton::BlockIdExt& id) {
std::vector<ton::BlockIdExt> prev;
ton::BlockIdExt mc_blkid;
bool after_split;
fail_unless(block::unpack_block_prev_blk_try(root, id, prev, mc_blkid, after_split));
std::cerr << "unpacked header of block " << id.to_str() << std::endl;
if (!id.is_masterchain()) {
throw IntError{"can modify only masterchain blocks"};
}
block::gen::Block::Record blk;
block::gen::BlockInfo::Record info;
if (!(tlb::unpack_cell(root, blk) && tlb::unpack_cell(blk.info, info))) {
throw IntError{"cannot unpack block header"};
}
if (!info.key_block) {
throw IntError{"can modify only key blocks"};
}
info.vert_seqno_incr = true;
info.vert_seq_no += vseqno_incr;
if (!block::tlb::t_ExtBlkRef.pack_to(info.prev_vert_ref, id, info.end_lt)) {
throw IntError{"cannot pack prev_vert_ref"};
}
if (!(tlb::pack_cell(blk.info, info) && tlb::pack_cell(root, blk))) {
throw IntError{"cannot pack block header"};
}
return root;
}
void usage() {
std::cout << "usage: adjust-block [-i<vs-incr>] <in-boc-file> <out-boc-file>\n\tor adjust-block -h\n\tAdjusts block "
"loaded from <in-boc-file> by incrementing vert_seqno by <vs-incr> (1 by default)\n";
std::exit(3);
}
int main(int argc, char* const argv[]) {
int i, vseqno_incr = 1;
int new_verbosity_level = VERBOSITY_NAME(INFO);
std::string in_fname, out_fname;
while ((i = getopt(argc, argv, "hi:v:")) != -1) {
switch (i) {
case 'h':
usage();
break;
case 'i':
vseqno_incr = td::to_integer<int>(td::Slice(optarg));
CHECK(vseqno_incr > 0 && vseqno_incr < 1000);
break;
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
default:
usage();
break;
}
}
SET_VERBOSITY_LEVEL(new_verbosity_level);
if (argc != optind + 2) {
usage();
return 2;
}
in_fname = argv[optind];
out_fname = argv[optind + 1];
try {
ton::BlockIdExt old_id, new_id;
auto root = load_block(in_fname, old_id);
if (root.is_null()) {
return fatal("cannot load BoC from file "s + in_fname);
}
bool ok = block::gen::t_Block.validate_ref(root);
if (!ok) {
return fatal("file `"s + in_fname + " does not contain a valid block");
}
auto adjusted = adjust_block(root, vseqno_incr, old_id);
if (adjusted.is_null()) {
return fatal("cannot adjust block");
}
ok = block::gen::t_Block.validate_ref(root);
if (!ok) {
return fatal("modified block is not valid");
}
new_id = old_id;
if (!save_block(out_fname, adjusted, new_id)) {
return fatal("cannot save modified block to file `"s + out_fname + "`");
}
std::cout << "old block id: " << old_id.to_str() << std::endl;
std::cout << "new block id: " << new_id.to_str() << std::endl;
} catch (IntError& err) {
std::cerr << "internal error: " << err.err_msg << std::endl;
return 1;
} catch (vm::VmError& err) {
std::cerr << "vm error: " << err.get_msg() << std::endl;
return 1;
}
return 0;
}

View file

@ -2005,6 +2005,23 @@ bool ExtBlkRef::unpack(Ref<vm::CellSlice> cs_ref, ton::BlockIdExt& blkid, ton::L
return true;
}
bool ExtBlkRef::store(vm::CellBuilder& cb, const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const {
return cb.store_long_bool(end_lt, 64) // ext_blk_ref$_ end_lt:uint64
&& cb.store_long_bool(blkid.seqno(), 32) // seq_no:uint32
&& cb.store_bits_bool(blkid.root_hash) // root_hash:bits256
&& cb.store_bits_bool(blkid.file_hash); // file_hash:bits256 = ExtBlkRef;
}
Ref<vm::Cell> ExtBlkRef::pack_cell(const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const {
vm::CellBuilder cb;
return store(cb, blkid, end_lt) ? cb.finalize() : Ref<vm::Cell>{};
}
bool ExtBlkRef::pack_to(Ref<vm::Cell>& cell, const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const {
vm::CellBuilder cb;
return store(cb, blkid, end_lt) && cb.finalize_to(cell);
}
const ExtBlkRef t_ExtBlkRef;
const BlkMasterInfo t_BlkMasterInfo;

View file

@ -921,6 +921,9 @@ struct ExtBlkRef final : TLB {
}
bool unpack(vm::CellSlice& cs, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
bool unpack(Ref<vm::CellSlice> cs_ref, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const;
bool store(vm::CellBuilder& cb, const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const;
Ref<vm::Cell> pack_cell(const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const;
bool pack_to(Ref<vm::Cell>& cell, const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const;
};
extern const ExtBlkRef t_ExtBlkRef;

View file

@ -1745,7 +1745,7 @@ td::Status unpack_block_prev_blk_ext(Ref<vm::Cell> block_root, const ton::BlockI
block::gen::ExtBlkRef::Record mcref; // _ ExtBlkRef = BlkMasterInfo;
ton::ShardIdFull shard;
if (!(tlb::unpack_cell(block_root, blk) && tlb::unpack_cell(blk.info, info) && !info.version &&
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard) && !info.vert_seq_no &&
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard) &&
(!info.not_master || tlb::unpack_cell(info.master_ref, mcref)))) {
return td::Status::Error("cannot unpack block header");
}
@ -1809,6 +1809,9 @@ td::Status unpack_block_prev_blk_ext(Ref<vm::Cell> block_root, const ton::BlockI
} else {
mc_blkid = ton::BlockIdExt{ton::masterchainId, ton::shardIdAll, mcref.seq_no, mcref.root_hash, mcref.file_hash};
}
if (shard.is_masterchain() && info.vert_seqno_incr && !info.key_block) {
return td::Status::Error("non-key masterchain block cannot have vert_seqno_incr set");
}
return td::Status::OK();
}
@ -1817,7 +1820,7 @@ td::Status check_block_header(Ref<vm::Cell> block_root, const ton::BlockIdExt& i
block::gen::BlockInfo::Record info;
ton::ShardIdFull shard;
if (!(tlb::unpack_cell(block_root, blk) && tlb::unpack_cell(blk.info, info) && !info.version &&
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard) && !info.vert_seq_no)) {
block::tlb::t_ShardIdent.unpack(info.shard.write(), shard))) {
return td::Status::Error("cannot unpack block header");
}
ton::BlockId hdr_id{shard, (unsigned)info.seq_no};

View file

@ -191,8 +191,8 @@ void test2(vm::CellSlice& cs) {
}
void usage() {
std::cout << "usage: test-block [-S][<boc-file>]\n\tor test-block -h\n\tDumps specified blockchain block or state "
"from <boc-file>, or runs some tests\n\t-S\tDump a blockchain state\n";
std::cout << "usage: dump-block [-S][<boc-file>]\n\tor dump-block -h\n\tDumps specified blockchain block or state "
"from <boc-file>, or runs some tests\n\t-S\tDump a blockchain state instead of a block\n";
std::exit(2);
}

21
crypto/fift/lib/Color.fif Normal file
View file

@ -0,0 +1,21 @@
library Color
{ 27 emit } : esc
{ char " word 27 chr swap $+ 1 ' type does create } :_ make-esc"
make-esc"[0m" ^reset
make-esc"[30m" ^black
make-esc"[31m" ^red
make-esc"[32m" ^green
make-esc"[33m" ^yellow
make-esc"[34m" ^blue
make-esc"[35m" ^magenta
make-esc"[36m" ^cyan
make-esc"[37m" ^white
// bold
make-esc"[30;1m" ^Black
make-esc"[31;1m" ^Red
make-esc"[32;1m" ^Green
make-esc"[33;1m" ^Yellow
make-esc"[34;1m" ^Blue
make-esc"[35;1m" ^Magenta
make-esc"[36;1m" ^Cyan
make-esc"[37;1m" ^White

View file

@ -96,6 +96,10 @@ void interpret_dotstack_list(IntCtx& ctx) {
*ctx.output_stream << std::endl;
}
void interpret_dotstack_list_dump(IntCtx& ctx) {
ctx.stack.dump(*ctx.output_stream, 3);
}
void interpret_dump(IntCtx& ctx) {
ctx.stack.pop_chk().dump(*ctx.output_stream);
*ctx.output_stream << ' ';
@ -105,6 +109,10 @@ void interpret_dump_internal(vm::Stack& stack) {
stack.push_string(stack.pop_chk().to_string());
}
void interpret_list_dump_internal(vm::Stack& stack) {
stack.push_string(stack.pop_chk().to_lisp_string());
}
void interpret_print_list(IntCtx& ctx) {
ctx.stack.pop_chk().print_list(*ctx.output_stream);
*ctx.output_stream << ' ';
@ -2434,10 +2442,12 @@ void init_words_common(Dictionary& d) {
d.def_ctx_word("csr. ", interpret_dot_cellslice_rec);
d.def_ctx_word(".s ", interpret_dotstack);
d.def_ctx_word(".sl ", interpret_dotstack_list);
d.def_ctx_word(".sL ", interpret_dotstack_list_dump); // TMP
d.def_ctx_word(".dump ", interpret_dump);
d.def_ctx_word(".l ", interpret_print_list);
d.def_ctx_word(".tc ", interpret_dottc);
d.def_stack_word("(dump) ", interpret_dump_internal);
d.def_stack_word("(ldump) ", interpret_list_dump_internal);
d.def_stack_word("(.) ", interpret_dot_internal);
d.def_stack_word("(x.) ", std::bind(interpret_dothex_internal, _1, false));
d.def_stack_word("(X.) ", std::bind(interpret_dothex_internal, _1, true));

View file

@ -949,7 +949,7 @@ void define_builtins() {
define_builtin_func("~touch2", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(XY, TypeExpr::new_tensor({XY, Unit}))),
AsmOp::Nop());
define_builtin_func("~dump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))),
AsmOp::Custom("s0 DUMP", 1, 1));
AsmOp::Custom("s0 DUMP", 1, 1), true);
define_builtin_func("run_method0", TypeExpr::new_map(Int, Unit),
[](auto a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true);
define_builtin_func("run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)),

View file

@ -792,6 +792,26 @@ _ participant_list() method_id {
return l;
}
;; returns the list of all participants of current elections with their data
_ participant_list_extended() method_id {
var elect = get_data().begin_parse().preload_dict();
if (elect.null?()) {
return nil;
}
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
var l = nil;
var id = (1 << 255) + ((1 << 255) - 1);
do {
(id, var cs, var f) = members.udict_get_prev?(256, id);
if (f) {
var (stake, time, max_factor, addr, adnl_addr) = (cs~load_grams(), cs~load_uint(32), cs~load_uint(32), cs~load_uint(256), cs~load_uint(256));
cs.end_parse();
l = cons(pair(id, tuple4(stake, max_factor, addr, adnl_addr)), l);
}
} until (~ f);
return l;
}
;; computes the return stake
int compute_returned_stake(int wallet_addr) method_id {
var cs = get_data().begin_parse();

View file

@ -3,9 +3,10 @@
def? $1 { @' $1 } { "" } cond constant suffix
{ suffix $+ } : +suffix
256 1<<1- 15 / constant AllOnes
wc_master setworkchain
-17 setglobalid // negative value means a test instance of the blockchain
-239 setglobalid // negative value means a test instance of the blockchain
// Initial state of Workchain 0 (Basic workchain)
@ -54,10 +55,11 @@ Libs{
x{ABACABADABACABA} s>c public_lib
x{1234} x{5678} |_ s>c private_lib
}Libs // libraries
GR$1700000000 // balance
GR$4999990000 // balance
0 // split_depth
0 // ticktock
2 // mode: create
AllOnes 0 * // address
6 // mode: create+setaddr
register_smc
dup make_special dup constant smc1_addr
Masterchain over
@ -82,8 +84,8 @@ Masterchain over
// code
<b 0 32 u, b> // data
empty_cell // libraries
GR$1000000 // initial balance (1m test Grams)
0 0 2 register_smc
GR$1000 // initial balance (1k test Grams)
0 0 AllOnes 6 * 6 register_smc
dup make_special dup constant smc2_addr
Masterchain over
2dup ."free test gram giver address = " .addr cr 2dup 6 .Addr cr
@ -120,7 +122,7 @@ Libs{
x{ABACABADABACABA} s>c public_lib
x{1234} x{5678} |_ s>c public_lib
}Libs // libraries
0x333333333 // balance
GR$666 // balance
0 // split_depth
3 // ticktock: tick
2 // mode: create
@ -139,7 +141,8 @@ empty_cell // libraries
GR$10 // balance: 10 grams
0 // split_depth
2 // ticktock: tick
2 // mode: create
AllOnes 3 * // address: -1:333...333
6 // mode: create + setaddr
register_smc
dup make_special dup constant smc4_addr dup constant elector_addr
Masterchain swap
@ -155,14 +158,15 @@ Masterchain swap
0 capCreateStats config.version!
// max-validators max-main-validators min-validators
// 9 4 1 config.validator_num!
1000 100 5 config.validator_num!
1000 100 13 config.validator_num!
// min-stake max-stake min-total-stake max-factor
GR$10000 GR$10000000 GR$1000000 sg~10 config.validator_stake_limits!
// elected-for elect-start-before elect-end-before stakes-frozen-for
// 400000 200000 4000 400000 config.election_params!
4000 2000 500 1000 config.election_params! // DEBUG
// 4000 2000 500 1000 config.election_params! // DEBUG
65536 32768 8192 32768 config.election_params! // TestNet DEBUG
// config-addr = -1:5555...5555
256 1<<1- 3 / constant config_addr
AllOnes 5 * constant config_addr
config_addr config.config_smc!
// elector-addr
elector_addr config.elector_smc!
@ -232,8 +236,8 @@ Masterchain swap
*/
// pubkey amount `create-wallet1` or pubkey amount `create-wallet2`
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100000000 create-wallet1
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0
PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100 create-wallet1
PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$170 create-wallet0
/*
*

View file

@ -0,0 +1,126 @@
/*
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 "HighloadWallet.h"
#include "GenericAccount.h"
#include "SmartContractCode.h"
#include "vm/boc.h"
#include "vm/cells/CellString.h"
#include "td/utils/base64.h"
#include <limits>
namespace ton {
td::Ref<vm::Cell> HighloadWallet::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> HighloadWallet::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 append_message = [&](auto&& cb) -> vm::CellBuilder& {
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32);
CHECK(cb.store_maybe_ref({}));
return cb;
};
auto signature = private_key.sign(append_message(vm::CellBuilder()).finalize()->get_hash().as_slice()).move_as_ok();
return append_message(vm::CellBuilder().store_bytes(signature)).finalize();
}
td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id,
td::uint32 seqno, td::uint32 valid_until,
td::Span<Gift> gifts) noexcept {
CHECK(gifts.size() <= 254);
vm::Dictionary messages(16);
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
send_mode += 128;
}
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms);
cb.store_bytes("\0\0\0\0", 4);
//vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner);
auto key = messages.integer_key(td::make_refint(i), 16, false);
messages.set_builder(key.bits(), 16, cb);
}
vm::CellBuilder cb;
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32);
CHECK(cb.store_maybe_ref(messages.get_root_cell()));
auto message_outer = cb.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> HighloadWallet::get_init_code() noexcept {
return SmartContractCode::highload_wallet();
}
vm::CellHash HighloadWallet::get_init_code_hash() noexcept {
return get_init_code()->get_hash();
}
td::Ref<vm::Cell> HighloadWallet::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> HighloadWallet::get_seqno() const {
return TRY_VM(get_seqno_or_throw());
}
td::Result<td::uint32> HighloadWallet::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> HighloadWallet::get_wallet_id() const {
return TRY_VM(get_wallet_id_or_throw());
}
td::Result<td::uint32> HighloadWallet::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,54 @@
/*
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 HighloadWallet : ton::SmartContract {
public:
explicit HighloadWallet(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;
struct Gift {
block::StdAddress destination;
td::int64 gramms;
std::string message;
};
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::Span<Gift> gifts) 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

@ -111,7 +111,7 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) {
LOG(DEBUG) << "VM log\n" << logger.res;
std::ostringstream os;
res.stack->dump(os);
res.stack->dump(os, 2);
LOG(DEBUG) << "VM stack:\n" << os.str();
LOG(DEBUG) << "VM exit code: " << res.code;
LOG(DEBUG) << "VM accepted: " << res.accepted;

View file

@ -39,6 +39,7 @@ const auto& get_map() {
#include "smartcont/auto/simple-wallet-ext-code.cpp"
#include "smartcont/auto/simple-wallet-code.cpp"
#include "smartcont/auto/wallet-code.cpp"
#include "smartcont/auto/highload-wallet-code.cpp"
return map;
}();
return map;
@ -69,4 +70,8 @@ td::Ref<vm::Cell> SmartContractCode::simple_wallet_ext() {
static auto res = load("simple-wallet-ext").move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::highload_wallet() {
static auto res = load("highload-wallet").move_as_ok();
return res;
}
} // namespace ton

View file

@ -26,5 +26,6 @@ class SmartContractCode {
static td::Ref<vm::Cell> wallet();
static td::Ref<vm::Cell> simple_wallet();
static td::Ref<vm::Cell> simple_wallet_ext();
static td::Ref<vm::Cell> highload_wallet();
};
} // namespace ton

View file

@ -35,6 +35,7 @@
#include "smc-envelope/TestWallet.h"
#include "smc-envelope/Wallet.h"
#include "smc-envelope/WalletV3.h"
#include "smc-envelope/HighloadWallet.h"
#include "td/utils/base64.h"
#include "td/utils/crypto.h"
@ -286,6 +287,73 @@ TEST(Tonlib, WalletV3) {
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
}
TEST(Tonlib, HighloadWallet) {
auto source_lookup = fift::create_mem_source_lookup(load_source("smartcont/new-highload-wallet.fif")).move_as_ok();
source_lookup.write_file("/auto/highload-wallet-code.fif", load_source("smartcont/auto/highload-wallet-code.fif"))
.ensure();
auto fift_output = fift::mem_run_fift(std::move(source_lookup), {"aba", "0", "239"}).move_as_ok();
LOG(ERROR) << fift_output.output;
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-wallet239-query.boc").move_as_ok().data;
auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet239.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::HighloadWallet::get_init_state(pub_key, 239);
auto init_message = ton::HighloadWallet::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) << "---smc-envelope----";
vm::load_cell_slice(res).print_rec(std::cerr);
LOG(ERROR) << "---fift scripts----";
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/highload-wallet.fif")).ensure();
std::string order;
std::vector<ton::HighloadWallet::Gift> gifts;
auto add_order = [&](td::Slice dest_str, td::int64 gramms) {
auto g = td::to_string(gramms);
if (g.size() < 10) {
g = std::string(10 - g.size(), '0') + g;
}
order += PSTRING() << "SEND " << dest_str << " " << g.substr(0, g.size() - 9) << "." << g.substr(g.size() - 9)
<< "\n";
ton::HighloadWallet::Gift gift;
gift.destination = block::StdAddress::parse(dest_str).move_as_ok();
gift.gramms = gramms;
gifts.push_back(gift);
};
std::string dest_str = "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX";
add_order(dest_str, 0);
add_order(dest_str, 321000000000ll);
add_order(dest_str, 321ll);
fift_output.source_lookup.write_file("/order", order).ensure();
class ZeroOsTime : public fift::OsTime {
public:
td::uint32 now() override {
return 0;
}
};
fift_output.source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup), {"aba", "new-wallet", "239", "123", "order"})
.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::HighloadWallet::make_a_gift_message(priv_key, 239, 123, 60, gifts));
LOG(ERROR) << "---smc-envelope----";
vm::load_cell_slice(gift_message).print_rec(std::cerr);
LOG(ERROR) << "---fift scripts----";
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

@ -692,7 +692,7 @@ int VmState::step() {
//VM_LOG(st) << "stack:"; stack->dump(VM_LOG(st));
//VM_LOG(st) << "; cr0.refcnt = " << get_c0()->get_refcnt() - 1 << std::endl;
if (stack_trace) {
stack->dump(std::cerr);
stack->dump(std::cerr, 3);
}
++steps;
if (code->size()) {

View file

@ -74,7 +74,8 @@ int exec_dump_stack(VmState* st) {
d = 255;
}
for (int i = d; i > 0; i--) {
std::cerr << stack[i - 1].to_string() << " ";
stack[i - 1].print_list(std::cerr);
std::cerr << ' ';
}
std::cerr << std::endl;
return 0;
@ -85,7 +86,9 @@ int exec_dump_value(VmState* st, unsigned arg) {
VM_LOG(st) << "execute DUMP s" << arg;
Stack& stack = st->get_stack();
if ((int)arg < stack.depth()) {
std::cerr << "#DEBUG#: s" << arg << " = " << stack[arg].to_string() << std::endl;
std::cerr << "#DEBUG#: s" << arg << " = ";
stack[arg].print_list(std::cerr);
std::cerr << std::endl;
} else {
std::cerr << "#DEBUG#: s" << arg << " is absent" << std::endl;
}

View file

@ -793,8 +793,7 @@ std::tuple<Ref<CellSlice>, Ref<Cell>, bool> dict_lookup_set(Ref<Cell> dict, td::
std::pair<Ref<Cell>, bool> pfx_dict_set(Ref<Cell> dict, td::ConstBitPtr key, int m, int n,
const PrefixDictionary::store_value_func_t& store_val,
Dictionary::SetMode mode) {
std::cerr << "up to " << n << "-bit prefix code dictionary modification for " << m << "-bit key = " << key.to_hex(m)
<< std::endl;
// std::cerr << "up to " << n << "-bit prefix code dictionary modification for " << m << "-bit key = " << key.to_hex(m) << std::endl;
if (m > n) {
return std::make_pair(Ref<Cell>{}, false);
}

View file

@ -45,6 +45,18 @@ const char* get_exception_msg(Excno exc_no) {
}
}
bool StackEntry::is_list(const StackEntry* se) {
Ref<Tuple> tuple;
while (!se->empty()) {
tuple = se->as_tuple_range(2, 2);
if (tuple.is_null()) {
return false;
}
se = &tuple->at(1);
}
return true;
}
static const char HEX_digits[] = "0123456789ABCDEF";
std::string str_to_hex(std::string data, std::string prefix) {
@ -62,6 +74,12 @@ std::string StackEntry::to_string() const {
return std::move(os).str();
}
std::string StackEntry::to_lisp_string() const {
std::ostringstream os;
print_list(os);
return std::move(os).str();
}
void StackEntry::dump(std::ostream& os) const {
switch (tp) {
case t_null:
@ -130,6 +148,12 @@ void StackEntry::print_list(std::ostream& os) const {
break;
case t_tuple: {
const auto& tuple = *static_cast<Ref<Tuple>>(ref);
if (is_list()) {
os << '(';
tuple[0].print_list(os);
print_list_tail(os, &tuple[1]);
break;
}
auto n = tuple.size();
if (!n) {
os << "[]";
@ -137,7 +161,7 @@ void StackEntry::print_list(std::ostream& os) const {
os << "[";
tuple[0].print_list(os);
os << "]";
} else if (n != 2) {
} else {
os << "[";
unsigned c = 0;
for (const auto& entry : tuple) {
@ -147,10 +171,6 @@ void StackEntry::print_list(std::ostream& os) const {
entry.print_list(os);
}
os << ']';
} else {
os << '(';
tuple[0].print_list(os);
tuple[1].print_list_tail(os);
}
break;
}
@ -159,26 +179,40 @@ void StackEntry::print_list(std::ostream& os) const {
}
}
void StackEntry::print_list_tail(std::ostream& os) const {
switch (tp) {
case t_null:
os << ')';
break;
case t_tuple: {
const auto& tuple = *static_cast<Ref<Tuple>>(ref);
if (tuple.size() == 2) {
os << ' ';
tuple[0].print_list(os);
tuple[1].print_list_tail(os);
break;
}
}
// fall through
default:
void StackEntry::print_list_tail(std::ostream& os, const StackEntry* se) {
Ref<Tuple> tuple;
while (!se->empty()) {
tuple = se->as_tuple_range(2, 2);
if (tuple.is_null()) {
os << " . ";
print_list(os);
os << ')';
se->print_list(os);
break;
}
os << ' ';
tuple->at(0).print_list(os);
se = &tuple->at(1);
}
os << ')';
}
StackEntry StackEntry::make_list(std::vector<StackEntry>&& elems) {
StackEntry tail;
std::size_t n = elems.size();
while (n > 0) {
--n;
tail = StackEntry{vm::make_tuple_ref(std::move(elems[n]), tail)};
}
return tail;
}
StackEntry StackEntry::make_list(const std::vector<StackEntry>& elems) {
StackEntry tail;
std::size_t n = elems.size();
while (n > 0) {
--n;
tail = StackEntry{vm::make_tuple_ref(elems[n], tail)};
}
return tail;
}
StackEntry::StackEntry(Ref<Stack> stack_ref) : ref(std::move(stack_ref)), tp(t_stack) {
@ -611,13 +645,21 @@ Ref<Stack> Stack::split_top(unsigned top_cnt, unsigned drop_cnt) {
return new_stk;
}
void Stack::dump(std::ostream& os, bool cr) const {
void Stack::dump(std::ostream& os, int mode) const {
os << " [ ";
for (const auto& x : stack) {
os << x.to_string() << ' ';
if (mode & 2) {
for (const auto& x : stack) {
x.print_list(os);
os << ' ';
}
} else {
for (const auto& x : stack) {
x.dump(os);
os << ' ';
}
}
os << "] ";
if (cr) {
if (mode & 1) {
os << std::endl;
}
}

View file

@ -142,6 +142,12 @@ class StackEntry {
bool is(int wanted) const {
return tp == wanted;
}
bool is_list() const {
return is_list(this);
}
static bool is_list(const StackEntry& se) {
return is_list(&se);
}
void swap(StackEntry& se) {
ref.swap(se.ref);
std::swap(tp, se.tp);
@ -157,8 +163,9 @@ class StackEntry {
}
private:
static bool is_list(const StackEntry* se);
template <typename T, Type tag>
Ref<T> dynamic_as() const& {
Ref<T> dynamic_as() const & {
return tp == tag ? static_cast<Ref<T>>(ref) : td::Ref<T>{};
}
template <typename T, Type tag>
@ -170,7 +177,7 @@ class StackEntry {
return tp == tag ? static_cast<Ref<T>>(std::move(ref)) : td::Ref<T>{};
}
template <typename T, Type tag>
Ref<T> as() const& {
Ref<T> as() const & {
return tp == tag ? Ref<T>{td::static_cast_ref(), ref} : td::Ref<T>{};
}
template <typename T, Type tag>
@ -183,6 +190,8 @@ class StackEntry {
}
public:
static StackEntry make_list(std::vector<StackEntry>&& elems);
static StackEntry make_list(const std::vector<StackEntry>& elems);
template <typename T>
static StackEntry maybe(Ref<T> ref) {
if (ref.is_null()) {
@ -191,31 +200,31 @@ class StackEntry {
return ref;
}
}
td::RefInt256 as_int() const& {
td::RefInt256 as_int() const & {
return as<td::CntInt256, t_int>();
}
td::RefInt256 as_int() && {
return move_as<td::CntInt256, t_int>();
}
Ref<Cell> as_cell() const& {
Ref<Cell> as_cell() const & {
return as<Cell, t_cell>();
}
Ref<Cell> as_cell() && {
return move_as<Cell, t_cell>();
}
Ref<CellBuilder> as_builder() const& {
Ref<CellBuilder> as_builder() const & {
return as<CellBuilder, t_builder>();
}
Ref<CellBuilder> as_builder() && {
return move_as<CellBuilder, t_builder>();
}
Ref<CellSlice> as_slice() const& {
Ref<CellSlice> as_slice() const & {
return as<CellSlice, t_slice>();
}
Ref<CellSlice> as_slice() && {
return move_as<CellSlice, t_slice>();
}
Ref<Continuation> as_cont() const&;
Ref<Continuation> as_cont() const &;
Ref<Continuation> as_cont() &&;
Ref<Cnt<std::string>> as_string_ref() const {
return as<Cnt<std::string>, t_string>();
@ -230,16 +239,16 @@ class StackEntry {
std::string as_bytes() const {
return tp == t_bytes ? *as_bytes_ref() : "";
}
Ref<Box> as_box() const&;
Ref<Box> as_box() const &;
Ref<Box> as_box() &&;
Ref<Tuple> as_tuple() const&;
Ref<Tuple> as_tuple() const &;
Ref<Tuple> as_tuple() &&;
Ref<Tuple> as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const&;
Ref<Tuple> as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const &;
Ref<Tuple> as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) &&;
Ref<Atom> as_atom() const&;
Ref<Atom> as_atom() const &;
Ref<Atom> as_atom() &&;
template <class T>
Ref<T> as_object() const& {
Ref<T> as_object() const & {
return dynamic_as<T, t_object>();
}
template <class T>
@ -248,8 +257,11 @@ class StackEntry {
}
void dump(std::ostream& os) const;
void print_list(std::ostream& os) const;
void print_list_tail(std::ostream& os) const;
std::string to_string() const;
std::string to_lisp_string() const;
private:
static void print_list_tail(std::ostream& os, const StackEntry* se);
};
inline void swap(StackEntry& se1, StackEntry& se2) {
@ -490,7 +502,8 @@ class Stack : public td::CntObject {
push(std::move(val));
}
}
void dump(std::ostream& os, bool cr = true) const;
// mode: +1 = add eoln, +2 = Lisp-style lists
void dump(std::ostream& os, int mode = 1) const;
};
} // namespace vm