mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
new db
new database fift/func bugfixes
This commit is contained in:
parent
950e292264
commit
e30d98eb30
110 changed files with 6102 additions and 2075 deletions
|
@ -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)
|
||||
|
|
203
crypto/block/adjust-block.cpp
Normal file
203
crypto/block/adjust-block.cpp
Normal 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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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
21
crypto/fift/lib/Color.fif
Normal 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
|
|
@ -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));
|
||||
|
|
|
@ -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)),
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
||||
/*
|
||||
*
|
||||
|
|
126
crypto/smc-envelope/HighloadWallet.cpp
Normal file
126
crypto/smc-envelope/HighloadWallet.cpp
Normal 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
|
54
crypto/smc-envelope/HighloadWallet.h
Normal file
54
crypto/smc-envelope/HighloadWallet.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue