From fd7a8de9708c9ece8d802890519735b55bc99a8e Mon Sep 17 00:00:00 2001 From: ton Date: Sat, 28 Sep 2019 11:44:38 +0400 Subject: [PATCH 001/667] updated tonlib, block routing - upated tonlib - fixed bug in message routing --- crypto/block/block.cpp | 4 +- crypto/block/block.h | 3 + crypto/block/check-proof.cpp | 18 +- crypto/block/check-proof.h | 6 +- crypto/block/transaction.cpp | 83 +++-- crypto/block/transaction.h | 2 +- crypto/fift/IntCtx.h | 1 + crypto/fift/SourceLookup.h | 21 +- crypto/fift/words.cpp | 6 +- crypto/smartcont/new-wallet-v2.fif | 62 ++++ crypto/smartcont/wallet-code.fc | 2 +- crypto/smartcont/wallet-v2.fif | 48 +++ crypto/tl/tlblib.hpp | 4 + crypto/vm/boc.cpp | 28 +- crypto/vm/boc.h | 18 +- crypto/vm/cells/CellBuilder.cpp | 5 +- doc/smc-guidelines.txt | 36 +-- .../java/drinkless/org/ton/TonTest.kt | 6 +- .../java/drinkless/org/ton/TonTestJava.java | 6 +- tdutils/td/utils/tl_storers.h | 13 + tl/generate/scheme/tonlib_api.tl | 20 +- tl/generate/scheme/tonlib_api.tlo | Bin 11624 -> 12496 bytes tonlib/test/offline.cpp | 139 +++++++-- tonlib/test/online.cpp | 20 +- tonlib/tonlib/Config.cpp | 83 ++--- tonlib/tonlib/Config.h | 1 + tonlib/tonlib/LastBlock.cpp | 171 ++++++++-- tonlib/tonlib/LastBlock.h | 29 +- tonlib/tonlib/TestWallet.cpp | 9 +- tonlib/tonlib/TonlibClient.cpp | 295 +++++++++++------- tonlib/tonlib/TonlibClient.h | 23 +- tonlib/tonlib/Wallet.cpp | 11 +- tonlib/tonlib/tonlib-cli.cpp | 212 ++++++++++--- 33 files changed, 1003 insertions(+), 382 deletions(-) create mode 100644 crypto/smartcont/new-wallet-v2.fif create mode 100644 crypto/smartcont/wallet-v2.fif diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index a3316b2022..7b8074bfbc 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -552,7 +552,9 @@ bool MsgProcessedUpto::already_processed(const EnqueuedMsgDescr& msg) const { if (msg.lt_ == last_inmsg_lt && last_inmsg_hash < msg.hash_) { return false; } - if (ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) { + if (msg.same_workchain() && ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) { + // this branch is needed only for messages generated in the same shard + // (such messages could have been processed without a reference from the masterchain) // ? enable this branch only if an extra boolean parameter is set ? return true; } diff --git a/crypto/block/block.h b/crypto/block/block.h index 377f99c2b8..9fbeedf29f 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -145,6 +145,9 @@ struct EnqueuedMsgDescr { return false; } bool unpack(vm::CellSlice& cs); + bool same_workchain() const { + return cur_prefix_.workchain == next_prefix_.workchain; + } }; using compute_shard_end_lt_func_t = std::function; diff --git a/crypto/block/check-proof.cpp b/crypto/block/check-proof.cpp index a856c876ee..c459b1b458 100644 --- a/crypto/block/check-proof.cpp +++ b/crypto/block/check-proof.cpp @@ -32,7 +32,7 @@ namespace block { using namespace std::literals::string_literals; td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to, - bool check_state_hash) { + bool check_state_hash, td::uint32* save_utime) { ton::RootHash vhash{root->get_hash().bits()}; if (vhash != blkid.root_hash) { return td::Status::Error(PSTRING() << " block header for block " << blkid.to_str() << " has incorrect root hash " @@ -47,6 +47,9 @@ td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blki if (!(tlb::unpack_cell(root, blk) && tlb::unpack_cell(blk.info, info))) { return td::Status::Error(std::string{"cannot unpack header for block "} + blkid.to_str()); } + if (save_utime) { + *save_utime = info.gen_utime; + } if (store_shard_hash_to) { vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update}; if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update @@ -153,7 +156,8 @@ td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td: } td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr, - td::Ref root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash) { + td::Ref root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash, + td::uint32* save_utime) { TRY_RESULT_PREFIX(Q_roots, vm::std_boc_deserialize_multi(std::move(proof)), "cannot deserialize account proof"); if (Q_roots.size() != 2) { return td::Status::Error(PSLICE() << "account state proof must have exactly two roots"); @@ -169,9 +173,9 @@ td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const return td::Status::Error("account state proof is invalid"); } ton::Bits256 state_hash = state_root->get_hash().bits(); - TRY_STATUS_PREFIX( - check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, &state_hash, true), - "error in account shard block header proof : "); + TRY_STATUS_PREFIX(check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, + &state_hash, true, save_utime), + "error in account shard block header proof : "); block::gen::ShardStateUnsplit::Record sstate; if (!(tlb::unpack_cell(std::move(state_root), sstate))) { return td::Status::Error("cannot unpack state header"); @@ -233,8 +237,8 @@ td::Result AccountState::validate(ton::BlockIdExt ref_blk, b TRY_STATUS(block::check_shard_proof(blk, shard_blk, shard_proof.as_slice())); Info res; - TRY_STATUS( - block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, &res.last_trans_hash)); + TRY_STATUS(block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, + &res.last_trans_hash, &res.gen_utime)); res.root = std::move(root); return res; diff --git a/crypto/block/check-proof.h b/crypto/block/check-proof.h index 664028155d..87d6b442b7 100644 --- a/crypto/block/check-proof.h +++ b/crypto/block/check-proof.h @@ -25,11 +25,12 @@ namespace block { using td::Ref; td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blkid, - ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false); + ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false, + td::uint32* save_utime = nullptr); td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof); td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr, td::Ref root, ton::LogicalTime* last_trans_lt = nullptr, - ton::Bits256* last_trans_hash = nullptr); + ton::Bits256* last_trans_hash = nullptr, td::uint32* save_utime = nullptr); td::Result check_state_proof(ton::BlockIdExt blkid, td::Slice proof); td::Result> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data); @@ -47,6 +48,7 @@ struct AccountState { td::Ref root; ton::LogicalTime last_trans_lt = 0; ton::Bits256 last_trans_hash; + td::uint32 gen_utime{0}; }; td::Result validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 77f3ad0756..11f118d4d3 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1027,6 +1027,12 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) { break; case block::gen::OutAction::action_send_msg: err_code = try_action_send_msg(cs, ap, cfg); + if (err_code == -2) { + err_code = try_action_send_msg(cs, ap, cfg, 1); + if (err_code == -2) { + err_code = try_action_send_msg(cs, ap, cfg, 2); + } + } break; case block::gen::OutAction::action_reserve_currency: err_code = try_action_reserve_currency(cs, ap, cfg); @@ -1240,22 +1246,60 @@ bool Transaction::check_rewrite_dest_addr(Ref& dest_addr, const A return true; } -int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) { +int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, const ActionPhaseConfig& cfg, + int redoing) { block::gen::OutAction::Record_action_send_msg act_rec; // mode: +128 = attach all remaining balance, +64 = attach all remaining balance of the inbound message, +1 = pay message fees, +2 = skip if message cannot be sent + vm::CellSlice cs{cs0}; if (!tlb::unpack_exact(cs, act_rec) || (act_rec.mode & ~0xc3) || (act_rec.mode & 0xc0) == 0xc0) { return -1; } bool skip_invalid = (act_rec.mode & 2); - auto cs2 = vm::load_cell_slice(act_rec.out_msg); - // try to parse suggested message in cs2 + // try to parse suggested message in act_rec.out_msg td::RefInt256 fwd_fee, ihr_fee; - bool ext_msg = cs2.prefetch_ulong(1); + block::gen::MessageRelaxed::Record msg; + if (!tlb::type_unpack_cell(act_rec.out_msg, block::gen::t_MessageRelaxed_Any, msg)) { + return -1; + } + if (redoing >= 1) { + if (msg.init->size_refs() >= 2) { + LOG(DEBUG) << "moving the StateInit of a suggested outbound message into a separate cell"; + // init:(Maybe (Either StateInit ^StateInit)) + // transform (just (left z:StateInit)) into (just (right z:^StateInit)) + CHECK(msg.init.write().fetch_ulong(2) == 2); + vm::CellBuilder cb; + Ref cell; + CHECK(cb.append_cellslice_bool(std::move(msg.init)) // StateInit + && cb.finalize_to(cell) // -> ^StateInit + && cb.store_long_bool(3, 2) // (just (right ... )) + && cb.store_ref_bool(std::move(cell)) // z:^StateInit + && cb.finalize_to(cell)); + msg.init = vm::load_cell_slice_ref(std::move(cell)); + } else { + redoing = 2; + } + } + if (redoing >= 2 && msg.body->size_ext() > 1 && msg.body->prefetch_ulong(1) == 0) { + LOG(DEBUG) << "moving the body of a suggested outbound message into a separate cell"; + // body:(Either X ^X) + // transform (left x:X) into (right x:^X) + CHECK(msg.body.write().fetch_ulong(1) == 0); + vm::CellBuilder cb; + Ref cell; + CHECK(cb.append_cellslice_bool(std::move(msg.body)) // X + && cb.finalize_to(cell) // -> ^X + && cb.store_long_bool(1, 1) // (right ... ) + && cb.store_ref_bool(std::move(cell)) // x:^X + && cb.finalize_to(cell)); + msg.body = vm::load_cell_slice_ref(std::move(cell)); + } + block::gen::CommonMsgInfoRelaxed::Record_int_msg_info info; + bool ext_msg = msg.info->prefetch_ulong(1); if (ext_msg) { // ext_out_msg_info$11 constructor of CommonMsgInfoRelaxed block::gen::CommonMsgInfoRelaxed::Record_ext_out_msg_info erec; - if (!tlb::unpack(cs2, erec)) { + if (!tlb::csr_unpack(msg.info, erec)) { return -1; } info.src = std::move(erec.src); @@ -1267,7 +1311,7 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A fwd_fee = ihr_fee = td::RefInt256{true, 0}; } else { // int_msg_info$0 constructor - if (!tlb::unpack(cs2, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) { + if (!tlb::csr_unpack(msg.info, info) || !block::tlb::t_CurrencyCollection.validate_csr(info.value)) { return -1; } fwd_fee = block::tlb::t_Grams.as_integer(info.fwd_fee); @@ -1295,12 +1339,11 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A // compute size of message vm::CellStorageStat sstat; // for message size // preliminary storage estimation of the resulting message - sstat.compute_used_storage(cs2); // message body + sstat.add_used_storage(msg.init, true, 3); // message init + sstat.add_used_storage(msg.body, true, 3); // message body (the root cell itself is not counted) if (!ext_msg) { sstat.add_used_storage(info.value->prefetch_ref()); } - sstat.bits -= cs2.size(); // bits in the root cells are free - sstat.cells--; // the root cell itself is not counted as a cell LOG(DEBUG) << "storage paid for a message: " << sstat.cells << " cells, " << sstat.bits << " bits"; if (sstat.bits > max_msg_bits || sstat.cells > max_msg_cells) { LOG(DEBUG) << "message too large, invalid"; @@ -1397,17 +1440,15 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A // re-pack message value CHECK(req.pack_to(info.value)); - vm::CellBuilder cb; - CHECK(block::tlb::t_Grams.store_integer_ref(cb, fwd_fee_remain) && - (info.fwd_fee = load_cell_slice_ref(cb.finalize())).not_null()); - CHECK(block::tlb::t_Grams.store_integer_ref(cb, ihr_fee) && - (info.ihr_fee = load_cell_slice_ref(cb.finalize())).not_null()); + CHECK(block::tlb::t_Grams.pack_integer(info.fwd_fee, fwd_fee_remain)); + CHECK(block::tlb::t_Grams.pack_integer(info.ihr_fee, ihr_fee)); // serialize message - CHECK(tlb::pack(cb, info)); - if (!cb.append_cellslice_bool(cs2)) { + CHECK(tlb::csr_pack(msg.info, info)); + vm::CellBuilder cb; + if (!tlb::type_pack(cb, block::gen::t_MessageRelaxed_Any, msg)) { LOG(DEBUG) << "outbound message does not fit into a cell after rewriting"; - return 39; + return redoing < 2 ? -2 : (skip_invalid ? 0 : 39); } new_msg_bits = cb.size(); @@ -1438,11 +1479,11 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A erec.dest = info.dest; erec.created_at = info.created_at; erec.created_lt = info.created_lt; + CHECK(tlb::csr_pack(msg.info, erec)); vm::CellBuilder cb; - CHECK(tlb::pack(cb, erec)); - if (!cb.append_cellslice_bool(cs2)) { + if (!tlb::type_pack(cb, block::gen::t_MessageRelaxed_Any, msg)) { LOG(DEBUG) << "outbound message does not fit into a cell after rewriting"; - return 39; + return redoing < 2 ? -2 : (skip_invalid ? 0 : 39); } new_msg_bits = cb.size(); @@ -1461,6 +1502,8 @@ int Transaction::try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const A } if (!block::gen::t_Message_Any.validate_ref(new_msg)) { LOG(ERROR) << "generated outbound message is not a valid (Message Any) according to automated check"; + block::gen::t_Message_Any.print_ref(std::cerr, new_msg); + vm::load_cell_slice(new_msg).print_rec(std::cerr); return -1; } if (verbosity > 2) { diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 94b2a85300..45e824c1e8 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -390,7 +390,7 @@ struct Transaction { Ref prepare_vm_c7(const ComputePhaseConfig& cfg) const; bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const; int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); - int try_action_send_msg(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); + int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0); int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); bool check_replace_src_addr(Ref& src_addr) const; bool check_rewrite_dest_addr(Ref& dest_addr, const ActionPhaseConfig& cfg, diff --git a/crypto/fift/IntCtx.h b/crypto/fift/IntCtx.h index 753d2cb1d4..1adb8f325a 100644 --- a/crypto/fift/IntCtx.h +++ b/crypto/fift/IntCtx.h @@ -77,6 +77,7 @@ struct IntCtx { vm::TonDb* ton_db{nullptr}; Dictionary* dictionary{nullptr}; SourceLookup* source_lookup{nullptr}; + int* now{nullptr}; private: std::string str; diff --git a/crypto/fift/SourceLookup.h b/crypto/fift/SourceLookup.h index 73d2cd918f..33ee71d4ef 100644 --- a/crypto/fift/SourceLookup.h +++ b/crypto/fift/SourceLookup.h @@ -20,6 +20,7 @@ #include #include "td/utils/Status.h" +#include "td/utils/Time.h" namespace fift { class FileLoader { @@ -43,10 +44,21 @@ class OsFileLoader : public FileLoader { bool is_file_exists(td::CSlice filename) override; }; +class OsTime { + public: + virtual ~OsTime() = default; + virtual td::uint32 now() = 0; +}; + +//TODO: rename SourceLookup class SourceLookup { public: SourceLookup() = default; - explicit SourceLookup(std::unique_ptr file_loader) : file_loader_(std::move(file_loader)) { + explicit SourceLookup(std::unique_ptr file_loader, std::unique_ptr os_time = {}) + : file_loader_(std::move(file_loader)), os_time_(std::move(os_time)) { + } + void set_os_time(std::unique_ptr os_time) { + os_time_ = std::move(os_time); } void add_include_path(td::string path); td::Result lookup_source(std::string filename, std::string current_dir); @@ -63,9 +75,16 @@ class SourceLookup { bool is_file_exists(td::CSlice filename) { return file_loader_->is_file_exists(filename); } + td::uint32 now() { + if (os_time_) { + return os_time_->now(); + } + return static_cast(td::Time::now()); + } protected: std::unique_ptr file_loader_; + std::unique_ptr os_time_; std::vector source_include_path_; }; } // namespace fift diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 5c217fad44..01dcf802c8 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -1289,8 +1289,8 @@ void interpret_file_exists(IntCtx& ctx) { // custom and crypto -void interpret_now(vm::Stack& stack) { - stack.push_smallint(std::time(nullptr)); +void interpret_now(IntCtx& ctx) { + ctx.stack.push_smallint(ctx.source_lookup->now()); } void interpret_new_keypair(vm::Stack& stack) { @@ -2534,7 +2534,7 @@ void init_words_common(Dictionary& d) { d.def_ctx_word("B>file ", interpret_write_file); d.def_ctx_word("file-exists? ", interpret_file_exists); // custom & crypto - d.def_stack_word("now ", interpret_now); + d.def_ctx_word("now ", interpret_now); d.def_stack_word("newkeypair ", interpret_new_keypair); d.def_stack_word("priv>pub ", interpret_priv_key_to_pub); d.def_stack_word("ed25519_sign ", interpret_ed25519_sign); diff --git a/crypto/smartcont/new-wallet-v2.fif b/crypto/smartcont/new-wallet-v2.fif new file mode 100644 index 0000000000..eac43b168b --- /dev/null +++ b/crypto/smartcont/new-wallet-v2.fif @@ -0,0 +1,62 @@ +#!/usr/bin/env fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a new advanced wallet in specified workchain, with private key saved to or loaded from .pk" cr + ."('new-wallet.pk' by default)" cr 1 halt +} : usage +$# 1- -2 and ' usage if + +$1 parse-workchain-id =: wc // set workchain id from command line argument +def? $2 { @' $2 } { "new-wallet" } cond constant file-base + +."Creating new advanced wallet in workchain " wc . cr + +// Create new advanced wallet; code adapted from `wallet-code.fif` +<{ 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 // signature in_msg msg_seqno valid_until cs + SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs + c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key + s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno + EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno + s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash + s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key + ACCEPT + s0 s2 XCHG // public_key stored_seqno cs + WHILE:<{ + DUP SREFS // public_key stored_seqno cs _40 + }>DO<{ // public_key stored_seqno cs + // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance + 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode + SENDRAWMSG // public_key stored_seqno cs + }> + ENDS INC // public_key seqno' + NEWC 32 STU 256 STU ENDC c4 POP +}>c // >libref +// code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."signing message: " +dup ."External message for initialization is " B dup Bx. cr +file-base +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/crypto/smartcont/wallet-code.fc b/crypto/smartcont/wallet-code.fc index d0c1fd1c79..3ce2fac154 100644 --- a/crypto/smartcont/wallet-code.fc +++ b/crypto/smartcont/wallet-code.fc @@ -8,7 +8,7 @@ var signature = in_msg~load_bits(512); var cs = in_msg; var (msg_seqno, valid_until) = (cs~load_uint(32), cs~load_uint(32)); - throw_if(35, valid_until < now()); + throw_if(35, valid_until <= now()); var ds = get_data().begin_parse(); var (stored_seqno, public_key) = (ds~load_uint(32), ds~load_uint(256)); ds.end_parse(); diff --git a/crypto/smartcont/wallet-v2.fif b/crypto/smartcont/wallet-v2.fif new file mode 100644 index 0000000000..eb4464e4e9 --- /dev/null +++ b/crypto/smartcont/wallet-v2.fif @@ -0,0 +1,48 @@ +#!/usr/bin/env fift -s +"TonUtil.fif" include + +{ ."usage: " @' $0 type ." [-B ] []" cr + ."Creates a request to advanced wallet created by new-wallet-v2.fif, with private key loaded from file .pk " + ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt +} : usage +def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond + @' $# 2- =: $# } if } if +$# dup 4 < swap 5 > or ' usage if + +true constant bounce + +$1 =: file-base +$2 bounce parse-load-address =: bounce 2=: dest_addr +$3 parse-int =: seqno +$4 $>GR =: amount +def? $5 { @' $5 } { "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 } { } cond +constant body-cell + +."Transferring " amount .GR ."to account " +dest_addr 2dup bounce 7 + .Addr ." = " .addr +."seqno=0x" seqno x. ."bounce=" bounce . cr +."Body of transfer message is " body-cell + +dup ."signing message: " +dup ."resulting external message: " B dup Bx. cr +savefile +".boc" tuck B>file +."Query expires in " timeout . ."seconds" cr +."(Saved to file " type .")" cr diff --git a/crypto/tl/tlblib.hpp b/crypto/tl/tlblib.hpp index 1e4f458c4e..b5d51c3154 100644 --- a/crypto/tl/tlblib.hpp +++ b/crypto/tl/tlblib.hpp @@ -176,6 +176,10 @@ class TLB { virtual bool store_integer_ref(vm::CellBuilder& cb, td::RefInt256 value) const { return value.not_null() && store_integer_value(cb, *value); } + bool pack_integer(Ref& csr, td::RefInt256 value) const { + vm::CellBuilder cb; + return store_integer_ref(cb, value) && (csr = vm::load_cell_slice_ref(cb.finalize())).not_null(); + } virtual bool add_values(vm::CellBuilder& cb, vm::CellSlice& cs1, vm::CellSlice& cs2) const { td::RefInt256 x = as_integer_skip(cs1), y = as_integer_skip(cs2); return x.not_null() && y.not_null() && store_integer_ref(cb, x += std::move(y)); diff --git a/crypto/vm/boc.cpp b/crypto/vm/boc.cpp index 0ea69f3b80..9b60884d10 100644 --- a/crypto/vm/boc.cpp +++ b/crypto/vm/boc.cpp @@ -988,27 +988,27 @@ td::Result std_boc_serialize_multi(std::vector> roots * */ -bool CellStorageStat::compute_used_storage(Ref cs_ref, bool kill_dup, bool skip_count_root) { +bool CellStorageStat::compute_used_storage(Ref cs_ref, bool kill_dup, unsigned skip_count_root) { clear(); return add_used_storage(std::move(cs_ref), kill_dup, skip_count_root) && clear_seen(); } -bool CellStorageStat::compute_used_storage(const CellSlice& cs, bool kill_dup, bool skip_count_root) { +bool CellStorageStat::compute_used_storage(const CellSlice& cs, bool kill_dup, unsigned skip_count_root) { clear(); return add_used_storage(cs, kill_dup, skip_count_root) && clear_seen(); } -bool CellStorageStat::compute_used_storage(CellSlice&& cs, bool kill_dup, bool skip_count_root) { +bool CellStorageStat::compute_used_storage(CellSlice&& cs, bool kill_dup, unsigned skip_count_root) { clear(); return add_used_storage(std::move(cs), kill_dup, skip_count_root) && clear_seen(); } -bool CellStorageStat::compute_used_storage(Ref cell, bool kill_dup, bool skip_count_root) { +bool CellStorageStat::compute_used_storage(Ref cell, bool kill_dup, unsigned skip_count_root) { clear(); return add_used_storage(std::move(cell), kill_dup, skip_count_root) && clear_seen(); } -bool CellStorageStat::add_used_storage(Ref cs_ref, bool kill_dup, bool skip_count_root) { +bool CellStorageStat::add_used_storage(Ref cs_ref, bool kill_dup, unsigned skip_count_root) { if (cs_ref->is_unique()) { return add_used_storage(std::move(cs_ref.unique_write()), kill_dup, skip_count_root); } else { @@ -1016,11 +1016,13 @@ bool CellStorageStat::add_used_storage(Ref cs_ref, bool kill_dup, } } -bool CellStorageStat::add_used_storage(const CellSlice& cs, bool kill_dup, bool skip_count_root) { - if (!skip_count_root) { +bool CellStorageStat::add_used_storage(const CellSlice& cs, bool kill_dup, unsigned skip_count_root) { + if (!(skip_count_root & 1)) { ++cells; } - bits += cs.size(); + if (!(skip_count_root & 2)) { + bits += cs.size(); + } for (unsigned i = 0; i < cs.size_refs(); i++) { if (!add_used_storage(cs.prefetch_ref(i), kill_dup)) { return false; @@ -1029,11 +1031,13 @@ bool CellStorageStat::add_used_storage(const CellSlice& cs, bool kill_dup, bool return true; } -bool CellStorageStat::add_used_storage(CellSlice&& cs, bool kill_dup, bool skip_count_root) { - if (!skip_count_root) { +bool CellStorageStat::add_used_storage(CellSlice&& cs, bool kill_dup, unsigned skip_count_root) { + if (!(skip_count_root & 1)) { ++cells; } - bits += cs.size(); + if (!(skip_count_root & 2)) { + bits += cs.size(); + } while (cs.size_refs()) { if (!add_used_storage(cs.fetch_ref(), kill_dup)) { return false; @@ -1042,7 +1046,7 @@ bool CellStorageStat::add_used_storage(CellSlice&& cs, bool kill_dup, bool skip_ return true; } -bool CellStorageStat::add_used_storage(Ref cell, bool kill_dup, bool skip_count_root) { +bool CellStorageStat::add_used_storage(Ref cell, bool kill_dup, unsigned skip_count_root) { if (cell.is_null()) { return false; } diff --git a/crypto/vm/boc.h b/crypto/vm/boc.h index 5b4e4e2c4b..29f582be70 100644 --- a/crypto/vm/boc.h +++ b/crypto/vm/boc.h @@ -122,15 +122,15 @@ struct CellStorageStat { cells = bits = public_cells = 0; clear_seen(); } - bool compute_used_storage(Ref cs_ref, bool kill_dup = true, bool skip_count_root = false); - bool compute_used_storage(const CellSlice& cs, bool kill_dup = true, bool skip_count_root = false); - bool compute_used_storage(CellSlice&& cs, bool kill_dup = true, bool skip_count_root = false); - bool compute_used_storage(Ref cell, bool kill_dup = true, bool skip_count_root = false); - - bool add_used_storage(Ref cs_ref, bool kill_dup = true, bool skip_count_root = false); - bool add_used_storage(const CellSlice& cs, bool kill_dup = true, bool skip_count_root = false); - bool add_used_storage(CellSlice&& cs, bool kill_dup = true, bool skip_count_root = false); - bool add_used_storage(Ref cell, bool kill_dup = true, bool skip_count_root = false); + bool compute_used_storage(Ref cs_ref, bool kill_dup = true, unsigned skip_count_root = 0); + bool compute_used_storage(const CellSlice& cs, bool kill_dup = true, unsigned skip_count_root = 0); + bool compute_used_storage(CellSlice&& cs, bool kill_dup = true, unsigned skip_count_root = 0); + bool compute_used_storage(Ref cell, bool kill_dup = true, unsigned skip_count_root = 0); + + bool add_used_storage(Ref cs_ref, bool kill_dup = true, unsigned skip_count_root = 0); + bool add_used_storage(const CellSlice& cs, bool kill_dup = true, unsigned skip_count_root = 0); + bool add_used_storage(CellSlice&& cs, bool kill_dup = true, unsigned skip_count_root = 0); + bool add_used_storage(Ref cell, bool kill_dup = true, unsigned skip_count_root = 0); }; struct CellSerializationInfo { diff --git a/crypto/vm/cells/CellBuilder.cpp b/crypto/vm/cells/CellBuilder.cpp index ec933c071b..37940f718a 100644 --- a/crypto/vm/cells/CellBuilder.cpp +++ b/crypto/vm/cells/CellBuilder.cpp @@ -207,9 +207,8 @@ bool CellBuilder::store_bits_bool(const unsigned char* str, std::size_t bit_coun CellBuilder& CellBuilder::store_bits(const unsigned char* str, std::size_t bit_count, int bit_offset) { unsigned pos = bits; - if (prepare_reserve(bit_count)) { - td::bitstring::bits_memcpy(data, pos, str, bit_offset, bit_count); - } + ensure_throw(prepare_reserve(bit_count)); + td::bitstring::bits_memcpy(data, pos, str, bit_offset, bit_count); return *this; } diff --git a/doc/smc-guidelines.txt b/doc/smc-guidelines.txt index 05746b621d..2a4bc1146c 100644 --- a/doc/smc-guidelines.txt +++ b/doc/smc-guidelines.txt @@ -5,15 +5,15 @@ This document collects some recommendations and best practices that could be fol Smart contracts interact with each other by sending the so-called *internal messages*. When an internal message reaches its intended destination, an ordinary transaction is created on behalf of the destination account, and the internal message is processed as specified by the code and the persistent data of this account (smart contract). In particular, the processing transaction can create one or several outbound internal messages, some of which could be addressed to the source address of the internal message being processed. This can be used to create simple "client-server applications", when a query is incapsulated in an internal message and sent to another smart contract, which processes the query and sends back a response, again as an internal message. -This approach leads to the necessity of distinguishing whether an internal message is intended as a "query" or as a "response", or just as a "simple money transfer" not requiring any additional processing. Furthermore, when a response is received, there must be a means to understand to which query it corresponds. +This approach leads to the necessity of distinguishing whether an internal message is intended as a "query" or as a "response", or doesn't require any additional processing (like a "simple money transfer"). Furthermore, when a response is received, there must be a means to understand to which query it corresponds. In order to achieve this goal, the following recommended internal message layout can be used (notice that the TON Blockchain does not enforce any restrictions on the message body, so these are indeed just recommendations): -0) The body of the message can be embedded into the message itself, or be stored in a separate cell referred to from the message, as indicated by TL-B scheme fragment +0) The body of the message can be embedded into the message itself, or be stored in a separate cell referred to from the message, as indicated by the TL-B scheme fragment message$_ {X:Type} ... body:(Either X ^X) = Message X; -The receiving smart contract should accept at least internal messages with embedded message bodies (whenever they fit into the cell containing the message). If it accepts message bodies in separate cells (using the `right` constructor of `(Either X ^X)`), the processing of the inbound message should not depend on the specific embedding option chosen for the message body. On the other side, it is perfectly valid not to support message bodies in separate cells at all for simpler queries and responces. +The receiving smart contract should accept at least internal messages with embedded message bodies (whenever they fit into the cell containing the message). If it accepts message bodies in separate cells (using the `right` constructor of `(Either X ^X)`), the processing of the inbound message should not depend on the specific embedding option chosen for the message body. On the other hand, it is perfectly valid not to support message bodies in separate cells at all for simpler queries and responses. 1) The message body typically begins with the following fields: @@ -21,20 +21,20 @@ The receiving smart contract should accept at least internal messages with embed * A 64-bit (big-endian) unsigned integer *query_id*, used in all query-response internal messages to indicate that a response is related to a query (the *query_id* of a response must be equal to the *query_id* of the corresponding query). If *op* is not a query-response method (e.g., it invokes a method that is not expected to send an answer), then *query_id* may be omitted. * The remainder of the message body is specific for each supported value of *op*. -2) If *op* is zero, then the message is a "simple transfer message with comment". The comment is contained in the remainder of the message body (without any *query_id* field, i.e., starting from the fifth byte). If it does not begin with the byte 0xff, the comment is a text one; it can be displayed "as is" to the end user of a wallet (after filtering invalid and control characters and checking that it is a valid UTF-8 string). For instance, users may indicate the purpose of a simple transfer from their wallet to the wallet of another user in this text field. On the other hand, if the comment begins with byte 0xff, the remainder is a "binary comment", which should not be displayed to the end user as text (only as hex dump if necessary). The intended use of "binary comments" is that it can be used, e.g., to contain a purchase indentifier for payments in a shop, to be automatically generated and processed by the shop's software. +2) If *op* is zero, then the message is a "simple transfer message with comment". The comment is contained in the remainder of the message body (without any *query_id* field, i.e., starting from the fifth byte). If it does not begin with the byte 0xff, the comment is a text one; it can be displayed "as is" to the end user of a wallet (after filtering invalid and control characters and checking that it is a valid UTF-8 string). For instance, users may indicate the purpose of a simple transfer from their wallet to the wallet of another user in this text field. On the other hand, if the comment begins with the byte 0xff, the remainder is a "binary comment", which should not be displayed to the end user as text (only as hex dump if necessary). The intended use of "binary comments" is, e.g., to contain a purchase identifier for payments in a store, to be automatically generated and processed by the store's software. -Most smart contracts should not perform non-trivial actions or reject the inbound message on receiving a "simple transfer message". In this way, once *op* is found to be zero, the smart contract function for processing inbound internal messages (usually called `recv_internal()`) should immediately terminate with a zero exit code indicating success (e.g., by throwing exception 0, if no custom exception handler has been installed by the smart contract). This will lead to crediting the receiving account with the value transfered by the message without any further effect. +Most smart contracts should not perform non-trivial actions or reject the inbound message on receiving a "simple transfer message". In this way, once *op* is found to be zero, the smart contract function for processing inbound internal messages (usually called `recv_internal()`) should immediately terminate with a zero exit code indicating success (e.g., by throwing exception 0, if no custom exception handler has been installed by the smart contract). This will lead to the receiving account being credited with the value transferred by the message without any further effect. 3) A "simple transfer message without comment" has an empty body (without even an *op* field). The above considerations apply to such messages as well. Note that such messages should have their bodies embedded into the message cell. -4) We expect "query" messages to have *op* with high-order bit clear, i.e., in the range 1 .. 2^31-1, and "response" messages to have *op* with high-order bit set, i.e., in the range 2^31 .. 2^32-1. If a method is neither a query nor a response (so that the corresponding message body does not contain a *query_id* field), it should use an *op* in the "query" range 1 .. 2^31 - 1. +4) We expect "query" messages to have an *op* with the high-order bit clear, i.e., in the range 1 .. 2^31-1, and "response" messages to have an *op* with the high-order bit set, i.e., in the range 2^31 .. 2^32-1. If a method is neither a query nor a response (so that the corresponding message body does not contain a *query_id* field), it should use an *op* in the "query" range 1 .. 2^31 - 1. -5) There are some "standard" response messages with *op* equal to 0xffffffff and 0xfffffffe. In general, the values of *op* from 0xfffffff0 to 0xffffffff are reserved for such standard responses. +5) There are some "standard" response messages with the *op* equal to 0xffffffff and 0xfffffffe. In general, the values of *op* from 0xfffffff0 to 0xffffffff are reserved for such standard responses. -* op = 0xffffffff means "operation not supported". It is followed by 64-bit *query_id* extracted from the original query, and a 32-bit *op* of the original query. All but the simplest smart contracts should return this error when they receive a query with unknown *op* in the range 1 .. 2^31-1. -* op = 0xfffffffe means "operation not allowed". It is followed by 64-bit *query_id* of the original query, followed by 32-bit *op* extracted from the original query. +* op = 0xffffffff means "operation not supported". It is followed by the 64-bit *query_id* extracted from the original query, and the 32-bit *op* of the original query. All but the simplest smart contracts should return this error when they receive a query with an unknown *op* in the range 1 .. 2^31-1. +* op = 0xfffffffe means "operation not allowed". It is followed by the 64-bit *query_id* of the original query, followed by the 32-bit *op* extracted from the original query. -Notice that unknown "responses" (with *op* in the range 2^31 .. 2^32-1) should be ignored (in particular, no response with *op* equal to 0xffffffff should be generated in response to them), as should unexpected bounced messages (with "bounced" flag set). +Notice that unknown "responses" (with an *op* in the range 2^31 .. 2^32-1) should be ignored (in particular, no response with an *op* equal to 0xffffffff should be generated in response to them), just as unexpected bounced messages (with "bounced" flag set). 2. Paying for processing queries and sending responses ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -43,33 +43,33 @@ In general, if a smart contract wants to send a query to another smart contract, In most cases, the sender will attach a small amount of Grams (e.g., one Gram) to the internal message (sufficient to pay for the processing of this message), and set its "bounce" flag (i.e., send a bounceable internal message); the receiver will return the unused portion of the received value with the answer (deducting message forwarding fees from it). This is normally accomplished by invoking SENDRAWMSG with mode=64 (cf. Appendix A of TON VM documentation). -If the receiver is unable to parse the received message and terminates with a non-zero exit code (for example, because of an unhandled cell deserialization exception), the message will be automatically "bounced" back to its sender, with the "bounce" flag cleared and the "bounced" flag set. The body of the bounced message will be the same as that of the original message; therefore, it is important to check the "bounced" flag of the incoming internal messages before parsing the *op* field in the smart contract and processing the corresponding query (otherwise there is a risk that the query contained in a bounced message will be processed by its original sender as a new separate query). If this flag is set, special code could find out which query has failed (e.g., by deserializing *op* and *query_id* from the bounced message) and take appropriate action. A simpler smart contract might simply ignore all bounced messages (terminate with zero exit code if "bounced" flag is set). +If the receiver is unable to parse the received message and terminates with a non-zero exit code (for example, because of an unhandled cell deserialization exception), the message will be automatically "bounced" back to its sender, with the "bounce" flag cleared and the "bounced" flag set. The body of the bounced message will be the same as that of the original message; therefore, it is important to check the "bounced" flag of incoming internal messages before parsing the *op* field in the smart contract and processing the corresponding query (otherwise there is a risk that the query contained in a bounced message will be processed by its original sender as a new separate query). If the "bounced" flag is set, special code could find out which query has failed (e.g., by deserializing *op* and *query_id* from the bounced message) and take appropriate action. A simpler smart contract might simply ignore all bounced messages (terminate with zero exit code if "bounced" flag is set). On the other hand, the receiver might parse the incoming query successfully and find out that the requested method *op* is not supported, or that another error condition is met. Then a response with *op* equal to 0xffffffff or another appropriate value should be sent back, using SENDRAWMSG with mode=64 as mentioned above. -In some situations, the sender wants both to transfer some value to the sender and to receive an error message or a confirmation. For instance, the validator elections smart contract receives an election participation request along with the stake as the attached value. In such cases, it makes sense to attach, say, one extra Gram to the intended value. If there is an error (e.g., the stake may not be accepted for any reason), the full received amount (minus processing fees) should be returned to the sender along with an error message (e.g., by using SENDRAWMSG with mode=64 as explained before). In the case of success, the confirmation message is created and exactly one Gram is sent back (with the message transferring fees deducted from this value; this is mode=1 of SENDRAWMSG). +In some situations, the sender wants both to transfer some value to the sender and to receive either a confirmation or an error message. For instance, the validator elections smart contract receives an election participation request along with the stake as the attached value. In such cases, it makes sense to attach, say, one extra Gram to the intended value. If there is an error (e.g., the stake may not be accepted for any reason), the full received amount (minus the processing fees) should be returned to the sender along with an error message (e.g., by using SENDRAWMSG with mode=64 as explained before). In the case of success, the confirmation message is created and exactly one Gram is sent back (with the message transferring fees deducted from this value; this is mode=1 of SENDRAWMSG). 3. Using non-bounceable messages ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Almost all internal messages sent between smart contracts should be bounceable, i.e., should have their "bounce" bit set. Then, if the destination smart contract does not exist, or if it throws an unhandled exception while processing this message, the message will be "bounced" back carrying the remainder of the original value (minus all message transfer and gas fees). The bounced message will have the same body, but with "bounce" flag cleared and "bounced" flag set. Therefore, all smart contracts should check the "bounced" flag of all inbound messages and either silently accept them (by immediately terminating with zero exit code) or perform special processing to detect which outbound query has failed. The query contained in the body of a bounced message should never be executed. +Almost all internal messages sent between smart contracts should be bounceable, i.e., should have their "bounce" bit set. Then, if the destination smart contract does not exist, or if it throws an unhandled exception while processing this message, the message will be "bounced" back carrying the remainder of the original value (minus all message transfer and gas fees). The bounced message will have the same body, but with the "bounce" flag cleared and the "bounced" flag set. Therefore, all smart contracts should check the "bounced" flag of all inbound messages and either silently accept them (by immediately terminating with a zero exit code) or perform some special processing to detect which outbound query has failed. The query contained in the body of a bounced message should never be executed. On some occasions, non-bounceable internal messages must be used. For instance, new accounts cannot be created without at least one non-bounceable internal message sent to them. Unless this message contains a StateInit with the code and data of the new smart contract, it does not make sense to have a non-empty body in a non-bounceable internal message. -It is a good idea not to allow the end user (e.g., of a wallet) to send unbounceable messages carrying large value (e.g., more than five Grams), or at least to warn them if they try to. It is a better idea to send a small amount, then initialize the new smart contract, and then send a larger amount. +It is a good idea not to allow the end user (e.g., of a wallet) to send unbounceable messages carrying large value (e.g., more than five Grams), or at least to warn them if they try this. It is a better idea to send a small amount first, then initialize the new smart contract, and then send a larger amount. 4. External messages ~~~~~~~~~~~~~~~~~~~~ -The external messages are sent from the outside to the smart contracts residing in the TON Blockchain to make them perform certain actions. For instance, a wallet smart contract expects to receive external messages containing orders (e.g., internal messages to be sent from the wallet smart contract) signed by the wallet's owner; when such an external message is received by the wallet smart contract, it first checks the signature, then accepts the message (by running the TVM primitive ACCEPT), and then performs whatever actions are required. +External messages are sent from the outside to the smart contracts residing in the TON Blockchain to make them perform certain actions. For instance, a wallet smart contract expects to receive external messages containing orders (e.g., internal messages to be sent from the wallet smart contract) signed by the wallet's owner; when such an external message is received by the wallet smart contract, it first checks the signature, then accepts the message (by running the TVM primitive ACCEPT), and then performs whatever actions are required. -Notice that all external messages must be protected against replay attacks. The validators normally remove an external message from the pool of suggested external messages (received from network); however, in some situations another validator could process the same external message twice (thus creating a second transaction for the same external message, leading to the duplication of the original action). Even worse, a malicious actor could extract the external message from the block containing the processing transaction and re-send it later. This would force a wallet smart contract to repeat a payment, for example. +Notice that all external messages must be protected against replay attacks. The validators normally remove an external message from the pool of suggested external messages (received from network); however, in some situations another validator could process the same external message twice (thus creating a second transaction for the same external message, leading to the duplication of the original action). Even worse, a malicious actor could extract the external message from the block containing the processing transaction and re-send it later. This could force a wallet smart contract to repeat a payment, for example. -The simplest way to protect smart contracts from replay attacks related to external messages is to store a 32-bit counter *cur-seqno* in the persistent data of the smart contract, and to expect a *req-seqno* value in (the signed part of) any inbound external messages. Then an external message is ACCEPTed if simultaneously the signature is valid and *req-seqno* equals *cur-seqno*. After successful processing, the *cur-seqno* value in the persistent data is increased by one, so the same external message will never be accepted again. +The simplest way to protect smart contracts from replay attacks related to external messages is to store a 32-bit counter *cur-seqno* in the persistent data of the smart contract, and to expect a *req-seqno* value in (the signed part of) any inbound external messages. Then an external message is ACCEPTed only if both the signature is valid and *req-seqno* equals *cur-seqno*. After successful processing, the *cur-seqno* value in the persistent data is increased by one, so the same external message will never be accepted again. One could also include an *expire-at* field in the external message, and accept an external message only if the current Unix time is less than the value of this field. This approach can be used in conjuction with *seqno*; alternatively, the receiving smart contract could store the set of (the hashes of) all recent (not expired) accepted external messages in its persistent data, and reject a new external message if it is a duplicate of one of the stored messages. Some garbage collecting of expired messages in this set should also be performed to avoid bloating the persistent data. -In general, an external message begins with a 256-bit signature (if needed), a 32-bit *req-seqno* (if needed), a 32-bit *expire-at* (if needed), and possibly a 32-bit *op* and other required parameters depending on *op*. The layout of external messages does not need to be so standard as that of the internal messages because external messages are not used for interaction between different smart contracts (written by different developers and managed by different owners). +In general, an external message begins with a 256-bit signature (if needed), a 32-bit *req-seqno* (if needed), a 32-bit *expire-at* (if needed), and possibly a 32-bit *op* and other required parameters depending on *op*. The layout of external messages does not need to be as standardized as that of the internal messages because external messages are not used for interaction between different smart contracts (written by different developers and managed by different owners). 5. Get-methods ~~~~~~~~~~~~~~ diff --git a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt index 68fe1a70b2..f2b94f9d22 100644 --- a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt +++ b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTest.kt @@ -59,12 +59,12 @@ class TonTest { val client = ClientKotlin() val dir = getContext().getExternalFilesDir(null).toString() + "/"; runBlocking { - client.send(TonApi.Init(TonApi.Options(config, dir))) + client.send(TonApi.Init(TonApi.Options(TonApi.Config(config, "", false, false), dir))) val key = client.send(TonApi.CreateNewKey("local password".toByteArray(), "mnemonic password".toByteArray(), "".toByteArray())) as TonApi.Key val walletAddress = client.send(TonApi.TestWalletGetAccountAddress(TonApi.TestWalletInitialAccountState(key.publicKey))) as TonApi.AccountAddress; val testGiverState = client.send(TonApi.TestGiverGetAccountState()) as TonApi.TestGiverAccountState - client.send(TonApi.TestGiverSendGrams(walletAddress, testGiverState.seqno, 6660000000)) as TonApi.Ok + client.send(TonApi.TestGiverSendGrams(walletAddress, testGiverState.seqno, 6660000000, "".toByteArray())) as TonApi.Ok while ((client.send(TonApi.GenericGetAccountState(walletAddress)) as TonApi.GenericAccountStateUninited).accountState.balance <= 0L) { delay(1000L) @@ -79,7 +79,7 @@ class TonTest { val state = client.send(TonApi.GenericGetAccountState(walletAddress)) as TonApi.GenericAccountStateTestWallet val balance = state.accountState.balance - client.send(TonApi.GenericSendGrams(inputKey, walletAddress, walletAddress, 10)) as TonApi.Ok + client.send(TonApi.GenericSendGrams(inputKey, walletAddress, walletAddress, 10, 0, true, "hello".toByteArray())) as TonApi.Ok while ((client.send(TonApi.GenericGetAccountState(walletAddress)) as TonApi.GenericAccountStateTestWallet).accountState.balance == balance) { delay(1000L) } diff --git a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java index 5f8de61db7..0c61abc46d 100644 --- a/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java +++ b/example/android/test/ton/src/androidTest/java/drinkless/org/ton/TonTestJava.java @@ -86,7 +86,7 @@ public void createTestWallet() { appendLog("start..."); JavaClient client = new JavaClient(); - Object result = client.send(new TonApi.Init(new TonApi.Options(config, getContext().getExternalFilesDir(null) + "/"))); + Object result = client.send(new TonApi.Init(new TonApi.Options(new TonApi.Config(config, "", false, false), getContext().getExternalFilesDir(null) + "/"))); if (!(result instanceof TonApi.Ok)) { appendLog("failed to set config"); return; @@ -98,7 +98,7 @@ public void createTestWallet() { appendLog("got account address"); appendLog("sending grams..."); TonApi.TestGiverAccountState testGiverState = (TonApi.TestGiverAccountState) client.send(new TonApi.TestGiverGetAccountState()); - result = client.send(new TonApi.TestGiverSendGrams(walletAddress, testGiverState.seqno, 6660000000L)); + result = client.send(new TonApi.TestGiverSendGrams(walletAddress, testGiverState.seqno, 6660000000L, "".getBytes())); if (!(result instanceof TonApi.Ok)) { appendLog("failed to send grams"); return; @@ -143,7 +143,7 @@ public void createTestWallet() { appendLog("sending grams..."); TonApi.GenericAccountStateTestWallet state = (TonApi.GenericAccountStateTestWallet) client.send(new TonApi.GenericGetAccountState(walletAddress)); long balance = state.accountState.balance; - result = client.send(new TonApi.GenericSendGrams(inputKey, walletAddress, walletAddress, 10)); + result = client.send(new TonApi.GenericSendGrams(inputKey, walletAddress, walletAddress, 10, 0, true, "hello".getBytes())); if (!(result instanceof TonApi.Ok)) { return; } diff --git a/tdutils/td/utils/tl_storers.h b/tdutils/td/utils/tl_storers.h index dd9bc38985..5e0e10a5b5 100644 --- a/tdutils/td/utils/tl_storers.h +++ b/tdutils/td/utils/tl_storers.h @@ -21,6 +21,7 @@ #include "td/utils/common.h" #include "td/utils/logging.h" #include "td/utils/misc.h" +#include "td/utils/SharedSlice.h" #include "td/utils/Slice.h" #include "td/utils/StorerBase.h" #include "td/utils/UInt.h" @@ -233,6 +234,12 @@ class TlStorerToString { store_field_end(); } + void store_field(const char *name, const SecureString &value) { + store_field_begin(name); + result.append(""); + store_field_end(); + } + template void store_field(const char *name, const T &value) { store_field_begin(name); @@ -240,6 +247,12 @@ class TlStorerToString { store_field_end(); } + void store_bytes_field(const char *name, const SecureString &value) { + store_field_begin(name); + result.append(""); + store_field_end(); + } + template void store_bytes_field(const char *name, const BytesT &value) { static const char *hex = "0123456789ABCDEF"; diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index db12f3f1cb..07a2f32c14 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -16,7 +16,8 @@ vector {t:Type} # [ t ] = Vector t; error code:int32 message:string = Error; ok = Ok; -options config:string keystore_directory:string use_callbacks_for_network:Bool = Options; +config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config; +options config:config keystore_directory:string = Options; key public_key:string secret:secureBytes = Key; inputKey key:key local_password:secureBytes = InputKey; @@ -28,6 +29,8 @@ bip39Hints words:vector = Bip39Hints; accountAddress account_address:string = AccountAddress; +unpackedAccountAddress workchain_id:int32 bounceable:Bool testnet:Bool addr:bytes = UnpackedAccountAddress; + internal.transactionId lt:int64 hash:bytes = internal.TransactionId; raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState; @@ -56,7 +59,7 @@ generic.accountStateWallet account_state:wallet.accountState = generic.AccountSt generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState; generic.accountStateUninited account_state:uninited.accountState = generic.AccountState; -//generic.sendGramsResult sent_until:int53 = generic.SendGramsResult; +sendGramsResult sent_until:int53 = SendGramsResult; updateSendLiteServerQuery id:int64 data:bytes = Update; @@ -83,7 +86,7 @@ logTags tags:vector = LogTags; init options:options = Ok; close = Ok; -options.setConfig config:string = Ok; +options.setConfig config:config = Ok; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; deleteKey key:key = Ok; @@ -95,6 +98,9 @@ importPemKey local_password:secureBytes key_password:secureBytes exported_key:ex importEncryptedKey local_password:secureBytes key_password:secureBytes exported_encrypted_key:exportedEncryptedKey = Key; changeLocalPassword input_key:inputKey new_local_password:secureBytes = Key; +unpackAccountAddress account_address:string = UnpackedAccountAddress; +packAccountAddress account_address:unpackedAccountAddress = AccountAddress; + getBip39Hints prefix:string = Bip39Hints; //raw.init initial_account_state:raw.initialAccountState = Ok; @@ -106,20 +112,20 @@ raw.getTransactions account_address:accountAddress from_transaction_id:internal. testWallet.init private_key:inputKey = Ok; testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress; testWallet.getAccountState account_address:accountAddress = testWallet.AccountState; -testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok; +testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult; wallet.init private_key:inputKey = Ok; wallet.getAccountAddress initital_account_state:wallet.initialAccountState = AccountAddress; wallet.getAccountState account_address:accountAddress = wallet.AccountState; -wallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = Ok; +wallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult; testGiver.getAccountState = testGiver.AccountState; testGiver.getAccountAddress = AccountAddress; -testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok; +testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult; //generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress; generic.getAccountState account_address:accountAddress = generic.AccountState; -generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 message:bytes = Ok; +generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = SendGramsResult; onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryError id:int64 error:error = Ok; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 0a810f234f4208ceb583c1e49916cce6390ac466..b77565e03e200204e3f53b0210c4bd17840045c8 100644 GIT binary patch delta 796 zcmaD6bs>@W(QJJy1}M;)$a|REg2`_Qn{$3%T4wsh|56(-$}{n}?HAT(_b({P%+D*H z#3(UYggHc{Qu6v4{@~QS6!)UU+~T0r;?kUwi3JjqGnhSi%|xygh=t}ABqnF4rcAU{ z-@JkO3KOIL=2o_JMp3Z&$zbytKg%kruV@F(TuCub*TBxdHt=OyN*PJSRP zF}Z-(KuG%8eNCRs^t}9{)cEAY=O4{;!f4KkAnXr?$= z&14H+lgR}f94sKI$!mBk^udlVMs++$45W_{7#JW6xq$K|@uhhsnK_f!bIY=To|sN>OTYF#|{r>>;QjJmvXCP#}Z0rzR%l zq=EyU!?x{U(<5$GFbArUy(G1`BrmlDB!*3%B@t-j4SZ}`H9>XW=P;E$S6$Sqx6I2=HU*t$vc!yAmIxX<$7ajvsMTcqqe7X>C3cW0~PlO>o-ChuVO*lfabg^5vTGbejG<7O95 z8CFq{EPH-INoIatF^GNhn2-w?2gIBUd=-g2z= z%99<`1UC05u3&~3WuT@oIYRXZi`N5Z#>pXSCO`{$CU20D*esxK!J;U7?b15olGNf7 z_sp`?B80IZLqTq2nLJUqA8aCrjsqjhB7@0HlYMk0SfG~f087o#Ght+@4Hwd%d_i9X MBK1Q54p52*0EcvJod5s; diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index f3ccf68d0c..63a52fae39 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -91,6 +91,35 @@ INC NEWC 32 STU 256 STU ENDC c4 POPCTR return fift::compile_asm(code).move_as_ok(); } +td::Ref get_wallet_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 // signature in_msg msg_seqno valid_until cs + SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs + c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key + s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno + EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno + s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash + s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key + ACCEPT + s0 s2 XCHG // public_key stored_seqno cs + WHILE:<{ + DUP SREFS // public_key stored_seqno cs _40 + }>DO<{ // public_key stored_seqno cs + // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance + 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode + SENDRAWMSG // public_key stored_seqno cs + }> + ENDS INC // public_key seqno' + NEWC 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()); CHECK(get_test_wallet_source()->get_hash() == TestWallet::get_init_code()->get_hash()); @@ -115,37 +144,74 @@ TEST(Tonlib, TestWallet) { 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.fif")).ensure(); + 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", "123", "321"}) + .move_as_ok(); + auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; + auto gift_message = GenericAccount::create_ext_message( + address, {}, TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", 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()); } -td::Ref get_wallet_source() { +td::Ref get_wallet_source_fc() { return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok(); } TEST(Tonlib, Wallet) { LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok()); CHECK(get_wallet_source()->get_hash() == Wallet::get_init_code()->get_hash()); - // TODO: fix ater new-wallet supports new type of wallet - //auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet.fif"), {"aba", "0"}).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; + auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.fif"), {"aba", "0"}).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 = TestWallet::get_init_state(pub_key); - //auto init_message = TestWallet::get_init_message(priv_key); - //auto address = GenericAccount::get_address(0, init_state); + td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}}; + auto pub_key = priv_key.get_public_key().move_as_ok(); + auto init_state = Wallet::get_init_state(pub_key); + auto init_message = Wallet::get_init_message(priv_key); + auto address = GenericAccount::get_address(0, init_state); + + CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); - //CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); + td::Ref res = GenericAccount::create_ext_message(address, init_state, init_message); - //td::Ref res = 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()); - //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-v2.fif")).ensure(); + class ZeroOsTime : public fift::OsTime { + public: + td::uint32 now() override { + return 0; + } + }; + fift_output.source_lookup.set_os_time(std::make_unique()); + 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", "123", "321"}) + .move_as_ok(); + auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; + auto gift_message = GenericAccount::create_ext_message( + address, {}, Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", 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) { @@ -201,20 +267,21 @@ static auto sync_send = [](auto &client, auto query) { TEST(Tonlib, InitClose) { using tonlib_api::make_object; + auto cfg = [](auto str) { return make_object(str, "", false, false); }; { Client client; sync_send(client, make_object()).ensure(); - sync_send(client, make_object(make_object("", ".", false))).ensure_error(); + sync_send(client, make_object(make_object(nullptr, "."))).ensure_error(); } { Client client; sync_send(client, make_object(nullptr)).ensure_error(); - sync_send(client, make_object(make_object("fdajkfldsjkafld", ".", false))) + sync_send(client, make_object(make_object(cfg("fdajkfldsjkafld"), "."))) .ensure_error(); - sync_send(client, make_object(make_object("", "fdhskfds", false))) + sync_send(client, make_object(make_object(nullptr, "fdhskfds"))) .ensure_error(); - sync_send(client, make_object(make_object("", ".", false))).ensure(); - sync_send(client, make_object(make_object("", ".", false))).ensure_error(); + sync_send(client, make_object(make_object(nullptr, "."))).ensure(); + sync_send(client, make_object(make_object(nullptr, "."))).ensure_error(); td::Slice bad_config = R"abc( { @@ -223,11 +290,11 @@ TEST(Tonlib, InitClose) { } )abc"; - sync_send(client, make_object(bad_config.str())).ensure_error(); + sync_send(client, make_object(cfg(bad_config.str()))).ensure_error(); sync_send(client, make_object()).ensure_error(); sync_send(client, make_object()).ensure(); sync_send(client, make_object()).ensure_error(); - sync_send(client, make_object(make_object("", ".", false))).ensure_error(); + sync_send(client, make_object(make_object(nullptr, "."))).ensure_error(); } } @@ -317,12 +384,32 @@ TEST(Tonlib, Keys) { CHECK(decrypted_key.private_key.as_octet_string() == other_decrypted_key.private_key.as_octet_string()); } +TEST(Tonlib, ParseAddres) { + using tonlib_api::make_object; + Client client; + + // init + sync_send(client, make_object(make_object(nullptr, "."))).ensure(); + + sync_send(client, make_object("hello")).ensure_error(); + auto addr = + sync_send(client, + make_object("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX")) + .move_as_ok(); + ASSERT_EQ(-1, addr->workchain_id_); + ASSERT_EQ(true, addr->bounceable_); + ASSERT_EQ(false, addr->testnet_); + + auto addr_str = sync_send(client, make_object(std::move(addr))).move_as_ok(); + ASSERT_EQ("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX", addr_str->account_address_); +} + TEST(Tonlib, KeysApi) { using tonlib_api::make_object; Client client; // init - sync_send(client, make_object(make_object("", ".", false))).ensure(); + sync_send(client, make_object(make_object(nullptr, "."))).ensure(); auto local_password = td::SecureString("local password"); auto mnemonic_password = td::SecureString("mnemonic password"); { diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 716f9153bc..0425f53757 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -118,7 +118,7 @@ void transfer_grams(Client& client, std::string from, std::string to, td::int64 auto balance = get_balance(client, to); sync_send(client, tonlib_api::make_object( std::move(input_key), tonlib_api::make_object(from), - tonlib_api::make_object(to), amount, "GIFT")) + tonlib_api::make_object(to), amount, 0, true, "GIFT")) .ensure(); while (balance == get_balance(client, to)) { client.receive(1); @@ -196,7 +196,8 @@ int main(int argc, char* argv[]) { Client client; { - sync_send(client, make_object(make_object(global_config_str, ".", false))) + sync_send(client, make_object(make_object( + make_object(global_config_str, "", false, false), "."))) .ensure(); } //dump_transaction_history(client, get_test_giver_address(client)); @@ -209,7 +210,8 @@ int main(int argc, char* argv[]) { return 0; { // init - sync_send(client, make_object(make_object(global_config_str, ".", false))) + sync_send(client, make_object(make_object( + make_object(global_config_str, "", false, false), "."))) .ensure(); auto key = sync_send(client, make_object( @@ -224,7 +226,9 @@ int main(int argc, char* argv[]) { auto public_key_raw = key->public_key_; td::Ed25519::PublicKey public_key_std(td::SecureString{public_key_raw}); - sync_send(client, make_object(global_config_str)).ensure(); + sync_send(client, make_object( + make_object(global_config_str, "", false, false))) + .ensure(); auto wallet_addr = GenericAccount::get_address(0, TestWallet::get_init_state(public_key_std)); { @@ -307,10 +311,10 @@ int main(int argc, char* argv[]) { } { - sync_send(client, - make_object( - create_input_key(), make_object(wallet_addr.rserialize()), - make_object(test_giver_address), 1000000000ll * 3333 / 1000, "GIFT")) + sync_send(client, make_object( + create_input_key(), make_object(wallet_addr.rserialize()), + make_object(test_giver_address), 1000000000ll * 3333 / 1000, 0, + true, "GIFT")) .ensure(); } while (true) { diff --git a/tonlib/tonlib/Config.cpp b/tonlib/tonlib/Config.cpp index 6ffdb1d6fc..4b04d1f1aa 100644 --- a/tonlib/tonlib/Config.cpp +++ b/tonlib/tonlib/Config.cpp @@ -21,6 +21,45 @@ #include "td/utils/JsonBuilder.h" namespace tonlib { +td::Result parse_block_id_ext(td::JsonObject &obj) { + ton::WorkchainId zero_workchain_id; + { + TRY_RESULT(wc, td::get_json_object_int_field(obj, "workchain")); + zero_workchain_id = wc; + } + ton::ShardId zero_shard_id; // uint64 + { + TRY_RESULT(shard_id, td::get_json_object_long_field(obj, "shard")); + zero_shard_id = static_cast(shard_id); + } + ton::BlockSeqno zero_seqno; + { + TRY_RESULT(seqno, td::get_json_object_int_field(obj, "seqno")); + zero_seqno = seqno; + } + + ton::RootHash zero_root_hash; + { + TRY_RESULT(hash_b64, td::get_json_object_string_field(obj, "root_hash")); + TRY_RESULT(hash, td::base64_decode(hash_b64)); + if (hash.size() * 8 != ton::RootHash::size()) { + return td::Status::Error("Invalid config (8)"); + } + zero_root_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin())); + } + ton::FileHash zero_file_hash; + { + TRY_RESULT(hash_b64, td::get_json_object_string_field(obj, "file_hash")); + TRY_RESULT(hash, td::base64_decode(hash_b64)); + if (hash.size() * 8 != ton::FileHash::size()) { + return td::Status::Error("Invalid config (9)"); + } + zero_file_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin())); + } + + return ton::BlockIdExt(zero_workchain_id, zero_shard_id, zero_seqno, std::move(zero_root_hash), + std::move(zero_file_hash)); +} td::Result Config::parse(std::string str) { TRY_RESULT(json, td::json_decode(str)); if (json.type() != td::JsonValue::Type::Object) { @@ -74,45 +113,13 @@ td::Result Config::parse(std::string str) { return td::Status::Error("Invalid config (7)"); } TRY_RESULT(zero_state_obj, td::get_json_object_field(validator, "zero_state", td::JsonValue::Type::Object, false)); - auto &zero_state = zero_state_obj.get_object(); - - ton::WorkchainId zero_workchain_id; - { - TRY_RESULT(wc, td::get_json_object_int_field(zero_state, "workchain")); - zero_workchain_id = wc; - } - ton::ShardId zero_shard_id; // uint64 - { - TRY_RESULT(shard_id, td::get_json_object_long_field(zero_state, "shard")); - zero_shard_id = static_cast(shard_id); - } - ton::BlockSeqno zero_seqno; - { - TRY_RESULT(seqno, td::get_json_object_int_field(zero_state, "seqno")); - zero_seqno = seqno; - } - - ton::RootHash zero_root_hash; - { - TRY_RESULT(hash_b64, td::get_json_object_string_field(zero_state, "root_hash")); - TRY_RESULT(hash, td::base64_decode(hash_b64)); - if (hash.size() * 8 != ton::RootHash::size()) { - return td::Status::Error("Invalid config (8)"); - } - zero_root_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin())); + TRY_RESULT(zero_state_id, parse_block_id_ext(zero_state_obj.get_object())); + res.zero_state_id = zero_state_id; + auto r_init_block_obj = td::get_json_object_field(validator, "init_block", td::JsonValue::Type::Object, false); + if (r_init_block_obj.is_ok()) { + TRY_RESULT(init_block_id, parse_block_id_ext(r_init_block_obj.move_as_ok().get_object())); + res.init_block_id = init_block_id; } - ton::FileHash zero_file_hash; - { - TRY_RESULT(hash_b64, td::get_json_object_string_field(zero_state, "file_hash")); - TRY_RESULT(hash, td::base64_decode(hash_b64)); - if (hash.size() * 8 != ton::FileHash::size()) { - return td::Status::Error("Invalid config (9)"); - } - zero_file_hash = ton::RootHash(td::ConstBitPtr(td::Slice(hash).ubegin())); - } - - res.zero_state_id = ton::BlockIdExt(zero_workchain_id, zero_shard_id, zero_seqno, std::move(zero_root_hash), - std::move(zero_file_hash)); return res; } diff --git a/tonlib/tonlib/Config.h b/tonlib/tonlib/Config.h index 30f7f3513e..746fee4ede 100644 --- a/tonlib/tonlib/Config.h +++ b/tonlib/tonlib/Config.h @@ -28,6 +28,7 @@ struct Config { td::IPAddress address; }; ton::BlockIdExt zero_state_id; + ton::BlockIdExt init_block_id; std::vector lite_clients; static td::Result parse(std::string str); }; diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index 3593adc5ec..7c4cda5dd3 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -29,27 +29,70 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state << td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime); } -LastBlock::LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr callback) - : state_(std::move(state)), callback_(std::move(callback)) { +LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr callback) + : state_(std::move(state)), config_(std::move(config)), callback_(std::move(callback)) { client_.set_client(client); + if (!config_.init_block_id.is_valid()) { + check_init_block_state_ = QueryState::Done; + } } void LastBlock::get_last_block(td::Promise promise) { + if (has_fatal_error()) { + promise.set_error(fatal_error_.clone()); + return; + } + if (promises_.empty() && get_last_block_state_ == QueryState::Done) { + get_last_block_state_ = QueryState::Empty; + } + promises_.push_back(std::move(promise)); + sync_loop(); +} + +void LastBlock::sync_loop() { if (promises_.empty()) { + return; + } + + update_zero_state(state_.zero_state_id); + update_zero_state(ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash, + config_.zero_state_id.file_hash)); + + if (get_mc_info_state_ == QueryState::Empty) { + get_mc_info_state_ = QueryState::Active; + client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(), + [this](auto r_info) { this->on_masterchain_info(std::move(r_info)); }); + } + + if (get_last_block_state_ == QueryState::Empty) { + get_last_block_state_ = QueryState::Active; total_sync_ = td::Timer(); validate_ = td::Timer(true); queries_ = 0; LOG(INFO) << "Begin last block synchronization " << state_; do_get_last_block(); } - promises_.push_back(std::move(promise)); + + if (check_init_block_state_ == QueryState::Empty) { + if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) { + check_init_block_state_ = QueryState::Active; + // validate + //total_sync_ = td::Timer(); + //validate_ = td::Timer(true); + //queries_ = 0; + LOG(INFO) << "Begin last block synchronization (check init_block)" << state_; + do_check_init_block(state_.last_key_block_id); + } else { + } + } + + if (get_mc_info_state_ == QueryState::Done && get_last_block_state_ == QueryState::Done && + check_init_block_state_ == QueryState::Done) { + on_sync_ok(); + } } void LastBlock::do_get_last_block() { - //client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(), - //[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); }); - //return; - //liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; queries_++; client_.send_query( @@ -59,14 +102,19 @@ void LastBlock::do_get_last_block() { }); } -td::Result LastBlock::process_block_proof( +void LastBlock::do_check_init_block(ton::BlockIdExt from) { + //liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; + //queries_++; + client_.send_query(ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from), + create_tl_lite_block_id(config_.init_block_id)), + [this, from = state_.last_key_block_id](auto r_block_proof) { + this->on_init_block_proof(from, std::move(r_block_proof)); + }); +} + +td::Result> LastBlock::process_block_proof( ton::BlockIdExt from, td::Result> r_block_proof) { - validate_.resume(); - SCOPE_EXIT { - validate_.pause(); - }; - TRY_RESULT(block_proof, std::move(r_block_proof)); LOG(DEBUG) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_); TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof))); @@ -86,51 +134,82 @@ td::Result LastBlock::process_block_proof( if (is_changed) { callback_->on_state_changed(state_); } - return chain->complete; + return std::move(chain); } void LastBlock::on_block_proof( ton::BlockIdExt from, td::Result> r_block_proof) { - auto r_is_ready = process_block_proof(from, std::move(r_block_proof)); + validate_.resume(); + auto r_chain = process_block_proof(from, std::move(r_block_proof)); + validate_.pause(); bool is_ready; - if (r_is_ready.is_error()) { - LOG(WARNING) << "Error during last block synchronization " << r_is_ready.error(); + if (r_chain.is_error()) { + LOG(WARNING) << "Error during last block synchronization " << r_chain.error(); + if (config_.init_block_id.is_valid()) { + if (state_.last_key_block_id.id.seqno < config_.init_block_id.id.seqno) { + on_sync_error(td::Status::Error(PSLICE() << "Sync failed and we can't validate config.init_block: " + << r_chain.move_as_error())); + } + } is_ready = true; } else { - is_ready = r_is_ready.move_as_ok(); + is_ready = r_chain.ok()->complete; } if (is_ready) { LOG(INFO) << "End last block synchronization " << state_ << "\n" << " net queries: " << queries_ << "\n" << " total: " << total_sync_ << " validation: " << validate_; - for (auto& promise : promises_) { - auto state = state_; - promise.set_value(std::move(state)); - } - promises_.clear(); + get_last_block_state_ = QueryState::Done; + sync_loop(); } else { do_get_last_block(); } } +void LastBlock::on_init_block_proof( + ton::BlockIdExt from, + td::Result> r_block_proof) { + validate_.resume(); + auto r_chain = process_block_proof(from, std::move(r_block_proof)); + validate_.pause(); + if (r_chain.is_error()) { + check_init_block_state_ = QueryState::Empty; + on_sync_error( + td::Status::Error(PSLICE() << "Error during last block synchronization (check init_block)" << r_chain.error())); + return; + } + auto chain = r_chain.move_as_ok(); + if (chain->complete) { + LOG(INFO) << "End last block synchronization " << state_ << "\n" + << " net queries: " << queries_ << "\n" + << " total: " << total_sync_ << " validation: " << validate_; + get_last_block_state_ = QueryState::Done; + sync_loop(); + } else { + do_check_init_block(chain->to); + } +} + void LastBlock::on_masterchain_info( td::Result> r_info) { if (r_info.is_ok()) { auto info = r_info.move_as_ok(); update_zero_state(create_zero_state_id(info->init_)); update_mc_last_block(create_block_id(info->last_)); + get_mc_info_state_ = QueryState::Done; } else { + get_mc_info_state_ = QueryState::Empty; LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error(); + on_sync_error(r_info.move_as_error()); } - for (auto& promise : promises_) { - auto state = state_; - promise.set_value(std::move(state)); - } - promises_.clear(); + sync_loop(); } void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) { + if (has_fatal_error()) { + return; + } if (!zero_state_id.is_valid()) { LOG(ERROR) << "Ignore invalid zero state update"; return; @@ -142,17 +221,18 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) { return; } - if (state_.zero_state_id == state_.zero_state_id) { + if (state_.zero_state_id == zero_state_id) { return; } - LOG(FATAL) << "Masterchain zerostate mismatch: expected: " << state_.zero_state_id.to_str() << ", found " - << zero_state_id.to_str(); - // TODO: all other updates will be inconsitent. - // One will have to restart ton client + on_fatal_error(td::Status::Error(PSLICE() << "Masterchain zerostate mismatch: expected: " + << state_.zero_state_id.to_str() << ", found " << zero_state_id.to_str())); } bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) { + if (has_fatal_error()) { + return false; + } if (!mc_block_id.is_valid()) { LOG(ERROR) << "Ignore invalid masterchain block"; return false; @@ -166,6 +246,9 @@ bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) { } bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) { + if (has_fatal_error()) { + return false; + } if (!mc_key_block_id.is_valid()) { LOG(ERROR) << "Ignore invalid masterchain block"; return false; @@ -183,4 +266,26 @@ void LastBlock::update_utime(td::int64 utime) { state_.utime = utime; } } + +void LastBlock::on_sync_ok() { + for (auto& promise : promises_) { + auto state = state_; + promise.set_value(std::move(state)); + } + promises_.clear(); +} +void LastBlock::on_sync_error(td::Status status) { + for (auto& promise : promises_) { + promise.set_error(status.clone()); + } + promises_.clear(); +} +void LastBlock::on_fatal_error(td::Status status) { + fatal_error_ = std::move(status); + on_sync_error(fatal_error_.clone()); +} + +bool LastBlock::has_fatal_error() const { + return fatal_error_.is_error(); +} } // namespace tonlib diff --git a/tonlib/tonlib/LastBlock.h b/tonlib/tonlib/LastBlock.h index bb250d749e..2add3bcfb9 100644 --- a/tonlib/tonlib/LastBlock.h +++ b/tonlib/tonlib/LastBlock.h @@ -19,8 +19,12 @@ #pragma once #include "td/actor/actor.h" +#include "tonlib/Config.h" #include "tonlib/ExtClient.h" +namespace block { +struct BlockProofChain; +} namespace tonlib { td::StringBuilder &operator<<(td::StringBuilder &sb, const LastBlockState &state); template @@ -116,14 +120,22 @@ class LastBlock : public td::actor::Actor { virtual void on_state_changed(LastBlockState state) = 0; }; - explicit LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr callback); + explicit LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr callback); void get_last_block(td::Promise promise); private: ExtClient client_; LastBlockState state_; + Config config_; td::unique_ptr callback_; + td::Status fatal_error_; + + enum class QueryState { Empty, Active, Done }; + QueryState get_mc_info_state_{QueryState::Empty}; + QueryState get_last_block_state_{QueryState::Empty}; + QueryState check_init_block_state_{QueryState::Empty}; + // stats td::Timer total_sync_; td::Timer validate_; @@ -131,11 +143,15 @@ class LastBlock : public td::actor::Actor { std::vector> promises_; - void do_get_last_block(); + void do_check_init_block(ton::BlockIdExt from); + void on_init_block_proof( + ton::BlockIdExt from, + td::Result> r_block_proof); void on_masterchain_info(td::Result> r_info); + void do_get_last_block(); void on_block_proof(ton::BlockIdExt from, td::Result> r_block_proof); - td::Result process_block_proof( + td::Result> process_block_proof( ton::BlockIdExt from, td::Result> r_block_proof); @@ -144,5 +160,12 @@ class LastBlock : public td::actor::Actor { bool update_mc_last_block(ton::BlockIdExt mc_block_id); bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id); void update_utime(td::int64 utime); + + void on_sync_ok(); + void on_sync_error(td::Status status); + void on_fatal_error(td::Status status); + bool has_fatal_error() const; + + void sync_loop(); }; } // namespace tonlib diff --git a/tonlib/tonlib/TestWallet.cpp b/tonlib/tonlib/TestWallet.cpp index 0bbf9205d7..022eaca9db 100644 --- a/tonlib/tonlib/TestWallet.cpp +++ b/tonlib/tonlib/TestWallet.cpp @@ -44,13 +44,16 @@ td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; - cb.append_cellslice(binary_bitstring_to_cellslice("b{010000100}").move_as_ok()) + cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok()) + .store_long(dest_address.bounceable, 1) + .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); - auto message_outer = vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); - std::string seq_no(4, 0); + td::int8 send_mode = 3; + auto message_outer = + vm::CellBuilder().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(); } diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 7c038178c8..d0a9cba7d9 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -74,7 +74,6 @@ struct RawAccountState { td::Ref code; td::Ref data; block::AccountState::Info info; - td::int64 sync_utime = 0; }; td::Result to_balance_or_throw(td::Ref balance_ref) { @@ -191,7 +190,7 @@ class GetRawAccountState : public td::actor::Actor { auto serialized_state = account_state.state.clone(); RawAccountState res; res.info = std::move(info); - res.sync_utime = last_block_.utime; + LOG_IF(ERROR, res.info.gen_utime > last_block_.utime) << res.info.gen_utime << " " << last_block_.utime; auto cell = res.info.root; if (cell.is_null()) { return res; @@ -307,44 +306,54 @@ void TonlibClient::init_ext_client() { private: td::actor::ActorShared<> parent_; }; + ext_client_outbound_ = {}; ref_cnt_++; raw_client_ = ExtClientLazy::create(lite_client.adnl_id, lite_client.address, td::make_unique(td::actor::actor_shared())); } } -void TonlibClient::update_last_block_state(LastBlockState state) { - last_block_storage_.save_state("none", state); +void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 config_generation) { + if (config_generation == config_generation_) { + last_block_storage_.save_state(blockchain_name_, state); + } } void TonlibClient::init_last_block() { ref_cnt_++; class Callback : public LastBlock::Callback { public: - Callback(td::actor::ActorShared client) : client_(std::move(client)) { + Callback(td::actor::ActorShared client, td::uint32 config_generation) + : client_(std::move(client)), config_generation_(config_generation) { } void on_state_changed(LastBlockState state) override { - send_closure(client_, &TonlibClient::update_last_block_state, std::move(state)); + send_closure(client_, &TonlibClient::update_last_block_state, std::move(state), config_generation_); } private: td::actor::ActorShared client_; + td::uint32 config_generation_; }; LastBlockState state; - auto r_state = last_block_storage_.get_state("none"); - if (r_state.is_error()) { - LOG(WARNING) << "Unknown LastBlockState: " << r_state.error(); + td::Result r_state; + if (!ignore_cache_) { + r_state = last_block_storage_.get_state(blockchain_name_); + } + if (ignore_cache_ || r_state.is_error()) { + LOG_IF(WARNING, !ignore_cache_) << "Unknown LastBlockState: " << r_state.error(); state.zero_state_id = ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash, config_.zero_state_id.file_hash), state.last_block_id = config_.zero_state_id; state.last_key_block_id = config_.zero_state_id; + last_block_storage_.save_state(blockchain_name_, state); } else { state = r_state.move_as_ok(); } - raw_last_block_ = td::actor::create_actor("LastBlock", get_client_ref(), std::move(state), - td::make_unique(td::actor::actor_shared(this))); + raw_last_block_ = + td::actor::create_actor("LastBlock", get_client_ref(), std::move(state), config_, + td::make_unique(td::actor::actor_shared(this), config_generation_)); client_.set_client(get_client_ref()); } @@ -416,6 +425,8 @@ bool TonlibClient::is_static_request(td::int32 id) { case tonlib_api::testWallet_getAccountAddress::ID: case tonlib_api::wallet_getAccountAddress::ID: case tonlib_api::testGiver_getAccountAddress::ID: + case tonlib_api::packAccountAddress::ID: + case tonlib_api::unpackAccountAddress::ID: case tonlib_api::getBip39Hints::ID: case tonlib_api::setLogStream::ID: case tonlib_api::getLogStream::ID: @@ -472,7 +483,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.error()); } - return tonlib_api::make_object(r_account_address.ok().rserialize()); + return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testWallet_getAccountAddress& request) { @@ -480,7 +491,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.error()); } - return tonlib_api::make_object(r_account_address.ok().rserialize()); + return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::wallet_getAccountAddress& request) { @@ -488,11 +499,39 @@ tonlib_api::object_ptr TonlibClient::do_static_request( if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.error()); } - return tonlib_api::make_object(r_account_address.ok().rserialize()); + return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testGiver_getAccountAddress& request) { - return tonlib_api::make_object(TestGiver::address().rserialize()); + return tonlib_api::make_object(TestGiver::address().rserialize(true)); +} + +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::unpackAccountAddress& request) { + auto r_account_address = block::StdAddress::parse(request.account_address_); + if (r_account_address.is_error()) { + return status_to_tonlib_api(r_account_address.move_as_error()); + } + auto account_address = r_account_address.move_as_ok(); + return tonlib_api::make_object( + account_address.workchain, account_address.bounceable, account_address.testnet, + account_address.addr.as_slice().str()); +} + +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::packAccountAddress& request) { + if (!request.account_address_) { + return status_to_tonlib_api(td::Status::Error(400, "Field account_address must not be empty")); + } + if (request.account_address_->addr_.size() != 32) { + return status_to_tonlib_api(td::Status::Error(400, "Field account_address.addr must not be exactly 32 bytes")); + } + block::StdAddress addr; + addr.workchain = request.account_address_->workchain_id_; + addr.bounceable = request.account_address_->bounceable_; + addr.testnet = request.account_address_->testnet_; + addr.addr.as_slice().copy_from(request.account_address_->addr_); + return tonlib_api::make_object(addr.rserialize(true)); } tonlib_api::object_ptr TonlibClient::do_static_request(tonlib_api::getBip39Hints& request) { @@ -510,8 +549,7 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, } TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_)); TRY_STATUS(last_block_storage_.set_directory(request.options_->keystore_directory_)); - use_callbacks_for_network_ = request.options_->use_callbacks_for_network_; - if (!request.options_->config_.empty()) { + if (request.options_->config_) { TRY_STATUS(set_config(std::move(request.options_->config_))); } state_ = State::Running; @@ -519,15 +557,26 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, return td::Status::OK(); } -td::Status TonlibClient::set_config(std::string config) { - if (config.empty()) { - return td::Status::Error("config is empty"); +td::Status TonlibClient::set_config(object_ptr config) { + if (!config) { + return td::Status::Error(400, "config is empty"); + } + if (config->config_.empty()) { + return td::Status::Error(400, "config is empty"); } - TRY_RESULT(new_config, Config::parse(std::move(config))); - if (new_config.lite_clients.empty()) { + TRY_RESULT(new_config, Config::parse(std::move(config->config_))); + if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) { return td::Status::Error("No lite clients in config"); } config_ = std::move(new_config); + config_generation_++; + if (config->blockchain_name_.empty()) { + blockchain_name_ = td::sha256(config_.zero_state_id.to_str()).substr(0, 16); + } else { + blockchain_name_ = config->blockchain_name_; + } + use_callbacks_for_network_ = config->use_callbacks_for_network_; + ignore_cache_ = config->ignore_cache_; init_ext_client(); init_last_block(); return td::Status::OK(); @@ -541,9 +590,9 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request, return td::Status::OK(); } -td::Status TonlibClient::do_request(const tonlib_api::options_setConfig& request, +td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise) { - TRY_STATUS(set_config(request.config_)); + TRY_STATUS(set_config(std::move(request.config_))); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } @@ -572,8 +621,8 @@ td::Result> to_raw_accountS .as_slice() .str(); } - return tonlib_api::make_object(raw_state.balance, std::move(code), std::move(data), - to_transaction_id(raw_state.info), raw_state.sync_utime); + return tonlib_api::make_object( + raw_state.balance, std::move(code), std::move(data), to_transaction_id(raw_state.info), raw_state.info.gen_utime); } td::Result to_std_address_or_throw(td::Ref cs) { @@ -588,7 +637,7 @@ td::Result to_std_address_or_throw(td::Ref cs) { if (!tlb::csr_unpack(cs, addr)) { return td::Status::Error("Failed to unpack MsgAddressInt"); } - return block::StdAddress(addr.workchain_id, addr.address).rserialize(); + return block::StdAddress(addr.workchain_id, addr.address).rserialize(true); } td::Result to_std_address(td::Ref cs) { @@ -740,7 +789,7 @@ td::Result> to_testW return td::Status::Error("Failed to parse seq_no"); } return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime); + raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); } td::Result> to_wallet_accountState( @@ -755,7 +804,7 @@ td::Result> to_wallet_ac return td::Status::Error("Failed to parse seq_no"); } return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime); + raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); } td::Result> to_testGiver_accountState( @@ -770,7 +819,7 @@ td::Result> to_testGi return td::Status::Error("Failed to parse seq_no"); } return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime); + raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); } td::Result> to_generic_accountState( @@ -778,7 +827,7 @@ td::Result> to_generic_ if (raw_state.code.is_null()) { return tonlib_api::make_object( tonlib_api::make_object(raw_state.balance, to_transaction_id(raw_state.info), - raw_state.sync_utime)); + raw_state.info.gen_utime)); } auto code_hash = raw_state.code->prefetch_ref()->get_hash(); @@ -892,21 +941,21 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_init& request, TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); auto init_message = TestWallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key))); return do_request( - tonlib_api::raw_sendMessage(tonlib_api::make_object(address.rserialize()), + tonlib_api::raw_sendMessage(tonlib_api::make_object(address.rserialize(true)), vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(), vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()), std::move(promise)); } td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& request, - td::Promise>&& promise) { + td::Promise>&& promise) { if (!request.destination_) { return td::Status::Error(400, "Field destination must not be empty"); } if (!request.private_key_) { return td::Status::Error(400, "Field private_key must not be empty"); } - if (request.message_.size() > 124) { + if (request.message_.size() > 70) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); @@ -914,16 +963,30 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( 0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); - TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); - return do_request(tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize()), "", - vm::std_boc_serialize(TestWallet::make_a_gift_message( - td::Ed25519::PrivateKey(std::move(private_key.private_key)), - request.seqno_, request.amount_, request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), - std::move(promise)); + TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key))); + auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); + std::string init_state; + if (request.seqno_ == 0) { + TRY_RESULT(public_key, private_key.get_public_key()); + init_state = vm::std_boc_serialize(TestWallet::get_init_state(public_key)).move_as_ok().as_slice().str(); + } + td::Promise> new_promise = + [promise = std::move(promise)](td::Result> res) mutable { + if (res.is_error()) { + promise.set_error(res.move_as_error()); + } else { + promise.set_value(tonlib_api::make_object(0)); + } + }; + return do_request( + tonlib_api::raw_sendMessage( + tonlib_api::make_object(address.rserialize(true)), std::move(init_state), + vm::std_boc_serialize(TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, + request.message_, account_address)) + .move_as_ok() + .as_slice() + .str()), + std::move(new_promise)); } td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request, @@ -956,21 +1019,21 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request, TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); auto init_message = Wallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key))); return do_request( - tonlib_api::raw_sendMessage(tonlib_api::make_object(address.rserialize()), + tonlib_api::raw_sendMessage(tonlib_api::make_object(address.rserialize(true)), vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(), vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()), std::move(promise)); } td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, - td::Promise>&& promise) { + td::Promise>&& promise) { if (!request.destination_) { return td::Status::Error(400, "Field destination must not be empty"); } if (!request.private_key_) { return td::Status::Error(400, "Field private_key must not be empty"); } - if (request.message_.size() > 124) { + if (request.message_.size() > 70) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(valid_until, td::narrow_cast_safe(request.valid_until_)); @@ -979,17 +1042,30 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( 0 /*zerochain*/, Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); - TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); + TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key))); + auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); + std::string init_state; + if (request.seqno_ == 0) { + TRY_RESULT(public_key, private_key.get_public_key()); + init_state = vm::std_boc_serialize(Wallet::get_init_state(public_key)).move_as_ok().as_slice().str(); + } + td::Promise> new_promise = + [promise = std::move(promise), valid_until](td::Result> res) mutable { + if (res.is_error()) { + promise.set_error(res.move_as_error()); + } else { + promise.set_value(tonlib_api::make_object(valid_until)); + } + }; return do_request( tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize()), "", - vm::std_boc_serialize(Wallet::make_a_gift_message(td::Ed25519::PrivateKey(std::move(private_key.private_key)), - request.seqno_, valid_until, request.amount_, + tonlib_api::make_object(address.rserialize(true)), std::move(init_state), + vm::std_boc_serialize(Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, request.message_, account_address)) .move_as_ok() .as_slice() .str()), - std::move(promise)); + std::move(new_promise)); } td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, @@ -1012,23 +1088,31 @@ td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, // TestGiver td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request, - td::Promise>&& promise) { + td::Promise>&& promise) { if (!request.destination_) { return td::Status::Error(400, "Field destination must not be empty"); } - if (request.message_.size() > 124) { + if (request.message_.size() > 70) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); account_address.bounceable = false; + td::Promise> new_promise = + [promise = std::move(promise)](td::Result> res) mutable { + if (res.is_error()) { + promise.set_error(res.move_as_error()); + } else { + promise.set_value(tonlib_api::make_object(0)); + } + }; return do_request(tonlib_api::raw_sendMessage( - tonlib_api::make_object(TestGiver::address().rserialize()), "", + tonlib_api::make_object(TestGiver::address().rserialize(true)), "", vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_, request.message_, account_address)) .move_as_ok() .as_slice() .str()), - std::move(promise)); + std::move(new_promise)); } td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request, @@ -1085,21 +1169,16 @@ class TonlibQueryActor : public td::actor::Actor { class GenericSendGrams : public TonlibQueryActor { public: GenericSendGrams(td::actor::ActorShared client, tonlib_api::generic_sendGrams send_grams, - td::Promise>&& promise) + td::Promise>&& promise) : TonlibQueryActor(std::move(client)), send_grams_(std::move(send_grams)), promise_(std::move(promise)) { - timeout_ = td::Timestamp::in(15); } private: tonlib_api::generic_sendGrams send_grams_; - td::Promise> promise_; + td::Promise> promise_; - enum class SourceAction { Wait, Init, WaitInited, Ok } source_action_ = SourceAction::Wait; tonlib_api::object_ptr source_state_; block::StdAddress source_address_; - td::Timestamp source_next_get_state_; - td::Timestamp timeout_; - bool has_source_state_query_{false}; tonlib_api::object_ptr destination_state_; bool is_destination_bounce_{false}; @@ -1117,6 +1196,7 @@ class GenericSendGrams : public TonlibQueryActor { } td::Status do_start_up() { + alarm_timestamp() = td::Timestamp::in(15); if (!send_grams_.destination_) { return td::Status::Error(400, "Field destination must not be empty"); } @@ -1161,42 +1241,29 @@ class GenericSendGrams : public TonlibQueryActor { } td::Status do_on_source_state(td::Result> r_state) { - has_source_state_query_ = false; TRY_RESULT(state, std::move(r_state)); source_state_ = std::move(state); - if (source_action_ == SourceAction::Wait) { - source_action_ = SourceAction::Ok; - if (false && source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && - send_grams_.private_key_ && send_grams_.private_key_->key_) { - TRY_RESULT(key_bytes, block::PublicKey::parse(send_grams_.private_key_->key_->public_key_)); - auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); - auto addr = GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)); - if (addr.addr == source_address_.addr) { - source_action_ = SourceAction::Init; - send_query(tonlib_api::testWallet_init(clone(send_grams_.private_key_)), - [actor_id = actor_id(this)](auto r_res) { - send_closure(actor_id, &GenericSendGrams::on_source_init, std::move(r_res)); - }); - } - } - } else if (source_action_ == SourceAction::WaitInited) { - if (source_state_->get_id() != tonlib_api::generic_accountStateUninited::ID) { - source_action_ = SourceAction::Ok; + if (source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && send_grams_.private_key_ && + send_grams_.private_key_->key_) { + TRY_RESULT(key_bytes, block::PublicKey::parse(send_grams_.private_key_->key_->public_key_)); + auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); + + if (GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)).addr == source_address_.addr) { + auto state = ton::move_tl_object_as(source_state_); + source_state_ = tonlib_api::make_object( + tonlib_api::make_object(-1, 0, nullptr, + state->account_state_->sync_utime_)); + } else if (GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)).addr == + source_address_.addr) { + auto state = ton::move_tl_object_as(source_state_); + source_state_ = tonlib_api::make_object( + tonlib_api::make_object(-1, 0, nullptr, + state->account_state_->sync_utime_)); } } return do_loop(); } - void on_source_init(td::Result> r_ok) { - do_on_source_init(std::move(r_ok)); - } - - td::Status do_on_source_init(td::Result> r_ok) { - TRY_RESULT(ok, std::move(r_ok)); - source_action_ = SourceAction::WaitInited; - return do_loop(); - } - void on_destination_state(td::Result> r_state) { check(do_on_destination_state(std::move(r_state))); } @@ -1204,34 +1271,18 @@ class GenericSendGrams : public TonlibQueryActor { td::Status do_on_destination_state(td::Result> r_state) { TRY_RESULT(state, std::move(r_state)); destination_state_ = std::move(state); - if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID) { - //return td::Status::Error("Transfer to uninited wallet"); + if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounce_ && + !send_grams_.allow_send_to_uninited_) { + return td::Status::Error(400, "DANGEROUS_TRANSACTION: Transfer to uninited wallet"); } return do_loop(); } void alarm() override { - check(do_loop()); + check(td::Status::Error("Timeout")); } td::Status do_loop() { - if (timeout_.is_in_past()) { - return td::Status::Error("Timeout"); - } - alarm_timestamp().relax(timeout_); - if (source_action_ == SourceAction::WaitInited && !has_source_state_query_) { - if (source_next_get_state_.is_in_past()) { - source_next_get_state_ = td::Timestamp::in(1); - has_source_state_query_ = true; - send_query(tonlib_api::generic_getAccountState( - tonlib_api::make_object(send_grams_.source_->account_address_)), - [actor_id = actor_id(this)](auto r_res) { - send_closure(actor_id, &GenericSendGrams::on_source_state, std::move(r_res)); - }); - } else { - alarm_timestamp().relax(source_next_get_state_); - } - } - if (source_action_ != SourceAction::Ok || !destination_state_) { + if (!source_state_ || !destination_state_) { return td::Status::OK(); } downcast_call(*source_state_, @@ -1254,7 +1305,10 @@ class GenericSendGrams : public TonlibQueryActor { [&](tonlib_api::generic_accountStateWallet& test_wallet_state) { send_query(tonlib_api::wallet_sendGrams( std::move(send_grams_.private_key_), std::move(send_grams_.destination_), - test_wallet_state.account_state_->seqno_, std::numeric_limits::max(), + test_wallet_state.account_state_->seqno_, + send_grams_.timeout_ == 0 + ? 60 + test_wallet_state.account_state_->sync_utime_ + : send_grams_.timeout_ + test_wallet_state.account_state_->sync_utime_, send_grams_.amount_, std::move(send_grams_.message_)), std::move(promise_)); stop(); @@ -1272,7 +1326,10 @@ class GenericSendGrams : public TonlibQueryActor { }; td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, - td::Promise>&& promise) { + td::Promise>&& promise) { + if (request.timeout_ < 0 || request.timeout_ > 300) { + return td::Status::Error(400, "Invalid timeout: must be between 0 and 300"); + } auto id = actor_id_++; actors_[id] = td::actor::create_actor("GenericSendGrams", actor_shared(this, id), std::move(request), std::move(promise)); @@ -1284,7 +1341,7 @@ td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request, TRY_RESULT(key, key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_), std::move(request.random_extra_seed_))); TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); - promise.set_value(tonlib_api::make_object(key_bytes.serialize(), std::move(key.secret))); + promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1321,7 +1378,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importKey& request, TRY_RESULT(key, key_storage_.import_key(std::move(request.local_password_), std::move(request.mnemonic_password_), KeyStorage::ExportedKey{std::move(request.exported_key_->word_list_)})); TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); - promise.set_value(tonlib_api::make_object(key_bytes.serialize(), std::move(key.secret))); + promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1350,7 +1407,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request, TRY_RESULT(key, key_storage_.import_pem_key(std::move(request.local_password_), std::move(request.key_password_), KeyStorage::ExportedPemKey{std::move(request.exported_key_->pem_)})); TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); - promise.set_value(tonlib_api::make_object(key_bytes.serialize(), std::move(key.secret))); + promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1374,7 +1431,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& reques std::move(request.local_password_), std::move(request.key_password_), KeyStorage::ExportedEncryptedKey{std::move(request.exported_encrypted_key_->data_)})); TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); - promise.set_value(tonlib_api::make_object(key_bytes.serialize(), std::move(key.secret))); + promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 5ac81c2ca2..9e7ecdafea 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -47,6 +47,9 @@ class TonlibClient : public td::actor::Actor { enum class State { Uninited, Running, Closed } state_ = State::Uninited; td::unique_ptr callback_; Config config_; + td::uint32 config_generation_{0}; + std::string blockchain_name_; + bool ignore_cache_{false}; bool use_callbacks_for_network_{false}; td::actor::ActorId ext_client_outbound_; @@ -85,7 +88,7 @@ class TonlibClient : public td::actor::Actor { } } - void update_last_block_state(LastBlockState state); + void update_last_block_state(LastBlockState state, td::uint32 config_generation_); void on_result(td::uint64 id, object_ptr response); static bool is_static_request(td::int32 id); static bool is_uninited_request(td::int32 id); @@ -98,6 +101,8 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::testWallet_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::wallet_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::testGiver_getAccountAddress& request); + static object_ptr do_static_request(const tonlib_api::packAccountAddress& request); + static object_ptr do_static_request(const tonlib_api::unpackAccountAddress& request); static object_ptr do_static_request(tonlib_api::getBip39Hints& request); static object_ptr do_static_request(tonlib_api::setLogStream& request); @@ -114,12 +119,10 @@ class TonlibClient : public td::actor::Actor { return td::Status::Error(400, "Function is unsupported"); } - td::Status set_config(std::string config); - + td::Status set_config(object_ptr config); td::Status do_request(const tonlib_api::init& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::close& request, td::Promise>&& promise); - td::Status do_request(const tonlib_api::options_setConfig& request, - td::Promise>&& promise); + td::Status do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise>&& promise); td::Status do_request(tonlib_api::raw_getAccountState& request, @@ -129,23 +132,25 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::testWallet_init& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::testWallet_sendGrams& request, - td::Promise>&& promise); + td::Promise>&& promise); td::Status do_request(tonlib_api::testWallet_getAccountState& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::wallet_init& request, td::Promise>&& promise); - td::Status do_request(const tonlib_api::wallet_sendGrams& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::wallet_sendGrams& request, + td::Promise>&& promise); td::Status do_request(tonlib_api::wallet_getAccountState& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::testGiver_getAccountState& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::testGiver_sendGrams& request, - td::Promise>&& promise); + td::Promise>&& promise); td::Status do_request(const tonlib_api::generic_getAccountState& request, td::Promise>&& promise); - td::Status do_request(tonlib_api::generic_sendGrams& request, td::Promise>&& promise); + td::Status do_request(tonlib_api::generic_sendGrams& request, + td::Promise>&& promise); td::Status do_request(const tonlib_api::createNewKey& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::exportKey& request, diff --git a/tonlib/tonlib/Wallet.cpp b/tonlib/tonlib/Wallet.cpp index c0758caa5d..7546e8fd22 100644 --- a/tonlib/tonlib/Wallet.cpp +++ b/tonlib/tonlib/Wallet.cpp @@ -49,15 +49,18 @@ td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; - cb.append_cellslice(binary_bitstring_to_cellslice("b{010000100}").move_as_ok()) + cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok()) + .store_long(dest_address.bounceable, 1) + .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); + td::int8 send_mode = 3; auto message_outer = vm::CellBuilder() .store_long(seqno, 32) .store_long(valid_until, 32) - .store_long(1, 8) + .store_long(send_mode, 8) .store_ref(message_inner) .finalize(); std::string seq_no(4, 0); @@ -68,8 +71,8 @@ td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri td::Ref Wallet::get_init_code() { static auto res = [] { auto serialized_code = td::base64_decode( - "te6ccgEEBgEAAAAAaAABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQCA8oMI1xgg0x/TH/gjErnyY+1E0NMf0//" - "RUTG68qED+QFUEEL5EPKi+AACkyDXSpbTB9QC+wDo0aTIyx/L/8ntVAAE0DAAEaCZL9qJoa4WPw==") + "te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" + "0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=") .move_as_ok(); return vm::std_boc_deserialize(serialized_code).move_as_ok(); }(); diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 191adba647..ce8d5f481f 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -25,9 +25,14 @@ class TonlibCli : public td::actor::Actor { struct Options { bool enable_readline{true}; std::string config; + std::string name; std::string key_dir{"."}; bool use_callbacks_for_network{false}; bool use_simple_wallet{false}; + bool ignore_cache{false}; + + bool one_shot{false}; + std::string cmd; }; TonlibCli(Options options) : options_(std::move(options)) { } @@ -65,8 +70,10 @@ class TonlibCli : public td::actor::Actor { td::actor::ActorShared id_; }; ref_cnt_++; - io_ = td::TerminalIO::create("> ", options_.enable_readline, std::make_unique(actor_shared(this))); - td::actor::send_closure(io_, &td::TerminalIO::set_log_interface); + if (!options_.one_shot) { + io_ = td::TerminalIO::create("> ", options_.enable_readline, std::make_unique(actor_shared(this))); + td::actor::send_closure(io_, &td::TerminalIO::set_log_interface); + } class TonlibCb : public tonlib::TonlibCallback { public: @@ -109,13 +116,20 @@ class TonlibCli : public td::actor::Actor { } using tonlib_api::make_object; - send_query(make_object(make_object(options_.config, options_.key_dir, - options_.use_callbacks_for_network)), + auto config = !options_.config.empty() + ? make_object(options_.config, options_.name, + options_.use_callbacks_for_network, options_.ignore_cache) + : nullptr; + send_query(make_object(make_object(std::move(config), options_.key_dir)), [](auto r_ok) { LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); td::TerminalIO::out() << "Tonlib is inited\n"; }); + if (options_.one_shot) { + td::actor::send_closure(actor_id(this), &TonlibCli::parse_line, td::BufferSlice(options_.cmd)); + } } + void hangup_shared() override { CHECK(ref_cnt_ > 0); ref_cnt_--; @@ -130,6 +144,26 @@ class TonlibCli : public td::actor::Actor { td::actor::SchedulerContext::get()->stop(); } + void on_wait() { + if (options_.one_shot) { + LOG(ERROR) << "FAILED (not enough data)"; + std::_Exit(2); + } + } + + void on_error() { + if (options_.one_shot) { + LOG(ERROR) << "FAILED"; + std::_Exit(1); + } + } + void on_ok() { + if (options_.one_shot) { + LOG(INFO) << "OK"; + std::_Exit(0); + } + } + void parse_line(td::BufferSlice line) { if (is_closing_) { return; @@ -148,13 +182,15 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "help - show this help\n"; td::TerminalIO::out() << "genkey - generate new secret key\n"; td::TerminalIO::out() << "keys - show all stored keys\n"; - td::TerminalIO::out() << "exportkey [key_id] - export key\n"; - td::TerminalIO::out() << "setconfig - set lite server config\n"; + td::TerminalIO::out() << "unpackaddress
- validate and parse address\n"; + td::TerminalIO::out() << "importkey - import key\n"; + td::TerminalIO::out() << "exportkey [] - export key\n"; + td::TerminalIO::out() << "setconfig [] [] [] - set lite server config\n"; td::TerminalIO::out() << "getstate - get state of simple wallet with requested key\n"; td::TerminalIO::out() << "gethistory - get history fo simple wallet with requested key (last 10 transactions)\n"; td::TerminalIO::out() << "init - init simple wallet with requested key\n"; - td::TerminalIO::out() << "transfer - transfer of grams from " + td::TerminalIO::out() << "transfer[f] - transfer of grams from " " to .\n" << "\t could also be 'giver'\n" << "\t could also be 'giver' or smartcontract address\n"; @@ -174,20 +210,36 @@ class TonlibCli : public td::actor::Actor { } else if (cmd == "importkey") { import_key(parser.read_all()); } else if (cmd == "setconfig") { - set_config(parser.read_word()); + auto config = parser.read_word(); + auto name = parser.read_word(); + auto to_bool = [](td::Slice word) { + if (word.empty()) { + return false; + } + if (word == "0" || word == "FALSE" || word == "false") { + return false; + } + return true; + }; + auto use_callback = parser.read_word(); + auto force = parser.read_word(); + set_config(config, name, to_bool(use_callback), to_bool(force)); } else if (cmd == "getstate") { get_state(parser.read_word()); } else if (cmd == "gethistory") { get_history(parser.read_word()); } else if (cmd == "init") { init_simple_wallet(parser.read_word()); - } else if (cmd == "transfer") { + } else if (cmd == "transfer" || cmd == "transferf") { auto from = parser.read_word(); auto to = parser.read_word(); auto grams = parser.read_word(); - transfer(from, to, grams); + auto message = parser.read_word(); + transfer(from, to, grams, message, cmd == "transferf"); } else if (cmd == "hint") { get_hints(parser.read_word()); + } else if (cmd == "unpackaddress") { + unpack_address(parser.read_word()); } } @@ -251,6 +303,17 @@ class TonlibCli : public td::actor::Actor { }; } + void unpack_address(td::Slice addr) { + send_query(tonlib_api::make_object(addr.str()), + [addr = addr.str()](auto r_parsed_addr) mutable { + if (r_parsed_addr.is_error()) { + LOG(ERROR) << "Failed to parse address: " << r_parsed_addr.error(); + return; + } + LOG(ERROR) << to_string(r_parsed_addr.ok()); + }); + } + void generate_key(td::SecureString entropy = {}) { if (entropy.size() < 20) { td::TerminalIO::out() << "Enter some entropy"; @@ -275,6 +338,7 @@ class TonlibCli : public td::actor::Actor { [this, password = std::move(password)](auto r_key) mutable { if (r_key.is_error()) { LOG(ERROR) << "Failed to create new key: " << r_key.error(); + return; } auto key = r_key.move_as_ok(); LOG(ERROR) << to_string(key); @@ -282,7 +346,7 @@ class TonlibCli : public td::actor::Actor { info.public_key = key->public_key_; info.secret = std::move(key->secret_); keys_.push_back(std::move(info)); - //export_key(key->public_key_, keys_.size() - 1, std::move(password)); + export_key(key->public_key_, keys_.size() - 1, std::move(password)); store_keys(); }); } @@ -305,7 +369,13 @@ class TonlibCli : public td::actor::Actor { auto db = r_db.move_as_ok(); td::ConstParser parser(db.as_slice()); while (true) { - auto public_key = parser.read_word(); + auto public_key = parser.read_word().str(); + { + auto tmp = td::base64_decode(public_key); + if (tmp.is_ok()) { + public_key = td::base64url_encode(tmp.move_as_ok()); + } + } auto secret_b64 = parser.read_word(); if (secret_b64.empty()) { break; @@ -313,10 +383,11 @@ class TonlibCli : public td::actor::Actor { auto r_secret = td::base64_decode_secure(secret_b64); if (r_secret.is_error()) { LOG(ERROR) << "Invalid secret database at " << key_db_path(); + return; } KeyInfo info; - info.public_key = public_key.str(); + info.public_key = public_key; info.secret = r_secret.move_as_ok(); LOG(INFO) << info.public_key; @@ -324,13 +395,16 @@ class TonlibCli : public td::actor::Actor { } } + void dump_key(size_t i) { + td::TerminalIO::out() << " #" << i << ": Public key: " << keys_[i].public_key << " " + << " Address: " + << to_account_address(PSLICE() << i, false).move_as_ok().address->account_address_ << "\n"; + } void dump_keys() { td::TerminalIO::out() << "Got " << keys_.size() << " keys" << "\n"; for (size_t i = 0; i < keys_.size(); i++) { - td::TerminalIO::out() << " #" << i << ": " << keys_[i].public_key << "\n"; - td::TerminalIO::out() << " " << to_account_address(PSLICE() << i, false).move_as_ok().address->account_address_ - << "\n"; + dump_key(i); } } @@ -447,12 +521,15 @@ class TonlibCli : public td::actor::Actor { send_query(make_object(make_object( make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), td::SecureString(password))), - [key = std::move(key)](auto r_res) { + [this, key = std::move(key), key_i](auto r_res) { if (r_res.is_error()) { td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n"; return; } - td::TerminalIO::out() << to_string(r_res.ok()); + dump_key(key_i); + for (auto& word : r_res.ok()->word_list_) { + td::TerminalIO::out() << " " << word.as_slice() << "\n"; + } }); } @@ -496,7 +573,7 @@ class TonlibCli : public td::actor::Actor { }); } - void set_config(td::Slice path) { + void set_config(td::Slice path, td::Slice name, bool use_callback, bool ignore_cache) { auto r_data = td::read_file_str(path.str()); if (r_data.is_error()) { td::TerminalIO::out() << "Can't read file [" << path << "] : " << r_data.error() << "\n"; @@ -505,13 +582,15 @@ class TonlibCli : public td::actor::Actor { auto data = r_data.move_as_ok(); using tonlib_api::make_object; - send_query(make_object(data), [](auto r_res) { - if (r_res.is_error()) { - td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n"; - return; - } - td::TerminalIO::out() << to_string(r_res.ok()); - }); + send_query(make_object( + make_object(std::move(data), name.str(), use_callback, ignore_cache)), + [](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n"; + return; + } + td::TerminalIO::out() << to_string(r_res.ok()); + }); } void get_state(td::Slice key) { @@ -519,23 +598,27 @@ class TonlibCli : public td::actor::Actor { dump_keys(); td::TerminalIO::out() << "Choose public key (hex prefix or #N)"; cont_ = [this](td::Slice key) { this->get_state(key); }; + on_wait(); return; } auto r_address = to_account_address(key, false); if (r_address.is_error()) { td::TerminalIO::out() << "Unknown key id: [" << key << "]\n"; + on_error(); return; } auto address = r_address.move_as_ok(); using tonlib_api::make_object; send_query(make_object( ton::move_tl_object_as(std::move(address.address))), - [](auto r_res) { + [this](auto r_res) { if (r_res.is_error()) { td::TerminalIO::out() << "Can't get state: " << r_res.error() << "\n"; + on_error(); return; } td::TerminalIO::out() << to_string(r_res.ok()); + on_ok(); }); } void get_history(td::Slice key) { @@ -586,54 +669,76 @@ class TonlibCli : public td::actor::Actor { }); } - void transfer(td::Slice from, td::Slice to, td::Slice grams) { + void transfer(td::Slice from, td::Slice to, td::Slice grams, td::Slice message, bool allow_send_to_uninited) { auto r_from_address = to_account_address(from, true); if (r_from_address.is_error()) { td::TerminalIO::out() << "Unknown key id: [" << from << "] : " << r_from_address.error() << "\n"; + on_error(); return; } auto r_to_address = to_account_address(to, false); if (r_to_address.is_error()) { td::TerminalIO::out() << "Unknown key id: [" << to << "] : " << r_to_address.error() << "\n"; + on_error(); return; } auto r_grams = td::to_integer_safe(grams); if (r_grams.is_error()) { td::TerminalIO::out() << "Invalid grams amount: [" << grams << "]\n"; + on_error(); return; } - if (from != "giver") { + if (options_.one_shot) { + transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "", "", + allow_send_to_uninited); + return; + } + if (from != "giver" && message.empty()) { td::TerminalIO::out() << "Enter password (could be empty)"; - cont_ = [this, from = r_from_address.move_as_ok(), to = r_to_address.move_as_ok(), grams = r_grams.move_as_ok()]( - td::Slice password) mutable { this->transfer(std::move(from), std::move(to), grams, password); }; + cont_ = [this, from = r_from_address.move_as_ok(), to = r_to_address.move_as_ok(), grams = r_grams.move_as_ok(), + allow_send_to_uninited](td::Slice password) mutable { + this->transfer(std::move(from), std::move(to), grams, password, allow_send_to_uninited); + }; + on_wait(); return; } - transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), ""); + if (message.empty()) { + transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "", + allow_send_to_uninited); + } else { + transfer(r_from_address.move_as_ok(), r_to_address.move_as_ok(), r_grams.move_as_ok(), "", message, + allow_send_to_uninited); + } } - void transfer(Address from, Address to, td::uint64 grams, td::Slice password) { + void transfer(Address from, Address to, td::uint64 grams, td::Slice password, bool allow_send_to_uninited) { td::TerminalIO::out() << "Enter message (could be empty)"; - cont_ = [this, from = std::move(from), to = std::move(to), grams, - password = password.str()](td::Slice message) mutable { - this->transfer(std::move(from), std::move(to), grams, password, message); + cont_ = [this, from = std::move(from), to = std::move(to), grams, password = password.str(), + allow_send_to_uninited](td::Slice message) mutable { + this->transfer(std::move(from), std::move(to), grams, password, message, allow_send_to_uninited); }; + on_wait(); return; } - void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message) { + void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message, + bool allow_send_to_uninited) { using tonlib_api::make_object; auto key = !from.secret.empty() ? make_object( make_object(from.public_key, from.secret.copy()), td::SecureString(password)) : nullptr; - send_query(make_object(std::move(key), std::move(from.address), - std::move(to.address), grams, message.str()), - [](auto r_res) { - if (r_res.is_error()) { - td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; - return; - } - td::TerminalIO::out() << to_string(r_res.ok()); - }); + send_query( + make_object(std::move(key), std::move(from.address), std::move(to.address), + grams, 30, allow_send_to_uninited, message.str()), + [this](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; + on_error(); + return; + } + td::TerminalIO::out() << to_string(r_res.ok()); + on_ok(); + }); } void init_simple_wallet(td::Slice key) { @@ -719,17 +824,28 @@ int main(int argc, char* argv[]) { options.key_dir = arg.str(); return td::Status::OK(); }); + p.add_option('E', "execute", "execute one command", [&](td::Slice arg) { + options.one_shot = true; + options.cmd = arg.str(); + return td::Status::OK(); + }); p.add_option('v', "verbosity", "set verbosity level", [&](td::Slice arg) { auto verbosity = td::to_integer(arg); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + verbosity); return (verbosity >= 0 && verbosity <= 20) ? td::Status::OK() : td::Status::Error("verbosity must be 0..20"); }); - p.add_option('C', "config", "set lite server config", [&](td::Slice arg) { + p.add_option('C', "config-force", "set lite server config, drop config related blockchain cache", [&](td::Slice arg) { + TRY_RESULT(data, td::read_file_str(arg.str())); + options.config = std::move(data); + options.ignore_cache = true; + return td::Status::OK(); + }); + p.add_option('c', "config", "set lite server config", [&](td::Slice arg) { TRY_RESULT(data, td::read_file_str(arg.str())); options.config = std::move(data); return td::Status::OK(); }); - p.add_option('c', "use-callbacks-for-network", "do not use this", [&]() { + p.add_option('n', "use-callbacks-for-network", "do not use this", [&]() { options.use_callbacks_for_network = true; return td::Status::OK(); }); From ecb3e06a06628c358b42ac43c96168d51dbb92e3 Mon Sep 17 00:00:00 2001 From: ton Date: Mon, 30 Sep 2019 12:53:00 +0400 Subject: [PATCH 002/667] tonlib: big update --- catchain/catchain-receiver.cpp | 6 +- crypto/CMakeLists.txt | 5 + crypto/parser/srcread.cpp | 5 + crypto/smartcont/highload-wallet-code.fc | 41 +++++++ crypto/smartcont/highload-wallet-v2-code.fc | 65 +++++++++++ crypto/smartcont/highload-wallet.fif | 68 ++++++++++++ crypto/smartcont/new-highload-wallet.fif | 44 ++++++++ crypto/smartcont/stdlib.fc | 2 + tl/generate/scheme/tonlib_api.tl | 8 +- tl/generate/scheme/tonlib_api.tlo | Bin 12496 -> 12700 bytes tonlib/CMakeLists.txt | 4 + tonlib/TonlibConfig.cmake | 3 + tonlib/test/offline.cpp | 39 +++++-- tonlib/test/online.cpp | 6 +- tonlib/tonlib/CellString.cpp | 64 +++++++++++ tonlib/tonlib/CellString.h | 22 ++++ tonlib/tonlib/KeyStorage.cpp | 47 +++----- tonlib/tonlib/KeyStorage.h | 9 +- tonlib/tonlib/KeyValue.cpp | 103 ++++++++++++++++++ tonlib/tonlib/KeyValue.h | 18 +++ tonlib/tonlib/LastBlockStorage.cpp | 20 ++-- tonlib/tonlib/LastBlockStorage.h | 7 +- tonlib/tonlib/TestGiver.cpp | 5 +- tonlib/tonlib/TestGiver.h | 2 + tonlib/tonlib/TestWallet.cpp | 5 +- tonlib/tonlib/TestWallet.h | 2 + tonlib/tonlib/TonlibClient.cpp | 34 ++++-- tonlib/tonlib/TonlibClient.h | 2 + tonlib/tonlib/Wallet.cpp | 6 +- tonlib/tonlib/Wallet.h | 2 + tonlib/tonlib/keys/DecryptedKey.cpp | 3 +- tonlib/tonlib/keys/EncryptedKey.h | 2 +- tonlib/tonlib/keys/SimpleEncryption.cpp | 2 +- tonlib/tonlib/tonlib-cli.cpp | 14 ++- validator-engine-console/CMakeLists.txt | 2 + validator-engine/CMakeLists.txt | 2 + .../validator-session-description.cpp | 2 +- 37 files changed, 581 insertions(+), 90 deletions(-) create mode 100644 crypto/smartcont/highload-wallet-code.fc create mode 100644 crypto/smartcont/highload-wallet-v2-code.fc create mode 100644 crypto/smartcont/highload-wallet.fif create mode 100644 crypto/smartcont/new-highload-wallet.fif create mode 100644 tonlib/TonlibConfig.cmake create mode 100644 tonlib/tonlib/CellString.cpp create mode 100644 tonlib/tonlib/CellString.h create mode 100644 tonlib/tonlib/KeyValue.cpp create mode 100644 tonlib/tonlib/KeyValue.h diff --git a/catchain/catchain-receiver.cpp b/catchain/catchain-receiver.cpp index 85f174987b..7b8a6415e3 100644 --- a/catchain/catchain-receiver.cpp +++ b/catchain/catchain-receiver.cpp @@ -588,7 +588,7 @@ void CatChainReceiverImpl::read_db() { read_db_ = true; next_rotate_ = td::Timestamp::in(60 + td::Random::fast(0, 60)); - next_sync_ = td::Timestamp::in(0.01 * td::Random::fast(0, 60)); + next_sync_ = td::Timestamp::in(0.001 * td::Random::fast(0, 60)); alarm_timestamp().relax(next_rotate_); alarm_timestamp().relax(next_sync_); @@ -627,7 +627,7 @@ void CatChainReceiverImpl::receive_query_from_overlay(adnl::AdnlNodeIdShort src, promise.set_error(td::Status::Error(ErrorCode::notready, "db not read")); return; } - td::PerfWarningTimer t{"catchain query process", 0.001}; + td::PerfWarningTimer t{"catchain query process", 0.05}; auto F = fetch_tl_object(data.clone(), true); if (F.is_error()) { callback_->on_custom_query(get_source_by_adnl_id(src)->get_hash(), std::move(data), std::move(promise)); @@ -909,7 +909,7 @@ void CatChainReceiverImpl::choose_neighbours() { void CatChainReceiverImpl::alarm() { alarm_timestamp() = td::Timestamp::never(); if (next_sync_ && next_sync_.is_in_past()) { - next_sync_ = td::Timestamp::in(td::Random::fast(2.0, 3.0)); + next_sync_ = td::Timestamp::in(td::Random::fast(0.1, 0.2)); for (auto i = 0; i < 3; i++) { auto S = get_source(td::Random::fast(0, get_sources_cnt() - 1)); CHECK(S != nullptr); diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index c76de707c9..acf88eeef6 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -315,6 +315,8 @@ if (NOT CMAKE_CROSSCOMPILING) GenFif(DEST smartcont/config-code.fif SOURCE smartcont/config-code.fc) GenFif(DEST smartcont/wallet-code.fif SOURCE smartcont/wallet-code.fc) GenFif(DEST smartcont/simple-wallet-code.fif SOURCE smartcont/simple-wallet-code.fc) + GenFif(DEST smartcont/highload-wallet-code.fif SOURCE smartcont/highload-wallet-code.fc) + GenFif(DEST smartcont/highload-wallet-v2-code.fif SOURCE smartcont/highload-wallet-v2-code.fc) GenFif(DEST smartcont/elector-code.fif SOURCE smartcont/elector-code.fc) endif() @@ -333,3 +335,6 @@ target_link_libraries(dump-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) diff --git a/crypto/parser/srcread.cpp b/crypto/parser/srcread.cpp index ac031e1e14..3b363242ba 100644 --- a/crypto/parser/srcread.cpp +++ b/crypto/parser/srcread.cpp @@ -169,6 +169,11 @@ bool SourceReader::load_line() { error("line too long"); return false; } + if (len && cur_line.back() == '\r') { + // CP/M line breaks support + cur_line.pop_back(); + --len; + } loc.text = cur_line; cur_line_len = (int)len; loc.line_pos = 0; diff --git a/crypto/smartcont/highload-wallet-code.fc b/crypto/smartcont/highload-wallet-code.fc new file mode 100644 index 0000000000..70abb48e23 --- /dev/null +++ b/crypto/smartcont/highload-wallet-code.fc @@ -0,0 +1,41 @@ +;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges) +;; accepts orders for up to 254 internal messages (transfers) in one external message + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); + throw_if(35, valid_until <= now()); + var ds = get_data().begin_parse(); + var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); + ds.end_parse(); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, subwallet_id == stored_subwallet); + throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); + var dict = cs~load_dict(); + cs.end_parse(); + accept_message(); + int i = -1; + do { + (i, var cs, var f) = dict.idict_get_next?(16, i); + if (f) { + var mode = cs~load_uint(8); + send_raw_message(cs~load_ref(), mode); + } + } until (~ f); + set_data(begin_cell() + .store_uint(stored_seqno + 1, 32) + .store_uint(stored_subwallet, 32) + .store_uint(public_key, 256) + .end_cell()); +} + +;; Get methods + +int seqno() method_id { + return get_data().begin_parse().preload_uint(32); +} diff --git a/crypto/smartcont/highload-wallet-v2-code.fc b/crypto/smartcont/highload-wallet-v2-code.fc new file mode 100644 index 0000000000..88d7885563 --- /dev/null +++ b/crypto/smartcont/highload-wallet-v2-code.fc @@ -0,0 +1,65 @@ +;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges) +;; accepts orders for up to 254 internal messages (transfers) in one external message +;; this version does not use seqno for replay protection; instead, it remembers all recent query_ids +;; in this way several external messages with different query_id can be sent in parallel + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64)); + var bound = (now() << 32); + throw_if(35, query_id < bound); + var ds = get_data().begin_parse(); + var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); + ds.end_parse(); + (_, var found?) = old_queries.udict_get?(64, query_id); + throw_if(32, found?); + throw_unless(34, subwallet_id == stored_subwallet); + throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); + var dict = cs~load_dict(); + cs.end_parse(); + accept_message(); + int i = -1; + do { + (i, var cs, var f) = dict.idict_get_next?(16, i); + if (f) { + var mode = cs~load_uint(8); + send_raw_message(cs~load_ref(), mode); + } + } until (~ f); + bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago + old_queries~udict_set_builder(64, query_id, begin_cell()); + var queries = old_queries; + do { + var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64); + f~touch(); + if (f) { + f = (i < bound); + } + if (f) { + old_queries = old_queries'; + last_cleaned = i; + } + } until (~ f); + set_data(begin_cell() + .store_uint(stored_subwallet, 32) + .store_uint(last_cleaned, 64) + .store_uint(public_key, 256) + .store_dict(old_queries) + .end_cell()); +} + +;; Get methods + +;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten) +int processed?(int query_id) method_id { + var ds = get_data().begin_parse(); + var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict()); + ds.end_parse(); + (_, var found) = old_queries.udict_get?(64, query_id); + return found ? true : - (query_id <= last_cleaned); +} diff --git a/crypto/smartcont/highload-wallet.fif b/crypto/smartcont/highload-wallet.fif new file mode 100644 index 0000000000..58ebda065b --- /dev/null +++ b/crypto/smartcont/highload-wallet.fif @@ -0,0 +1,68 @@ +#!/usr/bin/env fift -s +"TonUtil.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a request with up to 254 orders loaded from to high-load (sub)wallet created by new-highload-wallet.fif, with private key loaded from file .pk " + ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr + ." is a text file with lines `SEND `" cr 1 halt +} : usage +$# dup 4 < swap 5 > or ' usage if + +$1 =: file-base +$2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id +{ subwallet-id (.) $+ } : +subwallet +$3 parse-int =: seqno +$4 =: order-file +def? $5 { @' $5 } { "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 +subwallet +".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 + +variable orders dictnew orders ! +variable order# order# 0! +// c -- +{ = abort"more than 254 orders" + orders @ 16 udict!+ not abort"cannot add order to dictionary" + orders ! order# 1+! +} : add-order +// b body -- b' +{ tuck +} : create-int-msg +// ng wc addr bnc -- +{ ."Transferring " 3 roll .GR ."to account " + -rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr +} : .transfer +// addr$ ng -- c +{ swap parse-smc-addr // ng wc addr bnc + 2over 2over .transfer + create-int-msg +} : create-simple-transfer +// c m -- c' +{ } : create-order + +// addr$ ng -- +{ create-simple-transfer send-mode create-order add-order } : send +{ bl word bl word $>GR send } : SEND + +// parse order file +order-file include + +// create external message + +dup ."signing message: " +dup ."resulting external message: " B dup Bx. cr +savefile +".boc" tuck B>file +."(Saved to file " type .")" cr diff --git a/crypto/smartcont/new-highload-wallet.fif b/crypto/smartcont/new-highload-wallet.fif new file mode 100644 index 0000000000..b7a1a1db9f --- /dev/null +++ b/crypto/smartcont/new-highload-wallet.fif @@ -0,0 +1,44 @@ +#!/usr/bin/env fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a new high-load wallet in the specified workchain, with the controlling private key saved to or loaded from .pk " + ."('new-wallet.pk' by default)" cr + ." is the 32-bit identifier of this subwallet among all controlled by the same private key" cr 1 halt +} : usage +$# 2- -2 and ' usage if + +$1 parse-workchain-id =: wc // set workchain id from command line argument +$2 parse-int dup =: subwallet-id // parse subwallet-id +32 fits ' usage ifnot +{ subwallet-id (.) $+ } : +subwallet +def? $3 { @' $3 } { "new-wallet" } cond constant file-base + +."Creating new high-load wallet in workchain " wc . +."with subwallet id " subwallet-id . cr + +// Create new high-load wallet; source code included from `highload-wallet-code.fif` +"highload-wallet-code.fif" include +// code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."signing message: " +dup ."External message for initialization is " B dup Bx. cr +file-base +subwallet +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 4fb30bdd10..fb9b49f56a 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -86,6 +86,8 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va (cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; cell new_dict() asm "NEWDICT"; int dict_empty?(cell c) asm "DICTEMPTY"; diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 07a2f32c14..3729d3f353 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -16,8 +16,12 @@ vector {t:Type} # [ t ] = Vector t; error code:int32 message:string = Error; ok = Ok; +keyStoreTypeDirectory directory:string = KeyStoreType; +keyStoreTypeInMemory = KeyStoreType; + config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config; -options config:config keystore_directory:string = Options; + +options config:config keystore_type:KeyStoreType = Options; key public_key:string secret:secureBytes = Key; inputKey key:key local_password:secureBytes = InputKey; @@ -37,7 +41,7 @@ raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState; raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState; raw.message source:string destination:string value:int64 message:bytes = raw.Message; raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 in_msg:raw.message out_msgs:vector = raw.Transaction; -raw.transactions transactions:vector previous_transaction_id:internal.transactionId = raw.Transactions; +raw.transactions transactions:vector previous_transaction_id:internal.transactionId = raw.Transactions; testWallet.initialAccountState public_key:string = testWallet.InitialAccountState; testWallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = testWallet.AccountState; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index b77565e03e200204e3f53b0210c4bd17840045c8..a88c1fd14caecd8eb092b25e577fbd8cee5e5105 100644 GIT binary patch delta 353 zcmcbRI47C+(QJJy1}MB$vLb69?A`t)v= zU@l;q+`=fr^7h5Amdy)TGZ+~SH*>P*vzluOWxf>6M$+e!S(KU#G^CP&0c7UQV?sWR zK%1nW-Phzyfy+%^&nN2wl83qCsvK9$an7735oEJG^L$ft!A8N&0;w-@X4B!YZ953k zzd48J3!?zYk?i>eC7Jno#US?N0zMrBUZ7FMAn(MN0R0282&N5YEzBseFE8-j;Q;$^ x@&<8;S0|qkw_#+Q%qn3I@wtb@8y;|oOg2zc*c_p{gb6G@Nh1X!pP`Y#0RVYNd?Ww> delta 211 zcmbP}d?Atd(QJJy1}M3!YayK z6UMjs3~L4>qyA=7_I%dO9=tCY1;EPlA<9AQ$qxKFB0|}zmBl6bMXB*AnMJ9|KvpHg z{&qE%_5R- dc)(gGH>fFWR#98R1QwrsMk58Hz(g~H0|2BuL!STu diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index 3867779643..a46f3d3c7b 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -5,6 +5,7 @@ if (NOT OPENSSL_FOUND) endif() set(TONLIB_SOURCE + tonlib/CellString.cpp tonlib/Client.cpp tonlib/Config.cpp tonlib/ExtClient.cpp @@ -12,6 +13,7 @@ set(TONLIB_SOURCE tonlib/ExtClientOutbound.cpp tonlib/GenericAccount.cpp tonlib/KeyStorage.cpp + tonlib/KeyValue.cpp tonlib/LastBlock.cpp tonlib/LastBlockStorage.cpp tonlib/Logging.cpp @@ -21,6 +23,7 @@ set(TONLIB_SOURCE tonlib/utils.cpp tonlib/Wallet.cpp + tonlib/CellString.h tonlib/Client.h tonlib/Config.h tonlib/ExtClient.h @@ -28,6 +31,7 @@ set(TONLIB_SOURCE tonlib/ExtClientOutbound.h tonlib/GenericAccount.h tonlib/KeyStorage.h + tonlib/KeyValue.h tonlib/LastBlock.h tonlib/LastBlockStorage.h tonlib/Logging.h diff --git a/tonlib/TonlibConfig.cmake b/tonlib/TonlibConfig.cmake new file mode 100644 index 0000000000..5f3a33ebf7 --- /dev/null +++ b/tonlib/TonlibConfig.cmake @@ -0,0 +1,3 @@ +include(CMakeFindDependencyMacro) +#TODO: write all external dependencies +include("${CMAKE_CURRENT_LIST_DIR}/TonlibTargets.cmake") diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index 63a52fae39..d6f6d92ce1 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -28,6 +28,7 @@ #include "vm/boc.h" #include "vm/cells/MerkleProof.h" +#include "tonlib/CellString.h" #include "tonlib/utils.h" #include "tonlib/TestGiver.h" #include "tonlib/TestWallet.h" @@ -53,6 +54,20 @@ #include "tonlib/keys/Mnemonic.h" #include "tonlib/keys/SimpleEncryption.h" +TEST(Tonlib, CellString) { + for (unsigned size : + {0, 1, 7, 8, 35, 127, 128, 255, 256, (int)vm::CellString::max_bytes - 1, (int)vm::CellString::max_bytes}) { + auto str = td::rand_string('a', 'z', size); + for (unsigned head : {0, 1, 7, 8, 127, 35 * 8, 127 * 8, 1023, 1024}) { + vm::CellBuilder cb; + vm::CellString::store(cb, str, head).ensure(); + auto cs = vm::load_cell_slice(cb.finalize()); + auto got_str = vm::CellString::load(cs, head).move_as_ok(); + ASSERT_EQ(str, got_str); + } + } +}; + using namespace tonlib; std::string current_dir() { @@ -268,20 +283,23 @@ static auto sync_send = [](auto &client, auto query) { TEST(Tonlib, InitClose) { using tonlib_api::make_object; auto cfg = [](auto str) { return make_object(str, "", false, false); }; + auto dir = [](auto str) { return make_object(str); }; { Client client; sync_send(client, make_object()).ensure(); - sync_send(client, make_object(make_object(nullptr, "."))).ensure_error(); + sync_send(client, make_object(make_object(nullptr, dir(".")))) + .ensure_error(); } { Client client; sync_send(client, make_object(nullptr)).ensure_error(); - sync_send(client, make_object(make_object(cfg("fdajkfldsjkafld"), "."))) + sync_send(client, make_object(make_object(cfg("fdajkfldsjkafld"), dir(".")))) + .ensure_error(); + sync_send(client, make_object(make_object(nullptr, dir("fdhskfds")))) .ensure_error(); - sync_send(client, make_object(make_object(nullptr, "fdhskfds"))) + sync_send(client, make_object(make_object(nullptr, dir(".")))).ensure(); + sync_send(client, make_object(make_object(nullptr, dir(".")))) .ensure_error(); - sync_send(client, make_object(make_object(nullptr, "."))).ensure(); - sync_send(client, make_object(make_object(nullptr, "."))).ensure_error(); td::Slice bad_config = R"abc( { @@ -294,7 +312,8 @@ TEST(Tonlib, InitClose) { sync_send(client, make_object()).ensure_error(); sync_send(client, make_object()).ensure(); sync_send(client, make_object()).ensure_error(); - sync_send(client, make_object(make_object(nullptr, "."))).ensure_error(); + sync_send(client, make_object(make_object(nullptr, dir(".")))) + .ensure_error(); } } @@ -389,7 +408,9 @@ TEST(Tonlib, ParseAddres) { Client client; // init - sync_send(client, make_object(make_object(nullptr, "."))).ensure(); + sync_send(client, make_object( + make_object(nullptr, make_object(".")))) + .ensure(); sync_send(client, make_object("hello")).ensure_error(); auto addr = @@ -409,7 +430,9 @@ TEST(Tonlib, KeysApi) { Client client; // init - sync_send(client, make_object(make_object(nullptr, "."))).ensure(); + sync_send(client, make_object( + make_object(nullptr, make_object(".")))) + .ensure(); auto local_password = td::SecureString("local password"); auto mnemonic_password = td::SecureString("mnemonic password"); { diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 0425f53757..6ac47921df 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -197,7 +197,8 @@ int main(int argc, char* argv[]) { Client client; { sync_send(client, make_object(make_object( - make_object(global_config_str, "", false, false), "."))) + make_object(global_config_str, "", false, false), + make_object(".")))) .ensure(); } //dump_transaction_history(client, get_test_giver_address(client)); @@ -211,7 +212,8 @@ int main(int argc, char* argv[]) { { // init sync_send(client, make_object(make_object( - make_object(global_config_str, "", false, false), "."))) + make_object(global_config_str, "", false, false), + make_object(".")))) .ensure(); auto key = sync_send(client, make_object( diff --git a/tonlib/tonlib/CellString.cpp b/tonlib/tonlib/CellString.cpp new file mode 100644 index 0000000000..ad2cbf5f8f --- /dev/null +++ b/tonlib/tonlib/CellString.cpp @@ -0,0 +1,64 @@ +#include "CellString.h" +#include "td/utils/misc.h" + +#include "vm/cells/CellSlice.h" + +namespace vm { +td::Status CellString::store(CellBuilder &cb, td::Slice slice, unsigned int top_bits) { + td::uint32 size = td::narrow_cast(slice.size() * 8); + return store(cb, td::BitSlice(slice.ubegin(), size), top_bits); +} + +td::Status CellString::store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits) { + if (slice.size() > max_bytes * 8) { + return td::Status::Error("String is too long (1)"); + } + unsigned int head = td::min(slice.size(), td::min(cb.remaining_bits(), top_bits)) / 8 * 8; + auto max_bits = vm::Cell::max_bits / 8 * 8; + auto depth = 1 + (slice.size() - head + max_bits - 1) / max_bits; + if (depth > max_chain_length) { + return td::Status::Error("String is too long (2)"); + } + cb.append_bitslice(slice.subslice(0, head)); + slice.advance(head); + if (slice.size() == 0) { + return td::Status::OK(); + } + CellBuilder child_cb; + store(child_cb, std::move(slice)); + cb.store_ref(child_cb.finalize()); + return td::Status::OK(); +} + +template +void CellString::for_each(F &&f, CellSlice &cs, unsigned int top_bits) { + unsigned int head = td::min(cs.size(), top_bits); + f(cs.prefetch_bits(head)); + if (!cs.have_refs()) { + return; + } + auto ref = cs.prefetch_ref(); + while (true) { + auto cs = vm::load_cell_slice(ref); + f(cs.prefetch_bits(cs.size())); + if (!cs.have_refs()) { + return; + } + ref = cs.prefetch_ref(); + } +} + +td::Result CellString::load(CellSlice &cs, unsigned int top_bits) { + unsigned int size = 0; + for_each([&](auto slice) { size += slice.size(); }, cs, top_bits); + if (size % 8 != 0) { + return td::Status::Error("Size is not divisible by 8"); + } + std::string res(size / 8, 0); + + td::BitPtr to(td::MutableSlice(res).ubegin()); + for_each([&](auto slice) { to.concat(slice); }, cs, top_bits); + CHECK(to.offs == (int)size); + return res; +} +} // namespace vm diff --git a/tonlib/tonlib/CellString.h b/tonlib/tonlib/CellString.h new file mode 100644 index 0000000000..89c933d876 --- /dev/null +++ b/tonlib/tonlib/CellString.h @@ -0,0 +1,22 @@ +#pragma once + +#include "td/utils/Status.h" + +#include "vm/cells/CellBuilder.h" + +namespace vm { +class CellString { + public: + static constexpr unsigned int max_bytes = 1024; + static constexpr unsigned int max_chain_length = 16; + + static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits); + static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits); + static td::Result load(CellSlice &cs, unsigned int top_bits = Cell::max_bits); + + private: + template + static void for_each(F &&f, CellSlice &cs, unsigned int top_bits = Cell::max_bits); +}; + +} // namespace vm diff --git a/tonlib/tonlib/KeyStorage.cpp b/tonlib/tonlib/KeyStorage.cpp index 6137c0e347..8703a3ef41 100644 --- a/tonlib/tonlib/KeyStorage.cpp +++ b/tonlib/tonlib/KeyStorage.cpp @@ -27,29 +27,18 @@ #include "td/utils/crypto.h" namespace tonlib { +namespace { std::string to_file_name_old(const KeyStorage::Key &key) { return td::buffer_to_hex(key.public_key); } -std::string KeyStorage::to_file_path_old(const Key &key) { - return directory_ + TD_DIR_SLASH + to_file_name_old(key); -} - std::string to_file_name(const KeyStorage::Key &key) { return td::buffer_to_hex(td::sha512(key.secret.as_slice()).substr(0, 32)); } +} // namespace -std::string KeyStorage::to_file_path(const Key &key) { - return directory_ + TD_DIR_SLASH + to_file_name(key); -} -td::Status KeyStorage::set_directory(std::string directory) { - TRY_RESULT(path, td::realpath(directory)); - TRY_RESULT(stat, td::stat(path)); - if (!stat.is_dir_) { - return td::Status::Error("not a directory"); - } - directory_ = std::move(path); - return td::Status::OK(); +void KeyStorage::set_key_value(std::shared_ptr kv) { + kv_ = std::move(kv); } td::Result KeyStorage::save_key(const DecryptedKey &decrypted_key, td::Slice local_password) { @@ -58,17 +47,7 @@ td::Result KeyStorage::save_key(const DecryptedKey &decrypted_k Key res; res.public_key = encrypted_key.public_key.as_octet_string(); res.secret = std::move(encrypted_key.secret); - - auto size = encrypted_key.encrypted_data.size(); - - LOG(ERROR) << "SAVE " << to_file_name(res); - TRY_RESULT(to_file, td::FileFd::open(to_file_path(res), td::FileFd::CreateNew | td::FileFd::Write)); - TRY_RESULT(written, to_file.write(encrypted_key.encrypted_data)); - if (written != static_cast(size)) { - return td::Status::Error(PSLICE() << "Failed to write file: written " << written << " bytes instead of " << size); - } - to_file.close(); - + TRY_STATUS(kv_->set(to_file_name(res), encrypted_key.encrypted_data)); return std::move(res); } @@ -83,13 +62,14 @@ td::Result KeyStorage::create_new_key(td::Slice local_password, } td::Result KeyStorage::export_decrypted_key(InputKey input_key) { - auto r_encrypted_data = td::read_file_secure(to_file_path(input_key.key)); + auto r_encrypted_data = kv_->get(to_file_name(input_key.key)); if (r_encrypted_data.is_error()) { - r_encrypted_data = td::read_file_secure(to_file_path_old(input_key.key)); + r_encrypted_data = kv_->get(to_file_name_old(input_key.key)); if (r_encrypted_data.is_ok()) { - LOG(WARNING) << "Restore private from deprecated location " << to_file_path_old(input_key.key) << " --> " - << to_file_path(input_key.key); - td::rename(to_file_path_old(input_key.key), to_file_path(input_key.key)).ignore(); + LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> " + << to_file_name(input_key.key); + TRY_STATUS(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok())); + kv_->erase(to_file_name_old(input_key.key)).ignore(); } } TRY_RESULT(encrypted_data, std::move(r_encrypted_data)); @@ -113,7 +93,7 @@ td::Result KeyStorage::load_private_key(InputKey input_k } td::Status KeyStorage::delete_key(const Key &key) { - return td::unlink(to_file_path(key)); + return kv_->erase(to_file_name(key)); } td::Result KeyStorage::import_key(td::Slice local_password, td::Slice mnemonic_password, @@ -140,7 +120,8 @@ td::Result KeyStorage::change_local_password(InputKey input_key Key res; res.public_key = std::move(input_key.key.public_key); res.secret = std::move(new_secret); - TRY_STATUS(td::copy_file(to_file_path(input_key.key), to_file_path(res))); + TRY_RESULT(value, kv_->get(to_file_name(input_key.key))); + TRY_STATUS(kv_->add(to_file_name(res), value)); return std::move(res); } diff --git a/tonlib/tonlib/KeyStorage.h b/tonlib/tonlib/KeyStorage.h index f7982805ef..fbde2a3252 100644 --- a/tonlib/tonlib/KeyStorage.h +++ b/tonlib/tonlib/KeyStorage.h @@ -21,6 +21,8 @@ #include "td/utils/Status.h" #include "td/utils/SharedSlice.h" +#include "KeyValue.h" + #include namespace tonlib { @@ -48,7 +50,7 @@ class KeyStorage { td::SecureString private_key; }; - td::Status set_directory(std::string directory); + void set_key_value(std::shared_ptr kv); td::Result create_new_key(td::Slice local_password, td::Slice key_password, td::Slice entropy); @@ -67,12 +69,9 @@ class KeyStorage { td::Result load_private_key(InputKey input_key); private: - std::string directory_; + std::shared_ptr kv_; td::Result save_key(const DecryptedKey& mnemonic, td::Slice local_password); td::Result export_decrypted_key(InputKey input_key); - - std::string to_file_path(const Key& key); - std::string to_file_path_old(const Key& key); }; } // namespace tonlib diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp new file mode 100644 index 0000000000..fa2f8ae3ea --- /dev/null +++ b/tonlib/tonlib/KeyValue.cpp @@ -0,0 +1,103 @@ +#include "KeyValue.h" + +#include "td/utils/filesystem.h" +#include "td/utils/port/path.h" + +#include +#include + +namespace tonlib { +namespace detail { +class KeyValueDir : public KeyValue { + public: + static td::Result> create(td::CSlice directory) { + TRY_RESULT(path, td::realpath(directory)); + TRY_RESULT(stat, td::stat(path)); + if (!stat.is_dir_) { + return td::Status::Error("not a directory"); + } + return td::make_unique(path); + } + + KeyValueDir(std::string directory) : directory_(std::move(directory)) { + } + + td::Status add(td::Slice key, td::Slice value) override { + auto path = to_file_path(key.str()); + if (td::stat(path).is_ok()) { + return td::Status::Error(PSLICE() << "File " << path << "already exists"); + } + return td::atomic_write_file(path, value); + } + + td::Status set(td::Slice key, td::Slice value) override { + return td::atomic_write_file(to_file_path(key.str()), value); + } + + td::Result get(td::Slice key) override { + return td::read_file_secure(to_file_path(key.str())); + } + + td::Status erase(td::Slice key) override { + return td::unlink(key.str()); + } + + private: + std::string directory_; + + std::string to_file_path(std::string key) { + return directory_ + TD_DIR_SLASH + key; + } +}; + +class KeyValueInmemory : public KeyValue { + public: + td::Status add(td::Slice key, td::Slice value) override { + auto res = map_.insert(std::make_pair(key.str(), td::SecureString(value))); + if (!res.second) { + return td::Status::Error(PSLICE() << "Add failed: value with key=`" << key << "` already exists"); + } + return td::Status::OK(); + } + + td::Status set(td::Slice key, td::Slice value) override { + map_[key.str()] = td::SecureString(value); + return td::Status::OK(); + } + td::Result get(td::Slice key) override { + auto it = map_.find(key); + if (it == map_.end()) { + return td::Status::Error("Unknown key"); + } + return it->second.copy(); + } + static td::Result> create() { + return td::make_unique(); + } + td::Status erase(td::Slice key) override { + auto it = map_.find(key); + if (it == map_.end()) { + return td::Status::Error("Unknown key"); + } + map_.erase(it); + return td::Status::OK(); + } + + private: + class Cmp : public std::less<> { + public: + using is_transparent = void; + }; + std::map map_; +}; +} // namespace detail + +td::Result> KeyValue::create_dir(td::CSlice dir) { + TRY_RESULT(res, detail::KeyValueDir::create(dir.str())); + return std::move(res); +} +td::Result> KeyValue::create_inmemory() { + TRY_RESULT(res, detail::KeyValueInmemory::create()); + return std::move(res); +} +} // namespace tonlib diff --git a/tonlib/tonlib/KeyValue.h b/tonlib/tonlib/KeyValue.h new file mode 100644 index 0000000000..6ccd929f98 --- /dev/null +++ b/tonlib/tonlib/KeyValue.h @@ -0,0 +1,18 @@ +#pragma once +#include "td/utils/SharedSlice.h" +#include "td/utils/Slice.h" +#include "td/utils/Status.h" + +namespace tonlib { +class KeyValue { + public: + virtual ~KeyValue() = default; + virtual td::Status add(td::Slice key, td::Slice value) = 0; + virtual td::Status set(td::Slice key, td::Slice value) = 0; + virtual td::Status erase(td::Slice key) = 0; + virtual td::Result get(td::Slice key) = 0; + + static td::Result> create_dir(td::CSlice dir); + static td::Result> create_inmemory(); +}; +} // namespace tonlib diff --git a/tonlib/tonlib/LastBlockStorage.cpp b/tonlib/tonlib/LastBlockStorage.cpp index 684b2c056c..f25632c0ba 100644 --- a/tonlib/tonlib/LastBlockStorage.cpp +++ b/tonlib/tonlib/LastBlockStorage.cpp @@ -25,22 +25,18 @@ namespace tonlib { -td::Status LastBlockStorage::set_directory(std::string directory) { - TRY_RESULT(path, td::realpath(directory)); - TRY_RESULT(stat, td::stat(path)); - if (!stat.is_dir_) { - return td::Status::Error("not a directory"); - } - directory_ = std::move(path); - return td::Status::OK(); +void LastBlockStorage::set_key_value(std::shared_ptr kv) { + kv_ = std::move(kv); } -std::string LastBlockStorage::get_file_name(td::Slice name) { - return directory_ + TD_DIR_SLASH + td::buffer_to_hex(name) + ".blkstate"; +namespace { +std::string get_file_name(td::Slice name) { + return td::buffer_to_hex(name) + ".blkstate"; } +} // namespace td::Result LastBlockStorage::get_state(td::Slice name) { - TRY_RESULT(data, td::read_file(get_file_name(name))); + TRY_RESULT(data, kv_->get(get_file_name(name))); if (data.size() < 8) { return td::Status::Error("too short"); } @@ -57,6 +53,6 @@ void LastBlockStorage::save_state(td::Slice name, LastBlockState state) { std::string y(x.size() + 8, 0); td::MutableSlice(y).substr(8).copy_from(x); td::as(td::MutableSlice(y).data()) = td::crc64(x); - td::atomic_write_file(get_file_name(name), y); + kv_->set(get_file_name(name), y); } } // namespace tonlib diff --git a/tonlib/tonlib/LastBlockStorage.h b/tonlib/tonlib/LastBlockStorage.h index d8cbd3fcd1..df5dd12c9e 100644 --- a/tonlib/tonlib/LastBlockStorage.h +++ b/tonlib/tonlib/LastBlockStorage.h @@ -20,15 +20,16 @@ #include "tonlib/LastBlock.h" +#include "tonlib/KeyValue.h" + namespace tonlib { class LastBlockStorage { public: - td::Status set_directory(std::string directory); + void set_key_value(std::shared_ptr kv); td::Result get_state(td::Slice name); void save_state(td::Slice name, LastBlockState state); private: - std::string directory_; - std::string get_file_name(td::Slice name); + std::shared_ptr kv_; }; } // namespace tonlib diff --git a/tonlib/tonlib/TestGiver.cpp b/tonlib/tonlib/TestGiver.cpp index 50c68571ce..17c36a5534 100644 --- a/tonlib/tonlib/TestGiver.cpp +++ b/tonlib/tonlib/TestGiver.cpp @@ -34,7 +34,6 @@ vm::CellHash TestGiver::get_init_code_hash() { td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, const block::StdAddress& dest_address) { - CHECK(message.size() <= 124); td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; @@ -45,7 +44,9 @@ td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gr .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); + cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); + vm::CellString::store(cb, message, 35 * 8).ensure(); + auto message_inner = cb.finalize(); return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); } } // namespace tonlib diff --git a/tonlib/tonlib/TestGiver.h b/tonlib/tonlib/TestGiver.h index 6859be8ff0..a5d8b79c89 100644 --- a/tonlib/tonlib/TestGiver.h +++ b/tonlib/tonlib/TestGiver.h @@ -18,9 +18,11 @@ */ #pragma once #include "block/block.h" +#include "CellString.h" namespace tonlib { class TestGiver { public: + static constexpr unsigned max_message_size = vm::CellString::max_bytes; static const block::StdAddress& address(); static vm::CellHash get_init_code_hash(); static td::Ref make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, diff --git a/tonlib/tonlib/TestWallet.cpp b/tonlib/tonlib/TestWallet.cpp index 022eaca9db..dadb1748f7 100644 --- a/tonlib/tonlib/TestWallet.cpp +++ b/tonlib/tonlib/TestWallet.cpp @@ -40,7 +40,6 @@ td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::int64 gramms, td::Slice message, const block::StdAddress& dest_address) { - CHECK(message.size() <= 124); td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; @@ -50,7 +49,9 @@ td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); + cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); + vm::CellString::store(cb, message, 35 * 8).ensure(); + auto message_inner = cb.finalize(); td::int8 send_mode = 3; auto message_outer = vm::CellBuilder().store_long(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize(); diff --git a/tonlib/tonlib/TestWallet.h b/tonlib/tonlib/TestWallet.h index 921e3b3afe..28c97ce132 100644 --- a/tonlib/tonlib/TestWallet.h +++ b/tonlib/tonlib/TestWallet.h @@ -21,10 +21,12 @@ #include "vm/cells.h" #include "Ed25519.h" #include "block/block.h" +#include "CellString.h" namespace tonlib { class TestWallet { public: + static constexpr unsigned max_message_size = vm::CellString::max_bytes; static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key); static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key); static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index d0a9cba7d9..7a7e1d8d97 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -547,8 +547,21 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, if (!request.options_) { return td::Status::Error(400, "Field options must not be empty"); } - TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_)); - TRY_STATUS(last_block_storage_.set_directory(request.options_->keystore_directory_)); + if (!request.options_->keystore_type_) { + return td::Status::Error(400, "Field options.keystore_type must not be empty"); + } + + td::Result> r_kv; + downcast_call( + *request.options_->keystore_type_, + td::overloaded( + [&](tonlib_api::keyStoreTypeDirectory& directory) { r_kv = KeyValue::create_dir(directory.directory_); }, + [&](tonlib_api::keyStoreTypeInMemory& inmemory) { r_kv = KeyValue::create_inmemory(); })); + TRY_RESULT(kv, std::move(r_kv)); + kv_ = std::shared_ptr(kv.release()); + + key_storage_.set_key_value(kv_); + last_block_storage_.set_key_value(kv_); if (request.options_->config_) { TRY_STATUS(set_config(std::move(request.options_->config_))); } @@ -672,12 +685,11 @@ td::Result> to_raw_message_or_th body = vm::load_cell_slice_ref(message.body->prefetch_ref()); } std::string body_message; - if (body->size() % 8 == 0) { - body_message = std::string(body->size() / 8, 0); - body->prefetch_bytes(td::MutableSlice(body_message).ubegin(), body->size() / 8); - if (body_message.size() >= 4 && body_message[0] == 0 && body_message[1] == 0 && body_message[2] == 0 && - body_message[3] == 0) { - body_message = body_message.substr(4); + if (body->size() >= 32 && body->prefetch_long(32) == 0) { + body.write().fetch_long(32); + auto r_body_message = vm::CellString::load(body.write()); + if (r_body_message.is_ok()) { + body_message = r_body_message.move_as_ok(); } } @@ -955,7 +967,7 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ if (!request.private_key_) { return td::Status::Error(400, "Field private_key must not be empty"); } - if (request.message_.size() > 70) { + if (request.message_.size() > TestWallet::max_message_size) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); @@ -1033,7 +1045,7 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, if (!request.private_key_) { return td::Status::Error(400, "Field private_key must not be empty"); } - if (request.message_.size() > 70) { + if (request.message_.size() > Wallet::max_message_size) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(valid_until, td::narrow_cast_safe(request.valid_until_)); @@ -1092,7 +1104,7 @@ td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& reque if (!request.destination_) { return td::Status::Error(400, "Field destination must not be empty"); } - if (request.message_.size() > 70) { + if (request.message_.size() > TestGiver::max_message_size) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 9e7ecdafea..9006355133 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -24,6 +24,7 @@ #include "tonlib/ExtClient.h" #include "tonlib/ExtClientOutbound.h" #include "tonlib/KeyStorage.h" +#include "tonlib/KeyValue.h" #include "tonlib/LastBlockStorage.h" #include "td/actor/actor.h" @@ -55,6 +56,7 @@ class TonlibClient : public td::actor::Actor { td::actor::ActorId ext_client_outbound_; // KeyStorage + std::shared_ptr kv_; KeyStorage key_storage_; LastBlockStorage last_block_storage_; diff --git a/tonlib/tonlib/Wallet.cpp b/tonlib/tonlib/Wallet.cpp index 7546e8fd22..dbd12d2538 100644 --- a/tonlib/tonlib/Wallet.cpp +++ b/tonlib/tonlib/Wallet.cpp @@ -17,6 +17,7 @@ Copyright 2017-2019 Telegram Systems LLP */ #include "tonlib/Wallet.h" +#include "tonlib/CellString.h" #include "tonlib/GenericAccount.h" #include "tonlib/utils.h" @@ -45,7 +46,6 @@ td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& privat td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::uint32 valid_until, td::int64 gramms, td::Slice message, const block::StdAddress& dest_address) { - CHECK(message.size() <= 124); td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; @@ -55,7 +55,9 @@ td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); + cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); + vm::CellString::store(cb, message, 35 * 8).ensure(); + auto message_inner = cb.finalize(); td::int8 send_mode = 3; auto message_outer = vm::CellBuilder() .store_long(seqno, 32) diff --git a/tonlib/tonlib/Wallet.h b/tonlib/tonlib/Wallet.h index 2f2d8a8de1..e4ee5aeccc 100644 --- a/tonlib/tonlib/Wallet.h +++ b/tonlib/tonlib/Wallet.h @@ -21,10 +21,12 @@ #include "vm/cells.h" #include "Ed25519.h" #include "block/block.h" +#include "CellString.h" namespace tonlib { class Wallet { public: + static constexpr unsigned max_message_size = vm::CellString::max_bytes; static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key); static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key); static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, diff --git a/tonlib/tonlib/keys/DecryptedKey.cpp b/tonlib/tonlib/keys/DecryptedKey.cpp index 3161d904c7..746ca188c0 100644 --- a/tonlib/tonlib/keys/DecryptedKey.cpp +++ b/tonlib/tonlib/keys/DecryptedKey.cpp @@ -67,7 +67,8 @@ EncryptedKey DecryptedKey::encrypt(td::Slice local_password, td::Slice old_secre } td::SecureString encryption_secret(64); - pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", PBKDF_ITERATIONS, encryption_secret.as_mutable_slice()); + pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", EncryptedKey::PBKDF_ITERATIONS, + encryption_secret.as_mutable_slice()); std::vector mnemonic_words_copy; for (auto &w : mnemonic_words) { diff --git a/tonlib/tonlib/keys/EncryptedKey.h b/tonlib/tonlib/keys/EncryptedKey.h index 5a32c9f89f..56aa661531 100644 --- a/tonlib/tonlib/keys/EncryptedKey.h +++ b/tonlib/tonlib/keys/EncryptedKey.h @@ -25,9 +25,9 @@ #include namespace tonlib { -constexpr int PBKDF_ITERATIONS = 100000; struct DecryptedKey; struct EncryptedKey { + static constexpr int PBKDF_ITERATIONS = 100000; td::SecureString encrypted_data; td::Ed25519::PublicKey public_key; td::SecureString secret; diff --git a/tonlib/tonlib/keys/SimpleEncryption.cpp b/tonlib/tonlib/keys/SimpleEncryption.cpp index 760dda36b9..026a34292e 100644 --- a/tonlib/tonlib/keys/SimpleEncryption.cpp +++ b/tonlib/tonlib/keys/SimpleEncryption.cpp @@ -77,7 +77,7 @@ td::Result SimpleEncryption::decrypt_data(td::Slice encrypted_ return td::Status::Error("Failed to decrypt: data is too small"); } if (encrypted_data.size() % 16 != 0) { - return td::Status::Error("Failed to decrypt: data size is not divisible by 32"); + return td::Status::Error("Failed to decrypt: data size is not divisible by 16"); } auto data_hash = encrypted_data.substr(0, 32); encrypted_data = encrypted_data.substr(32); diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index ce8d5f481f..298027a0ac 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -27,6 +27,7 @@ class TonlibCli : public td::actor::Actor { std::string config; std::string name; std::string key_dir{"."}; + bool in_memory{false}; bool use_callbacks_for_network{false}; bool use_simple_wallet{false}; bool ignore_cache{false}; @@ -120,7 +121,14 @@ class TonlibCli : public td::actor::Actor { ? make_object(options_.config, options_.name, options_.use_callbacks_for_network, options_.ignore_cache) : nullptr; - send_query(make_object(make_object(std::move(config), options_.key_dir)), + + tonlib_api::object_ptr ks_type; + if (options_.in_memory) { + ks_type = make_object(); + } else { + ks_type = make_object(options_.key_dir); + } + send_query(make_object(make_object(std::move(config), std::move(ks_type))), [](auto r_ok) { LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); td::TerminalIO::out() << "Tonlib is inited\n"; @@ -824,6 +832,10 @@ int main(int argc, char* argv[]) { options.key_dir = arg.str(); return td::Status::OK(); }); + p.add_option('M', "in-memory", "store keys only in-memory", [&]() { + options.in_memory = true; + return td::Status::OK(); + }); p.add_option('E', "execute", "execute one command", [&](td::Slice arg) { options.one_shot = true; options.cmd = arg.str(); diff --git a/validator-engine-console/CMakeLists.txt b/validator-engine-console/CMakeLists.txt index 3fbfffb21e..42d60afb8e 100644 --- a/validator-engine-console/CMakeLists.txt +++ b/validator-engine-console/CMakeLists.txt @@ -5,3 +5,5 @@ add_executable (validator-engine-console validator-engine-console.cpp validator-engine-console-query.h ) target_link_libraries(validator-engine-console tdutils tdactor adnllite tl_api tl_lite_api tl-lite-utils ton_crypto ton_block terminal) +install(TARGETS validator-engine-console RUNTIME DESTINATION bin) + diff --git a/validator-engine/CMakeLists.txt b/validator-engine/CMakeLists.txt index d2f608d7d4..02040d5f03 100644 --- a/validator-engine/CMakeLists.txt +++ b/validator-engine/CMakeLists.txt @@ -14,3 +14,5 @@ add_executable(validator-engine ${VALIDATOR_ENGINE_SOURCE}) target_link_libraries(validator-engine overlay tdutils tdactor adnl tl_api dht rldp catchain validatorsession full-node validator ton_validator validator fift-lib memprof ${JEMALLOC_LIBRARIES}) + +install(TARGETS validator-engine RUNTIME DESTINATION bin) diff --git a/validator-session/validator-session-description.cpp b/validator-session/validator-session-description.cpp index 5f74df5ecf..f6e6f58216 100644 --- a/validator-session/validator-session-description.cpp +++ b/validator-session/validator-session-description.cpp @@ -55,7 +55,7 @@ ValidatorSessionDescriptionImpl::ValidatorSessionDescriptionImpl(ValidatorSessio pdata_temp_size_ = 1 << 30; pdata_temp_ = new td::uint8[pdata_temp_size_]; - pdata_perm_size_ = 1 << 30; + pdata_perm_size_ = 1ull << 30; pdata_perm_ptr_ = 0; for (auto &el : cache_) { From 2845f9a2cc22cfcfc0288f121a228d2b56df82fe Mon Sep 17 00:00:00 2001 From: ton Date: Mon, 30 Sep 2019 16:49:45 +0400 Subject: [PATCH 003/667] liteserver: bugfix liteserver/liteclient: fixed bug in proof validator: added stats smartcontracts: updates --- crypto/fift/lib/Fift.fif | 2 +- crypto/fift/words.cpp | 48 ++++++++++----- crypto/smartcont/gen-zerostate-test.fif | 8 +-- crypto/smartcont/gen-zerostate.fif | 8 +-- crypto/smartcont/highload-wallet.fif | 2 +- crypto/smartcont/new-highload-wallet.fif | 4 +- crypto/smartcont/new-pinger.fif | 2 +- crypto/smartcont/new-testgiver.fif | 2 +- crypto/smartcont/new-wallet-v2.fif | 4 +- crypto/smartcont/new-wallet.fif | 4 +- crypto/smartcont/update-config-smc.fif | 14 ++--- crypto/smartcont/update-config.fif | 14 ++--- crypto/smartcont/update-elector-smc.fif | 14 ++--- crypto/smartcont/wallet-v2.fif | 2 +- crypto/smartcont/wallet.fif | 2 +- crypto/vm/cells/MerkleProof.cpp | 6 +- crypto/vm/cells/MerkleProof.h | 5 +- doc/FullNode-HOWTO | 2 +- doc/fiftbase.tex | 18 ++++-- lite-client/lite-client.cpp | 6 +- validator/db/filedb.cpp | 39 +++++++++++++ validator/db/filedb.hpp | 2 + validator/db/rootdb.cpp | 8 +++ validator/db/rootdb.hpp | 2 + validator/impl/liteserver.cpp | 60 +++++++------------ validator/impl/liteserver.hpp | 5 +- validator/interfaces/db.h | 2 + validator/manager.cpp | 43 ++++++++++---- validator/stats-merger.h | 74 ++++++++++++++++++++++++ validator/validator-options.hpp | 2 +- 30 files changed, 280 insertions(+), 124 deletions(-) create mode 100644 validator/stats-merger.h diff --git a/crypto/fift/lib/Fift.fif b/crypto/fift/lib/Fift.fif index 59cbcb713f..65fecb3116 100644 --- a/crypto/fift/lib/Fift.fif +++ b/crypto/fift/lib/Fift.fif @@ -25,7 +25,7 @@ { bl word 1 { -rot 2 'nop does swap 0 (create) } } :: 2=: { } : s>c -{ s>c hash } : shash +{ s>c hashB } : shash // to be more efficiently re-implemented in C++ in the future { dup 0< ' negate if } : abs { 2dup > ' swap if } : minmax diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 01dcf802c8..ea4744c80f 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -764,13 +764,17 @@ void interpret_string_to_bytes(vm::Stack& stack) { stack.push_bytes(stack.pop_string()); } -void interpret_bytes_hash(vm::Stack& stack) { +void interpret_bytes_hash(vm::Stack& stack, bool as_uint) { std::string str = stack.pop_bytes(); unsigned char buffer[32]; digest::hash_str(buffer, str.c_str(), str.size()); - td::RefInt256 x{true}; - x.write().import_bytes(buffer, 32, false); - stack.push_int(std::move(x)); + if (as_uint) { + td::RefInt256 x{true}; + x.write().import_bytes(buffer, 32, false); + stack.push_int(std::move(x)); + } else { + stack.push_bytes(std::string{(char*)buffer, 32}); + } } void interpret_empty(vm::Stack& stack) { @@ -892,11 +896,15 @@ void interpret_builder_remaining_bitrefs(vm::Stack& stack, int mode) { } } -void interpret_cell_hash(vm::Stack& stack) { +void interpret_cell_hash(vm::Stack& stack, bool as_uint) { auto cell = stack.pop_cell(); - td::RefInt256 hash{true}; - hash.write().import_bytes(cell->get_hash().as_slice().ubegin(), 32, false); - stack.push_int(std::move(hash)); + if (as_uint) { + td::RefInt256 hash{true}; + hash.write().import_bytes(cell->get_hash().as_slice().ubegin(), 32, false); + stack.push_int(std::move(hash)); + } else { + stack.push_bytes(cell->get_hash().as_slice().str()); + } } void interpret_store_ref(vm::Stack& stack) { @@ -959,7 +967,9 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) { unsigned n = stack.pop_smallint_range(127); auto cs = stack.pop_cellslice(); if (!cs->have(n * 8)) { - stack.push(std::move(cs)); + if (mode & 2) { + stack.push(std::move(cs)); + } stack.push_bool(false); if (!(mode & 4)) { throw IntError{"end of data while reading byte string from cell"}; @@ -970,7 +980,7 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) { if (mode & 2) { cs.write().fetch_bytes(tmp, n); } else { - cs.write().prefetch_bytes(tmp, n); + cs->prefetch_bytes(tmp, n); } std::string s{tmp, tmp + n}; if (mode & 1) { @@ -978,7 +988,9 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) { } else { stack.push_string(std::move(s)); } - stack.push(std::move(cs)); + if (mode & 2) { + stack.push(std::move(cs)); + } if (mode & 4) { stack.push_bool(true); } @@ -1009,13 +1021,15 @@ void interpret_cell_remaining(vm::Stack& stack) { void interpret_fetch_ref(vm::Stack& stack, int mode) { auto cs = stack.pop_cellslice(); if (!cs->have_refs(1)) { - stack.push(std::move(cs)); + if (mode & 2) { + stack.push(std::move(cs)); + } stack.push_bool(false); if (!(mode & 4)) { throw IntError{"end of data while reading reference from cell"}; } } else { - auto cell = (mode & 2) ? cs.write().fetch_ref() : cs.write().prefetch_ref(); + auto cell = (mode & 2) ? cs.write().fetch_ref() : cs->prefetch_ref(); if (mode & 2) { stack.push(std::move(cs)); } @@ -2474,7 +2488,9 @@ void init_words_common(Dictionary& d) { d.def_stack_word("B>Lu@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x12)); d.def_stack_word("B>Li@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x13)); d.def_stack_word("$>B ", interpret_string_to_bytes); - d.def_stack_word("Bhash ", interpret_bytes_hash); + d.def_stack_word("Bhash ", std::bind(interpret_bytes_hash, _1, true)); + d.def_stack_word("Bhashu ", std::bind(interpret_bytes_hash, _1, true)); + d.def_stack_word("BhashB ", std::bind(interpret_bytes_hash, _1, false)); // cell manipulation (create, write and modify cells) d.def_stack_word("B dup Bx. cr dup "basestate0" +suffix +".boc" tuck B>file ."(Initial basechain state saved to file " type .")" cr -Bhash dup =: basestate0_fhash +Bhashu dup =: basestate0_fhash ."file hash=" dup x. space 256 u>B dup B>base64url type cr "basestate0" +suffix +".fhash" B>file -hash dup =: basestate0_rhash +hashu dup =: basestate0_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr "basestate0" +suffix +".rhash" B>file @@ -227,10 +227,10 @@ cr cr ."new state is:" cr dup B dup Bx. cr dup "zerostate" +suffix +".boc" tuck B>file ."(Initial masterchain state saved to file " type .")" cr -Bhash dup =: zerostate_fhash +Bhashu dup =: zerostate_fhash ."file hash=" dup x. space 256 u>B dup B>base64url type cr "zerostate" +suffix +".fhash" B>file -hash dup =: zerostate_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr +hashu dup =: zerostate_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr "zerostate" +suffix +".rhash" B>file basestate0_rhash ."Basestate0 root hash=" dup x. space 256 u>B B>base64url type cr basestate0_fhash ."Basestate0 file hash=" dup x. space 256 u>B B>base64url type cr diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index f5f9aac39e..43ba07b156 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -15,10 +15,10 @@ cr ."initial basechain state is:" cr dup B dup Bx. cr dup "basestate0" +suffix +".boc" tuck B>file ."(Initial basechain state saved to file " type .")" cr -Bhash dup =: basestate0_fhash +Bhashu dup =: basestate0_fhash ."file hash=" dup x. space 256 u>B dup B>base64url type cr "basestate0" +suffix +".fhash" B>file -hash dup =: basestate0_rhash +hashu dup =: basestate0_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr "basestate0" +suffix +".rhash" B>file @@ -231,10 +231,10 @@ cr cr ."new state is:" cr dup B dup Bx. cr dup "zerostate" +suffix +".boc" tuck B>file ."(Initial masterchain state saved to file " type .")" cr -Bhash dup =: zerostate_fhash +Bhashu dup =: zerostate_fhash ."file hash= " dup X. space 256 u>B dup B>base64url type cr "zerostate" +suffix +".fhash" B>file -hash dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr +hashu dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr "zerostate" +suffix +".rhash" B>file basestate0_rhash ."Basestate0 root hash= " dup X. space 256 u>B B>base64url type cr basestate0_fhash ."Basestate0 file hash= " dup X. space 256 u>B B>base64url type cr diff --git a/crypto/smartcont/highload-wallet.fif b/crypto/smartcont/highload-wallet.fif index 58ebda065b..ab69fdf368 100644 --- a/crypto/smartcont/highload-wallet.fif +++ b/crypto/smartcont/highload-wallet.fif @@ -59,7 +59,7 @@ order-file include // create external message dup ."signing message: " dup ."resulting external message: " // data null // no libraries // create StateInit dup ."StateInit: " dup ."signing message: " dup ."External message for initialization is " B dup Bx. cr diff --git a/crypto/smartcont/new-pinger.fif b/crypto/smartcont/new-pinger.fif index b833268889..646bfdf68c 100644 --- a/crypto/smartcont/new-pinger.fif +++ b/crypto/smartcont/new-pinger.fif @@ -41,7 +41,7 @@ def? $3 { @' $3 } { "new-pinger" } cond constant file-base // no libraries // create StateInit dup ."StateInit: " // create StateInit dup ."StateInit: " // data null // no libraries // create StateInit dup ."StateInit: " dup ."signing message: " dup ."External message for initialization is " B dup Bx. cr diff --git a/crypto/smartcont/new-wallet.fif b/crypto/smartcont/new-wallet.fif index c1ad403ca5..9bdb0bdb38 100644 --- a/crypto/smartcont/new-wallet.fif +++ b/crypto/smartcont/new-wallet.fif @@ -47,14 +47,14 @@ null // no libraries // Libs{ x{ABACABADABACABA} drop x{AAAA} s>c public_lib x{1234} x{5678} |_ s>c public_lib }Libs // create StateInit dup ."StateInit: " dup ."signing message: " dup ."External message for initialization is " B dup Bx. cr diff --git a/crypto/smartcont/update-config-smc.fif b/crypto/smartcont/update-config-smc.fif index 3840e128f8..22f01f0456 100644 --- a/crypto/smartcont/update-config-smc.fif +++ b/crypto/smartcont/update-config-smc.fif @@ -6,7 +6,7 @@ ."with private key loaded from file .pk, " ."and saves it into .boc ('config-query.boc' by default)" cr 1 halt } : usage -def? $# { @' $# dup 2 < swap 3 > or ' usage if } if +$# dup 2 < swap 3 > or ' usage if "config-master" constant file-base 0 constant seqno @@ -15,18 +15,16 @@ true constant bounce "config-code.fif" constant config-source 100 constant interval // valid for 100 seconds -def? $2 { - @' $1 =: file-base - @' $2 parse-int =: seqno -} if -def? $5 { @' $5 } { "config-query" } cond constant savefile +$1 =: file-base +$2 parse-int =: seqno +def? $3 { @' $3 } { "config-query" } cond constant savefile file-base +".addr" load-address 2dup 2constant config_addr ."Configuration smart contract address = " 2dup .addr cr 6 .Addr cr file-base +".pk" load-keypair nip constant config_pk -."Loading new configuration smart contract code from file file " config-source type cr +."Loading new configuration smart contract code from file " config-source type cr "Asm.fif" include config-source include dup dup ."signing message: " dup ."resulting external message: " .pk, " ."and saves it into .boc ('config-query.boc' by default)" cr 1 halt } : usage -def? $# { @' $# dup 4 < swap 5 > or ' usage if } if +$# dup 4 < swap 5 > or ' usage if "config-master" constant file-base 0 constant seqno @@ -15,12 +15,10 @@ true constant bounce "new-value.boc" constant boc-filename 100 constant interval // valid for 100 seconds -def? $4 { - @' $1 =: file-base - @' $2 parse-int =: seqno - @' $3 parse-int =: idx - @' $4 =: boc-filename -} if +$1 =: file-base +$2 parse-int =: seqno +$3 parse-int =: idx +$4 =: boc-filename def? $5 { @' $5 } { "config-query" } cond constant savefile file-base +".addr" load-address @@ -39,7 +37,7 @@ dup idx is-valid-config? not abort"not a valid value for chosen configuration pa // create a message dup ."signing message: " dup ."resulting external message: " .pk, " ."and saves it into .boc ('config-query.boc' by default)" cr 1 halt } : usage -def? $# { @' $# dup 2 < swap 3 > or ' usage if } if +$# dup 2 < swap 3 > or ' usage if "config-master" constant file-base 0 constant seqno @@ -15,18 +15,16 @@ true constant bounce "elector-code.fif" constant elector-source 100 constant interval // valid for 100 seconds -def? $2 { - @' $1 =: file-base - @' $2 parse-int =: seqno -} if -def? $5 { @' $5 } { "config-query" } cond constant savefile +$1 =: file-base +$2 parse-int =: seqno +def? $3 { @' $3 } { "config-query" } cond constant savefile file-base +".addr" load-address 2dup 2constant config_addr ."Configuration smart contract address = " 2dup .addr cr 6 .Addr cr file-base +".pk" load-keypair nip constant config_pk -."Loading new elector smart contract code from file file " elector-source type cr +."Loading new elector smart contract code from file " elector-source type cr "Asm.fif" include elector-source include dup dup ."signing message: " dup ."resulting external message: " dup ."resulting external message: " dup ."resulting external message: " root) usage_root = UsageCell::create(orig_root, usage_tree->root_ptr()); } -void MerkleProofBuilder::reset(Ref root) { +Ref MerkleProofBuilder::init(Ref root) { usage_tree = std::make_shared(); orig_root = std::move(root); usage_root = UsageCell::create(orig_root, usage_tree->root_ptr()); + return usage_root; } -void MerkleProofBuilder::clear() { +bool MerkleProofBuilder::clear() { usage_tree.reset(); orig_root.clear(); usage_root.clear(); + return true; } Ref MerkleProofBuilder::extract_proof() const { diff --git a/crypto/vm/cells/MerkleProof.h b/crypto/vm/cells/MerkleProof.h index 8dfb43a65c..5b3b8ebcef 100644 --- a/crypto/vm/cells/MerkleProof.h +++ b/crypto/vm/cells/MerkleProof.h @@ -51,9 +51,10 @@ class MerkleProofBuilder { Ref orig_root, usage_root; public: + MerkleProofBuilder() = default; MerkleProofBuilder(Ref root); - void reset(Ref root); - void clear(); + Ref init(Ref root); + bool clear(); Ref root() const { return usage_root; } diff --git a/doc/FullNode-HOWTO b/doc/FullNode-HOWTO index 843c680326..75432ffb87 100644 --- a/doc/FullNode-HOWTO +++ b/doc/FullNode-HOWTO @@ -107,7 +107,7 @@ Replace it with the following: } ], -`control.0.id` is set to the base64 identifier of the server's public key, and `control.0.allowed.0.id` is the base64 identifier of the client's public key. is the UDP port the server will listen to for console commands. +`control.0.id` is set to the base64 identifier of the server's public key, and `control.0.allowed.0.id` is the base64 identifier of the client's public key. is the TCP port the server will listen to for console commands. 7. Running the Full Node ~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/fiftbase.tex b/doc/fiftbase.tex index 35b21d9fa8..3347dd8626 100644 --- a/doc/fiftbase.tex +++ b/doc/fiftbase.tex @@ -1233,8 +1233,9 @@ \section*{Introduction} \mysubsection{Cell hash operations}\label{p:hash.ops} There are few words that operate on {\em Cell\/}s directly. The most important of them computes the {\em ($\Sha$-based) representation hash\/} of a given cell (cf.~\cite[3.1]{TVM}), which can be roughly described as the $\Sha$ hash of the cell's data bits concatenated with recursively computed hashes of the cells referred to by this cell: \begin{itemize} -\item {\tt hash} ($c$ -- $B$), computes the $\Sha$-based representation hash of {\em Cell\/}~$c$ (cf.~\cite[3.1]{TVM}), which unambiguously defines $c$ and all its descendants (provided there are no collisions for $\Sha$). The result is returned as a {\em Bytes\/} value consisting of exactly 32 bytes. -\item {\tt shash} ($s$ -- $B$), computes the $\Sha$-based representation hash of a {\em Slice\/} by first transforming it into a cell. Equivalent to {\tt s>c hash}. +\item {\tt hashB} ($c$ -- $B$), computes the $\Sha$-based representation hash of {\em Cell\/}~$c$ (cf.~\cite[3.1]{TVM}), which unambiguously defines $c$ and all its descendants (provided there are no collisions for $\Sha$). The result is returned as a {\em Bytes\/} value consisting of exactly 32 bytes. +\item {\tt hashu} ($c$ -- $x$), computes the $\Sha$-based representation hash of $c$ as above, but returns the result as a big-endian unsigned 256-bit {\em Integer}. +\item {\tt shash} ($s$ -- $B$), computes the $\Sha$-based representation hash of a {\em Slice\/} by first transforming it into a cell. Equivalent to {\tt s>c hashB}. \end{itemize} \mysubsection{Bag-of-cells operations}\label{p:boc.ops} @@ -1286,7 +1287,8 @@ \section*{Introduction} Additionally, there are several words for directly packing (serializing) data into {\em Bytes\/} values, and unpacking (deserializing) them afterwards. They can be combined with {\tt B>file} and {\tt file>B} to save data directly into binary files, and load them afterwards. \begin{itemize} \item {\tt Blen} ($B$ -- $x$), returns the length of a {\em Bytes\/} value~$B$ in bytes. -\item {\tt Bhash} ($B$ -- $B'$), computes the $\Sha$ hash of a {\em Bytes\/} value. The hash is returned as a 32-byte {\em Bytes\/} value. +\item {\tt BhashB} ($B$ -- $B'$), computes the $\Sha$ hash of a {\em Bytes\/} value. The hash is returned as a 32-byte {\em Bytes\/} value. +\item {\tt Bhashu} ($B$ -- $x$), computes the $\Sha$ hash of a {\em Bytes\/} value and returns the hash as an unsigned 256-bit big-endian integer. \item {\tt B=} ($B$ $B'$ -- $?$), checks whether two {\em Bytes\/} sequences are equal. \item {\tt Bcmp} ($B$ $B'$ -- $x$), lexicographically compares two {\em Bytes\/} sequences, and returns $-1$, $0$, or $1$, depending on the comparison result. \item {\tt B>i@} ($B$ $x$ -- $y$), deserializes the first $x/8$ bytes of a {\em Bytes} value~$B$ as a signed big-endian $x$-bit {\em Integer}~$y$. @@ -1970,7 +1972,9 @@ \section*{Introduction} \item {\tt B@?} ($s$ $x$ -- $B$ $-1$ or $0$), similar to {\tt B@}, but uses a flag to indicate failure instead of throwing an exception, cf.~\ptref{p:slice.ops}. \item {\tt B@?+} ($s$ $x$ -- $B$ $s'$ $-1$ or $s$ $0$), similar to {\tt B@+}, but uses a flag to indicate failure instead of throwing an exception, cf.~\ptref{p:slice.ops}. \item {\tt Bcmp} ($B$ $B'$ -- $x$), lexicographically compares two {\em Bytes\/} sequences, and returns $-1$, $0$, or $1$, depending on the comparison result, cf.~\ptref{p:bytes.ops}. -\item {\tt Bhash} ($B$ -- $B'$), computes the $\Sha$ hash of a {\em Bytes\/} value, cf.~\ptref{p:bytes.ops}. The hash is returned as a 32-byte {\em Bytes\/} value. +\item {\tt Bhash} ($B$ -- $x$), deprecated version of {\tt Bhashu}. Use {\tt Bhashu} or {\tt BhashB} instead. +\item {\tt BhashB} ($B$ -- $B'$), computes the $\Sha$ hash of a {\em Bytes\/} value, cf.~\ptref{p:bytes.ops}. The hash is returned as a 32-byte {\em Bytes\/} value. +\item {\tt Bhashu} ($B$ -- $x$), computes the $\Sha$ hash of a {\em Bytes\/} value, cf.~\ptref{p:bytes.ops}. The hash is returned as a big-endian unsigned 256-bit {\em Integer\/} value. \item {\tt Blen} ($B$ -- $x$), returns the length of a {\em Bytes\/} value~$B$ in bytes, cf.~\ptref{p:bytes.ops}. \item {\tt Bx.} ($B$ -- ), prints the hexadecimal representation of a {\em Bytes\/} value, cf.~\ptref{p:bytes.ops}. Each byte is represented by exactly two uppercase hexadecimal digits. \item {\tt \underline{B\{}$\langle{\textit{hex-digits}}\rangle$\}} ( -- $B$), pushes a {\em Bytes\/} literal containing data represented by an even number of hexadecimal digits, cf.~\ptref{p:bytes.ops}. @@ -2062,7 +2066,9 @@ \section*{Introduction} \item {\tt gasrunvmcode} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmcode}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ and with the gas limit set to $z$, thus executing code~$s$ in TVM. The original Fift stack (without $s$) is passed in its entirety as the initial stack of the new TVM instance. When TVM terminates, its resulting stack is used as the new Fift stack, with the exit code $x$ and the actually consumed gas $z'$ pushed at its top. If $x$ is non-zero, indicating that TVM has been terminated by an unhandled exception, the next stack entry from the top contains the parameter of this exception, and $x$ is the exception code. All other entries are removed from the stack in this case. \item {\tt gasrunvmdict} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmdict}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ and sets the gas limit to $z$ similarly to {\tt gasrunvmcode}, but also initializes the special register {\tt c3} with the same value, and pushes a zero into the initial TVM stack before the TVM execution begins. The actually consumed gas is returned as an {\em Integer\/} $z'$. In a typical application {\em Slice\/}~$s$ consists of a subroutine selection code that uses the top-of-stack {\em Integer\/} to select the subroutine to be executed, thus enabling the definition and execution of several mutually-recursive subroutines (cf.~\cite[4.6]{TVM} and~\ptref{p:asm.prog}). The selector equal to zero corresponds to the {\tt main()} subroutine in a large TVM program. \item {\tt halt} ($x$ -- ), quits to the operating system similarly to {\tt bye}, but uses {\em Integer\/} $x$ as the exit code, cf.~\ptref{p:exit.fift}. -\item {\tt hash} ($c$ -- $B$), computes the $\Sha$-based representation hash of {\em Cell\/}~$c$ (cf.~\cite[3.1]{TVM}), which unambiguously defines $c$ and all its descendants (provided there are no collisions for $\Sha$), cf.~\ptref{p:hash.ops}. The result is returned as a {\em Bytes\/} value consisting of exactly 32 bytes. +\item {\tt hash} ($c$ -- $x$), a deprecated version of {\tt hashu}. Use {\tt hashu} or {\tt hashB} instead. +\item {\tt hashB} ($c$ -- $B$), computes the $\Sha$-based representation hash of {\em Cell\/}~$c$ (cf.~\cite[3.1]{TVM}), which unambiguously defines $c$ and all its descendants (provided there are no collisions for $\Sha$), cf.~\ptref{p:hash.ops}. The result is returned as a {\em Bytes\/} value consisting of exactly 32 bytes. +\item {\tt hashu} ($c$ -- $x$), computes the $\Sha$-based representation hash of {\em Cell\/}~$c$ similarly to {\tt hashB}, but returns the result as a big-endian unsigned 256-bit {\em Integer}. \item {\tt hold} ($S$ $x$ -- $S'$), appends to {\em String\/}~$S$ one UTF-8 encoded character with Unicode codepoint~$x$. Equivalent to {\tt chr \$+}. \item {\tt hole} ( -- $p$), creates a new {\em Box\/}~$p$ that does not hold any value, cf.~\ptref{p:variables}. Equivalent to {\tt null box}. \item {\tt i,} ($b$ $x$ $y$ -- $b'$), appends the big-endian binary representation of a signed $y$-bit integer~$x$ to {\em Builder\/}~$b$, where $0\leq y\leq 257$, cf.~\ptref{p:builder.ops}. If there is not enough room in $b$ (i.e., if $b$ already contains more than $1023-y$ data bits), or if {\em Integer\/}~$x$ does not fit into $y$ bits, an exception is thrown. @@ -2120,7 +2126,7 @@ \section*{Introduction} \item {\tt sbits} ($s$ -- $x$), returns the number of data bits $x$ remaining in {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. \item {\tt second} ($t$ -- $x$), returns the second component of a {\em Tuple}, cf.~\ptref{p:tuples}. Equivalent to {\tt 1 []}. \item {\tt sgn} ($x$ -- $y$), computes the sign of an {\em Integer\/} $x$ (i.e., pushes $1$ if $x>0$, $-1$ if $x<0$, and $0$ if $x=0$), cf.~\ptref{p:int.comp}. Equivalent to {\tt 0 cmp}. -\item {\tt shash} ($s$ -- $B$), computes the $\Sha$-based representation hash of a {\em Slice\/} by first transforming it into a cell, cf.~\ptref{p:hash.ops}. Equivalent to {\tt s>c hash}. +\item {\tt shash} ($s$ -- $B$), computes the $\Sha$-based representation hash of a {\em Slice\/} by first transforming it into a cell, cf.~\ptref{p:hash.ops}. Equivalent to {\tt s>c hashB}. \item {\tt sign} ($S$ $x$ -- $S'$), appends a minus sign ``{\tt -}'' to {\em String\/}~$S$ if {\em Integer\/}~$x$ is negative. Otherwise leaves $S$ intact. \item {\tt single} ($x$ -- $t$), creates new singleton $t=(x)$, i.e., a one-element {\em Tuple}. Equivalent to {\tt 1 tuple}. \item {\tt skipspc} ( -- ), skips blank characters from the current input line until a non-blank or an end-of-line character is found. diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 0d446e6ec7..3817e987f8 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -914,8 +914,10 @@ bool TestNode::do_parse_line() { return parse_block_id_ext(blkid) && parse_account_addr(workchain, addr) && parse_lt(lt) && seekeoln() && get_one_transaction(blkid, workchain, addr, lt, true); } else if (word == "lasttrans" || word == "lasttransdump") { - return parse_account_addr(workchain, addr) && parse_lt(lt) && parse_hash(hash) && seekeoln() && - get_last_transactions(workchain, addr, lt, hash, 10, word == "lasttransdump"); + count = 10; + return parse_account_addr(workchain, addr) && parse_lt(lt) && parse_hash(hash) && + (seekeoln() || parse_uint32(count)) && seekeoln() && + get_last_transactions(workchain, addr, lt, hash, count, word == "lasttransdump"); } else if (word == "listblocktrans" || word == "listblocktransrev") { lt = 0; int mode = (word == "listblocktrans" ? 7 : 0x47); diff --git a/validator/db/filedb.cpp b/validator/db/filedb.cpp index 6e20998733..eea33b5ceb 100644 --- a/validator/db/filedb.cpp +++ b/validator/db/filedb.cpp @@ -353,6 +353,45 @@ FileDb::DbEntry::DbEntry(tl_object_ptr entry) , file_hash(entry->file_hash_) { } +void FileDb::prepare_stats(td::Promise>> promise) { + std::vector> rocksdb_stats; + auto stats = kv_->stats(); + if (stats.size() == 0) { + promise.set_value(std::move(rocksdb_stats)); + return; + } + size_t pos = 0; + while (pos < stats.size()) { + while (pos < stats.size() && + (stats[pos] == ' ' || stats[pos] == '\n' || stats[pos] == '\r' || stats[pos] == '\t')) { + pos++; + } + auto p = pos; + if (pos == stats.size()) { + break; + } + while (stats[pos] != '\n' && stats[pos] != '\r' && stats[pos] != ' ' && stats[pos] != '\t' && pos < stats.size()) { + pos++; + } + auto name = stats.substr(p, pos - p); + if (stats[pos] == '\n' || pos == stats.size()) { + rocksdb_stats.emplace_back(name, ""); + continue; + } + while (pos < stats.size() && + (stats[pos] == ' ' || stats[pos] == '\n' || stats[pos] == '\r' || stats[pos] == '\t')) { + pos++; + } + p = pos; + while (stats[pos] != '\n' && stats[pos] != '\r' && pos < stats.size()) { + pos++; + } + auto value = stats.substr(p, pos - p); + rocksdb_stats.emplace_back(name, value); + } + promise.set_value(std::move(rocksdb_stats)); +} + } // namespace validator } // namespace ton diff --git a/validator/db/filedb.hpp b/validator/db/filedb.hpp index ad8c03b591..5bb1ea8f0b 100644 --- a/validator/db/filedb.hpp +++ b/validator/db/filedb.hpp @@ -147,6 +147,8 @@ class FileDb : public td::actor::Actor { void load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, td::Promise promise); void check_file(RefId ref_id, td::Promise promise); + void prepare_stats(td::Promise>> promise); + void start_up() override; void alarm() override; diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index 9b487d7f18..fb59dfc460 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -24,6 +24,7 @@ #include "ton/ton-tl.hpp" #include "td/utils/overloaded.h" #include "common/checksum.h" +#include "validator/stats-merger.h" namespace ton { @@ -473,6 +474,13 @@ void RootDb::allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise p })); } +void RootDb::prepare_stats(td::Promise>> promise) { + auto merger = StatsMerger::create(std::move(promise)); + + td::actor::send_closure(file_db_, &FileDb::prepare_stats, merger.make_promise("filedb.")); + td::actor::send_closure(archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb.")); +} + } // namespace validator } // namespace ton diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index fc285fdd93..d03b590d80 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -110,6 +110,8 @@ class RootDb : public Db { void allow_block_gc(BlockIdExt block_id, td::Promise promise); void allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise promise); + void prepare_stats(td::Promise>> promise) override; + private: td::actor::ActorId validator_manager_; diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index b59a64fee2..2a01c99cc5 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -780,11 +780,10 @@ bool LiteQuery::make_state_root_proof(Ref& proof, Ref state_ CHECK(block_root.not_null() && state_root.not_null()); RootHash rhash{block_root->get_hash().bits()}; CHECK(rhash == blkid.root_hash); - auto usage_tree = std::make_shared(); - auto usage_cell = vm::UsageCell::create(block_root, usage_tree->root_ptr()); + vm::MerkleProofBuilder pb{std::move(block_root)}; block::gen::Block::Record blk; block::gen::BlockInfo::Record info; - if (!(tlb::unpack_cell(usage_cell, blk) && tlb::unpack_cell(blk.info, info))) { + if (!(tlb::unpack_cell(pb.root(), blk) && tlb::unpack_cell(blk.info, info))) { return fatal_error("cannot unpack block header"); } vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update}; @@ -797,8 +796,7 @@ bool LiteQuery::make_state_root_proof(Ref& proof, Ref state_ if (upd_hash.compare(state_hash, 256)) { return fatal_error("cannot construct Merkle proof for given masterchain state because of hash mismatch"); } - proof = vm::MerkleProof::generate(block_root, usage_tree.get()); - if (proof.is_null()) { + if (!pb.extract_proof_to(proof)) { return fatal_error("unknown error creating Merkle proof"); } return true; @@ -806,20 +804,17 @@ bool LiteQuery::make_state_root_proof(Ref& proof, Ref state_ bool LiteQuery::make_shard_info_proof(Ref& proof, vm::CellSlice& cs, ShardIdFull shard, ShardIdFull& true_shard, Ref& leaf, bool& found, bool exact) { - auto state_root = mc_state_->root_cell(); - auto usage_tree = std::make_shared(); - auto usage_cell = vm::UsageCell::create(state_root, usage_tree->root_ptr()); + vm::MerkleProofBuilder pb{mc_state_->root_cell()}; block::gen::ShardStateUnsplit::Record sstate; - if (!(tlb::unpack_cell(usage_cell, sstate))) { + if (!(tlb::unpack_cell(pb.root(), sstate))) { return fatal_error("cannot unpack state header"); } - auto shards_dict = block::ShardConfig::extract_shard_hashes_dict(usage_cell); + auto shards_dict = block::ShardConfig::extract_shard_hashes_dict(pb.root()); if (!shards_dict) { return fatal_error("cannot extract ShardHashes from last mc state"); } found = block::ShardConfig::get_shard_hash_raw_from(*shards_dict, cs, shard, true_shard, exact, &leaf); - proof = vm::MerkleProof::generate(state_root, usage_tree.get()); - if (proof.is_null()) { + if (!pb.extract_proof_to(proof)) { return fatal_error("unknown error creating Merkle proof"); } return true; @@ -921,11 +916,9 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) { if (!make_state_root_proof(proof1)) { return; } - auto state_root = state_->root_cell(); - auto usage_tree = std::make_shared(); - auto usage_cell = vm::UsageCell::create(state_root, usage_tree->root_ptr()); + vm::MerkleProofBuilder pb{state_->root_cell()}; block::gen::ShardStateUnsplit::Record sstate; - if (!(tlb::unpack_cell(usage_cell, sstate))) { + if (!tlb::unpack_cell(pb.root(), sstate)) { fatal_error("cannot unpack state header"); return; } @@ -935,10 +928,7 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) { if (acc_csr.not_null()) { acc_root = acc_csr->prefetch_ref(); } - auto proof2 = vm::MerkleProof::generate(state_root, usage_tree.get()); - usage_tree.reset(); - usage_cell.clear(); - auto proof = vm::std_boc_serialize_multi({std::move(proof1), std::move(proof2)}); + auto proof = vm::std_boc_serialize_multi({std::move(proof1), pb.extract_proof()}); if (proof.is_error()) { fatal_error(proof.move_as_error()); return; @@ -962,23 +952,18 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) { void LiteQuery::continue_getOneTransaction() { LOG(INFO) << "completing getOneTransaction() query"; CHECK(block_.not_null()); - auto block_root = block_->root_cell(); - auto usage_tree = std::make_shared(); - auto usage_cell = vm::UsageCell::create(block_root, usage_tree->root_ptr()); - auto trans_res = block::get_block_transaction(block_root, acc_workchain_, acc_addr_, trans_lt_); + vm::MerkleProofBuilder pb{block_->root_cell()}; + auto trans_res = block::get_block_transaction(pb.root(), acc_workchain_, acc_addr_, trans_lt_); if (trans_res.is_error()) { fatal_error(trans_res.move_as_error()); return; } auto trans_root = trans_res.move_as_ok(); - auto proof = vm::MerkleProof::generate(block_root, usage_tree.get()); - auto proof_boc = vm::std_boc_serialize(std::move(proof)); + auto proof_boc = pb.extract_proof_boc(); if (proof_boc.is_error()) { fatal_error(proof_boc.move_as_error()); return; } - usage_tree.reset(); - usage_cell.clear(); td::BufferSlice data; if (trans_root.not_null()) { auto res = vm::std_boc_serialize(std::move(trans_root)); @@ -999,10 +984,13 @@ void LiteQuery::perform_getTransactions(WorkchainId workchain, StdSmcAddress add unsigned count) { LOG(INFO) << "started a getTransactions(" << workchain << ", " << addr.to_hex() << ", " << lt << ", " << hash.to_hex() << ", " << count << ") liteserver query"; - if (count > 10) { + count = std::min(count, (unsigned)max_transaction_count); + /* + if (count > max_transaction_count) { fatal_error("cannot fetch more than 10 preceding transactions at one time"); return; } + */ if (workchain == ton::workchainInvalid) { fatal_error("invalid workchain specified"); return; @@ -1355,14 +1343,11 @@ void LiteQuery::finish_listBlockTransactions(int mode, int req_count) { CHECK(block_root.not_null()); RootHash rhash{block_root->get_hash().bits()}; CHECK(rhash == base_blk_id_.root_hash); - Ref usage_cell; - std::shared_ptr usage_tree; + vm::MerkleProofBuilder pb; + auto virt_root = block_root; if (mode & 32) { // proof requested - usage_tree = std::make_shared(); - usage_cell = vm::UsageCell::create(block_root, usage_tree->root_ptr()); - } else { - usage_cell = block_root; + virt_root = pb.init(std::move(virt_root)); } if ((mode & 192) == 64) { // reverse order, no starting point acc_addr_.set_ones(); @@ -1374,7 +1359,7 @@ void LiteQuery::finish_listBlockTransactions(int mode, int req_count) { try { block::gen::Block::Record blk; block::gen::BlockExtra::Record extra; - if (!(tlb::unpack_cell(usage_cell, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) { + if (!(tlb::unpack_cell(virt_root, blk) && tlb::unpack_cell(std::move(blk.extra), extra))) { fatal_error("cannot find account transaction data in block "s + base_blk_id_.to_str()); return; } @@ -1433,8 +1418,7 @@ void LiteQuery::finish_listBlockTransactions(int mode, int req_count) { td::BufferSlice proof_data; if (mode & 32) { // create proof - auto proof = vm::MerkleProof::generate(block_root, usage_tree.get()); - auto proof_boc = vm::std_boc_serialize(std::move(proof)); + auto proof_boc = pb.extract_proof_boc(); if (proof_boc.is_error()) { fatal_error(proof_boc.move_as_error()); return; diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 41077b2d22..580e086b4d 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -57,7 +57,10 @@ class LiteQuery : public td::actor::Actor { std::unique_ptr chain_; public: - enum { default_timeout_msec = 4500 }; // 4.5 seconds + enum { + default_timeout_msec = 4500, // 4.5 seconds + max_transaction_count = 16 // fetch at most 16 transactions in one query + }; enum { ls_version = 0x101, ls_capabilities = 3 diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index c9ec94f352..92b28e1285 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -93,6 +93,8 @@ class Db : public td::actor::Actor { virtual void get_async_serializer_state(td::Promise promise) = 0; virtual void archive(BlockIdExt block_id, td::Promise promise) = 0; + + virtual void prepare_stats(td::Promise>> promise) = 0; }; } // namespace validator diff --git a/validator/manager.cpp b/validator/manager.cpp index 8a0c79f63e..142107a48c 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -38,6 +38,8 @@ #include "common/delay.h" +#include "validator/stats-merger.h" + namespace ton { namespace validator { @@ -1811,6 +1813,7 @@ void ValidatorManagerImpl::advance_gc(BlockHandle handle, td::Ref>> promise) { + auto merger = StatsMerger::create(std::move(promise)); + + td::actor::send_closure(db_, &Db::prepare_stats, merger.make_promise("db.")); + std::vector> vec; vec.emplace_back("unixtime", td::to_string(static_cast(td::Clocks::system()))); - if (!last_masterchain_block_handle_) { - promise.set_value(std::move(vec)); - return; + if (last_masterchain_block_handle_) { + vec.emplace_back("masterchainblock", last_masterchain_block_id_.to_str()); + vec.emplace_back("masterchainblocktime", td::to_string(last_masterchain_block_handle_->unix_time())); + vec.emplace_back("gcmasterchainblock", gc_masterchain_handle_->id().to_str()); + vec.emplace_back("keymasterchainblock", last_key_block_handle_->id().to_str()); + vec.emplace_back("knownkeymasterchainblock", last_known_key_block_handle_->id().to_str()); + vec.emplace_back("rotatemasterchainblock", last_rotate_block_id_.to_str()); + //vec.emplace_back("shardclientmasterchainseqno", td::to_string(min_confirmed_masterchain_seqno_)); + vec.emplace_back("stateserializermasterchainseqno", td::to_string(state_serializer_masterchain_seqno_)); } - vec.emplace_back("masterchainblock", last_masterchain_block_id_.to_str()); - vec.emplace_back("masterchainblocktime", td::to_string(last_masterchain_block_handle_->unix_time())); - vec.emplace_back("gcmasterchainblock", gc_masterchain_handle_->id().to_str()); - vec.emplace_back("keymasterchainblock", last_key_block_handle_->id().to_str()); - vec.emplace_back("knownkeymasterchainblock", last_known_key_block_handle_->id().to_str()); - vec.emplace_back("rotatemasterchainblock", last_rotate_block_id_.to_str()); - vec.emplace_back("shardclientmasterchainseqno", td::to_string(min_confirmed_masterchain_seqno_)); - vec.emplace_back("stateserializermasterchainseqno", td::to_string(state_serializer_masterchain_seqno_)); - promise.set_value(std::move(vec)); + + if (!shard_client_.empty()) { + auto P = td::PromiseCreator::lambda([promise = merger.make_promise("")](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + return; + } + std::vector> vec; + vec.emplace_back("shardclientmasterchainseqno", td::to_string(R.move_as_ok())); + promise.set_value(std::move(vec)); + }); + td::actor::send_closure(shard_client_, &ShardClient::get_processed_masterchain_block, std::move(P)); + } + + merger.make_promise("").set_value(std::move(vec)); } td::actor::ActorOwn ValidatorManagerFactory::create( diff --git a/validator/stats-merger.h b/validator/stats-merger.h new file mode 100644 index 0000000000..e2c7f242b9 --- /dev/null +++ b/validator/stats-merger.h @@ -0,0 +1,74 @@ +#pragma once + +#include "td/utils/int_types.h" +#include "td/actor/actor.h" + +namespace ton { + +namespace validator { + +class StatsMerger : public td::actor::Actor { + public: + StatsMerger(td::Promise>> promise) + + : promise_(std::move(promise)) { + } + void start_up() override { + if (!pending_) { + finish(); + } + } + + void finish_subjob(td::Result>> R, std::string prefix) { + if (R.is_ok()) { + auto v = R.move_as_ok(); + for (auto &el : v) { + cur_.emplace_back(prefix + el.first, std::move(el.second)); + } + } + if (--pending_ == 0) { + finish(); + } + } + void inc() { + ++pending_; + } + void dec() { + if (--pending_ == 0) { + finish(); + } + } + void finish() { + promise_.set_value(std::move(cur_)); + stop(); + } + + struct InitGuard { + td::actor::ActorId merger; + ~InitGuard() { + td::actor::send_closure(merger, &StatsMerger::dec); + } + auto make_promise(std::string prefix) { + merger.get_actor_unsafe().inc(); + return td::PromiseCreator::lambda( + [merger = merger, prefix](td::Result>> R) { + td::actor::send_closure(merger, &StatsMerger::finish_subjob, std::move(R), std::move(prefix)); + }); + } + }; + + static InitGuard create(td::Promise>> promise) { + InitGuard ig; + ig.merger = td::actor::create_actor("m", std::move(promise)).release(); + return ig; + } + + private: + std::vector> cur_; + std::atomic pending_{1}; + td::Promise>> promise_; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 2bf00054ba..687117102d 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -125,7 +125,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { ValidatorManagerOptionsImpl(BlockIdExt zero_block_id, BlockIdExt init_block_id, std::function check_shard, bool allow_blockchain_init, td::ClocksBase::Duration sync_blocks_before, td::ClocksBase::Duration block_ttl, - td::ClocksBase::Duration archive_ttl, td::ClocksBase::Duration state_ttl, + td::ClocksBase::Duration state_ttl, td::ClocksBase::Duration archive_ttl, td::ClocksBase::Duration key_proof_ttl, bool initial_sync_disabled) : zero_block_id_(zero_block_id) , init_block_id_(init_block_id) From 841d5ebac2270476b6b7488f881ac90921a0c27e Mon Sep 17 00:00:00 2001 From: ton Date: Tue, 1 Oct 2019 14:07:35 +0400 Subject: [PATCH 004/667] updated fift updated fift updated some smartcontracts added partial support for hardforks --- crypto/fift/SourceLookup.h | 2 +- crypto/fift/words.cpp | 4 +- crypto/func/codegen.cpp | 4 +- crypto/smartcont/highload-wallet-v2.fif | 69 ++++++++++++++++++++ crypto/smartcont/new-highload-wallet-v2.fif | 47 +++++++++++++ tl/generate/scheme/ton_api.tl | 1 + tl/generate/scheme/ton_api.tlo | Bin 53872 -> 54168 bytes validator/fabric.h | 4 +- validator/impl/fabric.cpp | 7 +- validator/interfaces/liteserver.h | 16 +++++ validator/interfaces/validator-manager.h | 3 +- validator/manager-disk.hpp | 4 +- validator/manager.cpp | 63 ++++++++++-------- validator/manager.hpp | 5 +- validator/validator-options.hpp | 8 ++- validator/validator.h | 4 +- 16 files changed, 202 insertions(+), 39 deletions(-) create mode 100644 crypto/smartcont/highload-wallet-v2.fif create mode 100644 crypto/smartcont/new-highload-wallet-v2.fif create mode 100644 validator/interfaces/liteserver.h diff --git a/crypto/fift/SourceLookup.h b/crypto/fift/SourceLookup.h index 33ee71d4ef..7d51396cb3 100644 --- a/crypto/fift/SourceLookup.h +++ b/crypto/fift/SourceLookup.h @@ -79,7 +79,7 @@ class SourceLookup { if (os_time_) { return os_time_->now(); } - return static_cast(td::Time::now()); + return static_cast(td::Clocks::system()); } protected: diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index ea4744c80f..0b97ece94e 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -942,7 +942,9 @@ void interpret_fetch(vm::Stack& stack, int mode) { auto n = stack.pop_smallint_range(256 + (mode & 1)); auto cs = stack.pop_cellslice(); if (!cs->have(n)) { - stack.push(std::move(cs)); + if (mode & 2) { + stack.push(std::move(cs)); + } stack.push_bool(false); if (!(mode & 4)) { throw IntError{"end of data while reading integer from cell"}; diff --git a/crypto/func/codegen.cpp b/crypto/func/codegen.cpp index 0efea4d680..7e0d873ff6 100644 --- a/crypto/func/codegen.cpp +++ b/crypto/func/codegen.cpp @@ -628,7 +628,7 @@ bool Op::generate_code_step(Stack& stack) { stack.opt_show(); StackLayout layout1 = stack.vars(); bool next_empty = next->is_empty(); - stack.o << (next_empty ? "WHILEEND:<{" : "WHILE:<{"); + stack.o << "WHILE:<{"; stack.o.indent(); stack.forget_const(); block0->generate_code_all(stack); @@ -638,7 +638,7 @@ bool Op::generate_code_step(Stack& stack) { stack.modified(); stack.o.undent(); Stack stack_copy{stack}; - stack.o << (next_empty ? "}>" : "}>DO<{"); + stack.o << (next_empty ? "}>DO:" : "}>DO<{"); if (!next_empty) { stack.o.indent(); } diff --git a/crypto/smartcont/highload-wallet-v2.fif b/crypto/smartcont/highload-wallet-v2.fif new file mode 100644 index 0000000000..76f06ed590 --- /dev/null +++ b/crypto/smartcont/highload-wallet-v2.fif @@ -0,0 +1,69 @@ +#!/usr/bin/env fift -s +"TonUtil.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a request with up to 254 orders loaded from to high-load v2 (sub)wallet created by new-highload-v2-wallet.fif, with private key loaded from file .pk " + ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr + ." is a text file with lines `SEND `" cr 1 halt +} : usage +$# dup 3 < swap 4 > or ' usage if + +$1 =: file-base +$2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id +{ subwallet-id (.) $+ } : +subwallet +$3 =: order-file +def? $4 { @' $4 } { "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 +subwallet +".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 + +variable orders dictnew orders ! +variable order# order# 0! +// c -- +{ = abort"more than 254 orders" + orders @ 16 udict!+ not abort"cannot add order to dictionary" + orders ! order# 1+! +} : add-order +// b body -- b' +{ tuck +} : create-int-msg +// ng wc addr bnc -- +{ ."Transferring " 3 roll .GR ."to account " + -rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr +} : .transfer +// addr$ ng -- c +{ swap parse-smc-addr // ng wc addr bnc + 2over 2over .transfer + create-int-msg +} : create-simple-transfer +// c m -- c' +{ } : create-order + +// addr$ ng -- +{ create-simple-transfer send-mode create-order add-order } : send +{ bl word bl word $>GR send } : SEND + +// parse order file +order-file include + +// create external message +now timeout + 32 << hashu 32 1<<1- and + =: query_id + +dup ."signing message: " +dup ."resulting external message: " B dup Bx. cr +."Query_id is " query_id dup . ."= 0x" X. cr +savefile +".boc" tuck B>file +."(Saved to file " type .")" cr diff --git a/crypto/smartcont/new-highload-wallet-v2.fif b/crypto/smartcont/new-highload-wallet-v2.fif new file mode 100644 index 0000000000..df386cc72a --- /dev/null +++ b/crypto/smartcont/new-highload-wallet-v2.fif @@ -0,0 +1,47 @@ +#!/usr/bin/env fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a new v2 high-load wallet in the specified workchain, with the controlling private key saved to or loaded from .pk " + ."('new-wallet.pk' by default)" cr + ." is the 32-bit identifier of this subwallet among all controlled by the same private key" cr 1 halt +} : usage +$# 2- -2 and ' usage if + +$1 parse-workchain-id =: wc // set workchain id from command line argument +$2 parse-int dup =: subwallet-id // parse subwallet-id +32 fits ' usage ifnot +{ subwallet-id (.) $+ } : +subwallet +def? $3 { @' $3 } { "new-wallet" } cond constant file-base +65536 constant timeout // init query times out in 65536 seconds + +."Creating new v2 high-load wallet in workchain " wc . +."with subwallet id " subwallet-id . cr + +// Create new high-load wallet; source code included from `highload-wallet-v2-code.fif` +"highload-wallet-v2-code.fif" include +// code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."signing message: " +dup ."External message for initialization is " B dup Bx. cr +file-base +subwallet +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 89827f7f5c..655931396f 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -466,6 +466,7 @@ db.lt.status.value total_shards:int = db.lt.status.Value; validator.groupMember public_key_hash:int256 adnl:int256 weight:long = engine.validator.GroupMember; validator.group workchain:int shard:long catchain_seqno:int config_hash:int256 members:(vector validator.groupMember) = validator.Group; +validator.groupEx workchain:int shard:long vertical_seqno:int catchain_seqno:int config_hash:int256 members:(vector validator.groupMember) = validator.Group; ---functions--- diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 6959d9a3c980cb5ecd17266f18386e40d8a183c9..753814561913f951a715d115c99a261b9da10342 100644 GIT binary patch delta 135 zcmeycgn7nt<_#*cBJsvvPx#9cb23vBOY)2K+>7!{3nn^9Yz~l3VPfRpyj5Pgfd!;y z@}2ot3_%PAD7Y%e74y$`l8hiib2><~YXt)XNZrk2LOyJhAI|sTC`&CW$xKennQS;! RezMMj7O;7nw=CFW2mlMvGRFV_ delta 65 zcmbQSocY5N<_#*cA`9zo{NOK3%*jkiEXgm@b1%v-Etq_OSz>d5Yzh-2-{!6I$_*?a RMU(H$zXE1#zO!(TApn%)9q<4E diff --git a/validator/fabric.h b/validator/fabric.h index 69f8fd3aee..e7709f8dd5 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -27,6 +27,8 @@ namespace validator { td::actor::ActorOwn create_db_actor(td::actor::ActorId manager, std::string db_root_, td::uint32 depth); +td::actor::ActorOwn create_liteserver_cache_actor(td::actor::ActorId manager, + std::string db_root); td::Result> create_block(BlockIdExt block_id, td::BufferSlice data); td::Result> create_block(ReceivedBlock data); @@ -71,7 +73,7 @@ void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& m td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise); void run_liteserver_query(td::BufferSlice data, td::actor::ActorId manager, - td::Promise promise); + td::actor::ActorId cache, td::Promise promise); void run_validate_shard_block_description(td::BufferSlice data, BlockHandle masterchain_block, td::Ref masterchain_state, td::actor::ActorId manager, td::Timestamp timeout, diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index 5ad2a774f8..a037e122ed 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -43,6 +43,11 @@ td::actor::ActorOwn create_db_actor(td::actor::ActorId man return td::actor::create_actor("db", manager, db_root_, depth); } +td::actor::ActorOwn create_liteserver_cache_actor(td::actor::ActorId manager, + std::string db_root) { + return td::actor::create_actor("cache"); +} + td::Result> create_block(BlockIdExt block_id, td::BufferSlice data) { auto res = BlockQ::create(block_id, std::move(data)); if (res.is_error()) { @@ -196,7 +201,7 @@ void run_collate_query(ShardIdFull shard, td::uint32 min_ts, const BlockIdExt& m } void run_liteserver_query(td::BufferSlice data, td::actor::ActorId manager, - td::Promise promise) { + td::actor::ActorId cache, td::Promise promise) { LiteQuery::run_query(std::move(data), std::move(manager), std::move(promise)); } diff --git a/validator/interfaces/liteserver.h b/validator/interfaces/liteserver.h new file mode 100644 index 0000000000..ff1bf1650e --- /dev/null +++ b/validator/interfaces/liteserver.h @@ -0,0 +1,16 @@ +#pragma once + +#include "td/actor/actor.h" + +namespace ton { + +namespace validator { + +class LiteServerCache : public td::actor::Actor { + public: + virtual ~LiteServerCache() = default; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index a3260ab087..aaf40f49d3 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -26,6 +26,7 @@ #include "shard-block.h" #include "message-queue.h" #include "validator/validator.h" +#include "liteserver.h" namespace ton { @@ -147,7 +148,7 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void allow_block_info_gc(BlockIdExt block_id, td::Promise promise) = 0; virtual void check_is_hardfork(BlockIdExt block_id, td::Promise promise) = 0; - virtual void get_vertical_height(BlockSeqno seqno, td::Promise promise) = 0; + virtual void get_vertical_seqno(BlockSeqno seqno, td::Promise promise) = 0; virtual void update_last_known_key_block(BlockHandle handle, bool send_request) = 0; virtual void update_gc_block_handle(BlockHandle handle, td::Promise promise) = 0; diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index 750a823f9e..c5fa38de33 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -279,8 +279,8 @@ class ValidatorManagerImpl : public ValidatorManager { CHECK(block_id.is_masterchain()); promise.set_result(opts_->is_hardfork(block_id)); } - void get_vertical_height(BlockSeqno seqno, td::Promise promise) override { - promise.set_result(opts_->get_vertical_height(seqno)); + void get_vertical_seqno(BlockSeqno seqno, td::Promise promise) override { + promise.set_result(opts_->get_vertical_seqno(seqno)); } void run_ext_query(td::BufferSlice data, td::Promise promise) override { UNREACHABLE(); diff --git a/validator/manager.cpp b/validator/manager.cpp index 142107a48c..2d5b11753b 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -73,7 +73,8 @@ void ValidatorManagerImpl::validate_block_is_next_proof(BlockIdExt prev_block_id promise.set_value(td::Unit()); }); - run_check_proof_query(next_block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P)); + run_check_proof_query(next_block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P), + opts_->is_hardfork(next_block_id)); } void ValidatorManagerImpl::validate_block_proof(BlockIdExt block_id, td::BufferSlice proof, @@ -91,7 +92,8 @@ void ValidatorManagerImpl::validate_block_proof(BlockIdExt block_id, td::BufferS promise.set_value(td::Unit()); } }); - run_check_proof_query(block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P)); + run_check_proof_query(block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P), + opts_->is_hardfork(block_id)); } void ValidatorManagerImpl::validate_block_proof_link(BlockIdExt block_id, td::BufferSlice proof, @@ -129,28 +131,30 @@ void ValidatorManagerImpl::validate_block_proof_rel(BlockIdExt block_id, BlockId }); if (rel_block_id.id.seqno == 0) { - auto P = td::PromiseCreator::lambda([block_id, SelfId = actor_id(this), proof = pp.move_as_ok(), - promise = std::move(Q)](td::Result> R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - auto s = td::Ref{R.move_as_ok()}; + auto P = td::PromiseCreator::lambda( + [block_id, SelfId = actor_id(this), proof = pp.move_as_ok(), promise = std::move(Q), + skip_sig = opts_->is_hardfork(block_id)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + auto s = td::Ref{R.move_as_ok()}; - run_check_proof_query(block_id, std::move(proof), SelfId, td::Timestamp::in(2.0), std::move(promise), - std::move(s)); - } - }); + run_check_proof_query(block_id, std::move(proof), SelfId, td::Timestamp::in(2.0), std::move(promise), + std::move(s), skip_sig); + } + }); get_shard_state_from_db_short(rel_block_id, std::move(P)); } else { - auto P = td::PromiseCreator::lambda([block_id, SelfId = actor_id(this), proof = pp.move_as_ok(), - promise = std::move(Q)](td::Result> R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - run_check_proof_query(block_id, std::move(proof), SelfId, td::Timestamp::in(2.0), std::move(promise), - R.move_as_ok()); - } - }); + auto P = + td::PromiseCreator::lambda([block_id, SelfId = actor_id(this), proof = pp.move_as_ok(), promise = std::move(Q), + skip_sig = opts_->is_hardfork(block_id)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + run_check_proof_query(block_id, std::move(proof), SelfId, td::Timestamp::in(2.0), std::move(promise), + R.move_as_ok(), skip_sig); + } + }); get_block_proof_link_from_db_short(rel_block_id, std::move(P)); } } @@ -464,7 +468,7 @@ void ValidatorManagerImpl::run_ext_query(td::BufferSlice data, td::Promiseget_filedb_depth()); + lite_server_cache_ = create_liteserver_cache_actor(actor_id(this), db_root_); token_manager_ = td::actor::create_actor("tokenmanager"); auto Q = @@ -1583,13 +1588,19 @@ ValidatorSessionId ValidatorManagerImpl::get_validator_set_id(ShardIdFull shard, td::Bits256 opts_hash) { std::vector> vec; auto v = val_set->export_vector(); + auto vert_seqno = opts_->get_maximal_vertical_seqno(); for (auto &n : v) { auto pub_key = PublicKey{pubkeys::Ed25519{n.key}}; vec.push_back( create_tl_object(pub_key.compute_short_id().bits256_value(), n.addr, n.weight)); } - return create_hash_tl_object(shard.workchain, shard.shard, val_set->get_catchain_seqno(), - opts_hash, std::move(vec)); + if (vert_seqno == 0) { + return create_hash_tl_object(shard.workchain, shard.shard, val_set->get_catchain_seqno(), + opts_hash, std::move(vec)); + } else { + return create_hash_tl_object(shard.workchain, shard.shard, vert_seqno, + val_set->get_catchain_seqno(), opts_hash, std::move(vec)); + } } td::actor::ActorOwn ValidatorManagerImpl::create_validator_group( @@ -1975,8 +1986,6 @@ void ValidatorManagerImpl::send_peek_key_block_request() { void ValidatorManagerImpl::prepare_stats(td::Promise>> promise) { auto merger = StatsMerger::create(std::move(promise)); - td::actor::send_closure(db_, &Db::prepare_stats, merger.make_promise("db.")); - std::vector> vec; vec.emplace_back("unixtime", td::to_string(static_cast(td::Clocks::system()))); if (last_masterchain_block_handle_) { @@ -2004,6 +2013,8 @@ void ValidatorManagerImpl::prepare_stats(td::Promise ValidatorManagerFactory::create( diff --git a/validator/manager.hpp b/validator/manager.hpp index 2b88696a09..082432fa36 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -432,8 +432,8 @@ class ValidatorManagerImpl : public ValidatorManager { CHECK(block_id.is_masterchain()); promise.set_result(opts_->is_hardfork(block_id)); } - void get_vertical_height(BlockSeqno seqno, td::Promise promise) override { - promise.set_result(opts_->get_vertical_height(seqno)); + void get_vertical_seqno(BlockSeqno seqno, td::Promise promise) override { + promise.set_result(opts_->get_vertical_seqno(seqno)); } void add_shard_block_description(td::Ref desc); @@ -522,6 +522,7 @@ class ValidatorManagerImpl : public ValidatorManager { private: td::actor::ActorOwn lite_server_; + td::actor::ActorOwn lite_server_cache_; std::vector pending_ext_ports_; std::vector pending_ext_ids_; diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 687117102d..21bf79cb5c 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -67,7 +67,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { } return false; } - td::uint32 get_vertical_height(BlockSeqno seqno) const override { + td::uint32 get_vertical_seqno(BlockSeqno seqno) const override { size_t best = 0; for (size_t i = 0; i < hardforks_.size(); i++) { if (seqno >= hardforks_[i].seqno()) { @@ -76,6 +76,12 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { } return static_cast(best); } + td::uint32 get_maximal_vertical_seqno() const override { + return td::narrow_cast(hardforks_.size()); + } + td::uint32 get_last_fork_masterchain_seqno() const override { + return hardforks_.size() ? hardforks_.rbegin()->seqno() : 0; + } td::uint32 get_filedb_depth() const override { return db_depth_; } diff --git a/validator/validator.h b/validator/validator.h index 70b01e63b8..2e80c13580 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -57,7 +57,9 @@ struct ValidatorManagerOptions : public td::CntObject { virtual td::ClocksBase::Duration key_proof_ttl() const = 0; virtual bool initial_sync_disabled() const = 0; virtual bool is_hardfork(BlockIdExt block_id) const = 0; - virtual td::uint32 get_vertical_height(BlockSeqno seqno) const = 0; + virtual td::uint32 get_vertical_seqno(BlockSeqno seqno) const = 0; + virtual td::uint32 get_maximal_vertical_seqno() const = 0; + virtual td::uint32 get_last_fork_masterchain_seqno() const = 0; virtual td::uint32 get_filedb_depth() const = 0; virtual td::uint32 key_block_utime_step() const { return 86400; From 7ea00ebfcfa9ee3d206210d2382b6cdd6a3b0fb7 Mon Sep 17 00:00:00 2001 From: ton Date: Thu, 3 Oct 2019 17:04:52 +0400 Subject: [PATCH 005/667] updated tonlib, fixed bugs updated tonlib fixed bugs in func validator: partial support for hardforks liteserver: support for waitMasterchainBlock prefix transactions: support for gas flat rate --- crypto/block/block-parse.cpp | 95 +++- crypto/block/block-parse.h | 7 + crypto/block/block.tlb | 7 +- crypto/block/transaction.cpp | 71 ++- crypto/block/transaction.h | 4 + crypto/fift/words.cpp | 33 +- crypto/func/asmops.cpp | 4 + crypto/func/builtins.cpp | 24 +- crypto/func/func.h | 1 + crypto/smartcont/CreateState.fif | 8 +- crypto/smartcont/gen-zerostate.fif | 6 +- crypto/vm/cells/CellSlice.cpp | 20 + crypto/vm/cells/CellSlice.h | 2 + doc/FullNode-HOWTO | 2 +- doc/Validator-HOWTO | 8 +- tdactor/td/actor/common.h | 2 +- tdactor/td/actor/core/ActorExecutor.cpp | 4 +- tdactor/td/actor/core/ActorExecutor.h | 4 +- tdutils/td/utils/Status.h | 48 +- tdutils/td/utils/tl_helpers.h | 16 +- tdutils/td/utils/tl_parsers.h | 10 + tl/generate/scheme/lite_api.tl | 1 + tl/generate/scheme/lite_api.tlo | Bin 10948 -> 11088 bytes tl/generate/scheme/ton_api.tl | 2 + tl/generate/scheme/ton_api.tlo | Bin 54168 -> 54392 bytes tl/generate/scheme/tonlib_api.tl | 7 +- tl/generate/scheme/tonlib_api.tlo | Bin 12700 -> 13000 bytes tl/generate/tl_writer_td.cpp | 2 +- tonlib/CMakeLists.txt | 2 +- tonlib/test/offline.cpp | 7 +- tonlib/tonlib/ExtClient.cpp | 4 +- tonlib/tonlib/ExtClient.h | 44 +- tonlib/tonlib/ExtClientOutbound.cpp | 5 +- tonlib/tonlib/GenericAccount.cpp | 7 +- tonlib/tonlib/GenericAccount.h | 6 +- tonlib/tonlib/KeyStorage.cpp | 47 +- tonlib/tonlib/KeyStorage.h | 1 + tonlib/tonlib/KeyValue.cpp | 21 + tonlib/tonlib/KeyValue.h | 3 + tonlib/tonlib/LastBlock.cpp | 177 +++--- tonlib/tonlib/LastBlock.h | 59 +- tonlib/tonlib/LastBlockStorage.cpp | 3 + tonlib/tonlib/Logging.cpp | 5 +- tonlib/tonlib/TestGiver.cpp | 6 +- tonlib/tonlib/TestGiver.h | 6 +- tonlib/tonlib/TestWallet.cpp | 18 +- tonlib/tonlib/TestWallet.h | 12 +- tonlib/tonlib/TonlibClient.cpp | 634 ++++++++++++---------- tonlib/tonlib/TonlibClient.h | 6 +- tonlib/tonlib/TonlibError.h | 157 ++++++ tonlib/tonlib/Wallet.cpp | 18 +- tonlib/tonlib/Wallet.h | 12 +- tonlib/tonlib/keys/EncryptedKey.cpp | 1 - tonlib/tonlib/tonlib-cli.cpp | 24 + tonlib/tonlib/utils.cpp | 4 + tonlib/tonlib/utils.h | 15 + validator/block-handle.hpp | 15 + validator/db/blockdb.cpp | 67 +++ validator/db/blockdb.hpp | 2 + validator/db/filedb.cpp | 4 +- validator/db/files-async.hpp | 13 +- validator/db/ltdb.cpp | 89 +++ validator/db/ltdb.hpp | 4 + validator/db/rootdb.cpp | 18 + validator/db/rootdb.hpp | 5 + validator/db/statedb.cpp | 38 ++ validator/db/statedb.hpp | 3 + validator/db/staticfilesdb.cpp | 4 +- validator/downloaders/wait-block-data.cpp | 49 ++ validator/downloaders/wait-block-data.hpp | 6 +- validator/fabric.h | 2 + validator/impl/collator.cpp | 22 +- validator/impl/fabric.cpp | 6 + validator/impl/liteserver.cpp | 56 +- validator/impl/validate-query.cpp | 24 +- validator/interfaces/block-handle.h | 3 + validator/interfaces/db.h | 5 + validator/interfaces/validator-manager.h | 8 +- validator/manager-disk.cpp | 2 +- validator/manager-disk.hpp | 11 +- validator/manager-init.cpp | 172 +++++- validator/manager-init.hpp | 15 +- validator/manager.cpp | 142 ++++- validator/manager.hpp | 14 +- validator/shard-client.cpp | 12 +- validator/shard-client.hpp | 1 + validator/validator-options.cpp | 2 +- validator/validator-options.hpp | 14 +- validator/validator.h | 8 +- 89 files changed, 1921 insertions(+), 607 deletions(-) create mode 100644 tonlib/tonlib/TonlibError.h diff --git a/crypto/block/block-parse.cpp b/crypto/block/block-parse.cpp index 89fc3d6f02..c410e2cb53 100644 --- a/crypto/block/block-parse.cpp +++ b/crypto/block/block-parse.cpp @@ -1143,6 +1143,20 @@ bool TrStoragePhase::validate_skip(vm::CellSlice& cs, bool weak) const { && t_AccStatusChange.validate_skip(cs, weak); // status_change:AccStatusChange } +bool TrStoragePhase::get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const { + return t_Grams.as_integer_skip_to(cs, storage_fees); // storage_fees_collected:Grams +} + +bool TrStoragePhase::maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const { + auto z = cs.fetch_ulong(1); + if (!z) { + storage_fees = td::make_refint(0); + return true; + } else { + return z == 1 && get_storage_fees(cs, storage_fees); + } +} + const TrStoragePhase t_TrStoragePhase; bool TrCreditPhase::skip(vm::CellSlice& cs) const { @@ -1322,13 +1336,14 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const { && t_TrStoragePhase.skip(cs); // storage_ph:TrStoragePhase case trans_tick_tock: return cs.advance(4) // trans_tick_tock$001 is_tock:Bool - && t_TrStoragePhase.skip(cs) // storage:TrStoragePhase + && t_TrStoragePhase.skip(cs) // storage_ph:TrStoragePhase && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) && cs.advance(2); // aborted:Bool destroyed:Bool case trans_split_prepare: return cs.advance(4) // trans_split_prepare$0100 && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && Maybe{}.skip(cs) // storage_ph:(Maybe TrStoragePhase) && t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) && cs.advance(2); // aborted:Bool destroyed:Bool @@ -1346,6 +1361,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const { return cs.advance(4) // trans_merge_install$0111 && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo && t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction + && Maybe{}.skip(cs) // storage_ph:(Maybe TrStoragePhase) && Maybe{}.skip(cs) // credit_ph:(Maybe TrCreditPhase) && Maybe{}.skip(cs) // compute_ph:TrComputePhase && Maybe>{}.skip(cs) // action:(Maybe ^TrActionPhase) @@ -1370,13 +1386,14 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const { && t_TrStoragePhase.validate_skip(cs, weak); // storage_ph:TrStoragePhase case trans_tick_tock: return cs.advance(4) // trans_tick_tock$001 is_tock:Bool - && t_TrStoragePhase.validate_skip(cs, weak) // storage:TrStoragePhase + && t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase && t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase && Maybe>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) && cs.advance(2); // aborted:Bool destroyed:Bool case trans_split_prepare: return cs.advance(4) // trans_split_prepare$0100 && t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo + && Maybe{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) && t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase && Maybe>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) && cs.advance(2); // aborted:Bool destroyed:Bool @@ -1394,6 +1411,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const { return cs.advance(4) // trans_merge_install$0111 && t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo && t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction + && Maybe{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase) && Maybe{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase) && Maybe{}.validate_skip(cs, weak) // compute_ph:TrComputePhase && Maybe>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase) @@ -1407,6 +1425,53 @@ int TransactionDescr::get_tag(const vm::CellSlice& cs) const { return (t >= 0 && t <= 7) ? (t == 3 ? 2 : t) : -1; } +bool TransactionDescr::skip_to_storage_phase(vm::CellSlice& cs, bool& found) const { + found = false; + switch (get_tag(cs)) { + case trans_ord: + return cs.advance(4 + 1) // trans_ord$0000 storage_first:Bool + && cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase) + case trans_storage: + return cs.advance(4) // trans_storage$0001 + && (found = true); // storage_ph:TrStoragePhase + case trans_tick_tock: + return cs.advance(4) // trans_tick_tock$001 is_tock:Bool + && (found = true); // storage_ph:TrStoragePhase + case trans_split_prepare: + return cs.advance(4) // trans_split_prepare$0100 + && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase) + case trans_split_install: + return true; + case trans_merge_prepare: + return cs.advance(4) // trans_merge_prepare$0110 + && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && (found = true); // storage_ph:TrStoragePhase + case trans_merge_install: + return cs.advance(4) // trans_merge_install$0111 + && t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo + && t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction + && cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase) + } + return false; +} + +bool TransactionDescr::get_storage_fees(Ref cell, td::RefInt256& storage_fees) const { + if (cell.is_null()) { + return false; + } + auto cs = vm::load_cell_slice(std::move(cell)); + bool found; + if (!skip_to_storage_phase(cs, found)) { + return false; + } else if (found) { + return t_TrStoragePhase.get_storage_fees(cs, storage_fees); + } else { + storage_fees = td::make_refint(0); + return true; + } +} + const TransactionDescr t_TransactionDescr; bool Transaction_aux::skip(vm::CellSlice& cs) const { @@ -1447,6 +1512,32 @@ bool Transaction::validate_skip(vm::CellSlice& cs, bool weak) const { && RefTo{}.validate_skip(cs, weak); // description:^TransactionDescr } +bool Transaction::get_storage_fees(Ref cell, td::RefInt256& storage_fees) const { + Ref tdescr; + return get_descr(std::move(cell), tdescr) && t_TransactionDescr.get_storage_fees(std::move(tdescr), storage_fees); +} + +bool Transaction::get_descr(Ref cell, Ref& tdescr) const { + if (cell.is_null()) { + return false; + } else { + auto cs = vm::load_cell_slice(std::move(cell)); + return cs.is_valid() && get_descr(cs, tdescr) && cs.empty_ext(); + } +} + +bool Transaction::get_descr(vm::CellSlice& cs, Ref& tdescr) const { + return cs.advance( + 4 + 256 + 64 + 256 + 64 + 32 + + 15) // transaction$0111 account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15 + && t_AccountStatus.skip(cs) // orig_status:AccountStatus + && t_AccountStatus.skip(cs) // end_status:AccountStatus + && cs.advance_refs(1) // ^[ in_msg:(Maybe ^Message) out_msgs:(HashmapE 15 ^Message) ] + && t_CurrencyCollection.skip(cs) // total_fees:CurrencyCollection + && cs.advance_refs(1) // state_update:^(MERKLE_UPDATE Account) + && cs.fetch_ref_to(tdescr); // description:^TransactionDescr +} + bool Transaction::get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const { return cs.is_valid() && cs.fetch_ulong(4) == 7 // transaction$0111 && diff --git a/crypto/block/block-parse.h b/crypto/block/block-parse.h index 381dc149f3..acbe9dd426 100644 --- a/crypto/block/block-parse.h +++ b/crypto/block/block-parse.h @@ -614,6 +614,8 @@ extern const AccStatusChange t_AccStatusChange; struct TrStoragePhase final : TLB_Complex { bool skip(vm::CellSlice& cs) const override; bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; + bool get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const; + bool maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const; }; extern const TrStoragePhase t_TrStoragePhase; @@ -693,6 +695,8 @@ struct TransactionDescr final : TLB_Complex { bool skip(vm::CellSlice& cs) const override; bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; int get_tag(const vm::CellSlice& cs) const override; + bool skip_to_storage_phase(vm::CellSlice& cs, bool& found) const; + bool get_storage_fees(Ref cell, td::RefInt256& storage_fees) const; }; extern const TransactionDescr t_TransactionDescr; @@ -708,6 +712,9 @@ struct Transaction final : TLB_Complex { bool skip(vm::CellSlice& cs) const override; bool validate_skip(vm::CellSlice& cs, bool weak = false) const override; bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const; + bool get_descr(Ref cell, Ref& tdescr) const; + bool get_descr(vm::CellSlice& cs, Ref& tdescr) const; + bool get_storage_fees(Ref cell, td::RefInt256& storage_fees) const; }; extern const Transaction t_Transaction; diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index b9db5fe6ec..5108294770 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -325,7 +325,7 @@ trans_ord$0000 credit_first:Bool trans_storage$0001 storage_ph:TrStoragePhase = TransactionDescr; -trans_tick_tock$001 is_tock:Bool storage:TrStoragePhase +trans_tick_tock$001 is_tock:Bool storage_ph:TrStoragePhase compute_ph:TrComputePhase action:(Maybe ^TrActionPhase) aborted:Bool destroyed:Bool = TransactionDescr; // @@ -333,6 +333,7 @@ split_merge_info$_ cur_shard_pfx_len:(## 6) acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256 = SplitMergeInfo; trans_split_prepare$0100 split_info:SplitMergeInfo + storage_ph:(Maybe TrStoragePhase) compute_ph:TrComputePhase action:(Maybe ^TrActionPhase) aborted:Bool destroyed:Bool = TransactionDescr; @@ -345,6 +346,7 @@ trans_merge_prepare$0110 split_info:SplitMergeInfo = TransactionDescr; trans_merge_install$0111 split_info:SplitMergeInfo prepare_transaction:^Transaction + storage_ph:(Maybe TrStoragePhase) credit_ph:(Maybe TrCreditPhase) compute_ph:TrComputePhase action:(Maybe ^TrActionPhase) aborted:Bool destroyed:Bool @@ -609,6 +611,9 @@ gas_prices_ext#de gas_price:uint64 gas_limit:uint64 special_gas_limit:uint64 gas block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64 = GasLimitsPrices; +gas_flat_pfx#d1 flat_gas_limit:uint64 flat_gas_price:uint64 other:GasLimitsPrices + = GasLimitsPrices; + config_mc_gas_prices#_ GasLimitsPrices = ConfigParam 20; config_gas_prices#_ GasLimitsPrices = ConfigParam 21; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 11f118d4d3..24fc731ee4 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -672,9 +672,56 @@ bool Transaction::prepare_credit_phase() { return true; } +bool ComputePhaseConfig::parse_GasLimitsPrices(Ref cell, td::RefInt256& freeze_due_limit, + td::RefInt256& delete_due_limit) { + return cell.not_null() && + parse_GasLimitsPrices(vm::load_cell_slice_ref(std::move(cell)), freeze_due_limit, delete_due_limit); +} + +bool ComputePhaseConfig::parse_GasLimitsPrices(Ref cs, td::RefInt256& freeze_due_limit, + td::RefInt256& delete_due_limit) { + if (cs.is_null()) { + return false; + } + block::gen::GasLimitsPrices::Record_gas_flat_pfx flat; + if (tlb::csr_unpack(cs, flat)) { + bool ok = parse_GasLimitsPrices(std::move(flat.other), freeze_due_limit, delete_due_limit); + flat_gas_limit = flat.flat_gas_limit; + flat_gas_price = flat.flat_gas_price; + return ok; + } + flat_gas_limit = flat_gas_price = 0; + auto f = [&](const auto& r, td::uint64 spec_limit) { + gas_limit = r.gas_limit; + special_gas_limit = spec_limit; + gas_credit = r.gas_credit; + gas_price = r.gas_price; + freeze_due_limit = td::RefInt256{true, r.freeze_due_limit}; + delete_due_limit = td::RefInt256{true, r.delete_due_limit}; + }; + block::gen::GasLimitsPrices::Record_gas_prices_ext rec; + if (tlb::csr_unpack(cs, rec)) { + f(rec, rec.special_gas_limit); + } else { + block::gen::GasLimitsPrices::Record_gas_prices rec0; + if (tlb::csr_unpack(std::move(cs), rec0)) { + f(rec0, rec0.gas_limit); + } else { + return false; + } + } + compute_threshold(); + return true; +} + void ComputePhaseConfig::compute_threshold() { gas_price256 = td::RefInt256{true, gas_price}; - max_gas_threshold = td::rshift(gas_price256 * gas_limit, 16, 1); + if (gas_limit > flat_gas_limit) { + max_gas_threshold = + td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_refint(flat_gas_price); + } else { + max_gas_threshold = td::make_refint(flat_gas_price); + } } td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const { @@ -684,8 +731,11 @@ td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const { if (nanograms >= max_gas_threshold) { return gas_limit; } - auto res = td::div(std::move(nanograms) << 16, gas_price256); - return res->to_long(); + if (nanograms < flat_gas_price) { + return 0; + } + auto res = td::div((std::move(nanograms) - flat_gas_price) << 16, gas_price256); + return res->to_long() + flat_gas_limit; } td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const { @@ -855,6 +905,16 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { cp.skip_reason = ComputePhase::sk_no_gas; return true; } + // Compute gas limits + if (!compute_gas_limits(cp, cfg)) { + compute_phase.reset(); + return false; + } + if (!cp.gas_limit && !cp.gas_credit) { + // no gas + cp.skip_reason = ComputePhase::sk_no_gas; + return true; + } if (in_msg_state.not_null()) { LOG(DEBUG) << "HASH(in_msg_state) = " << in_msg_state->get_hash().bits().to_hex(256) << ", account_state_hash = " << account.state_hash.to_hex(); @@ -883,11 +943,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { } else if (in_msg_state.not_null()) { unpack_msg_state(true); // use only libraries } - // Compute gas limits - if (!compute_gas_limits(cp, cfg)) { - compute_phase.reset(); - return false; - } // initialize VM Ref stack = prepare_vm_stack(cp); if (stack.is_null()) { diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 45e824c1e8..015dc3f39e 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -98,6 +98,8 @@ struct ComputePhaseConfig { td::uint64 gas_limit; td::uint64 special_gas_limit; td::uint64 gas_credit; + td::uint64 flat_gas_limit = 0; + td::uint64 flat_gas_price = 0; static constexpr td::uint64 gas_infty = (1ULL << 63) - 1; td::RefInt256 gas_price256; td::RefInt256 max_gas_threshold; @@ -126,6 +128,8 @@ struct ComputePhaseConfig { Ref get_lib_root() const { return libraries ? libraries->get_root_cell() : Ref{}; } + bool parse_GasLimitsPrices(Ref cs, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit); + bool parse_GasLimitsPrices(Ref cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit); }; // msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 0b97ece94e..87c269b78e 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -764,6 +764,10 @@ void interpret_string_to_bytes(vm::Stack& stack) { stack.push_bytes(stack.pop_string()); } +void interpret_bytes_to_string(vm::Stack& stack) { + stack.push_string(stack.pop_bytes()); +} + void interpret_bytes_hash(vm::Stack& stack, bool as_uint) { std::string str = stack.pop_bytes(); unsigned char buffer[32]; @@ -796,7 +800,9 @@ void interpret_store_str(vm::Stack& stack) { stack.check_underflow(2); auto str = stack.pop_string(); auto cell = stack.pop_builder(); - cell.write().store_bytes(str); // may throw CellWriteError + if (!cell.write().store_bytes_bool(str)) { + throw IntError{"string does not fit into cell"}; + } stack.push(cell); } @@ -804,14 +810,18 @@ void interpret_store_bytes(vm::Stack& stack) { stack.check_underflow(2); auto str = stack.pop_bytes(); auto cell = stack.pop_builder(); - cell.write().store_bytes(str); // may throw CellWriteError + if (!cell.write().store_bytes_bool(str)) { + throw IntError{"byte string does not fit into cell"}; + } stack.push(cell); } void interpret_string_to_cellslice(vm::Stack& stack) { auto str = stack.pop_string(); vm::CellBuilder cb; - cb.store_bytes(str); // may throw CellWriteError + if (!cb.store_bytes_bool(str)) { + throw IntError{"string does not fit into cell"}; + } stack.push_cellslice(td::Ref{true, cb.finalize()}); } @@ -819,7 +829,9 @@ void interpret_store_cellslice(vm::Stack& stack) { stack.check_underflow(2); auto cs = stack.pop_cellslice(); auto cb = stack.pop_builder(); - vm::cell_builder_add_slice(cb.write(), *cs); + if (!vm::cell_builder_add_slice_bool(cb.write(), *cs)) { + throw IntError{"slice does not fit into cell"}; + } stack.push(std::move(cb)); } @@ -840,9 +852,11 @@ void interpret_concat_cellslice(vm::Stack& stack) { auto cs2 = stack.pop_cellslice(); auto cs1 = stack.pop_cellslice(); vm::CellBuilder cb; - vm::cell_builder_add_slice(cb, *cs1); - vm::cell_builder_add_slice(cb, *cs2); - stack.push_cellslice(td::Ref{true, cb.finalize()}); + if (vm::cell_builder_add_slice_bool(cb, *cs1) && vm::cell_builder_add_slice_bool(cb, *cs2)) { + stack.push_cellslice(td::Ref{true, cb.finalize()}); + } else { + throw IntError{"concatenation of two slices does not fit into a cell"}; + } } void interpret_concat_cellslice_ref(vm::Stack& stack) { @@ -862,7 +876,9 @@ void interpret_concat_builders(vm::Stack& stack) { stack.check_underflow(2); auto cb2 = stack.pop_builder(); auto cb1 = stack.pop_builder(); - cb1.write().append_builder(std::move(cb2)); + if (!cb1.write().append_builder_bool(std::move(cb2))) { + throw IntError{"cannot concatenate two builders"}; + } stack.push_builder(std::move(cb1)); } @@ -2490,6 +2506,7 @@ void init_words_common(Dictionary& d) { d.def_stack_word("B>Lu@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x12)); d.def_stack_word("B>Li@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x13)); d.def_stack_word("$>B ", interpret_string_to_bytes); + d.def_stack_word("B>$ ", interpret_bytes_to_string); d.def_stack_word("Bhash ", std::bind(interpret_bytes_hash, _1, true)); d.def_stack_word("Bhashu ", std::bind(interpret_bytes_hash, _1, true)); d.def_stack_word("BhashB ", std::bind(interpret_bytes_hash, _1, false)); diff --git a/crypto/func/asmops.cpp b/crypto/func/asmops.cpp index 6afdac8452..afc164715c 100644 --- a/crypto/func/asmops.cpp +++ b/crypto/func/asmops.cpp @@ -149,6 +149,10 @@ AsmOp AsmOp::IntConst(td::RefInt256 x) { return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT"); } +AsmOp AsmOp::BoolConst(bool f) { + return AsmOp::Const(f ? "TRUE" : "FALSE"); +} + AsmOp AsmOp::Parse(std::string custom_op) { if (custom_op == "NOP") { return AsmOp::Nop(); diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 2393e9c694..5010a2564b 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -620,7 +620,7 @@ int compute_compare(td::RefInt256 x, td::RefInt256 y, int mode) { if (mode == 7) { return s; } else { - return (mode >> (1 - s)) & 1; + return -((mode >> (1 - s)) & 1); } } @@ -641,8 +641,8 @@ int compute_compare(const VarDescr& x, const VarDescr& y, int mode) { return x.always_less(y) ? 1 : (x.always_geq(y) ? 2 : 3); case 5: // <> return x.always_neq(y) ? 1 : (x.always_equal(y) ? 2 : 3); - case 6: // >= - return x.always_geq(y) ? 1 : (x.always_less(y) ? 2 : 3); + case 6: // <= + return x.always_leq(y) ? 1 : (x.always_greater(y) ? 2 : 3); case 7: // <=> return x.always_less(y) ? 1 @@ -661,10 +661,11 @@ AsmOp compile_cmp_int(std::vector& res, std::vector& args, i assert(res.size() == 1 && args.size() == 2); VarDescr &r = res[0], &x = args[0], &y = args[1]; if (x.is_int_const() && y.is_int_const()) { - r.set_const(compute_compare(x.int_const, y.int_const, mode)); + int v = compute_compare(x.int_const, y.int_const, mode); + r.set_const(v); x.unused(); y.unused(); - return push_const(r.int_const); + return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v != 0); } int v = compute_compare(x, y, mode); assert(v); @@ -672,7 +673,7 @@ AsmOp compile_cmp_int(std::vector& res, std::vector& args, i r.set_const(v - (v >> 2) - 2); x.unused(); y.unused(); - return push_const(r.int_const); + return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v & 1); } r.val = ~0; if (v & 1) { @@ -957,12 +958,11 @@ void define_builtins() { 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)); - 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_x( - "run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)), - [](auto a, auto b, auto c) { return compile_run_method(a, b, c, 1, false); }, {1, 0}, {}, 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_x("run_method1", + TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)), + [](auto a, auto b, auto c) { return compile_run_method(a, b, c, 1, false); }, {1, 0}, {}, true); define_builtin_func_x( "run_method2", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X, Y}), Unit)), [](auto a, auto b, auto c) { return compile_run_method(a, b, c, 2, false); }, {1, 2, 0}, {}, true); diff --git a/crypto/func/func.h b/crypto/func/func.h index f4b66380ea..8fc881c88b 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -950,6 +950,7 @@ struct AsmOp { static AsmOp make_stk2(int a, int b, const char* str, int delta); static AsmOp make_stk3(int a, int b, int c, const char* str, int delta); static AsmOp IntConst(td::RefInt256 value); + static AsmOp BoolConst(bool f); static AsmOp Const(std::string push_op) { return AsmOp(a_const, 0, 1, std::move(push_op)); } diff --git a/crypto/smartcont/CreateState.fif b/crypto/smartcont/CreateState.fif index b16df7cacf..d701ecdc69 100644 --- a/crypto/smartcont/CreateState.fif +++ b/crypto/smartcont/CreateState.fif @@ -101,8 +101,12 @@ dictnew constant special-dict 1 'nop } ::_ sg~ -// gas_price gas_limit spec_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit -- c -{ 7 0 reverse +// gas_price gas_limit spec_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit b -- c +{ 7 1 reverse x{de} s, { swap 64 u, } 7 times b> +} : make-gas-prices-basic +// gas_price ... delete_due_limit flat_gas_limit flat_gas_rate -- c +{ 2dup or { CellSlice::fetch_ref() { } } +bool CellSlice::prefetch_maybe_ref(Ref& res) const { + auto z = prefetch_ulong(1); + if (!z) { + res.clear(); + return true; + } else { + return z == 1 && prefetch_ref_to(res); + } +} + +bool CellSlice::fetch_maybe_ref(Ref& res) { + auto z = prefetch_ulong(1); + if (!z) { + res.clear(); + return advance(1); + } else { + return z == 1 && prefetch_ref_to(res) && advance_ext(1, 1); + } +} + bool CellSlice::begins_with(unsigned bits, unsigned long long value) const { return have(bits) && !((prefetch_ulong(bits) ^ value) & ((1ULL << bits) - 1)); } diff --git a/crypto/vm/cells/CellSlice.h b/crypto/vm/cells/CellSlice.h index 5d7f906bc4..433344f982 100644 --- a/crypto/vm/cells/CellSlice.h +++ b/crypto/vm/cells/CellSlice.h @@ -185,6 +185,8 @@ class CellSlice : public td::CntObject { bool prefetch_ref_to(Ref& ref, unsigned offset = 0) const { return (ref = prefetch_ref(offset)).not_null(); } + bool fetch_maybe_ref(Ref& ref); + bool prefetch_maybe_ref(Ref& ref) const; td::BitSlice fetch_bits(unsigned bits); td::BitSlice prefetch_bits(unsigned bits) const; td::Ref fetch_subslice(unsigned bits, unsigned refs = 0); diff --git a/doc/FullNode-HOWTO b/doc/FullNode-HOWTO index 75432ffb87..66840c95ff 100644 --- a/doc/FullNode-HOWTO +++ b/doc/FullNode-HOWTO @@ -1,6 +1,6 @@ The aim of this document is to provide step-by-step instructions for setting up a full node for the TON Blockchain. We assume some familiarity with the TON Blockchain Lite Client, at least to the extent explained in the Lite Client HOWTO. -Notice that you need a machine with a public IP address and a high-bandwidth network connection to run a TON Blockchain Full Node. Typically you'll need a sufficiently powerful server in a datacenter. It is a bad idea to run a Full Node on your home computer; instead, you could run a Full Node on a remote server, and use the TON Blockchain Lite Client to connect to it from home. +Note that you need a machine with a public IP address and a high-bandwidth network connection to run a TON Blockchain Full Node. Typically you'll need a sufficiently powerful server in a datacenter with good network connectivity, using at least a 1 Gbit/s connection to reliably accommodate peak loads (the average load is expected to be approximately 100 Mbit/s). We recommend a dual-processor server with at least eight cores in each processor, at least 256 MiB RAM, at least 8 TB of conventional HDD storage and at least 512 GB of faster SSD storage. It is a bad idea to run a Full Node on your home computer; instead, you could run a Full Node on a remote server, and use the TON Blockchain Lite Client to connect to it from home. 0. Downloading and compiling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/Validator-HOWTO b/doc/Validator-HOWTO index 72efc978e9..689bc6b56f 100644 --- a/doc/Validator-HOWTO +++ b/doc/Validator-HOWTO @@ -1,6 +1,6 @@ The aim of this document is to provide step-by-step instructions for setting up a full node for the TON Blockchain as a validator. We assume that a TON Blockchain Full Node is already up and running as explained in FullNode-HOWTO. We also assume some familiarity with the TON Blockchain Lite Client. -Notice that a validator must be run on a dedicated high-performance server with high network bandwidth installed in a reliable datacenter, and that you'll need a large amount of Grams (test Grams, if you want to run a validator in the "testnet") as stakes for your validator. If your validator works incorrectly or is not available for prolonged periods of time, you may lose part or all of your stake, so it makes sense to use high-performance, reliable servers. +Note that a validator must be run on a dedicated high-performance server with high network bandwidth installed in a reliable datacenter, and that you'll need a large amount of Grams (test Grams, if you want to run a validator in the "testnet") as stakes for your validator. If your validator works incorrectly or is not available for prolonged periods of time, you may lose part or all of your stake, so it makes sense to use high-performance, reliable servers. We recommend a dual-processor server with at least eight cores in each processor, at least 256 MiB RAM, at least 8 TB of conventional HDD storage and at least 512 GB of faster SSD storage, with 1 Gbit/s network (and Internet) connectivity to reliably accomodate peak loads. 0. Downloading and compiling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -12,9 +12,9 @@ The basic instructions are the same as for a TON Blockchain Full Node, as explai In order to run a Validator, you'll need a Full Node that is already up and running (and completely synchronized with the current blockchain state), and a wallet in the masterchain holding a large amount of Grams (or test Grams, if you want to run a validator in the "testnet" TON Blockchain instance). Typically you'll need at least 100,000 Grams. -Each validator is identified by its (Ed25519) public key. During the validator elections, the validator (or rather its public key) is also associated with a smart contract residing in the masterchain. For simplicity, we say that the validator is "controlled" by this smart contract (e.g., a wallet smart contract). Stakes are accepted on behalf of this validator only if they arrive from its associated smart contract, and only that associated smart contract is entitled to collect the validator's stake after it is unfrozen, along with the validator's share of bonuses (e.g., block mining fees, transaction and message forwarding fees collected from the users of the TON Blockchain by the validator pool). Typically the bonuses are distributed proportionally to the (effective) stakes of the validators. On the other hand, validators with higher stakes are assigned a larger amount of work to perform (i.e., they have to create and validate blocks for more shardchains), so it is important not to get too greedy. +Each validator is identified by its (Ed25519) public key. During the validator elections, the validator (or rather its public key) is also associated with a smart contract residing in the masterchain. For simplicity, we say that the validator is "controlled" by this smart contract (e.g., a wallet smart contract). Stakes are accepted on behalf of this validator only if they arrive from its associated smart contract, and only that associated smart contract is entitled to collect the validator's stake after it is unfrozen, along with the validator's share of bonuses (e.g., block mining fees, transaction and message forwarding fees collected from the users of the TON Blockchain by the validator pool). Typically the bonuses are distributed proportionally to the (effective) stakes of the validators. On the other hand, validators with higher stakes are assigned a larger amount of work to perform (i.e., they have to create and validate blocks for more shardchains), so it is important not to stake an amount that will yield more validation work than your node is capable of handling. -Notice that each validator (identified by its public key) can be associated with at most one controlling smart contract (residing in the masterchain), but the same controlling smart contract may be associated with several validators. In this way you can run several validators (on different physical servers) and make stakes for them from the same smart contract. If one of these validators breaks down and you lose its stake, at least the other validators will continue working and will keep their stakes and receive bonuses. +Notice that each validator (identified by its public key) can be associated with at most one controlling smart contract (residing in the masterchain), but the same controlling smart contract may be associated with several validators. In this way you can run several validators (on different physical servers) and make stakes for them from the same smart contract. If one of these validators stops functioning and you lose its stake, the other validators should continue operating and will keep their stakes and potentially receive bonuses. 2. Creating the controlling smart contract ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -231,4 +231,4 @@ If you have done everything correctly (in particular indicated the correct seqno 7. Participating in the next elections ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Notice that even before the term of the validator group containing your elected validator finishes, new elections for the next validator group will be announced. You'll probably want to participate in them as well. For this, you can use the same validator, but you must generate a new validator key and new ADNL address. You'll also have to make a new stake before your previous stake is returned (because your previous stake will be unfrozen and returned only some time after the next validator group becomes active), so it does not make sense to stake more than half of your Grams. +Notice that even before the term of the validator group containing your elected validator finishes, new elections for the next validator group will be announced. You'll probably want to participate in them as well. For this, you can use the same validator, but you must generate a new validator key and new ADNL address. You'll also have to make a new stake before your previous stake is returned (because your previous stake will be unfrozen and returned only some time after the next validator group becomes active), so if you want to participate in concurrent elections, it likely does not make sense to stake more than half of your Grams. diff --git a/tdactor/td/actor/common.h b/tdactor/td/actor/common.h index 039f25b2c9..3a29b4736a 100644 --- a/tdactor/td/actor/common.h +++ b/tdactor/td/actor/common.h @@ -364,7 +364,7 @@ inline void register_actor_info_ptr(core::ActorInfoPtr actor_info_ptr) { } template -core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) { +core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) noexcept { auto *scheduler_context = core::SchedulerContext::get(); if (!options.has_scheduler()) { options.on_scheduler(scheduler_context->get_scheduler_id()); diff --git a/tdactor/td/actor/core/ActorExecutor.cpp b/tdactor/td/actor/core/ActorExecutor.cpp index fcca341fd7..7554a05d10 100644 --- a/tdactor/td/actor/core/ActorExecutor.cpp +++ b/tdactor/td/actor/core/ActorExecutor.cpp @@ -75,7 +75,7 @@ void ActorExecutor::send(ActorSignals signals) { pending_signals_.add_signals(signals); } -void ActorExecutor::start() { +void ActorExecutor::start() noexcept { //LOG(ERROR) << "START " << actor_info_.get_name() << " " << tag("from_queue", options.from_queue); if (is_closed()) { return; @@ -126,7 +126,7 @@ void ActorExecutor::start() { } } -void ActorExecutor::finish() { +void ActorExecutor::finish() noexcept { //LOG(ERROR) << "FINISH " << actor_info_.get_name() << " " << tag("own_lock", actor_locker_.own_lock()); if (!actor_locker_.own_lock()) { if (!pending_signals_.empty() && actor_locker_.add_signals(pending_signals_)) { diff --git a/tdactor/td/actor/core/ActorExecutor.h b/tdactor/td/actor/core/ActorExecutor.h index d11c77b2c2..53fc1e11ac 100644 --- a/tdactor/td/actor/core/ActorExecutor.h +++ b/tdactor/td/actor/core/ActorExecutor.h @@ -106,8 +106,8 @@ class ActorExecutor { return flags_; } - void start(); - void finish(); + void start() noexcept; + void finish() noexcept; bool flush_one(ActorSignals &signals); bool flush_one_signal(ActorSignals &signals); diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index f96ca82776..1759c0abef 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -50,6 +50,9 @@ #define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result) +#define TRY_RESULT_PROMISE(promise_name, name, result) \ + TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result) + #define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result) #define TRY_RESULT_PREFIX(name, result, prefix) \ @@ -78,10 +81,18 @@ } \ name = r_name.move_as_ok(); +#define TRY_RESULT_PROMISE_IMPL(promise_name, r_name, name, result) \ + auto r_name = (result); \ + if (r_name.is_error()) { \ + promise_name.set_error(r_name.move_as_error()); \ + return; \ + } \ + name = r_name.move_as_ok(); + #define TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, r_name, name, result, prefix) \ auto r_name = (result); \ if (r_name.is_error()) { \ - promise.set_error(r_name.move_as_error()); \ + promise_name.set_error(r_name.move_as_error_prefix(prefix)); \ return; \ } \ name = r_name.move_as_ok(); @@ -298,14 +309,31 @@ class Status { return std::move(*this); } - Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT { + Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT { + return status.move_as_error_suffix(message()); + } + + Status move_as_error_prefix(Slice prefix) const TD_WARN_UNUSED_RESULT { CHECK(is_error()); Info info = get_info(); switch (info.error_type) { case ErrorType::general: - return Error(code(), PSLICE() << prefix << message()); + return Error(code(), PSLICE() << prefix << " " << message()); case ErrorType::os: - return Status(false, ErrorType::os, code(), PSLICE() << prefix << message()); + return Status(false, ErrorType::os, code(), PSLICE() << prefix << " " << message()); + default: + UNREACHABLE(); + return {}; + } + } + Status move_as_error_suffix(Slice suffix) const TD_WARN_UNUSED_RESULT { + CHECK(is_error()); + Info info = get_info(); + switch (info.error_type) { + case ErrorType::general: + return Error(code(), PSLICE() << message() << " " << suffix); + case ErrorType::os: + return Status(false, ErrorType::os, code(), PSLICE() << message() << " " << suffix); default: UNREACHABLE(); return {}; @@ -488,6 +516,18 @@ class Result { }; return status_.move_as_error_prefix(prefix); } + Status move_as_error_prefix(const Status &prefix) TD_WARN_UNUSED_RESULT { + SCOPE_EXIT { + status_ = Status::Error<-5>(); + }; + return status_.move_as_error_prefix(prefix); + } + Status move_as_error_suffix(Slice suffix) TD_WARN_UNUSED_RESULT { + SCOPE_EXIT { + status_ = Status::Error<-5>(); + }; + return status_.move_as_error_suffix(suffix); + } const T &ok() const { LOG_CHECK(status_.is_ok()) << status_; return value_; diff --git a/tdutils/td/utils/tl_helpers.h b/tdutils/td/utils/tl_helpers.h index 30cf09556a..df89ed1a71 100644 --- a/tdutils/td/utils/tl_helpers.h +++ b/tdutils/td/utils/tl_helpers.h @@ -32,10 +32,10 @@ #include #include -#define BEGIN_STORE_FLAGS() \ - do { \ - uint32 flags_store = 0; \ - uint32 bit_offset_store = 0 +#define BEGIN_STORE_FLAGS() \ + do { \ + td::uint32 flags_store = 0; \ + td::uint32 bit_offset_store = 0 #define STORE_FLAG(flag) \ flags_store |= (flag) << bit_offset_store; \ @@ -47,10 +47,10 @@ } \ while (false) -#define BEGIN_PARSE_FLAGS() \ - do { \ - uint32 flags_parse; \ - uint32 bit_offset_parse = 0; \ +#define BEGIN_PARSE_FLAGS() \ + do { \ + td::uint32 flags_parse; \ + td::uint32 bit_offset_parse = 0; \ td::parse(flags_parse, parser) #define PARSE_FLAG(flag) \ diff --git a/tdutils/td/utils/tl_parsers.h b/tdutils/td/utils/tl_parsers.h index 734afdb713..e0fc419447 100644 --- a/tdutils/td/utils/tl_parsers.h +++ b/tdutils/td/utils/tl_parsers.h @@ -99,6 +99,16 @@ class TlParser { } } + bool can_prefetch_int() const { + return get_left_len() >= sizeof(int32); + } + + int32 prefetch_int_unsafe() const { + int32 result; + std::memcpy(&result, data, sizeof(int32)); + return result; + } + int32 fetch_int_unsafe() { int32 result; std::memcpy(&result, data, sizeof(int32)); diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index dc85227c5c..9604624a74 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -74,3 +74,4 @@ liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) liteServer.queryPrefix = Object; liteServer.query data:bytes = Object; +liteServer.waitMasterchainSeqno seqno:int timeout_ms:int = Object; // query prefix diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index 495668b85f4dc01d9b06fc0efe39021437ffb695..9b01c0d881f3ff2d6c37ae801295789e59e6ed3f 100644 GIT binary patch delta 88 zcmV-e0H^=NRnS(jU?2e%vtb}(3Nh6l3S5%7>bf6nX>?^%WpZ|9axQmaX>?6tb97~L uV`yP%Zc}A(Zf~GWa4Ffw5=;V`^*j=bQquB^vWqA8bNhTKTkz>T5oZvA%SvcCFkF@6irJ<9 X<(U}VtJ$fQdI-c}&O$q`k%e?G;m9*#N*MK5pRG~ K=jIRb9VGw=o>1-p diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 3729d3f353..deae3fa970 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -39,8 +39,8 @@ internal.transactionId lt:int64 hash:bytes = internal.TransactionId; raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState; raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState; -raw.message source:string destination:string value:int64 message:bytes = raw.Message; -raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 in_msg:raw.message out_msgs:vector = raw.Transaction; +raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes message:bytes = raw.Message; +raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector = raw.Transaction; raw.transactions transactions:vector previous_transaction_id:internal.transactionId = raw.Transactions; testWallet.initialAccountState public_key:string = testWallet.InitialAccountState; @@ -63,7 +63,7 @@ generic.accountStateWallet account_state:wallet.accountState = generic.AccountSt generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState; generic.accountStateUninited account_state:uninited.accountState = generic.AccountState; -sendGramsResult sent_until:int53 = SendGramsResult; +sendGramsResult sent_until:int53 body_hash:bytes = SendGramsResult; updateSendLiteServerQuery id:int64 data:bytes = Update; @@ -94,6 +94,7 @@ options.setConfig config:config = Ok; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; deleteKey key:key = Ok; +deleteAllKeys = Ok; exportKey input_key:inputKey = ExportedKey; exportPemKey input_key:inputKey key_password:secureBytes = ExportedPemKey; exportEncryptedKey input_key:inputKey key_password:secureBytes = ExportedEncryptedKey; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index a88c1fd14caecd8eb092b25e577fbd8cee5e5105..180f3521725fa8e4ac53b9db1d7e62587be7636c 100644 GIT binary patch delta 430 zcmbP}d?Izj4rZ2hy@isS_b~5ZWRbJ`eR6UIs|rhFOH;w-1*|1pvLH$R;?%qp_oBqy z;-J*x(wq{I*v(@?K1`Dh1=RVZpWWBwOv+EGjL%3c&X`;$CI>MwMZkki5@aHGQDV7X zZfbFHVtOiw57Npp`KVY9OImqK{N#_~(yZ*68AV_gCs%S&YGO$$NL)mk3(4F*QK`u} zhAO%s6BwZ2svK7g$f^7w8%l~2^NJIbOEUBGKw=PE8pXRgxrTWnh?GASyALM?wJNaRmtz36SZGP}7T?*>pH;+YW+Eo17z&011@~5;iQ1I-7ZA ze=`az|9%|Lo06K7T9WFRljEIQSqJ0LR~-ixbmRL41%}mdP*0 zawgvuSJnlIFhIdoIj$IxUHl*&B}IvO#fix!nfZAjF_12{$+{BVlOzQoE)$V7kpSst zgz7GGX4B!YZ9523Jvl)#0pjcworkchain_id_); ASSERT_EQ(true, addr->bounceable_); ASSERT_EQ(false, addr->testnet_); + auto raw = addr->addr_; auto addr_str = sync_send(client, make_object(std::move(addr))).move_as_ok(); ASSERT_EQ("Ef9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfYFX", addr_str->account_address_); + auto addr_str2 = sync_send(client, make_object( + make_object(-1, false, false, raw))) + .move_as_ok(); + ASSERT_EQ("Uf9Tj6fMJP-OqhAdhKXxq36DL-HYSzCc3-9O6UNzqsgPfdyS", addr_str2->account_address_); } TEST(Tonlib, KeysApi) { @@ -550,7 +555,7 @@ TEST(Tonlib, KeysApi) { make_object( make_object(key->public_key_, imported_key->secret_.copy()), new_local_password.copy()), pem_password.copy())); - if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "Not supported") { + if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "INTERNAL Not supported") { return; } auto exported_pem_key = r_exported_pem_key.move_as_ok(); diff --git a/tonlib/tonlib/ExtClient.cpp b/tonlib/tonlib/ExtClient.cpp index 0ec942ed90..442d65033a 100644 --- a/tonlib/tonlib/ExtClient.cpp +++ b/tonlib/tonlib/ExtClient.cpp @@ -30,7 +30,7 @@ void ExtClient::with_last_block(td::Promise promise) { }); }; if (client_.last_block_actor_.empty()) { - return P.set_error(td::Status::Error(500, "No lite clients")); + return P.set_error(TonlibError::NoLiteServers()); } td::actor::send_closure(client_.last_block_actor_, &LastBlock::get_last_block, std::move(P)); } @@ -44,7 +44,7 @@ void ExtClient::send_raw_query(td::BufferSlice query, td::Promise promise); template - void send_query(QueryT query, td::Promise promise) { + void send_query(QueryT query, td::Promise promise, td::int32 seq_no = -1) { auto raw_query = ton::serialize_tl_object(&query, true); - LOG(ERROR) << "send query to liteserver: " << to_string(query); + td::uint32 tag = td::Random::fast_uint32(); + VLOG(lite_server) << "send query to liteserver: " << tag << " " << to_string(query); td::BufferSlice liteserver_query = ton::serialize_tl_object(ton::create_tl_object(std::move(raw_query)), true); - send_raw_query(std::move(liteserver_query), [promise = std::move(promise)](td::Result R) mutable { - promise.set_result([&]() -> td::Result { - TRY_RESULT(data, std::move(R)); - auto r_error = ton::fetch_tl_object(data.clone(), true); - if (r_error.is_ok()) { - auto f = r_error.move_as_ok(); - return td::Status::Error(f->code_, f->message_); - } - return ton::fetch_result(std::move(data)); - }()); - }); + if (seq_no >= 0) { + auto wait = ton::lite_api::liteServer_waitMasterchainSeqno(seq_no, 5000); + VLOG(lite_server) << " with prefix " << to_string(wait); + auto prefix = ton::serialize_tl_object(&wait, true); + liteserver_query = td::BufferSlice(PSLICE() << prefix.as_slice() << liteserver_query.as_slice()); + } + + send_raw_query( + std::move(liteserver_query), [promise = std::move(promise), tag](td::Result R) mutable { + auto res = [&]() -> td::Result { + TRY_RESULT_PREFIX(data, std::move(R), TonlibError::LiteServerNetwork()); + auto r_error = ton::fetch_tl_object(data.clone(), true); + if (r_error.is_ok()) { + auto f = r_error.move_as_ok(); + return TonlibError::LiteServer(f->code_, f->message_); + } + return ton::fetch_result(std::move(data)); + } + (); + VLOG_IF(lite_server, res.is_ok()) + << "got result from liteserver: " << tag << " " << td::Slice(to_string(res.ok())).truncate(1 << 12); + VLOG_IF(lite_server, res.is_error()) << "got error from liteserver: " << tag << " " << res.error(); + promise.set_result(std::move(res)); + }); } private: diff --git a/tonlib/tonlib/ExtClientOutbound.cpp b/tonlib/tonlib/ExtClientOutbound.cpp index a20a8c59f2..f1746af5d6 100644 --- a/tonlib/tonlib/ExtClientOutbound.cpp +++ b/tonlib/tonlib/ExtClientOutbound.cpp @@ -18,6 +18,7 @@ Copyright 2017-2019 Telegram Systems LLP */ #include "ExtClientOutbound.h" +#include "TonlibError.h" #include namespace tonlib { @@ -40,7 +41,7 @@ class ExtClientOutboundImp : public ExtClientOutbound { void on_query_result(td::int64 id, td::Result r_data, td::Promise promise) override { auto it = queries_.find(id); if (it == queries_.end()) { - promise.set_error(td::Status::Error(400, "Unknown query id")); + promise.set_error(TonlibError::Internal("Unknown query id")); } it->second.set_result(std::move(r_data)); queries_.erase(it); @@ -54,7 +55,7 @@ class ExtClientOutboundImp : public ExtClientOutbound { void tear_down() override { for (auto &it : queries_) { - it.second.set_error(td::Status::Error(400, "Query cancelled")); + it.second.set_error(TonlibError::Cancelled()); } queries_.clear(); } diff --git a/tonlib/tonlib/GenericAccount.cpp b/tonlib/tonlib/GenericAccount.cpp index 08ea33bdc9..9f44ede274 100644 --- a/tonlib/tonlib/GenericAccount.cpp +++ b/tonlib/tonlib/GenericAccount.cpp @@ -20,18 +20,19 @@ #include "tonlib/utils.h" #include "block/block-auto.h" namespace tonlib { -td::Ref GenericAccount::get_init_state(td::Ref code, td::Ref data) { +td::Ref GenericAccount::get_init_state(td::Ref code, td::Ref data) noexcept { return vm::CellBuilder() .append_cellslice(binary_bitstring_to_cellslice("b{00110}").move_as_ok()) .store_ref(std::move(code)) .store_ref(std::move(data)) .finalize(); } -block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) { +block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, + const td::Ref& init_state) noexcept { return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/); } td::Ref GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref new_state, - td::Ref body) { + td::Ref body) noexcept { block::gen::Message::Record message; /*info*/ { block::gen::CommonMsgInfo::Record_ext_in_msg_info info; diff --git a/tonlib/tonlib/GenericAccount.h b/tonlib/tonlib/GenericAccount.h index 253274f05e..4a36d78a53 100644 --- a/tonlib/tonlib/GenericAccount.h +++ b/tonlib/tonlib/GenericAccount.h @@ -22,9 +22,9 @@ namespace tonlib { class GenericAccount { public: - static td::Ref get_init_state(td::Ref code, td::Ref data); - static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state); + static td::Ref get_init_state(td::Ref code, td::Ref data) noexcept; + static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) noexcept; static td::Ref create_ext_message(const block::StdAddress& address, td::Ref new_state, - td::Ref body); + td::Ref body) noexcept; }; } // namespace tonlib diff --git a/tonlib/tonlib/KeyStorage.cpp b/tonlib/tonlib/KeyStorage.cpp index 8703a3ef41..fd6607775c 100644 --- a/tonlib/tonlib/KeyStorage.cpp +++ b/tonlib/tonlib/KeyStorage.cpp @@ -22,9 +22,12 @@ #include "tonlib/keys/DecryptedKey.h" #include "tonlib/keys/EncryptedKey.h" +#include "tonlib/TonlibError.h" + #include "td/utils/filesystem.h" #include "td/utils/port/path.h" #include "td/utils/crypto.h" +#include "td/utils/PathView.h" namespace tonlib { namespace { @@ -47,7 +50,7 @@ td::Result KeyStorage::save_key(const DecryptedKey &decrypted_k Key res; res.public_key = encrypted_key.public_key.as_octet_string(); res.secret = std::move(encrypted_key.secret); - TRY_STATUS(kv_->set(to_file_name(res), encrypted_key.encrypted_data)); + TRY_STATUS_PREFIX(kv_->set(to_file_name(res), encrypted_key.encrypted_data), TonlibError::Internal()); return std::move(res); } @@ -68,14 +71,16 @@ td::Result KeyStorage::export_decrypted_key(InputKey input_key) { if (r_encrypted_data.is_ok()) { LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> " << to_file_name(input_key.key); - TRY_STATUS(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok())); + TRY_STATUS_PREFIX(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()), TonlibError::Internal()); kv_->erase(to_file_name_old(input_key.key)).ignore(); } } - TRY_RESULT(encrypted_data, std::move(r_encrypted_data)); + TRY_RESULT_PREFIX(encrypted_data, std::move(r_encrypted_data), TonlibError::KeyUnknown()); EncryptedKey encrypted_key{std::move(encrypted_data), td::Ed25519::PublicKey(std::move(input_key.key.public_key)), std::move(input_key.key.secret)}; - return encrypted_key.decrypt(std::move(input_key.local_password)); + TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(std::move(input_key.local_password)), + TonlibError::KeyDecrypt()); + return std::move(decrypted_key); } td::Result KeyStorage::export_key(InputKey input_key) { @@ -93,24 +98,43 @@ td::Result KeyStorage::load_private_key(InputKey input_k } td::Status KeyStorage::delete_key(const Key &key) { + LOG(WARNING) << "Delete private key stored at " << to_file_name(key); return kv_->erase(to_file_name(key)); } +td::Status KeyStorage::delete_all_keys() { + std::vector keys; + kv_->foreach_key([&](td::Slice key) { + if (td::PathView(key).extension().empty()) { + keys.push_back(key.str()); + } + }); + td::Status status; + for (auto key : keys) { + LOG(WARNING) << "Delete private key stored at " << key; + auto err = kv_->erase(key); + if (err.is_error() && status.is_ok()) { + status = std::move(err); + } + } + return status; +} + td::Result KeyStorage::import_key(td::Slice local_password, td::Slice mnemonic_password, ExportedKey exported_key) { TRY_RESULT(mnemonic, Mnemonic::create(std::move(exported_key.mnemonic_words), td::SecureString(mnemonic_password))); if (!mnemonic.is_basic_seed()) { if (mnemonic_password.empty() && mnemonic.is_password_seed()) { - return td::Status::Error("Mnemonic password is expected"); + return TonlibError::NeedMnemonicPassword(); } - return td::Status::Error("Invalid mnemonic words or password (invalid checksum)"); + return TonlibError::InvalidMnemonic(); } return save_key(DecryptedKey(std::move(mnemonic)), local_password); } td::Result KeyStorage::export_pem_key(InputKey input_key, td::Slice key_password) { TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key))); - TRY_RESULT(pem, decrypted_key.private_key.as_pem(key_password)); + TRY_RESULT_PREFIX(pem, decrypted_key.private_key.as_pem(key_password), TonlibError::Internal()); return ExportedPemKey{std::move(pem)}; } @@ -120,14 +144,15 @@ td::Result KeyStorage::change_local_password(InputKey input_key Key res; res.public_key = std::move(input_key.key.public_key); res.secret = std::move(new_secret); - TRY_RESULT(value, kv_->get(to_file_name(input_key.key))); - TRY_STATUS(kv_->add(to_file_name(res), value)); + TRY_RESULT_PREFIX(value, kv_->get(to_file_name(input_key.key)), TonlibError::KeyUnknown()); + TRY_STATUS_PREFIX(kv_->add(to_file_name(res), value), TonlibError::Internal()); return std::move(res); } td::Result KeyStorage::import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key) { - TRY_RESULT(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password)); + TRY_RESULT_PREFIX(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password), + TonlibError::InvalidPemKey()); return save_key(DecryptedKey({}, std::move(key)), local_password); } @@ -143,7 +168,7 @@ td::Result KeyStorage::import_encrypted_key(td::Slice local_pas ExportedEncryptedKey exported_key) { EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()), td::SecureString(dummy_secret)}; - TRY_RESULT(decrypted_key, encrypted_key.decrypt(key_password, false)); + TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(key_password, false), TonlibError::KeyDecrypt()); return save_key(std::move(decrypted_key), local_password); } diff --git a/tonlib/tonlib/KeyStorage.h b/tonlib/tonlib/KeyStorage.h index fbde2a3252..1e37082885 100644 --- a/tonlib/tonlib/KeyStorage.h +++ b/tonlib/tonlib/KeyStorage.h @@ -60,6 +60,7 @@ class KeyStorage { td::Result change_local_password(InputKey input_key, td::Slice new_local_password); td::Status delete_key(const Key& key); + td::Status delete_all_keys(); td::Result import_key(td::Slice local_password, td::Slice mnemonic_password, ExportedKey exported_key); td::Result import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key); diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index fa2f8ae3ea..d71744f04d 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -42,6 +42,22 @@ class KeyValueDir : public KeyValue { return td::unlink(key.str()); } + void foreach_key(std::function f) override { + int cnt = 0; + td::WalkPath::run(directory_, [&](td::Slice path, td::WalkPath::Type type) { + cnt++; + if (type == td::WalkPath::Type::EnterDir) { + if (cnt != 1) { + return td::WalkPath::Action::SkipDir; + } + } else if (type == td::WalkPath::Type::NotDir) { + f(path); + } + + return td::WalkPath::Action::Continue; + }).ignore(); + } + private: std::string directory_; @@ -82,6 +98,11 @@ class KeyValueInmemory : public KeyValue { map_.erase(it); return td::Status::OK(); } + void foreach_key(std::function f) override { + for (auto &it : map_) { + f(it.first); + } + } private: class Cmp : public std::less<> { diff --git a/tonlib/tonlib/KeyValue.h b/tonlib/tonlib/KeyValue.h index 6ccd929f98..7d2020acc9 100644 --- a/tonlib/tonlib/KeyValue.h +++ b/tonlib/tonlib/KeyValue.h @@ -3,6 +3,8 @@ #include "td/utils/Slice.h" #include "td/utils/Status.h" +#include + namespace tonlib { class KeyValue { public: @@ -11,6 +13,7 @@ class KeyValue { virtual td::Status set(td::Slice key, td::Slice value) = 0; virtual td::Status erase(td::Slice key) = 0; virtual td::Result get(td::Slice key) = 0; + virtual void foreach_key(std::function f) = 0; static td::Result> create_dir(td::CSlice dir); static td::Result> create_inmemory(); diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index 7c4cda5dd3..fd15fed80c 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -18,12 +18,18 @@ */ #include "tonlib/LastBlock.h" +#include "tonlib/utils.h" + #include "ton/lite-tl.hpp" #include "lite-client/lite-client-common.h" namespace tonlib { +// init_state <-> last_key_block +// state.valitated_init_state +// last_key_block -> +// td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state) { return sb << td::tag("last_block", state.last_block_id.to_str()) << td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime); @@ -32,9 +38,10 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr callback) : state_(std::move(state)), config_(std::move(config)), callback_(std::move(callback)) { client_.set_client(client); - if (!config_.init_block_id.is_valid()) { - check_init_block_state_ = QueryState::Done; - } + state_.last_block_id = state_.last_key_block_id; + + VLOG(last_block) << "check_init_block: skip - FIXME before release"; + check_init_block_state_ = QueryState::Done; } void LastBlock::get_last_block(td::Promise promise) { @@ -42,9 +49,13 @@ void LastBlock::get_last_block(td::Promise promise) { promise.set_error(fatal_error_.clone()); return; } + if (promises_.empty() && get_last_block_state_ == QueryState::Done) { + VLOG(last_block) << "sync: start"; + VLOG(last_block) << "get_last_block: reset"; get_last_block_state_ = QueryState::Empty; } + promises_.push_back(std::move(promise)); sync_loop(); } @@ -54,38 +65,45 @@ void LastBlock::sync_loop() { return; } - update_zero_state(state_.zero_state_id); + update_zero_state(state_.zero_state_id, "cache"); update_zero_state(ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash, - config_.zero_state_id.file_hash)); + config_.zero_state_id.file_hash), + "config"); if (get_mc_info_state_ == QueryState::Empty) { + VLOG(last_block) << "get_masterchain_info: start"; get_mc_info_state_ = QueryState::Active; client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(), [this](auto r_info) { this->on_masterchain_info(std::move(r_info)); }); } - if (get_last_block_state_ == QueryState::Empty) { - get_last_block_state_ = QueryState::Active; - total_sync_ = td::Timer(); - validate_ = td::Timer(true); - queries_ = 0; - LOG(INFO) << "Begin last block synchronization " << state_; - do_get_last_block(); - } - if (check_init_block_state_ == QueryState::Empty) { - if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) { - check_init_block_state_ = QueryState::Active; - // validate - //total_sync_ = td::Timer(); - //validate_ = td::Timer(true); - //queries_ = 0; - LOG(INFO) << "Begin last block synchronization (check init_block)" << state_; - do_check_init_block(state_.last_key_block_id); + if (!config_.init_block_id.is_valid()) { + check_init_block_state_ = QueryState::Done; + VLOG(last_block) << "check_init_block: skip - no init_block in config"; + } else if (config_.init_block_id == state_.init_block_id) { + check_init_block_state_ = QueryState::Done; + VLOG(last_block) << "check_init_block: skip - was checked before"; } else { + check_init_block_state_ = QueryState::Active; + check_init_block_stats_.start(); + if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) { + VLOG(last_block) << "check_init_block: start - init_block -> last_block"; + do_check_init_block(config_.init_block_id, state_.last_key_block_id); + } else { + VLOG(last_block) << "check_init_block: start - last_block -> init_block"; + do_check_init_block(state_.last_key_block_id, config_.init_block_id); + } } } + if (get_last_block_state_ == QueryState::Empty && check_init_block_state_ == QueryState::Done) { + VLOG(last_block) << "get_last_block: start"; + get_last_block_stats_.start(); + get_last_block_state_ = QueryState::Active; + do_get_last_block(); + } + if (get_mc_info_state_ == QueryState::Done && get_last_block_state_ == QueryState::Done && check_init_block_state_ == QueryState::Done) { on_sync_ok(); @@ -94,7 +112,8 @@ void LastBlock::sync_loop() { void LastBlock::do_get_last_block() { //liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; - queries_++; + VLOG(last_block) << "get_last_block: continue " << state_.last_key_block_id.to_str() << " -> ?"; + get_last_block_stats_.queries_++; client_.send_query( ton::lite_api::liteServer_getBlockProof(0, create_tl_lite_block_id(state_.last_key_block_id), nullptr), [this, from = state_.last_key_block_id](auto r_block_proof) { @@ -102,64 +121,69 @@ void LastBlock::do_get_last_block() { }); } -void LastBlock::do_check_init_block(ton::BlockIdExt from) { +void LastBlock::do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to) { + VLOG(last_block) << "check_init_block: continue " << from.to_str() << " -> " << to.to_str(); //liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; - //queries_++; - client_.send_query(ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from), - create_tl_lite_block_id(config_.init_block_id)), - [this, from = state_.last_key_block_id](auto r_block_proof) { - this->on_init_block_proof(from, std::move(r_block_proof)); - }); + check_init_block_stats_.queries_++; + client_.send_query( + ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from), create_tl_lite_block_id(to)), + [this, from, to](auto r_block_proof) { this->on_init_block_proof(from, to, std::move(r_block_proof)); }); } td::Result> LastBlock::process_block_proof( ton::BlockIdExt from, td::Result> r_block_proof) { - TRY_RESULT(block_proof, std::move(r_block_proof)); - LOG(DEBUG) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_); + TRY_RESULT(block_proof, std::move(r_block_proof)); //TODO: it is fatal? + TRY_RESULT_PREFIX(chain, TRY_VM(process_block_proof(from, std::move(block_proof))), + TonlibError::ValidateBlockProof()); + return std::move(chain); +} + +td::Result> LastBlock::process_block_proof( + ton::BlockIdExt from, ton::ton_api::object_ptr block_proof) { + VLOG(last_block) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_); TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof))); if (chain->from != from) { return td::Status::Error(PSLICE() << "block proof chain starts from block " << chain->from.to_str() << ", not from requested block " << from.to_str()); } TRY_STATUS(chain->validate()); + return std::move(chain); +} + +void LastBlock::update_state(block::BlockProofChain& chain) { + // Update state_ bool is_changed = false; - is_changed |= update_mc_last_block(chain->to); - if (chain->has_key_block) { - is_changed |= update_mc_last_key_block(chain->key_blkid); + is_changed |= update_mc_last_block(chain.to); + if (chain.has_key_block) { + is_changed |= update_mc_last_key_block(chain.key_blkid); } - if (chain->has_utime) { - update_utime(chain->last_utime); + if (chain.has_utime) { + update_utime(chain.last_utime); } if (is_changed) { callback_->on_state_changed(state_); } - return std::move(chain); } void LastBlock::on_block_proof( ton::BlockIdExt from, td::Result> r_block_proof) { - validate_.resume(); + get_last_block_stats_.validate_.resume(); auto r_chain = process_block_proof(from, std::move(r_block_proof)); - validate_.pause(); - bool is_ready; + get_last_block_stats_.validate_.pause(); if (r_chain.is_error()) { - LOG(WARNING) << "Error during last block synchronization " << r_chain.error(); - if (config_.init_block_id.is_valid()) { - if (state_.last_key_block_id.id.seqno < config_.init_block_id.id.seqno) { - on_sync_error(td::Status::Error(PSLICE() << "Sync failed and we can't validate config.init_block: " - << r_chain.move_as_error())); - } - } - is_ready = true; - } else { - is_ready = r_chain.ok()->complete; + get_last_block_state_ = QueryState::Empty; + VLOG(last_block) << "get_last_block: error " << r_chain.error(); + on_sync_error(r_chain.move_as_error_suffix("(during last block synchronization)")); + return; } - if (is_ready) { - LOG(INFO) << "End last block synchronization " << state_ << "\n" - << " net queries: " << queries_ << "\n" - << " total: " << total_sync_ << " validation: " << validate_; + + auto chain = r_chain.move_as_ok(); + CHECK(chain); + update_state(*chain); + if (chain->complete) { + VLOG(last_block) << "get_last_block: done\n" << get_last_block_stats_; get_last_block_state_ = QueryState::Done; sync_loop(); } else { @@ -168,26 +192,26 @@ void LastBlock::on_block_proof( } void LastBlock::on_init_block_proof( - ton::BlockIdExt from, + ton::BlockIdExt from, ton::BlockIdExt to, td::Result> r_block_proof) { - validate_.resume(); + check_init_block_stats_.validate_.resume(); auto r_chain = process_block_proof(from, std::move(r_block_proof)); - validate_.pause(); + check_init_block_stats_.validate_.pause(); if (r_chain.is_error()) { check_init_block_state_ = QueryState::Empty; - on_sync_error( - td::Status::Error(PSLICE() << "Error during last block synchronization (check init_block)" << r_chain.error())); + VLOG(last_block) << "check_init_block: error " << r_chain.error(); + on_sync_error(r_chain.move_as_error_suffix("(during check init block)")); return; } auto chain = r_chain.move_as_ok(); + CHECK(chain); + update_state(*chain); if (chain->complete) { - LOG(INFO) << "End last block synchronization " << state_ << "\n" - << " net queries: " << queries_ << "\n" - << " total: " << total_sync_ << " validation: " << validate_; - get_last_block_state_ = QueryState::Done; + VLOG(last_block) << "check_init_block: done\n" << check_init_block_stats_; + check_init_block_state_ = QueryState::Done; sync_loop(); } else { - do_check_init_block(chain->to); + do_check_init_block(chain->to, to); } } @@ -195,28 +219,30 @@ void LastBlock::on_masterchain_info( td::Result> r_info) { if (r_info.is_ok()) { auto info = r_info.move_as_ok(); - update_zero_state(create_zero_state_id(info->init_)); - update_mc_last_block(create_block_id(info->last_)); + update_zero_state(create_zero_state_id(info->init_), "masterchain info"); + // last block is not validated! Do not update it get_mc_info_state_ = QueryState::Done; + VLOG(last_block) << "get_masterchain_info: done"; } else { get_mc_info_state_ = QueryState::Empty; + VLOG(last_block) << "get_masterchain_info: error " << r_info.error(); LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error(); on_sync_error(r_info.move_as_error()); } sync_loop(); } -void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) { +void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source) { if (has_fatal_error()) { return; } if (!zero_state_id.is_valid()) { - LOG(ERROR) << "Ignore invalid zero state update"; + LOG(ERROR) << "Ignore invalid zero state update from " << source; return; } if (!state_.zero_state_id.is_valid()) { - LOG(INFO) << "Init zerostate: " << zero_state_id.to_str(); + LOG(INFO) << "Init zerostate from " << source << ": " << zero_state_id.to_str(); state_.zero_state_id = std::move(zero_state_id); return; } @@ -225,8 +251,9 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) { return; } - on_fatal_error(td::Status::Error(PSLICE() << "Masterchain zerostate mismatch: expected: " - << state_.zero_state_id.to_str() << ", found " << zero_state_id.to_str())); + on_fatal_error(TonlibError::ValidateZeroState(PSLICE() << "Masterchain zerostate mismatch: expected: " + << state_.zero_state_id.to_str() << ", found " + << zero_state_id.to_str() << " from " << source)); } bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) { @@ -256,6 +283,9 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) { if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) { state_.last_key_block_id = mc_key_block_id; LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str(); + //LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " " + //<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " " + //<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice()); return true; } return false; @@ -268,6 +298,7 @@ void LastBlock::update_utime(td::int64 utime) { } void LastBlock::on_sync_ok() { + VLOG(last_block) << "sync: ok " << state_; for (auto& promise : promises_) { auto state = state_; promise.set_value(std::move(state)); @@ -275,12 +306,14 @@ void LastBlock::on_sync_ok() { promises_.clear(); } void LastBlock::on_sync_error(td::Status status) { + VLOG(last_block) << "sync: error " << status; for (auto& promise : promises_) { promise.set_error(status.clone()); } promises_.clear(); } void LastBlock::on_fatal_error(td::Status status) { + VLOG(last_block) << "sync: fatal error " << status; fatal_error_ = std::move(status); on_sync_error(fatal_error_.clone()); } diff --git a/tonlib/tonlib/LastBlock.h b/tonlib/tonlib/LastBlock.h index 2add3bcfb9..bb2a62ed4e 100644 --- a/tonlib/tonlib/LastBlock.h +++ b/tonlib/tonlib/LastBlock.h @@ -22,6 +22,8 @@ #include "tonlib/Config.h" #include "tonlib/ExtClient.h" +#include "td/utils/tl_helpers.h" + namespace block { struct BlockProofChain; } @@ -89,25 +91,44 @@ struct LastBlockState { ton::BlockIdExt last_key_block_id; ton::BlockIdExt last_block_id; td::int64 utime{0}; + ton::BlockIdExt init_block_id; + + static constexpr td::int32 magic = 0xa7f171a4; + enum Version { None = 0, Magic, InitBlock, Next }; + static constexpr td::int32 version = Version::Next - 1; template void store(StorerT &storer) const { using td::store; using tonlib::store; + store(magic, storer); + store(version, storer); + store(zero_state_id, storer); store(last_key_block_id, storer); store(last_block_id, storer); store(utime, storer); + store(init_block_id, storer); } template void parse(ParserT &parser) { using td::parse; using tonlib::parse; + td::int32 version = 0; + if (parser.can_prefetch_int() && parser.prefetch_int_unsafe() == magic) { + td::int32 magic; + parse(magic, parser); + parse(version, parser); + } + parse(zero_state_id, parser); parse(last_key_block_id, parser); parse(last_block_id, parser); parse(utime, parser); + if (version >= InitBlock) { + parse(init_block_id, parser); + } } }; @@ -132,20 +153,36 @@ class LastBlock : public td::actor::Actor { td::Status fatal_error_; enum class QueryState { Empty, Active, Done }; - QueryState get_mc_info_state_{QueryState::Empty}; - QueryState get_last_block_state_{QueryState::Empty}; - QueryState check_init_block_state_{QueryState::Empty}; + QueryState get_mc_info_state_{QueryState::Empty}; // just to check zero state + QueryState check_init_block_state_{QueryState::Empty}; // init_block <---> last_key_block (from older to newer) + QueryState get_last_block_state_{QueryState::Empty}; // last_key_block_id --> ? // stats - td::Timer total_sync_; - td::Timer validate_; - td::uint32 queries_; + struct Stats { + td::Timer total_sync_; + td::Timer validate_; + td::uint32 queries_; + + void start() { + total_sync_ = td::Timer(); + validate_ = td::Timer(true); + queries_ = 0; + } + + friend td::StringBuilder &operator<<(td::StringBuilder &sb, const Stats &stats) { + return sb << " net queries: " << stats.queries_ << "\n" + << " total: " << stats.total_sync_ << " validation: " << stats.validate_; + } + }; + + Stats check_init_block_stats_; + Stats get_last_block_stats_; std::vector> promises_; - void do_check_init_block(ton::BlockIdExt from); + void do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to); void on_init_block_proof( - ton::BlockIdExt from, + ton::BlockIdExt from, ton::BlockIdExt to, td::Result> r_block_proof); void on_masterchain_info(td::Result> r_info); void do_get_last_block(); @@ -155,7 +192,11 @@ class LastBlock : public td::actor::Actor { ton::BlockIdExt from, td::Result> r_block_proof); - void update_zero_state(ton::ZeroStateIdExt zero_state_id); + td::Result> process_block_proof( + ton::BlockIdExt from, ton::ton_api::object_ptr block_proof); + + void update_state(block::BlockProofChain &chain); + void update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source); bool update_mc_last_block(ton::BlockIdExt mc_block_id); bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id); diff --git a/tonlib/tonlib/LastBlockStorage.cpp b/tonlib/tonlib/LastBlockStorage.cpp index f25632c0ba..dff4265d62 100644 --- a/tonlib/tonlib/LastBlockStorage.cpp +++ b/tonlib/tonlib/LastBlockStorage.cpp @@ -18,6 +18,8 @@ */ #include "LastBlockStorage.h" +#include "tonlib/utils.h" + #include "td/utils/as.h" #include "td/utils/filesystem.h" #include "td/utils/port/path.h" @@ -49,6 +51,7 @@ td::Result LastBlockStorage::get_state(td::Slice name) { } void LastBlockStorage::save_state(td::Slice name, LastBlockState state) { + VLOG(last_block) << "Save to cache: " << state; auto x = td::serialize(state); std::string y(x.size() + 8, 0); td::MutableSlice(y).substr(8).copy_from(x); diff --git a/tonlib/tonlib/Logging.cpp b/tonlib/tonlib/Logging.cpp index 043fe0190d..31dc275c00 100644 --- a/tonlib/tonlib/Logging.cpp +++ b/tonlib/tonlib/Logging.cpp @@ -17,6 +17,7 @@ Copyright 2017-2019 Telegram Systems LLP */ #include "Logging.h" +#include "utils.h" #include "auto/tl/tonlib_api.h" @@ -36,11 +37,9 @@ static td::FileLog file_log; static td::TsLog ts_log(&file_log); static td::NullLog null_log; -td::int32 VERBOSITY_NAME(abc) = VERBOSITY_NAME(DEBUG); -td::int32 VERBOSITY_NAME(bcd) = VERBOSITY_NAME(DEBUG); #define ADD_TAG(tag) \ { #tag, &VERBOSITY_NAME(tag) } -static const std::map log_tags{ADD_TAG(abc), ADD_TAG(bcd)}; +static const std::map log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block)}; #undef ADD_TAG td::Status Logging::set_current_stream(tonlib_api::object_ptr stream) { diff --git a/tonlib/tonlib/TestGiver.cpp b/tonlib/tonlib/TestGiver.cpp index 17c36a5534..b906193d9e 100644 --- a/tonlib/tonlib/TestGiver.cpp +++ b/tonlib/tonlib/TestGiver.cpp @@ -22,18 +22,18 @@ #include "td/utils/base64.h" namespace tonlib { -const block::StdAddress& TestGiver::address() { +const block::StdAddress& TestGiver::address() noexcept { static block::StdAddress res = block::StdAddress::parse("kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny").move_as_ok(); return res; } -vm::CellHash TestGiver::get_init_code_hash() { +vm::CellHash TestGiver::get_init_code_hash() noexcept { return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok()); } td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, - const block::StdAddress& dest_address) { + const block::StdAddress& dest_address) noexcept { td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; diff --git a/tonlib/tonlib/TestGiver.h b/tonlib/tonlib/TestGiver.h index a5d8b79c89..f8b62599fe 100644 --- a/tonlib/tonlib/TestGiver.h +++ b/tonlib/tonlib/TestGiver.h @@ -23,9 +23,9 @@ namespace tonlib { class TestGiver { public: static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static const block::StdAddress& address(); - static vm::CellHash get_init_code_hash(); + static const block::StdAddress& address() noexcept; + static vm::CellHash get_init_code_hash() noexcept; static td::Ref make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, - const block::StdAddress& dest_address); + const block::StdAddress& dest_address) noexcept; }; } // namespace tonlib diff --git a/tonlib/tonlib/TestWallet.cpp b/tonlib/tonlib/TestWallet.cpp index dadb1748f7..8bdf78c94c 100644 --- a/tonlib/tonlib/TestWallet.cpp +++ b/tonlib/tonlib/TestWallet.cpp @@ -24,13 +24,13 @@ #include "td/utils/base64.h" namespace tonlib { -td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) { +td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { auto code = get_init_code(); auto data = get_init_data(public_key); return GenericAccount::get_init_state(std::move(code), std::move(data)); } -td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) { +td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { std::string seq_no(4, 0); auto signature = private_key.sign(vm::CellBuilder().store_bytes(seq_no).finalize()->get_hash().as_slice()).move_as_ok(); @@ -39,7 +39,7 @@ td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) { + const block::StdAddress& dest_address) noexcept { td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; @@ -48,18 +48,22 @@ td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); + td::int32 send_mode = 3; + if (gramms == -1) { + gramms = 0; + send_mode += 128; + } block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); vm::CellString::store(cb, message, 35 * 8).ensure(); auto message_inner = cb.finalize(); - td::int8 send_mode = 3; auto message_outer = vm::CellBuilder().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 TestWallet::get_init_code() { +td::Ref TestWallet::get_init_code() noexcept { static auto res = [] { auto serialized_code = td::base64_decode( "te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/" @@ -70,11 +74,11 @@ td::Ref TestWallet::get_init_code() { return res; } -vm::CellHash TestWallet::get_init_code_hash() { +vm::CellHash TestWallet::get_init_code_hash() noexcept { return get_init_code()->get_hash(); } -td::Ref TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) { +td::Ref TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); } } // namespace tonlib diff --git a/tonlib/tonlib/TestWallet.h b/tonlib/tonlib/TestWallet.h index 28c97ce132..ef726b552d 100644 --- a/tonlib/tonlib/TestWallet.h +++ b/tonlib/tonlib/TestWallet.h @@ -27,14 +27,14 @@ namespace tonlib { class TestWallet { public: static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key); - static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key); + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address); + const block::StdAddress& dest_address) noexcept; - static td::Ref get_init_code(); - static vm::CellHash get_init_code_hash(); - static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key); + static td::Ref get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; }; } // namespace tonlib diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 7a7e1d8d97..7989e18e03 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -29,6 +29,8 @@ #include "tonlib/utils.h" #include "tonlib/keys/Mnemonic.h" +#include "tonlib/TonlibError.h" + #include "auto/tl/tonlib_api.hpp" #include "block/block-auto.h" #include "block/check-proof.h" @@ -45,17 +47,6 @@ namespace tonlib { -template -auto try_f(F&& f) noexcept -> decltype(f()) { - try { - return f(); - } catch (vm::VmError error) { - return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg()); - } -} - -#define TRY_VM(f) try_f([&] { return f; }) - tonlib_api::object_ptr status_to_tonlib_api(const td::Status& status) { return tonlib_api::make_object(status.code(), status.message().str()); } @@ -73,6 +64,7 @@ struct RawAccountState { td::int64 balance = -1; td::Ref code; td::Ref data; + std::string frozen_hash; block::AccountState::Info info; }; @@ -96,8 +88,12 @@ td::Result to_balance(td::Ref balance_ref) { class GetTransactionHistory : public td::actor::Actor { public: GetTransactionHistory(ExtClientRef ext_client_ref, block::StdAddress address, ton::LogicalTime lt, ton::Bits256 hash, - td::Promise promise) - : address_(std::move(address)), lt_(std::move(lt)), hash_(std::move(hash)), promise_(std::move(promise)) { + td::actor::ActorShared<> parent, td::Promise promise) + : address_(std::move(address)) + , lt_(std::move(lt)) + , hash_(std::move(hash)) + , parent_(std::move(parent)) + , promise_(std::move(promise)) { client_.set_client(ext_client_ref); } @@ -107,6 +103,7 @@ class GetTransactionHistory : public td::actor::Actor { ton::Bits256 hash_; ExtClient client_; td::int32 count_{10}; + td::actor::ActorShared<> parent_; td::Promise promise_; void check(td::Status status) { @@ -117,26 +114,22 @@ class GetTransactionHistory : public td::actor::Actor { } } - td::Status do_with_transactions(std::vector blkids, td::BufferSlice transactions) { - LOG(INFO) << "got up to " << count_ << " transactions for " << address_ << " from last transaction " << lt_ << ":" - << hash_.to_hex(); - block::TransactionList list; - list.blkids = std::move(blkids); - list.hash = hash_; - list.lt = lt_; - list.transactions_boc = std::move(transactions); - TRY_RESULT(info, list.validate()); - if (info.transactions.size() > static_cast(count_)) { - LOG(WARNING) << "obtained " << info.transactions.size() << " transaction, but only " << count_ - << " have been requested"; - } - promise_.set_value(std::move(info)); - return td::Status::OK(); + void with_transactions( + td::Result> r_transactions) { + check(do_with_transactions(std::move(r_transactions))); + stop(); } td::Status do_with_transactions( td::Result> r_transactions) { TRY_RESULT(transactions, std::move(r_transactions)); + TRY_RESULT_PREFIX(info, TRY_VM(do_with_transactions(std::move(transactions))), TonlibError::ValidateTransactions()); + promise_.set_value(std::move(info)); + return td::Status::OK(); + } + + td::Result do_with_transactions( + ton::lite_api::object_ptr transactions) { std::vector blkids; for (auto& id : transactions->ids_) { blkids.push_back(ton::create_block_id(std::move(id))); @@ -144,10 +137,21 @@ class GetTransactionHistory : public td::actor::Actor { return do_with_transactions(std::move(blkids), std::move(transactions->transactions_)); } - void with_transactions( - td::Result> r_transactions) { - check(TRY_VM(do_with_transactions(std::move(r_transactions)))); - stop(); + td::Result do_with_transactions(std::vector blkids, + td::BufferSlice transactions) { + LOG(INFO) << "got up to " << count_ << " transactions for " << address_ << " from last transaction " << lt_ << ":" + << hash_.to_hex(); + block::TransactionList list; + list.blkids = std::move(blkids); + list.hash = hash_; + list.lt = lt_; + list.transactions_boc = std::move(transactions); + TRY_RESULT(info, list.validate()); + if (info.transactions.size() > static_cast(count_)) { + LOG(WARNING) << "obtained " << info.transactions.size() << " transaction, but only " << count_ + << " have been requested"; + } + return info; } void start_up() override { @@ -166,25 +170,35 @@ class GetTransactionHistory : public td::actor::Actor { class GetRawAccountState : public td::actor::Actor { public: - GetRawAccountState(ExtClientRef ext_client_ref, block::StdAddress address, td::Promise&& promise) - : address_(std::move(address)), promise_(std::move(promise)) { + GetRawAccountState(ExtClientRef ext_client_ref, block::StdAddress address, td::actor::ActorShared<> parent, + td::Promise&& promise) + : address_(std::move(address)), promise_(std::move(promise)), parent_(std::move(parent)) { client_.set_client(ext_client_ref); } private: block::StdAddress address_; td::Promise promise_; + td::actor::ActorShared<> parent_; ExtClient client_; LastBlockState last_block_; void with_account_state(td::Result> r_account_state) { - promise_.set_result(TRY_VM(do_with_account_state(std::move(r_account_state)))); + check(do_with_account_state(std::move(r_account_state))); + } + + td::Status do_with_account_state( + td::Result> r_raw_account_state) { + TRY_RESULT(raw_account_state, std::move(r_raw_account_state)); + TRY_RESULT_PREFIX(state, TRY_VM(do_with_account_state(std::move(raw_account_state))), + TonlibError::ValidateAccountState()); + promise_.set_value(std::move(state)); stop(); + return td::Status::OK(); } td::Result do_with_account_state( - td::Result> r_account_state) { - TRY_RESULT(raw_account_state, std::move(r_account_state)); + ton::tl_object_ptr raw_account_state) { auto account_state = create_account_state(std::move(raw_account_state)); TRY_RESULT(info, account_state.validate(last_block_.last_block_id, address_)); auto serialized_state = account_state.state.clone(); @@ -192,10 +206,12 @@ class GetRawAccountState : public td::actor::Actor { res.info = std::move(info); LOG_IF(ERROR, res.info.gen_utime > last_block_.utime) << res.info.gen_utime << " " << last_block_.utime; auto cell = res.info.root; + std::ostringstream outp; + block::gen::t_Account.print_ref(outp, cell); + LOG(ERROR) << outp.str(); if (cell.is_null()) { return res; } - //block::gen::t_Account.print_ref(std::cerr, cell); block::gen::Account::Record_account account; if (!tlb::unpack_cell(cell, account)) { return td::Status::Error("Failed to unpack Account"); @@ -210,7 +226,14 @@ class GetRawAccountState : public td::actor::Actor { if (state_tag < 0) { return td::Status::Error("Failed to parse AccountState tag"); } - // TODO: handle frozen account + if (state_tag == block::gen::AccountState::account_frozen) { + block::gen::AccountState::Record_account_frozen state; + if (!tlb::csr_unpack(storage.state, state)) { + return td::Status::Error("Failed to parse AccountState"); + } + res.frozen_hash = state.state_hash.as_slice().str(); + return res; + } if (state_tag != block::gen::AccountState::account_active) { return res; } @@ -228,19 +251,24 @@ class GetRawAccountState : public td::actor::Actor { return res; } - void start_up() override { - client_.with_last_block([self = this](td::Result r_last_block) { - if (r_last_block.is_error()) { - return self->check(r_last_block.move_as_error()); - } - self->last_block_ = r_last_block.move_as_ok(); + void with_last_block(td::Result r_last_block) { + check(do_with_last_block(std::move(r_last_block))); + } - self->client_.send_query( - ton::lite_api::liteServer_getAccountState(ton::create_tl_lite_block_id(self->last_block_.last_block_id), - ton::create_tl_object( - self->address_.workchain, self->address_.addr)), - [self](auto r_state) { self->with_account_state(std::move(r_state)); }); - }); + td::Status do_with_last_block(td::Result r_last_block) { + TRY_RESULT_ASSIGN(last_block_, std::move(r_last_block)); + client_.send_query( + ton::lite_api::liteServer_getAccountState( + ton::create_tl_lite_block_id(last_block_.last_block_id), + ton::create_tl_object(address_.workchain, address_.addr)), + [self = this](auto r_state) { self->with_account_state(std::move(r_state)); }, + last_block_.last_block_id.id.seqno); + return td::Status::OK(); + } + + void start_up() override { + client_.with_last_block( + [self = this](td::Result r_last_block) { self->with_last_block(std::move(r_last_block)); }); } void check(td::Status status) { @@ -354,18 +382,19 @@ void TonlibClient::init_last_block() { raw_last_block_ = td::actor::create_actor("LastBlock", get_client_ref(), std::move(state), config_, td::make_unique(td::actor::actor_shared(this), config_generation_)); - client_.set_client(get_client_ref()); } void TonlibClient::on_result(td::uint64 id, tonlib_api::object_ptr response) { + VLOG(tonlib_query) << "Tonlib answer query " << td::tag("id", id) << " " << to_string(response); if (response->get_id() == tonlib_api::error::ID) { callback_->on_error(id, tonlib_api::move_object_as(response)); return; } callback_->on_result(id, std::move(response)); } + void TonlibClient::request(td::uint64 id, tonlib_api::object_ptr function) { - LOG(ERROR) << to_string(function); + VLOG(tonlib_query) << "Tonlib got query " << td::tag("id", id) << " " << to_string(function); if (function == nullptr) { LOG(ERROR) << "Receive empty static request"; return on_result(id, tonlib_api::make_object(400, "Request is empty")); @@ -408,6 +437,7 @@ void TonlibClient::close() { } tonlib_api::object_ptr TonlibClient::static_request( tonlib_api::object_ptr function) { + VLOG(tonlib_query) << "Tonlib got static query " << to_string(function); if (function == nullptr) { LOG(ERROR) << "Receive empty static request"; return tonlib_api::make_object(400, "Request is empty"); @@ -415,6 +445,7 @@ tonlib_api::object_ptr TonlibClient::static_request( tonlib_api::object_ptr response; downcast_call(*function, [&response](auto& request) { response = TonlibClient::do_static_request(request); }); + VLOG(tonlib_query) << " answer static query " << to_string(function); return response; } @@ -459,24 +490,35 @@ tonlib_api::object_ptr TonlibClient::do_static_request(const runner.run_all(); return tonlib_api::make_object(); } + +td::Result get_public_key(td::Slice public_key) { + TRY_RESULT_PREFIX(address, block::PublicKey::parse(public_key), TonlibError::InvalidPublicKey()); + return address; +} + td::Result get_account_address(const tonlib_api::raw_initialAccountState& raw_state) { - TRY_RESULT(code, vm::std_boc_deserialize(raw_state.code_)); - TRY_RESULT(data, vm::std_boc_deserialize(raw_state.data_)); + TRY_RESULT_PREFIX(code, vm::std_boc_deserialize(raw_state.code_), TonlibError::InvalidBagOfCells("raw_state.code")); + TRY_RESULT_PREFIX(data, vm::std_boc_deserialize(raw_state.data_), TonlibError::InvalidBagOfCells("raw_state.data")); return GenericAccount::get_address(0 /*zerochain*/, GenericAccount::get_init_state(std::move(code), std::move(data))); } td::Result get_account_address(const tonlib_api::testWallet_initialAccountState& test_wallet_state) { - TRY_RESULT(key_bytes, block::PublicKey::parse(test_wallet_state.public_key_)); + TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); return GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)); } td::Result get_account_address(const tonlib_api::wallet_initialAccountState& test_wallet_state) { - TRY_RESULT(key_bytes, block::PublicKey::parse(test_wallet_state.public_key_)); + TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); return GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)); } +td::Result get_account_address(td::Slice account_address) { + TRY_RESULT_PREFIX(address, block::StdAddress::parse(account_address), TonlibError::InvalidAccountAddress()); + return address; +} + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::raw_getAccountAddress& request) { auto r_account_address = get_account_address(*request.initital_account_state_); @@ -485,6 +527,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testWallet_getAccountAddress& request) { auto r_account_address = get_account_address(*request.initital_account_state_); @@ -493,6 +536,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::wallet_getAccountAddress& request) { auto r_account_address = get_account_address(*request.initital_account_state_); @@ -501,6 +545,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } return tonlib_api::make_object(r_account_address.ok().rserialize(true)); } + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testGiver_getAccountAddress& request) { return tonlib_api::make_object(TestGiver::address().rserialize(true)); @@ -508,7 +553,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::unpackAccountAddress& request) { - auto r_account_address = block::StdAddress::parse(request.account_address_); + auto r_account_address = get_account_address(request.account_address_); if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.move_as_error()); } @@ -521,10 +566,10 @@ tonlib_api::object_ptr TonlibClient::do_static_request( tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::packAccountAddress& request) { if (!request.account_address_) { - return status_to_tonlib_api(td::Status::Error(400, "Field account_address must not be empty")); + return status_to_tonlib_api(TonlibError::EmptyField("account_address")); } if (request.account_address_->addr_.size() != 32) { - return status_to_tonlib_api(td::Status::Error(400, "Field account_address.addr must not be exactly 32 bytes")); + return status_to_tonlib_api(TonlibError::InvalidField("account_address.addr", "must be 32 bytes long")); } block::StdAddress addr; addr.workchain = request.account_address_->workchain_id_; @@ -545,10 +590,10 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, return td::Status::Error(400, "Tonlib is already inited"); } if (!request.options_) { - return td::Status::Error(400, "Field options must not be empty"); + return TonlibError::EmptyField("options"); } if (!request.options_->keystore_type_) { - return td::Status::Error(400, "Field options.keystore_type must not be empty"); + return TonlibError::EmptyField("options.keystore_type"); } td::Result> r_kv; @@ -571,15 +616,14 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, } td::Status TonlibClient::set_config(object_ptr config) { - if (!config) { - return td::Status::Error(400, "config is empty"); - } + CHECK(config); if (config->config_.empty()) { - return td::Status::Error(400, "config is empty"); + return TonlibError::InvalidConfig("config is empty"); } - TRY_RESULT(new_config, Config::parse(std::move(config->config_))); + TRY_RESULT_PREFIX(new_config, Config::parse(std::move(config->config_)), + TonlibError::InvalidConfig("can't parse config")); if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) { - return td::Status::Error("No lite clients in config"); + return TonlibError::InvalidConfig("no lite clients"); } config_ = std::move(new_config); config_generation_++; @@ -592,6 +636,7 @@ td::Status TonlibClient::set_config(object_ptr config) { ignore_cache_ = config->ignore_cache_; init_ext_client(); init_last_block(); + client_.set_client(get_client_ref()); return td::Status::OK(); } @@ -605,6 +650,9 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request, td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise) { + if (!request.config_) { + return TonlibError::EmptyField("config"); + } TRY_STATUS(set_config(std::move(request.config_))); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); @@ -677,6 +725,9 @@ td::Result> to_raw_message_or_th TRY_RESULT(balance, to_balance(msg_info.value)); TRY_RESULT(src, to_std_address(msg_info.src)); TRY_RESULT(dest, to_std_address(msg_info.dest)); + TRY_RESULT(fwd_fee, to_balance(msg_info.fwd_fee)); + TRY_RESULT(ihr_fee, to_balance(msg_info.ihr_fee)); + auto created_lt = static_cast(msg_info.created_lt); td::Ref body; if (message.body->prefetch_long(1) == 0) { body = std::move(message.body); @@ -684,6 +735,7 @@ td::Result> to_raw_message_or_th } else { body = vm::load_cell_slice_ref(message.body->prefetch_ref()); } + auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str(); std::string body_message; if (body->size() >= 32 && body->prefetch_long(32) == 0) { body.write().fetch_long(32); @@ -693,7 +745,8 @@ td::Result> to_raw_message_or_th } } - return tonlib_api::make_object(std::move(src), std::move(dest), balance, + return tonlib_api::make_object(std::move(src), std::move(dest), balance, fwd_fee, + ihr_fee, created_lt, std::move(body_hash), std::move(body_message)); } case block::gen::CommonMsgInfo::ext_in_msg_info: { @@ -702,7 +755,16 @@ td::Result> to_raw_message_or_th return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info"); } TRY_RESULT(dest, to_std_address(msg_info.dest)); - return tonlib_api::make_object("", std::move(dest), 0, ""); + td::Ref body; + if (message.body->prefetch_long(1) == 0) { + body = std::move(message.body); + body.write().advance(1); + } else { + body = vm::load_cell_slice_ref(message.body->prefetch_ref()); + } + auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str(); + return tonlib_api::make_object("", std::move(dest), 0, 0, 0, 0, std::move(body_hash), + ""); } case block::gen::CommonMsgInfo::ext_out_msg_info: { block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info; @@ -710,7 +772,7 @@ td::Result> to_raw_message_or_th return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info"); } TRY_RESULT(src, to_std_address(msg_info.src)); - return tonlib_api::make_object(std::move(src), "", 0, ""); + return tonlib_api::make_object(std::move(src), "", 0, 0, 0, 0, "", ""); } } @@ -728,6 +790,7 @@ td::Result> to_raw_transacti tonlib_api::object_ptr in_msg; std::vector> out_msgs; td::int64 fees = 0; + td::int64 storage_fee = 0; if (info.transaction.not_null()) { TRY_RESULT(copy_data, vm::std_boc_serialize(info.transaction)); data = copy_data.as_slice().str(); @@ -736,8 +799,7 @@ td::Result> to_raw_transacti return td::Status::Error("Failed to unpack Transaction"); } - TRY_RESULT(copy_fees, to_balance(trans.total_fees)); - fees = copy_fees; + TRY_RESULT_ASSIGN(fees, to_balance(trans.total_fees)); std::ostringstream outp; block::gen::t_Transaction.print_ref(outp, info.transaction); @@ -757,15 +819,22 @@ td::Result> to_raw_transacti vm::Dictionary dict{trans.r1.out_msgs, 15}; for (int x = 0; x < trans.outmsg_cnt && x < 100; x++) { TRY_RESULT(out_msg, to_raw_message(dict.lookup_ref(td::BitArray<15>{x}))); + fees += out_msg->fwd_fee_; + fees += out_msg->ihr_fee_; out_msgs.push_back(std::move(out_msg)); } } + td::RefInt256 storage_fees; + if (!block::tlb::t_TransactionDescr.get_storage_fees(trans.description, storage_fees)) { + return td::Status::Error("Failed to fetch storage fee from transaction"); + } + storage_fee = storage_fees->to_long(); } return tonlib_api::make_object( info.now, data, tonlib_api::make_object(info.prev_trans_lt, info.prev_trans_hash.as_slice().str()), - fees, std::move(in_msg), std::move(out_msgs)); + fees, storage_fee, fees - storage_fee, std::move(in_msg), std::move(out_msgs)); } td::Result> to_raw_transaction(block::Transaction::Info&& info) { @@ -791,8 +860,11 @@ td::Result> to_raw_transact td::Result> to_testWallet_accountState( RawAccountState&& raw_state) { - if (raw_state.data.is_null()) { - return td::Status::Error(400, "Not a TestWallet"); + if (raw_state.code.is_null()) { + return TonlibError::AccountNotInited(); + } + if (raw_state.code->prefetch_ref()->get_hash() != TestWallet::get_init_code_hash()) { + return TonlibError::AccountTypeUnexpected("TestWallet"); } auto ref = raw_state.data->prefetch_ref(); auto cs = vm::load_cell_slice(std::move(ref)); @@ -806,8 +878,11 @@ td::Result> to_testW td::Result> to_wallet_accountState( RawAccountState&& raw_state) { - if (raw_state.data.is_null()) { - return td::Status::Error(400, "Not a Wallet"); + if (raw_state.code.is_null()) { + return TonlibError::AccountNotInited(); + } + if (raw_state.code->prefetch_ref()->get_hash() != Wallet::get_init_code_hash()) { + return TonlibError::AccountTypeUnexpected("Wallet"); } auto ref = raw_state.data->prefetch_ref(); auto cs = vm::load_cell_slice(std::move(ref)); @@ -821,8 +896,11 @@ td::Result> to_wallet_ac td::Result> to_testGiver_accountState( RawAccountState&& raw_state) { - if (raw_state.data.is_null()) { - return td::Status::Error(400, "Not a TestGiver"); + if (raw_state.code.is_null()) { + return TonlibError::AccountNotInited(); + } + if (raw_state.code->prefetch_ref()->get_hash() != TestGiver::get_init_code_hash()) { + return TonlibError::AccountTypeUnexpected("TestGiver"); } auto ref = raw_state.data->prefetch_ref(); auto cs = vm::load_cell_slice(std::move(ref)); @@ -865,21 +943,18 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, td::Promise>&& promise) { td::Ref init_state; if (!request.initial_account_state_.empty()) { - TRY_RESULT(new_init_state, vm::std_boc_deserialize(request.initial_account_state_)); + TRY_RESULT_PREFIX(new_init_state, vm::std_boc_deserialize(request.initial_account_state_), + TonlibError::InvalidBagOfCells("initial_account_state")); init_state = std::move(new_init_state); } - TRY_RESULT(data, vm::std_boc_deserialize(request.data_)); - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT_PREFIX(data, vm::std_boc_deserialize(request.data_), TonlibError::InvalidBagOfCells("data")); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); auto message = GenericAccount::create_ext_message(account_address, std::move(init_state), std::move(data)); client_.send_query(ton::lite_api::liteServer_sendMessage(vm::std_boc_serialize(message).move_as_ok()), [promise = std::move(promise)](auto r_info) mutable { - if (r_info.is_error()) { - promise.set_error(r_info.move_as_error()); - } else { - auto info = r_info.move_as_ok(); - LOG(ERROR) << "info: " << to_string(info); - promise.set_value(tonlib_api::make_object()); - } + TRY_RESULT_PROMISE(promise, info, std::move(r_info)); + LOG(ERROR) << "info: " << to_string(info); + promise.set_value(tonlib_api::make_object()); }); return td::Status::OK(); } @@ -887,30 +962,28 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, td::Status TonlibClient::do_request(tonlib_api::raw_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_raw_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_raw_accountState(std::move(state))); + }); return td::Status::OK(); } td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } if (!request.from_transaction_id_) { - return td::Status::Error(400, "Field from_transaction_id must not be empty"); + return TonlibError::EmptyField("from_transaction_id"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); auto lt = request.from_transaction_id_->lt_; auto hash_str = request.from_transaction_id_->hash_; if (hash_str.size() != 32) { @@ -919,24 +992,22 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request, td::Bits256 hash; hash.as_slice().copy_from(hash_str); - td::actor::create_actor( - "GetTransactionHistory", client_.get_client(), account_address, lt, hash, + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetTransactionHistory", client_.get_client(), account_address, lt, hash, actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_info) mutable { - if (r_info.is_error()) { - return promise.set_error(r_info.move_as_error()); - } - promise.set_result(to_raw_transactions(r_info.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, info, std::move(r_info)); + promise.set_result(to_raw_transactions(std::move(info))); + }); return td::Status::OK(); } td::Result from_tonlib(tonlib_api::inputKey& input_key) { if (!input_key.key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } - TRY_RESULT(key_bytes, block::PublicKey::parse(input_key.key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(input_key.key_->public_key_)); return KeyStorage::InputKey{{td::SecureString(key_bytes.key), std::move(input_key.key_->secret_)}, std::move(input_key.local_password_)}; } @@ -962,15 +1033,15 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_init& request, td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } if (!request.private_key_) { - return td::Status::Error(400, "Field private_key must not be empty"); + return TonlibError::EmptyField("private_key"); } if (request.message_.size() > TestWallet::max_message_size) { - return td::Status::Error(400, "Message is too long"); + return TonlibError::MessageTooLong(); } - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); account_address.bounceable = false; TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( @@ -979,43 +1050,37 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); std::string init_state; if (request.seqno_ == 0) { - TRY_RESULT(public_key, private_key.get_public_key()); + TRY_RESULT_PREFIX(public_key, private_key.get_public_key(), TonlibError::Internal()); init_state = vm::std_boc_serialize(TestWallet::get_init_state(public_key)).move_as_ok().as_slice().str(); } + auto message = + TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, request.message_, account_address); + auto message_hash = message->get_hash().as_slice().str(); td::Promise> new_promise = - [promise = std::move(promise)](td::Result> res) mutable { - if (res.is_error()) { - promise.set_error(res.move_as_error()); - } else { - promise.set_value(tonlib_api::make_object(0)); - } + [promise = std::move(promise), + message_hash = std::move(message_hash)](td::Result> res) mutable { + TRY_RESULT_PROMISE(promise, ok, std::move(res)); + promise.set_value(tonlib_api::make_object(0, std::move(message_hash))); }; - return do_request( - tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize(true)), std::move(init_state), - vm::std_boc_serialize(TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, - request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), - std::move(new_promise)); + return do_request(tonlib_api::raw_sendMessage( + tonlib_api::make_object(address.rserialize(true)), + std::move(init_state), vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), + std::move(new_promise)); } td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_testWallet_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_testWallet_accountState(std::move(state))); + }); return td::Status::OK(); } @@ -1023,7 +1088,7 @@ td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& requ td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request, td::Promise>&& promise) { if (!request.private_key_) { - return td::Status::Error(400, "Field private_key must not be empty"); + return TonlibError::EmptyField("private_key"); } TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto init_state = Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())); @@ -1040,16 +1105,17 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request, td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } if (!request.private_key_) { - return td::Status::Error(400, "Field private_key must not be empty"); + return TonlibError::EmptyField("private_key"); } if (request.message_.size() > Wallet::max_message_size) { - return td::Status::Error(400, "Message is too long"); + return TonlibError::MessageTooLong(); } - TRY_RESULT(valid_until, td::narrow_cast_safe(request.valid_until_)); - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT_PREFIX(valid_until, td::narrow_cast_safe(request.valid_until_), + TonlibError::InvalidField("valid_until", "overflow")); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); account_address.bounceable = false; TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( @@ -1058,43 +1124,37 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); std::string init_state; if (request.seqno_ == 0) { - TRY_RESULT(public_key, private_key.get_public_key()); + TRY_RESULT_PREFIX(public_key, private_key.get_public_key(), TonlibError::Internal()); init_state = vm::std_boc_serialize(Wallet::get_init_state(public_key)).move_as_ok().as_slice().str(); } + auto message = Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, + request.message_, account_address); + auto message_hash = message->get_hash().as_slice().str(); td::Promise> new_promise = - [promise = std::move(promise), valid_until](td::Result> res) mutable { - if (res.is_error()) { - promise.set_error(res.move_as_error()); - } else { - promise.set_value(tonlib_api::make_object(valid_until)); - } + [promise = std::move(promise), valid_until, + message_hash = std::move(message_hash)](td::Result> res) mutable { + TRY_RESULT_PROMISE(promise, ok, std::move(res)); + promise.set_value(tonlib_api::make_object(valid_until, std::move(message_hash))); }; - return do_request( - tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize(true)), std::move(init_state), - vm::std_boc_serialize(Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, - request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), - std::move(new_promise)); + return do_request(tonlib_api::raw_sendMessage( + tonlib_api::make_object(address.rserialize(true)), + std::move(init_state), vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), + std::move(new_promise)); } td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_wallet_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_wallet_accountState(std::move(state))); + }); return td::Status::OK(); } @@ -1102,60 +1162,52 @@ td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } if (request.message_.size() > TestGiver::max_message_size) { - return td::Status::Error(400, "Message is too long"); + return TonlibError::MessageTooLong(); } - TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); account_address.bounceable = false; + auto message = TestGiver::make_a_gift_message(request.seqno_, request.amount_, request.message_, account_address); + auto message_hash = message->get_hash().as_slice().str(); td::Promise> new_promise = - [promise = std::move(promise)](td::Result> res) mutable { - if (res.is_error()) { - promise.set_error(res.move_as_error()); - } else { - promise.set_value(tonlib_api::make_object(0)); - } + [promise = std::move(promise), + message_hash = std::move(message_hash)](td::Result> res) mutable { + TRY_RESULT_PROMISE(promise, ok, std::move(res)); + promise.set_value(tonlib_api::make_object(0, std::move(message_hash))); }; return do_request(tonlib_api::raw_sendMessage( tonlib_api::make_object(TestGiver::address().rserialize(true)), "", - vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_, - request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), + vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), std::move(new_promise)); } td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request, td::Promise>&& promise) { - td::actor::create_actor( - "GetAccountState", client_.get_client(), TestGiver::address(), + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), TestGiver::address(), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_testGiver_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_testGiver_accountState(std::move(state))); + }); return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::generic_getAccountState& request, td::Promise>&& promise) { if (!request.account_address_) { - return td::Status::Error(400, "Field account_address must not be empty"); + return TonlibError::EmptyField("account_address"); } - TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), [promise = std::move(promise)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - promise.set_result(to_generic_accountState(r_state.move_as_ok())); - }) - .release(); + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + promise.set_result(to_generic_accountState(std::move(state))); + }); return td::Status::OK(); } @@ -1197,7 +1249,6 @@ class GenericSendGrams : public TonlibQueryActor { void check(td::Status status) { if (status.is_error()) { - LOG(ERROR) << status; promise_.set_error(std::move(status)); return stop(); } @@ -1208,17 +1259,16 @@ class GenericSendGrams : public TonlibQueryActor { } td::Status do_start_up() { - alarm_timestamp() = td::Timestamp::in(15); if (!send_grams_.destination_) { - return td::Status::Error(400, "Field destination must not be empty"); + return TonlibError::EmptyField("destination"); } - TRY_RESULT(destination_address, block::StdAddress::parse(send_grams_.destination_->account_address_)); + TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_)); is_destination_bounce_ = destination_address.bounceable; if (!send_grams_.source_) { - return td::Status::Error(400, "Field source must not be empty"); + return TonlibError::EmptyField("destination"); } - TRY_RESULT(source_address, block::StdAddress::parse(send_grams_.source_->account_address_)); + TRY_RESULT(source_address, get_account_address(send_grams_.source_->account_address_)); source_address_ = std::move(source_address); send_query(tonlib_api::generic_getAccountState( @@ -1257,19 +1307,19 @@ class GenericSendGrams : public TonlibQueryActor { source_state_ = std::move(state); if (source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && send_grams_.private_key_ && send_grams_.private_key_->key_) { - TRY_RESULT(key_bytes, block::PublicKey::parse(send_grams_.private_key_->key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(send_grams_.private_key_->key_->public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); if (GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)).addr == source_address_.addr) { auto state = ton::move_tl_object_as(source_state_); source_state_ = tonlib_api::make_object( - tonlib_api::make_object(-1, 0, nullptr, + tonlib_api::make_object(state->account_state_->balance_, 0, nullptr, state->account_state_->sync_utime_)); } else if (GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)).addr == source_address_.addr) { auto state = ton::move_tl_object_as(source_state_); source_state_ = tonlib_api::make_object( - tonlib_api::make_object(-1, 0, nullptr, + tonlib_api::make_object(state->account_state_->balance_, 0, nullptr, state->account_state_->sync_utime_)); } } @@ -1285,54 +1335,68 @@ class GenericSendGrams : public TonlibQueryActor { destination_state_ = std::move(state); if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounce_ && !send_grams_.allow_send_to_uninited_) { - return td::Status::Error(400, "DANGEROUS_TRANSACTION: Transfer to uninited wallet"); + return TonlibError::DangerousTransaction("Transfer to uninited wallet"); } return do_loop(); } - void alarm() override { - check(td::Status::Error("Timeout")); - } td::Status do_loop() { if (!source_state_ || !destination_state_) { return td::Status::OK(); } - downcast_call(*source_state_, - td::overloaded( - [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { - send_query(tonlib_api::testGiver_sendGrams( - std::move(send_grams_.destination_), test_giver_state.account_state_->seqno_, - send_grams_.amount_, std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { - send_query(tonlib_api::testWallet_sendGrams( - std::move(send_grams_.private_key_), std::move(send_grams_.destination_), - test_wallet_state.account_state_->seqno_, send_grams_.amount_, - std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateWallet& test_wallet_state) { - send_query(tonlib_api::wallet_sendGrams( - std::move(send_grams_.private_key_), std::move(send_grams_.destination_), - test_wallet_state.account_state_->seqno_, - send_grams_.timeout_ == 0 - ? 60 + test_wallet_state.account_state_->sync_utime_ - : send_grams_.timeout_ + test_wallet_state.account_state_->sync_utime_, - send_grams_.amount_, std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateUninited&) { - promise_.set_error(td::Status::Error(400, "Account is not inited")); - stop(); - }, - [&](tonlib_api::generic_accountStateRaw&) { - promise_.set_error(td::Status::Error(400, "Unknown account type")); - stop(); - })); + downcast_call( + *source_state_, + td::overloaded( + [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { + auto amount = send_grams_.amount_; + send_query(tonlib_api::testGiver_sendGrams(std::move(send_grams_.destination_), + test_giver_state.account_state_->seqno_, amount, + std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { + auto amount = send_grams_.amount_; + auto balance = test_wallet_state.account_state_->balance_; + if (false && amount == balance) { + amount = -1; + } else if (amount >= balance) { + promise_.set_error(TonlibError::NotEnoughFunds()); + return stop(); + } + send_query(tonlib_api::testWallet_sendGrams( + std::move(send_grams_.private_key_), std::move(send_grams_.destination_), + test_wallet_state.account_state_->seqno_, amount, std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateWallet& wallet_state) { + auto amount = send_grams_.amount_; + auto balance = wallet_state.account_state_->balance_; + if (false && amount == balance) { + amount = -1; + } else if (amount >= balance) { + promise_.set_error(TonlibError::NotEnoughFunds()); + return stop(); + } + send_query( + tonlib_api::wallet_sendGrams(std::move(send_grams_.private_key_), std::move(send_grams_.destination_), + wallet_state.account_state_->seqno_, + send_grams_.timeout_ == 0 + ? 60 + wallet_state.account_state_->sync_utime_ + : send_grams_.timeout_ + wallet_state.account_state_->sync_utime_, + amount, std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateUninited&) { + promise_.set_error(TonlibError::AccountNotInited()); + stop(); + }, + [&](tonlib_api::generic_accountStateRaw&) { + promise_.set_error(TonlibError::AccountTypeUnknown()); + stop(); + })); return td::Status::OK(); } }; @@ -1340,7 +1404,7 @@ class GenericSendGrams : public TonlibQueryActor { td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, td::Promise>&& promise) { if (request.timeout_ < 0 || request.timeout_ > 300) { - return td::Status::Error(400, "Invalid timeout: must be between 0 and 300"); + return TonlibError::InvalidField("timeout", "must be between 0 and 300"); } auto id = actor_id_++; actors_[id] = td::actor::create_actor("GenericSendGrams", actor_shared(this, id), @@ -1348,11 +1412,19 @@ td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, return td::Status::OK(); } +td::Result public_key_from_bytes(td::Slice bytes) { + TRY_RESULT_PREFIX(key_bytes, block::PublicKey::from_bytes(bytes), TonlibError::Internal()); + return key_bytes; +} + td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request, td::Promise>&& promise) { - TRY_RESULT(key, key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_), - std::move(request.random_extra_seed_))); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT_PREFIX( + key, + key_storage_.create_new_key(std::move(request.local_password_), std::move(request.mnemonic_password_), + std::move(request.random_extra_seed_)), + TonlibError::Internal()); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1360,7 +1432,7 @@ td::Status TonlibClient::do_request(const tonlib_api::createNewKey& request, td::Status TonlibClient::do_request(const tonlib_api::exportKey& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(exported_key, key_storage_.export_key(std::move(input_key))); @@ -1371,13 +1443,20 @@ td::Status TonlibClient::do_request(const tonlib_api::exportKey& request, td::Status TonlibClient::do_request(const tonlib_api::deleteKey& request, td::Promise>&& promise) { if (!request.key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } - TRY_RESULT(key_bytes, block::PublicKey::parse(request.key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(request.key_->public_key_)); KeyStorage::Key key; key.public_key = td::SecureString(key_bytes.key); key.secret = std::move(request.key_->secret_); - TRY_STATUS(key_storage_.delete_key(key)); + TRY_STATUS_PREFIX(key_storage_.delete_key(key), TonlibError::KeyUnknown()); + promise.set_value(tonlib_api::make_object()); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(const tonlib_api::deleteAllKeys& request, + td::Promise>&& promise) { + TRY_STATUS_PREFIX(key_storage_.delete_all_keys(), TonlibError::Internal()); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } @@ -1385,11 +1464,11 @@ td::Status TonlibClient::do_request(const tonlib_api::deleteKey& request, td::Status TonlibClient::do_request(const tonlib_api::importKey& request, td::Promise>&& promise) { if (!request.exported_key_) { - return td::Status::Error(400, "Field exported_key must not be empty"); + return TonlibError::EmptyField("exported_key"); } TRY_RESULT(key, key_storage_.import_key(std::move(request.local_password_), std::move(request.mnemonic_password_), KeyStorage::ExportedKey{std::move(request.exported_key_->word_list_)})); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1397,13 +1476,13 @@ td::Status TonlibClient::do_request(const tonlib_api::importKey& request, td::Status TonlibClient::do_request(const tonlib_api::exportPemKey& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } if (!request.input_key_->key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } - TRY_RESULT(key_bytes, block::PublicKey::parse(request.input_key_->key_->public_key_)); + TRY_RESULT(key_bytes, get_public_key(request.input_key_->key_->public_key_)); KeyStorage::InputKey input_key{{td::SecureString(key_bytes.key), std::move(request.input_key_->key_->secret_)}, std::move(request.input_key_->local_password_)}; TRY_RESULT(exported_pem_key, key_storage_.export_pem_key(std::move(input_key), std::move(request.key_password_))); @@ -1414,11 +1493,11 @@ td::Status TonlibClient::do_request(const tonlib_api::exportPemKey& request, td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request, td::Promise>&& promise) { if (!request.exported_key_) { - return td::Status::Error(400, "Field exported_key must not be empty"); + return TonlibError::EmptyField("exported_key"); } TRY_RESULT(key, key_storage_.import_pem_key(std::move(request.local_password_), std::move(request.key_password_), KeyStorage::ExportedPemKey{std::move(request.exported_key_->pem_)})); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1426,7 +1505,7 @@ td::Status TonlibClient::do_request(const tonlib_api::importPemKey& request, td::Status TonlibClient::do_request(const tonlib_api::exportEncryptedKey& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(exported_key, key_storage_.export_encrypted_key(std::move(input_key), request.key_password_)); @@ -1437,12 +1516,12 @@ td::Status TonlibClient::do_request(const tonlib_api::exportEncryptedKey& reques td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& request, td::Promise>&& promise) { if (!request.exported_encrypted_key_) { - return td::Status::Error(400, "Field exported_encrypted_key must not be empty"); + return TonlibError::EmptyField("exported_encrypted_key"); } TRY_RESULT(key, key_storage_.import_encrypted_key( std::move(request.local_password_), std::move(request.key_password_), KeyStorage::ExportedEncryptedKey{std::move(request.exported_encrypted_key_->data_)})); - TRY_RESULT(key_bytes, block::PublicKey::from_bytes(key.public_key.as_slice())); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } @@ -1450,10 +1529,10 @@ td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& reques td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& request, td::Promise>&& promise) { if (!request.input_key_) { - return td::Status::Error(400, "Field input_key must not be empty"); + return TonlibError::EmptyField("input_key"); } if (!request.input_key_->key_) { - return td::Status::Error(400, "Field key must not be empty"); + return TonlibError::EmptyField("key"); } TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(key, key_storage_.change_local_password(std::move(input_key), std::move(request.new_local_password_))); @@ -1477,7 +1556,8 @@ td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryResult& r td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& request, td::Promise>&& promise) { send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, - td::Status::Error(request.error_->code_, request.error_->message_), + td::Status::Error(request.error_->code_, request.error_->message_) + .move_as_error_prefix(TonlibError::LiteServerNetwork()), [promise = std::move(promise)](td::Result res) mutable { if (res.is_ok()) { promise.set_value(tonlib_api::make_object()); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 9006355133..649df52d74 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -47,13 +47,13 @@ class TonlibClient : public td::actor::Actor { private: enum class State { Uninited, Running, Closed } state_ = State::Uninited; td::unique_ptr callback_; + + // Config Config config_; td::uint32 config_generation_{0}; std::string blockchain_name_; bool ignore_cache_{false}; - bool use_callbacks_for_network_{false}; - td::actor::ActorId ext_client_outbound_; // KeyStorage std::shared_ptr kv_; @@ -62,6 +62,7 @@ class TonlibClient : public td::actor::Actor { // network td::actor::ActorOwn raw_client_; + td::actor::ActorId ext_client_outbound_; td::actor::ActorOwn raw_last_block_; ExtClient client_; @@ -158,6 +159,7 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::exportKey& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::deleteKey& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::deleteAllKeys& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::importKey& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::exportPemKey& request, diff --git a/tonlib/tonlib/TonlibError.h b/tonlib/tonlib/TonlibError.h new file mode 100644 index 0000000000..e0b4d0b189 --- /dev/null +++ b/tonlib/tonlib/TonlibError.h @@ -0,0 +1,157 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ + +#pragma once + +#include "td/utils/Status.h" +#include "common/errorcode.h" +// NEED_MNEMONIC_PASSWORD +// KEY_UNKNOWN +// KEY_DECRYPT +// INVALID_MNEMONIC +// INVALID_BAG_OF_CELLS +// INVALID_PUBLIC_KEY +// INVALID_ACCOUNT_ADDRESS +// INVALID_CONFIG +// INVALID_PEM_KEY +// MESSAGE_TOO_LONG +// EMPTY_FIELD +// INVALID_FIELD +// DANGEROUS_TRANSACTION +// ACCOUNT_NOT_INITED +// ACCOUNT_TYPE_UNKNOWN +// ACCOUNT_TYPE_UNEXPECTED +// VALIDATE_ACCOUNT_STATE +// VALIDATE_TRANSACTION +// VALIDATE_ZERO_STATE +// VALIDATE_BLOCK_PROOF +// NO_LITE_SERVERS +// LITE_SERVER_NETWORK +// CANCELLED +// NOT_ENOUGH_FUNDS +// LITE_SERVER +// INTERNAL + +namespace tonlib { +struct TonlibError { + static td::Status NeedMnemonicPassword() { + return td::Status::Error(400, "NEED_MNEMONIC_PASSWORD"); + } + static td::Status InvalidMnemonic() { + return td::Status::Error(400, "INVALID_MNEMONIC: Invalid mnemonic words or password (invalid checksum)"); + } + static td::Status InvalidBagOfCells(td::Slice comment) { + return td::Status::Error(400, PSLICE() << "INVALID_BAG_OF_CELLS: " << comment); + } + static td::Status InvalidPublicKey() { + return td::Status::Error(400, "INVALID_PUBLIC_KEY"); + } + static td::Status InvalidAccountAddress() { + return td::Status::Error(400, "INVALID_ACCOUNT_ADDRESS"); + } + static td::Status InvalidConfig(td::Slice reason) { + return td::Status::Error(400, PSLICE() << "INVALID_CONFIG: " << reason); + } + static td::Status InvalidPemKey() { + return td::Status::Error(400, "INVALID_PEM_KEY"); + } + static td::Status MessageTooLong() { + return td::Status::Error(400, "MESSAGE_TOO_LONG"); + } + static td::Status EmptyField(td::Slice field_name) { + return td::Status::Error(400, PSLICE() << "EMPTY_FIELD: Field " << field_name << " must not be emtpy"); + } + static td::Status InvalidField(td::Slice field_name, td::Slice reason) { + return td::Status::Error(400, PSLICE() << "INVALID_FIELD: Field " << field_name << " has invalid value " << reason); + } + static td::Status DangerousTransaction(td::Slice reason) { + return td::Status::Error(400, PSLICE() << "DANGEROUS_TRANSACTION: " << reason); + } + static td::Status AccountNotInited() { + return td::Status::Error(400, "ACCOUNT_NOT_INITED"); + } + static td::Status AccountTypeUnknown() { + return td::Status::Error(400, "ACCOUNT_TYPE_UNKNOWN"); + } + static td::Status AccountTypeUnexpected(td::Slice expected) { + return td::Status::Error(400, PSLICE() << "ACCOUNT_TYPE_UNEXPECTED: not a " << expected); + } + static td::Status Internal() { + return td::Status::Error(500, "INTERNAL"); + } + static td::Status Internal(td::Slice message) { + return td::Status::Error(500, PSLICE() << "INTERNAL: " << message); + } + static td::Status KeyUnknown() { + return td::Status::Error(500, "KEY_UNKNOWN"); + } + static td::Status KeyDecrypt() { + return td::Status::Error(500, "KEY_DECRYPT"); + } + static td::Status ValidateAccountState() { + return td::Status::Error(500, "VALIDATE_ACCOUNT_STATE"); + } + static td::Status ValidateTransactions() { + return td::Status::Error(500, "VALIDATE_TRANSACTION"); + } + static td::Status ValidateZeroState(td::Slice message) { + return td::Status::Error(500, PSLICE() << "VALIDATE_ZERO_STATE: " << message); + } + static td::Status ValidateBlockProof() { + return td::Status::Error(500, "VALIDATE_BLOCK_PROOF"); + } + static td::Status NoLiteServers() { + return td::Status::Error(500, "NO_LITE_SERVERS"); + } + static td::Status LiteServerNetwork() { + return td::Status::Error(500, "LITE_SERVER_NETWORK"); + } + static td::Status Cancelled() { + return td::Status::Error(500, "CANCELLED"); + } + static td::Status NotEnoughFunds() { + return td::Status::Error(500, "NOT_ENOUGH_FUNDS"); + } + + static td::Status LiteServer(td::int32 code, td::Slice message) { + auto f = [&](td::Slice code_description) { return LiteServer(code, code_description, message); }; + switch (ton::ErrorCode(code)) { + case ton::ErrorCode::cancelled: + return f("CANCELLED"); + case ton::ErrorCode::failure: + return f("FAILURE"); + case ton::ErrorCode::error: + return f("ERROR"); + case ton::ErrorCode::warning: + return f("WARNING"); + case ton::ErrorCode::protoviolation: + return f("PROTOVIOLATION"); + case ton::ErrorCode::timeout: + return f("TIMEOUT"); + case ton::ErrorCode::notready: + return f("NOTREADY"); + } + return f("UNKNOWN"); + } + + static td::Status LiteServer(td::int32 code, td::Slice code_description, td::Slice message) { + return td::Status::Error(500, PSLICE() << "LITE_SERVER_" << code_description << ": " << message); + } +}; +} // namespace tonlib diff --git a/tonlib/tonlib/Wallet.cpp b/tonlib/tonlib/Wallet.cpp index dbd12d2538..b4682823f7 100644 --- a/tonlib/tonlib/Wallet.cpp +++ b/tonlib/tonlib/Wallet.cpp @@ -27,13 +27,13 @@ #include namespace tonlib { -td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) { +td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { auto code = get_init_code(); auto data = get_init_data(public_key); return GenericAccount::get_init_state(std::move(code), std::move(data)); } -td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) { +td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { td::uint32 seqno = 0; td::uint32 valid_until = std::numeric_limits::max(); auto signature = @@ -45,7 +45,7 @@ td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& privat td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::uint32 valid_until, td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) { + const block::StdAddress& dest_address) noexcept { td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; @@ -54,11 +54,15 @@ td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); + td::int32 send_mode = 3; + if (gramms == -1) { + gramms = 0; + send_mode += 128; + } block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4); vm::CellString::store(cb, message, 35 * 8).ensure(); auto message_inner = cb.finalize(); - td::int8 send_mode = 3; auto message_outer = vm::CellBuilder() .store_long(seqno, 32) .store_long(valid_until, 32) @@ -70,7 +74,7 @@ td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize(); } -td::Ref Wallet::get_init_code() { +td::Ref Wallet::get_init_code() noexcept { static auto res = [] { auto serialized_code = td::base64_decode( "te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" @@ -81,11 +85,11 @@ td::Ref Wallet::get_init_code() { return res; } -vm::CellHash Wallet::get_init_code_hash() { +vm::CellHash Wallet::get_init_code_hash() noexcept { return get_init_code()->get_hash(); } -td::Ref Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) { +td::Ref Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); } } // namespace tonlib diff --git a/tonlib/tonlib/Wallet.h b/tonlib/tonlib/Wallet.h index e4ee5aeccc..dd114cce77 100644 --- a/tonlib/tonlib/Wallet.h +++ b/tonlib/tonlib/Wallet.h @@ -27,14 +27,14 @@ namespace tonlib { class Wallet { public: static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key); - static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key); + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::uint32 valid_until, td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address); + const block::StdAddress& dest_address) noexcept; - static td::Ref get_init_code(); - static vm::CellHash get_init_code_hash(); - static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key); + static td::Ref get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; }; } // namespace tonlib diff --git a/tonlib/tonlib/keys/EncryptedKey.cpp b/tonlib/tonlib/keys/EncryptedKey.cpp index 075e89f479..143677914f 100644 --- a/tonlib/tonlib/keys/EncryptedKey.cpp +++ b/tonlib/tonlib/keys/EncryptedKey.cpp @@ -25,7 +25,6 @@ namespace tonlib { td::Result EncryptedKey::decrypt(td::Slice local_password, bool check_public_key) { - LOG(ERROR) << "decrypt"; if (secret.size() != 32) { return td::Status::Error("Failed to decrypt key: invalid secret size"); } diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 298027a0ac..a783d2aa37 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -192,6 +192,7 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "keys - show all stored keys\n"; td::TerminalIO::out() << "unpackaddress
- validate and parse address\n"; td::TerminalIO::out() << "importkey - import key\n"; + td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n"; td::TerminalIO::out() << "exportkey [] - export key\n"; td::TerminalIO::out() << "setconfig [] [] [] - set lite server config\n"; td::TerminalIO::out() << "getstate - get state of simple wallet with requested key\n"; @@ -213,6 +214,8 @@ class TonlibCli : public td::actor::Actor { try_stop(); } else if (cmd == "keys") { dump_keys(); + } else if (cmd == "deletekeys") { + delete_all_keys(); } else if (cmd == "exportkey") { export_key(parser.read_word()); } else if (cmd == "importkey") { @@ -415,6 +418,27 @@ class TonlibCli : public td::actor::Actor { dump_key(i); } } + void delete_all_keys() { + static td::Slice password = td::Slice("I have written down mnemonic words"); + td::TerminalIO::out() << "You are going to delete ALL PRIVATE KEYS. To confirm enter `" << password << "`\n"; + cont_ = [this](td::Slice entered) { + if (password == entered) { + this->do_delete_all_keys(); + } else { + td::TerminalIO::out() << "Your keys left intact\n"; + } + }; + } + + void do_delete_all_keys() { + send_query(tonlib_api::make_object(), [](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Something went wrong: " << r_res.error() << "\n"; + return; + } + td::TerminalIO::out() << "All your keys have been deleted\n"; + }); + } std::string key_db_path() { return options_.key_dir + TD_DIR_SLASH + "key_db"; diff --git a/tonlib/tonlib/utils.cpp b/tonlib/tonlib/utils.cpp index b577faf3f0..5cef21f56d 100644 --- a/tonlib/tonlib/utils.cpp +++ b/tonlib/tonlib/utils.cpp @@ -20,6 +20,10 @@ #include "td/utils/misc.h" #include "vm/cellslice.h" namespace tonlib { +int VERBOSITY_NAME(tonlib_query) = VERBOSITY_NAME(INFO); +int VERBOSITY_NAME(last_block) = VERBOSITY_NAME(INFO); +int VERBOSITY_NAME(lite_server) = VERBOSITY_NAME(INFO); + td::Result> binary_bitstring_to_cellslice(td::Slice literal) { unsigned char buff[128]; if (!begins_with(literal, "b{") || !ends_with(literal, "}")) { diff --git a/tonlib/tonlib/utils.h b/tonlib/tonlib/utils.h index 924e86d695..840bc10d20 100644 --- a/tonlib/tonlib/utils.h +++ b/tonlib/tonlib/utils.h @@ -21,6 +21,21 @@ #include "ton/ton-types.h" #include "block/block.h" #include "block/block-parse.h" + namespace tonlib { +template +auto try_f(F&& f) noexcept -> decltype(f()) { + try { + return f(); + } catch (vm::VmError error) { + return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg()); + } +} + +#define TRY_VM(f) try_f([&] { return f; }) + +extern int VERBOSITY_NAME(tonlib_query); +extern int VERBOSITY_NAME(last_block); +extern int VERBOSITY_NAME(lite_server); td::Result> binary_bitstring_to_cellslice(td::Slice literal); } // namespace tonlib diff --git a/validator/block-handle.hpp b/validator/block-handle.hpp index 204d4054eb..42eddccb25 100644 --- a/validator/block-handle.hpp +++ b/validator/block-handle.hpp @@ -473,6 +473,21 @@ struct BlockHandleImpl : public BlockHandleInterface { } } + void unsafe_clear_applied() override { + if (is_applied()) { + lock(); + flags_ &= ~Flags::dbf_applied; + unlock(); + } + } + void unsafe_clear_next() override { + if (inited_next_left() || inited_next_right()) { + lock(); + flags_ &= ~(Flags::dbf_inited_next_left | Flags::dbf_inited_next_right); + unlock(); + } + } + td::BufferSlice serialize() const override; BlockHandleImpl(BlockIdExt id) : id_(id), flags_(id_.is_masterchain() ? static_cast(dbf_masterchain) : 0) { diff --git a/validator/db/blockdb.cpp b/validator/db/blockdb.cpp index ac9c688363..1e1c0cf00a 100644 --- a/validator/db/blockdb.cpp +++ b/validator/db/blockdb.cpp @@ -229,6 +229,73 @@ tl_object_ptr BlockDb::DbEntry::release() { return create_tl_object(create_tl_block_id(block_id), prev, next); } +void BlockDb::truncate(td::Ref state, td::Promise promise) { + std::map max_seqno; + max_seqno.emplace(ShardIdFull{masterchainId}, state->get_seqno() + 1); + + auto shards = state->get_shards(); + auto it = KeyHash::zero(); + kv_->begin_transaction().ensure(); + while (true) { + auto R = get_block_lru(it); + R.ensure(); + auto v = R.move_as_ok(); + it = v.next; + R = get_block_lru(it); + R.ensure(); + v = R.move_as_ok(); + if (v.is_empty()) { + break; + } + + auto s = v.block_id.shard_full(); + if (!max_seqno.count(s)) { + bool found = false; + for (auto &shard : shards) { + if (shard_intersects(shard->shard(), s)) { + found = true; + max_seqno.emplace(s, shard->top_block_id().seqno() + 1); + break; + } + } + if (!found) { + max_seqno.emplace(s, 0); + } + } + + bool to_delete = v.block_id.seqno() >= max_seqno[s]; + if (to_delete) { + auto key_hash = get_block_value_key(v.block_id); + auto B = get_block_value(key_hash); + B.ensure(); + auto handleR = create_block_handle(B.move_as_ok()); + handleR.ensure(); + auto handle = handleR.move_as_ok(); + + handle->unsafe_clear_applied(); + handle->unsafe_clear_next(); + + if (handle->need_flush()) { + set_block_value(key_hash, handle->serialize()); + } + } else if (v.block_id.seqno() + 1 == max_seqno[s]) { + auto key_hash = get_block_value_key(v.block_id); + auto B = get_block_value(key_hash); + B.ensure(); + auto handleR = create_block_handle(B.move_as_ok()); + handleR.ensure(); + auto handle = handleR.move_as_ok(); + + handle->unsafe_clear_next(); + + if (handle->need_flush()) { + set_block_value(key_hash, handle->serialize()); + } + } + } + kv_->commit_transaction().ensure(); +} + } // namespace validator } // namespace ton diff --git a/validator/db/blockdb.hpp b/validator/db/blockdb.hpp index 949b8e32e6..e9e26c8c30 100644 --- a/validator/db/blockdb.hpp +++ b/validator/db/blockdb.hpp @@ -41,6 +41,8 @@ class BlockDb : public td::actor::Actor { void gc(); void skip_gc(); + void truncate(td::Ref state, td::Promise promise); + BlockDb(td::actor::ActorId root_db, std::string db_path); private: diff --git a/validator/db/filedb.cpp b/validator/db/filedb.cpp index eea33b5ceb..4532641ea4 100644 --- a/validator/db/filedb.cpp +++ b/validator/db/filedb.cpp @@ -138,7 +138,7 @@ void FileDb::load_file(RefId ref_id, td::Promise promise) { } }); - td::actor::create_actor("readfile", get_file_name(ref_id, false), 0, -1, std::move(P)).release(); + td::actor::create_actor("readfile", get_file_name(ref_id, false), 0, -1, 0, std::move(P)).release(); } void FileDb::load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, td::Promise promise) { @@ -159,7 +159,7 @@ void FileDb::load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, } }); - td::actor::create_actor("readfile", get_file_name(ref_id, false), offset, max_size, std::move(P)) + td::actor::create_actor("readfile", get_file_name(ref_id, false), offset, max_size, 0, std::move(P)) .release(); } diff --git a/validator/db/files-async.hpp b/validator/db/files-async.hpp index 9d1b7cae23..4f42077620 100644 --- a/validator/db/files-async.hpp +++ b/validator/db/files-async.hpp @@ -81,25 +81,32 @@ class WriteFile : public td::actor::Actor { class ReadFile : public td::actor::Actor { public: + enum Flags : td::uint32 { f_disable_log = 1 }; void start_up() override { auto S = td::read_file(file_name_, max_length_, offset_); if (S.is_ok()) { promise_.set_result(S.move_as_ok()); } else { // TODO check error code - LOG(ERROR) << "missing file " << file_name_; + if (flags_ & Flags::f_disable_log) { + LOG(DEBUG) << "missing file " << file_name_; + } else { + LOG(ERROR) << "missing file " << file_name_; + } promise_.set_error(td::Status::Error(ErrorCode::notready, "file does not exist")); } stop(); } - ReadFile(std::string file_name, td::int64 offset, td::int64 max_length, td::Promise promise) - : file_name_(file_name), offset_(offset), max_length_(max_length), promise_(std::move(promise)) { + ReadFile(std::string file_name, td::int64 offset, td::int64 max_length, td::uint32 flags, + td::Promise promise) + : file_name_(file_name), offset_(offset), max_length_(max_length), flags_(flags), promise_(std::move(promise)) { } private: std::string file_name_; td::int64 offset_; td::int64 max_length_; + td::uint32 flags_; td::Promise promise_; }; diff --git a/validator/db/ltdb.cpp b/validator/db/ltdb.cpp index e0f854366d..eea2aa63ed 100644 --- a/validator/db/ltdb.cpp +++ b/validator/db/ltdb.cpp @@ -216,6 +216,95 @@ void LtDb::start_up() { kv_ = std::make_shared(td::RocksDb::open(db_path_).move_as_ok()); } +void LtDb::truncate_workchain(ShardIdFull shard, td::Ref state) { + auto key = get_desc_key(shard); + std::string value; + auto R = kv_->get(key, value); + R.ensure(); + CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok); + auto F = fetch_tl_object(td::BufferSlice{value}, true); + F.ensure(); + auto f = F.move_as_ok(); + + auto shards = state->get_shards(); + BlockSeqno seqno = 0; + if (shard.is_masterchain()) { + seqno = state->get_seqno(); + } else { + for (auto s : shards) { + if (shard_intersects(s->shard(), shard)) { + seqno = s->top_block_id().seqno(); + break; + } + } + } + + while (f->last_idx_ > f->first_idx_) { + auto db_key = get_el_key(shard, f->last_idx_ - 1); + R = kv_->get(db_key, value); + R.ensure(); + CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok); + auto E = fetch_tl_object(td::BufferSlice{value}, true); + E.ensure(); + auto e = E.move_as_ok(); + + bool to_delete = static_cast(e->id_->seqno_) > seqno; + + if (!to_delete) { + break; + } else { + f->last_idx_--; + kv_->erase(db_key).ensure(); + } + } + + if (f->first_idx_ == f->last_idx_) { + f->last_ts_ = 0; + f->last_lt_ = 0; + f->last_seqno_ = 0; + } + + kv_->set(key, serialize_tl_object(f, true)).ensure(); +} + +void LtDb::truncate(td::Ref state, td::Promise promise) { + auto status_key = create_serialize_tl_object(); + td::Result R; + td::uint32 total_shards = 0; + { + std::string value; + R = kv_->get(status_key.as_slice(), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_value(td::Unit()); + return; + } + auto F = fetch_tl_object(value, true); + F.ensure(); + auto f = F.move_as_ok(); + total_shards = f->total_shards_; + if (total_shards == 0) { + promise.set_value(td::Unit()); + return; + } + } + kv_->begin_transaction().ensure(); + for (td::uint32 idx = 0; idx < total_shards; idx++) { + auto shard_key = create_serialize_tl_object(idx); + std::string value; + R = kv_->get(shard_key.as_slice(), value); + R.ensure(); + CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok); + auto F = fetch_tl_object(value, true); + F.ensure(); + auto f = F.move_as_ok(); + + truncate_workchain(ShardIdFull{f->workchain_, static_cast(f->shard_)}, state); + } + kv_->commit_transaction().ensure(); + promise.set_value(td::Unit()); +} + } // namespace validator } // namespace ton diff --git a/validator/db/ltdb.hpp b/validator/db/ltdb.hpp index 804c02c889..4c7651ec5d 100644 --- a/validator/db/ltdb.hpp +++ b/validator/db/ltdb.hpp @@ -20,6 +20,7 @@ #include "td/actor/actor.h" #include "td/db/KeyValueAsync.h" +#include "validator/interfaces/db.h" #include "ton/ton-types.h" @@ -42,6 +43,9 @@ class LtDb : public td::actor::Actor { void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + void truncate_workchain(ShardIdFull shard, td::Ref state); + void truncate(td::Ref state, td::Promise promise); + void start_up() override; LtDb(td::actor::ActorId root_db, std::string db_path) : root_db_(root_db), db_path_(std::move(db_path)) { diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index fb59dfc460..2a21d0c272 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -25,6 +25,7 @@ #include "td/utils/overloaded.h" #include "common/checksum.h" #include "validator/stats-merger.h" +#include "td/actor/MultiPromise.h" namespace ton { @@ -413,6 +414,14 @@ void RootDb::get_async_serializer_state(td::Promise promis td::actor::send_closure(state_db_, &StateDb::get_async_serializer_state, std::move(promise)); } +void RootDb::update_hardforks(std::vector blocks, td::Promise promise) { + td::actor::send_closure(state_db_, &StateDb::update_hardforks, std::move(blocks), std::move(promise)); +} + +void RootDb::get_hardforks(td::Promise> promise) { + td::actor::send_closure(state_db_, &StateDb::get_hardforks, std::move(promise)); +} + void RootDb::start_up() { cell_db_ = td::actor::create_actor("celldb", actor_id(this), root_path_ + "/celldb/"); block_db_ = td::actor::create_actor("blockdb", actor_id(this), root_path_ + "/blockdb/"); @@ -481,6 +490,15 @@ void RootDb::prepare_stats(td::Promise state, td::Promise promise) { + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(promise)); + + td::actor::send_closure(lt_db_, &LtDb::truncate, state, ig.get_promise()); + td::actor::send_closure(block_db_, &BlockDb::truncate, state, ig.get_promise()); +} + } // namespace validator } // namespace ton diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index d03b590d80..a584ac9746 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -104,6 +104,9 @@ class RootDb : public Db { void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) override; void get_async_serializer_state(td::Promise promise) override; + void update_hardforks(std::vector blocks, td::Promise promise) override; + void get_hardforks(td::Promise> promise) override; + void archive(BlockIdExt block_id, td::Promise promise) override; void allow_state_gc(BlockIdExt block_id, td::Promise promise); @@ -112,6 +115,8 @@ class RootDb : public Db { void prepare_stats(td::Promise>> promise) override; + void truncate(td::Ref state, td::Promise promise) override; + private: td::actor::ActorId validator_manager_; diff --git a/validator/db/statedb.cpp b/validator/db/statedb.cpp index 0b0b3e86dc..ed78626e6d 100644 --- a/validator/db/statedb.cpp +++ b/validator/db/statedb.cpp @@ -179,6 +179,44 @@ void StateDb::get_async_serializer_state(td::Promise promi static_cast(obj->last_ts_)}); } +void StateDb::update_hardforks(std::vector blocks, td::Promise promise) { + auto key = create_hash_tl_object(); + + std::vector> vec; + + for (auto &e : blocks) { + vec.push_back(create_tl_block_id(e)); + } + + kv_->begin_transaction().ensure(); + kv_->set(key.as_slice(), create_serialize_tl_object(std::move(vec))).ensure(); + kv_->commit_transaction(); + + promise.set_value(td::Unit()); +} + +void StateDb::get_hardforks(td::Promise> promise) { + auto key = create_hash_tl_object(); + + std::string value; + auto R = kv_->get(key.as_slice(), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_value(std::vector{}); + return; + } + auto F = fetch_tl_object(value, true); + F.ensure(); + auto f = F.move_as_ok(); + + std::vector vec; + for (auto &e : f->blocks_) { + vec.push_back(create_block_id(e)); + } + + promise.set_value(std::move(vec)); +} + StateDb::StateDb(td::actor::ActorId root_db, std::string db_path) : root_db_(root_db), db_path_(db_path) { } diff --git a/validator/db/statedb.hpp b/validator/db/statedb.hpp index 0ef819a25b..af5fde4f4c 100644 --- a/validator/db/statedb.hpp +++ b/validator/db/statedb.hpp @@ -47,6 +47,9 @@ class StateDb : public td::actor::Actor { void update_async_serializer_state(AsyncSerializerState state, td::Promise promise); void get_async_serializer_state(td::Promise promise); + void update_hardforks(std::vector blocks, td::Promise promise); + void get_hardforks(td::Promise> promise); + StateDb(td::actor::ActorId root_db, std::string path); void start_up() override; diff --git a/validator/db/staticfilesdb.cpp b/validator/db/staticfilesdb.cpp index d4037c66a2..e6b22319a2 100644 --- a/validator/db/staticfilesdb.cpp +++ b/validator/db/staticfilesdb.cpp @@ -25,7 +25,9 @@ namespace validator { void StaticFilesDb::load_file(FileHash file_hash, td::Promise promise) { auto path = path_ + "/" + file_hash.to_hex(); - td::actor::create_actor("read file", path, 0, -1, std::move(promise)).release(); + td::actor::create_actor("read file", path, 0, -1, db::ReadFile::Flags::f_disable_log, + std::move(promise)) + .release(); } } // namespace validator diff --git a/validator/downloaders/wait-block-data.cpp b/validator/downloaders/wait-block-data.cpp index b5d1aa8498..def64ea2dc 100644 --- a/validator/downloaders/wait-block-data.cpp +++ b/validator/downloaders/wait-block-data.cpp @@ -56,6 +56,20 @@ void WaitBlockData::start_up() { alarm_timestamp() = timeout_; CHECK(handle_); + if (!handle_->id().is_masterchain()) { + start(); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + auto value = R.move_as_ok(); + td::actor::send_closure(SelfId, &WaitBlockData::set_is_hardfork, value); + }); + td::actor::send_closure(manager_, &ValidatorManager::check_is_hardfork, handle_->id(), std::move(P)); + } +} + +void WaitBlockData::set_is_hardfork(bool value) { + is_hardfork_ = value; start(); } @@ -76,6 +90,18 @@ void WaitBlockData::start() { }); td::actor::send_closure(manager_, &ValidatorManager::get_block_data_from_db, handle_, std::move(P)); + } else if (try_read_static_file_.is_in_past() && (is_hardfork_ || !handle_->id().is_masterchain())) { + try_read_static_file_ = td::Timestamp::in(30.0); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &WaitBlockData::start); + } else { + td::actor::send_closure(SelfId, &WaitBlockData::got_static_file, R.move_as_ok()); + } + }); + + td::actor::send_closure(manager_, &ValidatorManager::try_get_static_file, handle_->id().file_hash, std::move(P)); } else { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { @@ -149,6 +175,29 @@ void WaitBlockData::force_read_from_db() { td::actor::send_closure(manager_, &ValidatorManager::get_block_data_from_db, handle_, std::move(P)); } +void WaitBlockData::got_static_file(td::BufferSlice data) { + CHECK(td::sha256_bits256(data.as_slice()) == handle_->id().file_hash); + + auto R = create_block(handle_->id(), std::move(data)); + if (R.is_error()) { + LOG(ERROR) << "bad static file block: " << R.move_as_error(); + start(); + return; + } + data_ = R.move_as_ok(); + + CHECK(is_hardfork_ || !handle_->id().is_masterchain()); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &WaitBlockData::abort_query, R.move_as_error_prefix("bad static file block: ")); + } else { + td::actor::send_closure(SelfId, &WaitBlockData::finish_query); + } + }); + run_hardfork_accept_block_query(handle_->id(), data_, manager_, std::move(P)); +} + } // namespace validator } // namespace ton diff --git a/validator/downloaders/wait-block-data.hpp b/validator/downloaders/wait-block-data.hpp index aa0512739c..88b342b20e 100644 --- a/validator/downloaders/wait-block-data.hpp +++ b/validator/downloaders/wait-block-data.hpp @@ -51,12 +51,14 @@ class WaitBlockData : public td::actor::Actor { void force_read_from_db(); void start_up() override; - void got_block_handle(BlockHandle handle); + void set_is_hardfork(bool value); void start(); void got_block_data_from_db(td::Ref data); void got_block_data_from_net(ReceivedBlock data); void failed_to_get_block_data_from_net(td::Status reason); + void got_static_file(td::BufferSlice data); + private: BlockHandle handle_; @@ -69,6 +71,8 @@ class WaitBlockData : public td::actor::Actor { td::Ref data_; bool reading_from_db_ = false; + bool is_hardfork_ = false; + td::Timestamp try_read_static_file_ = td::Timestamp::now(); //td::PerfWarningTimer perf_timer_{"waitdata", 1.0}; }; diff --git a/validator/fabric.h b/validator/fabric.h index e7709f8dd5..54ad09a9c8 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -52,6 +52,8 @@ void run_accept_block_query(BlockIdExt id, td::Ref data, std::vector< void run_fake_accept_block_query(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::actor::ActorId manager, td::Promise promise); +void run_hardfork_accept_block_query(BlockIdExt id, td::Ref data, + td::actor::ActorId manager, td::Promise promise); void run_apply_block_query(BlockIdExt id, td::Ref block, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise); void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index f6739bfb53..16bc873113 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -1466,26 +1466,10 @@ bool Collator::fetch_config_params() { if (cell.is_null()) { return fatal_error("cannot fetch current gas prices and limits from masterchain configuration"); } - auto f = [self = this](const auto& r, td::uint64 spec_limit) { - self->compute_phase_cfg_.gas_limit = r.gas_limit; - self->compute_phase_cfg_.special_gas_limit = spec_limit; - self->compute_phase_cfg_.gas_credit = r.gas_credit; - self->compute_phase_cfg_.gas_price = r.gas_price; - self->storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, r.freeze_due_limit}; - self->storage_phase_cfg_.delete_due_limit = td::RefInt256{true, r.delete_due_limit}; - }; - block::gen::GasLimitsPrices::Record_gas_prices_ext rec; - if (tlb::unpack_cell(cell, rec)) { - f(rec, rec.special_gas_limit); - } else { - block::gen::GasLimitsPrices::Record_gas_prices rec0; - if (tlb::unpack_cell(std::move(cell), rec0)) { - f(rec0, rec0.gas_limit); - } else { - return fatal_error("cannot unpack current gas prices and limits from masterchain configuration"); - } + if (!compute_phase_cfg_.parse_GasLimitsPrices(std::move(cell), storage_phase_cfg_.freeze_due_limit, + storage_phase_cfg_.delete_due_limit)) { + return fatal_error("cannot unpack current gas prices and limits from masterchain configuration"); } - compute_phase_cfg_.compute_threshold(); compute_phase_cfg_.block_rand_seed = rand_seed_; compute_phase_cfg_.libraries = std::make_unique(config_->get_libraries_root(), 256); compute_phase_cfg_.global_config = config_->get_root_cell(); diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index a037e122ed..a4671a8b23 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -33,6 +33,7 @@ #include "top-shard-descr.hpp" #include "ton/ton-io.hpp" #include "liteserver.hpp" +#include "validator/fabric.h" namespace ton { @@ -131,6 +132,11 @@ void run_fake_accept_block_query(BlockIdExt id, td::Ref data, std::ve .release(); } +void run_hardfork_accept_block_query(BlockIdExt id, td::Ref data, + td::actor::ActorId manager, td::Promise promise) { + promise.set_error(td::Status::Error(ErrorCode::error, "not implemented")); +} + void run_apply_block_query(BlockIdExt id, td::Ref block, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise) { td::actor::create_actor(PSTRING() << "apply " << id, id, std::move(block), manager, timeout, diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 2a01c99cc5..2cf588bd2a 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -192,7 +192,7 @@ void LiteQuery::perform_getMasterchainInfo(int mode) { } td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [ Self = actor_id(this), mode ](td::Result, BlockIdExt>> res) { + [Self = actor_id(this), mode](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -230,7 +230,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -256,7 +256,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid, mode ](td::Result> res) { + [Self = actor_id(this), blkid, mode](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -371,7 +371,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { } if (blkid.id.seqno) { td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -381,7 +381,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { }); } else { td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid, - [ Self = actor_id(this), blkid ](td::Result res) { + [Self = actor_id(this), blkid](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -440,7 +440,7 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -466,7 +466,7 @@ bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_from_db_short, blkid, - [ Self = actor_id(this), blkid, mode ](td::Result> res) { + [Self = actor_id(this), blkid, mode](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : ")); @@ -488,7 +488,7 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -519,7 +519,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -541,7 +541,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -563,7 +563,7 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : ")); @@ -588,7 +588,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_zero_state, blkid, - [ Self = actor_id(this), blkid ](td::Result res) { + [Self = actor_id(this), blkid](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : ")); @@ -632,7 +632,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, LOG(INFO) << "sending a get_top_masterchain_state_block query to manager"; td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this)](td::Result, BlockIdExt>> res)->void { + [Self = actor_id(this)](td::Result, BlockIdExt>> res) -> void { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1067,7 +1067,7 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) { << " " << trans_lt_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_by_lt_from_db, ton::extract_addr_prefix(acc_workchain_, acc_addr_), - trans_lt_, [ Self = actor_id(this), remaining, manager = manager_ ](td::Result res) { + trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{}); } else { @@ -1141,7 +1141,7 @@ void LiteQuery::perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool e void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector param_list) { LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", ) liteserver query"; - set_continuation([ this, mode, param_list = std::move(param_list) ]() mutable { + set_continuation([this, mode, param_list = std::move(param_list)]() mutable { continue_getConfigParams(mode, std::move(param_list)); }); request_mc_block_data_state(blkid); @@ -1294,7 +1294,7 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni LOG(INFO) << "performing a lookupBlock(" << blkid.to_str() << ", " << mode << ", " << lt << ", " << utime << ") query"; auto P = td::PromiseCreator::lambda( - [ Self = actor_id(this), manager = manager_, mode = (mode >> 4) ](td::Result res) { + [Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1445,10 +1445,21 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, fatal_error("destination block "s + to.to_str() + " is not a valid masterchain block id"); return; } - if (!(mode & 1)) { + if (mode & 1) { + base_blk_id_ = (from.seqno() > to.seqno()) ? from : to; + td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, base_blk_id_, + [Self = actor_id(this), from, to, mode](td::Result> res) { + if (res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); + } else { + td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, to, + mode, Ref(res.move_as_ok())); + } + }); + } else if (mode & 2) { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [ Self = actor_id(this), from, mode ](td::Result, BlockIdExt>> res)->void { + [Self = actor_id(this), from, mode](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1458,14 +1469,13 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, } }); } else { - base_blk_id_ = (from.seqno() > to.seqno()) ? from : to; - td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, base_blk_id_, - [ Self = actor_id(this), from, to, mode ](td::Result> res) { + td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false, + [Self = actor_id(this), from, mode](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { - td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, to, - mode, Ref(res.move_as_ok())); + td::actor::send_closure_later(Self, &LiteQuery::perform_getBlockProof, from, + res.move_as_ok(), mode | 1); } }); } diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 3b574cdb23..1989287415 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -734,26 +734,10 @@ bool ValidateQuery::fetch_config_params() { if (cell.is_null()) { return fatal_error("cannot fetch current gas prices and limits from masterchain configuration"); } - auto f = [self = this](const auto& r, td::uint64 spec_limit) { - self->compute_phase_cfg_.gas_limit = r.gas_limit; - self->compute_phase_cfg_.special_gas_limit = spec_limit; - self->compute_phase_cfg_.gas_credit = r.gas_credit; - self->compute_phase_cfg_.gas_price = r.gas_price; - self->storage_phase_cfg_.freeze_due_limit = td::RefInt256{true, r.freeze_due_limit}; - self->storage_phase_cfg_.delete_due_limit = td::RefInt256{true, r.delete_due_limit}; - }; - block::gen::GasLimitsPrices::Record_gas_prices_ext rec; - if (tlb::unpack_cell(cell, rec)) { - f(rec, rec.special_gas_limit); - } else { - block::gen::GasLimitsPrices::Record_gas_prices rec0; - if (tlb::unpack_cell(std::move(cell), rec0)) { - f(rec0, rec0.gas_limit); - } else { - return fatal_error("cannot unpack current gas prices and limits from masterchain configuration"); - } + if (!compute_phase_cfg_.parse_GasLimitsPrices(std::move(cell), storage_phase_cfg_.freeze_due_limit, + storage_phase_cfg_.delete_due_limit)) { + return fatal_error("cannot unpack current gas prices and limits from masterchain configuration"); } - compute_phase_cfg_.compute_threshold(); compute_phase_cfg_.block_rand_seed = rand_seed_; compute_phase_cfg_.libraries = std::make_unique(config_->get_libraries_root(), 256); compute_phase_cfg_.global_config = config_->get_root_cell(); @@ -5227,7 +5211,7 @@ bool ValidateQuery::check_block_create_stats() { auto key = td::Bits256::zero(); auto old_val = ps_.block_create_stats_->lookup(key); auto new_val = ns_.block_create_stats_->lookup(key); - if (new_val.is_null()) { + if (new_val.is_null() && (!created_by_.is_zero() || block_create_total_)) { return reject_query( "new masterchain state does not contain a BlockCreator entry with zero key with total statistics"); } diff --git a/validator/interfaces/block-handle.h b/validator/interfaces/block-handle.h index 23aaf3c03b..1f6ad877fe 100644 --- a/validator/interfaces/block-handle.h +++ b/validator/interfaces/block-handle.h @@ -93,6 +93,9 @@ struct BlockHandleInterface { virtual void set_archived() = 0; virtual void set_applied() = 0; + virtual void unsafe_clear_applied() = 0; + virtual void unsafe_clear_next() = 0; + virtual td::BufferSlice serialize() const = 0; virtual ~BlockHandleInterface() = default; diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index 92b28e1285..846371a19b 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -92,9 +92,14 @@ class Db : public td::actor::Actor { virtual void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) = 0; virtual void get_async_serializer_state(td::Promise promise) = 0; + virtual void update_hardforks(std::vector blocks, td::Promise promise) = 0; + virtual void get_hardforks(td::Promise> promise) = 0; + virtual void archive(BlockIdExt block_id, td::Promise promise) = 0; virtual void prepare_stats(td::Promise>> promise) = 0; + + virtual void truncate(td::Ref state, td::Promise promise) = 0; }; } // namespace validator diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index aaf40f49d3..4328ce4f20 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -128,7 +128,7 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void send_block_broadcast(BlockBroadcast broadcast) = 0; virtual void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) = 0; - virtual void get_shard_client_state(td::Promise promise) = 0; + virtual void get_shard_client_state(bool from_db, td::Promise promise) = 0; virtual void subscribe_to_shard(ShardIdFull shard) = 0; virtual void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) = 0; @@ -153,6 +153,12 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void update_last_known_key_block(BlockHandle handle, bool send_request) = 0; virtual void update_gc_block_handle(BlockHandle handle, td::Promise promise) = 0; + virtual void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) = 0; + + virtual void truncate(td::Ref state, td::Promise promise) = 0; + + virtual void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) = 0; + static bool is_persistent_state(UnixTime ts, UnixTime prev_ts) { return ts / 1024 != prev_ts / 1024; } diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index d521ebc46b..9798bb17fa 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -897,7 +897,7 @@ void ValidatorManagerImpl::update_shard_client_state(BlockIdExt masterchain_bloc td::actor::send_closure(db_, &Db::update_shard_client_state, masterchain_block_id, std::move(promise)); } -void ValidatorManagerImpl::get_shard_client_state(td::Promise promise) { +void ValidatorManagerImpl::get_shard_client_state(bool from_db, td::Promise promise) { td::actor::send_closure(db_, &Db::get_shard_client_state, std::move(promise)); } diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index c5fa38de33..6da9110ed0 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -241,7 +241,7 @@ class ValidatorManagerImpl : public ValidatorManager { } void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; - void get_shard_client_state(td::Promise promise) override; + void get_shard_client_state(bool from_db, td::Promise promise) override; void subscribe_to_shard(ShardIdFull shard) override { } @@ -329,11 +329,20 @@ class ValidatorManagerImpl : public ValidatorManager { } void update_last_known_key_block(BlockHandle handle, bool send_request) override { } + void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) override { + } void prepare_stats(td::Promise>> promise) override { UNREACHABLE(); } + void truncate(td::Ref state, td::Promise promise) override { + UNREACHABLE(); + } + void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) override { + UNREACHABLE(); + } + private: PublicKeyHash local_id_; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index 8fd1473b03..3a8fe1fd5d 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -25,6 +25,7 @@ #include "adnl/utils.hpp" #include "validator/downloaders/download-state.hpp" #include "common/delay.h" +#include "td/actor/MultiPromise.h" namespace ton { @@ -33,7 +34,16 @@ namespace validator { void ValidatorManagerMasterchainReiniter::start_up() { CHECK(block_id_.is_masterchain()); CHECK(block_id_.id.shard == shardIdAll); + CHECK(block_id_.seqno() >= opts_->get_last_fork_masterchain_seqno()); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::written_hardforks); + }); + td::actor::send_closure(db_, &Db::update_hardforks, opts_->get_hardforks(), std::move(P)); +} + +void ValidatorManagerMasterchainReiniter::written_hardforks() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::got_masterchain_handle, R.move_as_ok()); @@ -268,7 +278,7 @@ void ValidatorManagerMasterchainStarter::start_up() { } void ValidatorManagerMasterchainStarter::failed_to_get_init_block_id() { - td::actor::create_actor("reiniter", opts_, manager_, std::move(promise_)) + td::actor::create_actor("reiniter", opts_, manager_, db_, std::move(promise_)) .release(); stop(); } @@ -351,7 +361,7 @@ void ValidatorManagerMasterchainStarter::got_gc_block_state(td::Ref("shardclient", opts_, manager_); + client_block_id_ = block_id; finish(); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_hardforks, R.move_as_ok()); + }); + td::actor::send_closure(db_, &Db::get_hardforks, std::move(P)); +} + +void ValidatorManagerMasterchainStarter::got_hardforks(std::vector vec) { + auto h = opts_->get_hardforks(); + if (h.size() < vec.size()) { + LOG(FATAL) << "cannot start: number of hardforks decreased"; + return; + } + if (h.size() == vec.size()) { + if (h.size() > 0) { + if (*h.rbegin() != *vec.rbegin()) { + LOG(FATAL) << "cannot start: hardforks list changed"; + return; + } + } + finish(); + return; + } + if (h.size() > vec.size() + 1) { + LOG(FATAL) << "cannot start: number of hardforks increase is too big"; + return; + } + + auto b = *h.rbegin(); + if (b.seqno() > handle_->id().seqno()) { + truncated(); + return; + } + if (b.seqno() <= gc_handle_->id().seqno()) { + LOG(FATAL) << "cannot start: new hardfork is on too old block (already gc'd)"; + return; + } + + BlockIdExt id; + if (state_->get_old_mc_block_id(b.seqno() - 1, id)) { + got_truncate_block_id(id); + return; + } + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_id, R.move_as_ok()); + }); + td::actor::send_closure(db_, &Db::get_block_by_seqno, AccountIdPrefixFull{masterchainId, 0}, b.seqno() - 1, + std::move(P)); +} + +void ValidatorManagerMasterchainStarter::got_truncate_block_id(BlockIdExt block_id) { + block_id_ = block_id; + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_handle, R.move_as_ok()); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id_, false, std::move(P)); +} + +void ValidatorManagerMasterchainStarter::got_truncate_block_handle(BlockHandle handle) { + handle_ = std::move(handle); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_state, + td::Ref{R.move_as_ok()}); + }); + td::actor::send_closure(db_, &Db::get_block_state, handle_, std::move(P)); +} + +void ValidatorManagerMasterchainStarter::got_truncate_state(td::Ref state) { + state_ = std::move(state); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::truncated_db); + }); + td::actor::send_closure(manager_, &ValidatorManager::truncate, state_, std::move(P)); +} + +void ValidatorManagerMasterchainStarter::truncated_db() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::truncated); + }); + + auto key = state_->last_key_block_id(); + + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(P)); + + td::actor::send_closure(db_, &Db::update_init_masterchain_block, block_id_, ig.get_promise()); + + if (client_block_id_.seqno() > block_id_.seqno()) { + client_block_id_ = block_id_; + td::actor::send_closure(db_, &Db::update_shard_client_state, client_block_id_, ig.get_promise()); + } + + if (last_key_block_handle_->id().seqno() > key.seqno()) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), + promise = ig.get_promise()](td::Result R) mutable { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_prev_key_block_handle, R.move_as_ok()); + promise.set_value(td::Unit()); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, key, false, std::move(P)); + } + + { + auto P = td::PromiseCreator::lambda( + [b = block_id_, key, db = db_, promise = ig.get_promise()](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(td::Unit()); + return; + } + auto s = R.move_as_ok(); + if (s.last_block_id.seqno() <= b.seqno()) { + promise.set_value(td::Unit()); + return; + } + s.last_block_id = b; + if (s.last_written_block_id.seqno() > b.seqno()) { + s.last_written_block_id = key; + s.last_written_block_ts = 0; // may lead to extra state snapshot on disk. Does not seem like a problem + } + td::actor::send_closure(db, &Db::update_async_serializer_state, s, std::move(promise)); + }); + td::actor::send_closure(db_, &Db::get_async_serializer_state, std::move(P)); + } +} + +void ValidatorManagerMasterchainStarter::got_prev_key_block_handle(BlockHandle handle) { + last_key_block_handle_ = std::move(handle); +} + +void ValidatorManagerMasterchainStarter::truncated() { + handle_->set_next(*opts_->get_hardforks().rbegin()); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::written_next); + }); + handle_->flush(manager_, handle_, std::move(P)); +} + +void ValidatorManagerMasterchainStarter::written_next() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::finish); + }); + td::actor::send_closure(db_, &Db::update_hardforks, opts_->get_hardforks(), std::move(P)); } void ValidatorManagerMasterchainStarter::finish() { + client_ = td::actor::create_actor("shardclient", opts_, manager_); promise_.set_value( ValidatorManagerInitResult{handle_, state_, std::move(client_), gc_handle_, gc_state_, last_key_block_handle_}); stop(); diff --git a/validator/manager-init.hpp b/validator/manager-init.hpp index 3004134732..e8c2ad4b04 100644 --- a/validator/manager-init.hpp +++ b/validator/manager-init.hpp @@ -34,12 +34,13 @@ namespace validator { class ValidatorManagerMasterchainReiniter : public td::actor::Actor { public: ValidatorManagerMasterchainReiniter(td::Ref opts, - td::actor::ActorId manager, + td::actor::ActorId manager, td::actor::ActorId db, td::Promise promise) - : opts_(std::move(opts)), manager_(manager), promise_(std::move(promise)) { + : opts_(std::move(opts)), manager_(manager), db_(db), promise_(std::move(promise)) { block_id_ = opts_->init_block_id(); } void start_up() override; + void written_hardforks(); void got_masterchain_handle(BlockHandle handle); void download_proof_link(); void downloaded_proof_link(td::BufferSlice data); @@ -70,6 +71,7 @@ class ValidatorManagerMasterchainReiniter : public td::actor::Actor { std::vector shards_; td::actor::ActorId manager_; + td::actor::ActorId db_; td::Promise promise_; @@ -95,6 +97,14 @@ class ValidatorManagerMasterchainStarter : public td::actor::Actor { void got_gc_block_state(td::Ref state); void got_key_block_handle(BlockHandle handle); void got_shard_block_id(BlockIdExt block_id); + void got_hardforks(std::vector hardforks); + void got_truncate_block_id(BlockIdExt block_id); + void got_truncate_block_handle(BlockHandle handle); + void got_truncate_state(td::Ref state); + void truncated_db(); + void got_prev_key_block_handle(BlockHandle handle); + void truncated(); + void written_next(); void finish(); private: @@ -112,6 +122,7 @@ class ValidatorManagerMasterchainStarter : public td::actor::Actor { td::Promise promise_; + BlockIdExt client_block_id_; td::actor::ActorOwn client_; }; diff --git a/validator/manager.cpp b/validator/manager.cpp index 2d5b11753b..30467363e5 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -52,29 +52,52 @@ void ValidatorManagerImpl::validate_block_is_next_proof(BlockIdExt prev_block_id td::Status::Error(ErrorCode::protoviolation, "validate_block_is_next_proof() can only work for masterchain")); return; } + if (prev_block_id.seqno() + 1 != next_block_id.seqno()) { + VLOG(VALIDATOR_NOTICE) << "prev=" << prev_block_id << " next=" << next_block_id; + promise.set_error(td::Status::Error(ErrorCode::protoviolation, "validate_block_is_next_proof(): bad seqno")); + return; + } + CHECK(last_masterchain_state_.not_null()); auto pp = create_proof(next_block_id, std::move(proof)); if (pp.is_error()) { promise.set_error(pp.move_as_error_prefix("failed to create proof: ")); return; } - auto P = - td::PromiseCreator::lambda([promise = std::move(promise), id = prev_block_id](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - return; - } - auto handle = R.move_as_ok(); - CHECK(!handle->merge_before()); - if (handle->one_prev(true) != id) { - promise.set_error(td::Status::Error(ErrorCode::protoviolation, "prev block mismatch")); - return; - } - promise.set_value(td::Unit()); - }); + if (last_masterchain_seqno_ == prev_block_id.seqno()) { + CHECK(last_masterchain_block_id_ == prev_block_id); - run_check_proof_query(next_block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P), - opts_->is_hardfork(next_block_id)); + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise), id = prev_block_id](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + return; + } + auto handle = R.move_as_ok(); + CHECK(!handle->merge_before()); + if (handle->one_prev(true) != id) { + promise.set_error(td::Status::Error(ErrorCode::protoviolation, "prev block mismatch")); + return; + } + promise.set_value(td::Unit()); + }); + + run_check_proof_query(next_block_id, pp.move_as_ok(), actor_id(this), td::Timestamp::in(2.0), std::move(P), + last_masterchain_state_, opts_->is_hardfork(next_block_id)); + } else { + auto P = + td::PromiseCreator::lambda([promise = std::move(promise), next_block_id](td::Result R) mutable { + R.ensure(); + auto handle = R.move_as_ok(); + CHECK(handle->inited_next_left()); + if (handle->one_next(true) == next_block_id) { + promise.set_value(td::Unit()); + } else { + promise.set_error(td::Status::Error("next block id mismatch")); + } + }); + get_block_handle(prev_block_id, false, std::move(P)); + } } void ValidatorManagerImpl::validate_block_proof(BlockIdExt block_id, td::BufferSlice proof, @@ -468,7 +491,27 @@ void ValidatorManagerImpl::run_ext_query(td::BufferSlice data, td::Promise(data, true); + if (E.is_error()) { + run_liteserver_query(std::move(data), actor_id(this), lite_server_cache_.get(), std::move(P)); + } else { + auto e = E.move_as_ok(); + if (static_cast(e->seqno_) <= min_confirmed_masterchain_seqno_) { + run_liteserver_query(std::move(data), actor_id(this), lite_server_cache_.get(), std::move(P)); + } else { + auto t = e->timeout_ms_ < 10000 ? e->timeout_ms_ * 0.001 : 10.0; + auto Q = + td::PromiseCreator::lambda([data = std::move(data), SelfId = actor_id(this), cache = lite_server_cache_.get(), + promise = std::move(P)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + return; + } + run_liteserver_query(std::move(data), SelfId, cache, std::move(promise)); + }); + wait_shard_client_state(e->seqno_, td::Timestamp::in(t), std::move(Q)); + } + } } void ValidatorManagerImpl::wait_block_state(BlockHandle handle, td::uint32 priority, td::Timestamp timeout, @@ -1387,7 +1430,8 @@ void ValidatorManagerImpl::new_masterchain_block() { } void ValidatorManagerImpl::update_shards() { - if (last_masterchain_state_->rotated_all_shards() || last_masterchain_seqno_ == 0) { + if ((last_masterchain_state_->rotated_all_shards() || last_masterchain_seqno_ == 0) && + opts_->get_last_fork_masterchain_seqno() <= last_masterchain_seqno_) { allow_validate_ = true; } auto exp_vec = last_masterchain_state_->get_shards(); @@ -1468,7 +1512,7 @@ void ValidatorManagerImpl::update_shards() { auto val_set = last_masterchain_state_->get_validator_set(shard); auto x = val_set->export_vector(); - auto validator_id = get_validator(val_set); + auto validator_id = get_validator(shard, val_set); if (!validator_id.is_zero()) { auto val_group_id = get_validator_set_id(shard, val_set, opts_hash); @@ -1499,7 +1543,7 @@ void ValidatorManagerImpl::update_shards() { for (auto &shard : future_shards) { auto val_set = last_masterchain_state_->get_next_validator_set(shard); - auto validator_id = get_validator(val_set); + auto validator_id = get_validator(shard, val_set); if (!validator_id.is_zero()) { auto val_group_id = get_validator_set_id(shard, val_set, opts_hash); auto it = next_validator_groups_.find(val_group_id); @@ -1548,7 +1592,7 @@ void ValidatorManagerImpl::update_shards() { }); td::actor::send_closure(db_, &Db::update_destroyed_validator_sessions, gc_list_, std::move(P)); } -} +} // namespace validator void ValidatorManagerImpl::written_destroyed_validator_sessions(std::vector> list) { for (auto &v : list) { @@ -1609,7 +1653,7 @@ td::actor::ActorOwn ValidatorManagerImpl::create_validator_group if (check_gc_list_.count(session_id) == 1) { return td::actor::ActorOwn{}; } else { - auto validator_id = get_validator(validator_set); + auto validator_id = get_validator(shard, validator_set); CHECK(!validator_id.is_zero()); auto G = td::actor::create_actor("validatorgroup", shard, validator_id, session_id, validator_set, opts, keyring_, adnl_, rldp_, overlays_, db_root_, actor_id(this), @@ -1827,10 +1871,26 @@ void ValidatorManagerImpl::advance_gc(BlockHandle handle, td::Ref promise) { + auto seqno = handle->id().seqno(); + shard_client_update(seqno); + promise.set_value(td::Unit()); +} + void ValidatorManagerImpl::shard_client_update(BlockSeqno seqno) { if (min_confirmed_masterchain_seqno_ < seqno) { min_confirmed_masterchain_seqno_ = seqno; } + while (shard_client_waiters_.size() > 0) { + auto it = shard_client_waiters_.begin(); + if (it->first > seqno) { + break; + } + for (auto &y : it->second.waiting_) { + y.promise.set_value(td::Unit()); + } + shard_client_waiters_.erase(it); + } } void ValidatorManagerImpl::state_serializer_update(BlockSeqno seqno) { @@ -1870,6 +1930,9 @@ void ValidatorManagerImpl::alarm() { for (auto &w : wait_state_) { w.second.check_timers(); } + for (auto &w : shard_client_waiters_) { + w.second.check_timers(); + } } alarm_timestamp().relax(check_waiters_at_); if (check_shard_clients_.is_in_past()) { @@ -1904,8 +1967,12 @@ void ValidatorManagerImpl::update_shard_client_state(BlockIdExt masterchain_bloc td::actor::send_closure(db_, &Db::update_shard_client_state, masterchain_block_id, std::move(promise)); } -void ValidatorManagerImpl::get_shard_client_state(td::Promise promise) { - td::actor::send_closure(db_, &Db::get_shard_client_state, std::move(promise)); +void ValidatorManagerImpl::get_shard_client_state(bool from_db, td::Promise promise) { + if (!shard_client_.empty() && !from_db) { + td::actor::send_closure(shard_client_, &ShardClient::get_processed_masterchain_block_id, std::move(promise)); + } else { + td::actor::send_closure(db_, &Db::get_shard_client_state, std::move(promise)); + } } void ValidatorManagerImpl::subscribe_to_shard(ShardIdFull shard) { @@ -1928,7 +1995,10 @@ bool ValidatorManagerImpl::is_validator() { return temp_keys_.size() > 0 || permanent_keys_.size() > 0; } -PublicKeyHash ValidatorManagerImpl::get_validator(td::Ref val_set) { +PublicKeyHash ValidatorManagerImpl::get_validator(ShardIdFull shard, td::Ref val_set) { + if (!opts_->need_validate(shard)) { + return PublicKeyHash::zero(); + } for (auto &key : temp_keys_) { if (val_set->is_validator(key.bits256_value())) { return key; @@ -2017,6 +2087,28 @@ void ValidatorManagerImpl::prepare_stats(td::Promise state, td::Promise promise) { + td::actor::send_closure(db_, &Db::truncate, std::move(state), std::move(promise)); +} + +void ValidatorManagerImpl::wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, + td::Promise promise) { + if (seqno <= min_confirmed_masterchain_seqno_) { + promise.set_value(td::Unit()); + return; + } + if (timeout.is_in_past()) { + promise.set_error(td::Status::Error(ErrorCode::timeout, "timeout")); + return; + } + if (seqno > min_confirmed_masterchain_seqno_ + 100) { + promise.set_error(td::Status::Error(ErrorCode::notready, "too big masterchain block seqno")); + return; + } + + shard_client_waiters_[seqno].waiting_.emplace_back(timeout, 0, std::move(promise)); +} + td::actor::ActorOwn ValidatorManagerFactory::create( td::Ref opts, std::string db_root, td::actor::ActorId keyring, td::actor::ActorId adnl, td::actor::ActorId rldp, diff --git a/validator/manager.hpp b/validator/manager.hpp index 082432fa36..6ce036ca74 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -143,6 +143,8 @@ class ValidatorManagerImpl : public ValidatorManager { std::vector> waiting_; td::actor::ActorId actor_; + WaitList() = default; + std::pair get_timeout() const { td::Timestamp t = td::Timestamp::now(); td::uint32 prio = 0; @@ -256,6 +258,7 @@ class ValidatorManagerImpl : public ValidatorManager { void advance_gc(BlockHandle handle, td::Ref state); void try_advance_gc_masterchain_block(); void update_gc_block_handle(BlockHandle handle, td::Promise promise) override; + void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) override; public: void install_callback(std::unique_ptr new_callback, td::Promise promise) override { @@ -414,7 +417,7 @@ class ValidatorManagerImpl : public ValidatorManager { void send_block_broadcast(BlockBroadcast broadcast) override; void update_shard_client_state(BlockIdExt masterchain_block_id, td::Promise promise) override; - void get_shard_client_state(td::Promise promise) override; + void get_shard_client_state(bool from_db, td::Promise promise) override; void subscribe_to_shard(ShardIdFull shard) override; void update_async_serializer_state(AsyncSerializerState state, td::Promise promise) override; @@ -448,7 +451,7 @@ class ValidatorManagerImpl : public ValidatorManager { void read_gc_list(std::vector list); bool is_validator(); - PublicKeyHash get_validator(td::Ref val_set); + PublicKeyHash get_validator(ShardIdFull shard, td::Ref val_set); ValidatorManagerImpl(td::Ref opts, std::string db_root, td::actor::ActorId keyring, td::actor::ActorId adnl, @@ -502,6 +505,10 @@ class ValidatorManagerImpl : public ValidatorManager { void prepare_stats(td::Promise>> promise) override; + void truncate(td::Ref state, td::Promise promise) override; + + void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) override; + private: td::Timestamp resend_shard_blocks_at_; td::Timestamp check_waiters_at_; @@ -558,6 +565,9 @@ class ValidatorManagerImpl : public ValidatorManager { double block_ttl() const { return opts_->block_ttl(); } + + private: + std::map> shard_client_waiters_; }; } // namespace validator diff --git a/validator/shard-client.cpp b/validator/shard-client.cpp index 44973e4478..2f05bdac98 100644 --- a/validator/shard-client.cpp +++ b/validator/shard-client.cpp @@ -36,7 +36,7 @@ void ShardClient::start_up() { R.ensure(); td::actor::send_closure(SelfId, &ShardClient::got_state_from_db, R.move_as_ok()); }); - td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, std::move(P)); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, true, std::move(P)); } void ShardClient::got_state_from_db(BlockIdExt state) { @@ -94,6 +94,8 @@ void ShardClient::saved_to_db() { } CHECK(masterchain_block_handle_); + td::actor::send_closure(manager_, &ValidatorManager::update_shard_client_block_handle, masterchain_block_handle_, + [](td::Unit) {}); if (masterchain_block_handle_->inited_next_left()) { new_masterchain_block_id(masterchain_block_handle_->one_next(true)); } else { @@ -198,6 +200,14 @@ void ShardClient::get_processed_masterchain_block(td::Promise promis promise.set_result(seqno); } +void ShardClient::get_processed_masterchain_block_id(td::Promise promise) { + if (masterchain_block_handle_) { + promise.set_result(masterchain_block_handle_->id()); + } else { + promise.set_error(td::Status::Error(ErrorCode::notready, "shard client not started")); + } +} + void ShardClient::build_shard_overlays() { auto v = masterchain_state_->get_shards(); diff --git a/validator/shard-client.hpp b/validator/shard-client.hpp index e00f4477cc..2025041fca 100644 --- a/validator/shard-client.hpp +++ b/validator/shard-client.hpp @@ -88,6 +88,7 @@ class ShardClient : public td::actor::Actor { void new_masterchain_block_notification(BlockHandle handle, td::Ref state); void get_processed_masterchain_block(td::Promise promise); + void get_processed_masterchain_block_id(td::Promise promise); }; } // namespace validator diff --git a/validator/validator-options.cpp b/validator/validator-options.cpp index 856bdd7328..8dcc6a80e8 100644 --- a/validator/validator-options.cpp +++ b/validator/validator-options.cpp @@ -25,7 +25,7 @@ namespace ton { namespace validator { td::Ref ValidatorManagerOptions::create( - BlockIdExt zero_block_id, BlockIdExt init_block_id, std::function check_shard, + BlockIdExt zero_block_id, BlockIdExt init_block_id, std::function check_shard, bool allow_blockchain_init, td::ClocksBase::Duration sync_blocks_before, td::ClocksBase::Duration block_ttl, td::ClocksBase::Duration state_ttl, td::ClocksBase::Duration archive_ttl, td::ClocksBase::Duration key_proof_ttl, bool initial_sync_disabled) { diff --git a/validator/validator-options.hpp b/validator/validator-options.hpp index 21bf79cb5c..315027e9b9 100644 --- a/validator/validator-options.hpp +++ b/validator/validator-options.hpp @@ -33,7 +33,10 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { return init_block_id_; } bool need_monitor(ShardIdFull shard) const override { - return check_shard_(shard); + return check_shard_(shard, ShardCheckMode::m_monitor); + } + bool need_validate(ShardIdFull shard) const override { + return check_shard_(shard, ShardCheckMode::m_validate); } bool allow_blockchain_init() const override { return allow_blockchain_init_; @@ -82,6 +85,9 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { td::uint32 get_last_fork_masterchain_seqno() const override { return hardforks_.size() ? hardforks_.rbegin()->seqno() : 0; } + std::vector get_hardforks() const override { + return hardforks_; + } td::uint32 get_filedb_depth() const override { return db_depth_; } @@ -92,7 +98,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { void set_init_block_id(BlockIdExt block_id) override { init_block_id_ = block_id; } - void set_shard_check_function(std::function check_shard) override { + void set_shard_check_function(std::function check_shard) override { check_shard_ = std::move(check_shard); } void set_allow_blockchain_init(bool value) override { @@ -129,7 +135,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { } ValidatorManagerOptionsImpl(BlockIdExt zero_block_id, BlockIdExt init_block_id, - std::function check_shard, bool allow_blockchain_init, + std::function check_shard, bool allow_blockchain_init, td::ClocksBase::Duration sync_blocks_before, td::ClocksBase::Duration block_ttl, td::ClocksBase::Duration state_ttl, td::ClocksBase::Duration archive_ttl, td::ClocksBase::Duration key_proof_ttl, bool initial_sync_disabled) @@ -148,7 +154,7 @@ struct ValidatorManagerOptionsImpl : public ValidatorManagerOptions { private: BlockIdExt zero_block_id_; BlockIdExt init_block_id_; - std::function check_shard_; + std::function check_shard_; bool allow_blockchain_init_; td::ClocksBase::Duration sync_blocks_before_; td::ClocksBase::Duration block_ttl_; diff --git a/validator/validator.h b/validator/validator.h index 2e80c13580..1387fc95c3 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -46,9 +46,12 @@ class DownloadToken { struct ValidatorManagerOptions : public td::CntObject { public: + enum class ShardCheckMode { m_monitor, m_validate }; + virtual BlockIdExt zero_block_id() const = 0; virtual BlockIdExt init_block_id() const = 0; virtual bool need_monitor(ShardIdFull shard) const = 0; + virtual bool need_validate(ShardIdFull shard) const = 0; virtual bool allow_blockchain_init() const = 0; virtual td::ClocksBase::Duration sync_blocks_before() const = 0; virtual td::ClocksBase::Duration block_ttl() const = 0; @@ -60,6 +63,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual td::uint32 get_vertical_seqno(BlockSeqno seqno) const = 0; virtual td::uint32 get_maximal_vertical_seqno() const = 0; virtual td::uint32 get_last_fork_masterchain_seqno() const = 0; + virtual std::vector get_hardforks() const = 0; virtual td::uint32 get_filedb_depth() const = 0; virtual td::uint32 key_block_utime_step() const { return 86400; @@ -67,7 +71,7 @@ struct ValidatorManagerOptions : public td::CntObject { virtual void set_zero_block_id(BlockIdExt block_id) = 0; virtual void set_init_block_id(BlockIdExt block_id) = 0; - virtual void set_shard_check_function(std::function check_shard) = 0; + virtual void set_shard_check_function(std::function check_shard) = 0; virtual void set_allow_blockchain_init(bool value) = 0; virtual void set_sync_blocks_before(td::ClocksBase::Duration value) = 0; virtual void set_block_ttl(td::ClocksBase::Duration value) = 0; @@ -80,7 +84,7 @@ struct ValidatorManagerOptions : public td::CntObject { static td::Ref create( BlockIdExt zero_block_id, BlockIdExt init_block_id, - std::function check_shard = [](ShardIdFull) { return true; }, + std::function check_shard = [](ShardIdFull, ShardCheckMode) { return true; }, bool allow_blockchain_init = false, td::ClocksBase::Duration sync_blocks_before = 300, td::ClocksBase::Duration block_ttl = 86400 * 7, td::ClocksBase::Duration state_ttl = 3600, td::ClocksBase::Duration archive_ttl = 86400 * 365, td::ClocksBase::Duration key_proof_ttl = 86400 * 3650, From dd745485e26cf581019c2b8c3013d4c14ec0473e Mon Sep 17 00:00:00 2001 From: ton Date: Fri, 4 Oct 2019 16:08:02 +0400 Subject: [PATCH 006/667] update tonlib tonlib: update collator: increased collation speed for masterchain fift: bugfixes --- crypto/block/mc-config.cpp | 2 +- crypto/common/bigint.hpp | 6 +- crypto/fift/fift-main.cpp | 4 +- crypto/func/analyzer.cpp | 2 +- crypto/tl/boc.tlb | 3 +- crypto/vm/boc.cpp | 11 +-- crypto/vm/cells/CellBuilder.cpp | 3 - doc/tblkch.tex | 46 +++++++----- tl/generate/scheme/tonlib_api.tl | 4 +- tl/generate/scheme/tonlib_api.tlo | Bin 13000 -> 13072 bytes tonlib/test/offline.cpp | 5 +- tonlib/tonlib/ExtClient.h | 7 +- tonlib/tonlib/KeyStorage.cpp | 28 +++++--- tonlib/tonlib/KeyValue.cpp | 2 +- tonlib/tonlib/LastBlock.cpp | 37 ++++++++-- tonlib/tonlib/LastBlock.h | 2 + tonlib/tonlib/TonlibClient.cpp | 40 +++++++---- tonlib/tonlib/TonlibError.h | 3 + tonlib/tonlib/keys/DecryptedKey.cpp | 23 +----- tonlib/tonlib/keys/DecryptedKey.h | 2 - tonlib/tonlib/keys/EncryptedKey.cpp | 14 ++-- tonlib/tonlib/keys/EncryptedKey.h | 2 +- tonlib/tonlib/tonlib-cli.cpp | 75 +++++++++++++++++--- validator/impl/collator.cpp | 40 +++++++++-- validator/impl/liteserver.cpp | 105 +++++++++++++++++----------- validator/impl/liteserver.hpp | 7 +- validator/manager.cpp | 4 +- 27 files changed, 309 insertions(+), 168 deletions(-) diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index a8fbeed262..ceefca79e5 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -897,7 +897,7 @@ static int process_workchain_shard_hashes(Ref& branch, ton::ShardIdFul int f = (int)cs.fetch_ulong(1); if (f == 1) { if ((shard.shard & 1) || cs.size_ext() != 0x20000) { - return false; + return -1; } auto left = cs.prefetch_ref(0), right = cs.prefetch_ref(1); int r = process_workchain_shard_hashes(left, ton::shard_child(shard, true), func); diff --git a/crypto/common/bigint.hpp b/crypto/common/bigint.hpp index 63bfe464c9..6734f07e32 100644 --- a/crypto/common/bigint.hpp +++ b/crypto/common/bigint.hpp @@ -1922,7 +1922,7 @@ bool AnyIntView::export_bits_any(unsigned char* buff, int offs, unsigned bit if (--ptr > buff) { *ptr = (unsigned char)(v & 0xff); } else { - int mask = (-0x100 >> offs) & 0xff; + int mask = (0xff00 >> offs) & 0xff; if (((unsigned char)v ^ s) & mask) { return false; } @@ -1943,7 +1943,7 @@ bool AnyIntView::export_bits_any(unsigned char* buff, int offs, unsigned bit if (--ptr > buff) { *ptr = (unsigned char)(v & 0xff); } else { - int mask = (-0x100 >> offs) & 0xff; + int mask = (0xff00 >> offs) & 0xff; if (((unsigned char)v ^ s) & mask) { return false; } @@ -1961,7 +1961,7 @@ bool AnyIntView::export_bits_any(unsigned char* buff, int offs, unsigned bit *ptr = (unsigned char)(v & 0xff); v >>= 8; } - int mask = (-0x100 >> offs) & 0xff; + int mask = (0xff00 >> offs) & 0xff; if (((unsigned char)v ^ s) & mask) { return false; } diff --git a/crypto/fift/fift-main.cpp b/crypto/fift/fift-main.cpp index e040b6e8cf..8702f13dcb 100644 --- a/crypto/fift/fift-main.cpp +++ b/crypto/fift/fift-main.cpp @@ -132,9 +132,7 @@ int main(int argc, char* const argv[]) { if (!no_env) { const char* path = std::getenv("FIFTPATH"); - if (path) { - parse_include_path_set(path ? path : "/usr/lib/fift", source_include_path); - } + parse_include_path_set(path ? path : "/usr/lib/fift", source_include_path); } std::string current_dir; auto r_current_dir = td::realpath("."); diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index 207aa64b4a..4d2e790298 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -138,7 +138,7 @@ bool Op::set_var_info_except(const VarDescrList& new_var_info, const std::vector } VarDescrList tmp_info{new_var_info}; tmp_info -= var_list; - return set_var_info(new_var_info); + return set_var_info(tmp_info); } bool Op::set_var_info_except(VarDescrList&& new_var_info, const std::vector& var_list) { diff --git a/crypto/tl/boc.tlb b/crypto/tl/boc.tlb index c296cb6ffb..d283c78c10 100644 --- a/crypto/tl/boc.tlb +++ b/crypto/tl/boc.tlb @@ -31,8 +31,9 @@ serialized_boc#b5ee9c72 has_idx:(## 1) has_crc32c:(## 1) absent:(##(size * 8)) { roots + absent <= cells } tot_cells_size:(##(off_bytes * 8)) root_list:(roots * ##(size * 8)) - index:(cells * ##(off_bytes * 8)) + index:has_idx?(cells * ##(off_bytes * 8)) cell_data:(tot_cells_size * [ uint8 ]) + crc32c:has_crc32c?uint32 = BagOfCells; compiled_smart_contract diff --git a/crypto/vm/boc.cpp b/crypto/vm/boc.cpp index 9b60884d10..fbb39dfe23 100644 --- a/crypto/vm/boc.cpp +++ b/crypto/vm/boc.cpp @@ -75,7 +75,7 @@ td::Result CellSerializationInfo::get_bits(td::Slice cell) const { if (data_with_bits) { DCHECK(data_len != 0); int last = cell[data_offset + data_len - 1]; - if (!last || last == 0x80) { + if (!(last & 0x7f)) { return td::Status::Error("overlong encoding"); } return td::narrow_cast((data_len - 1) * 8 + 7 - td::count_trailing_zeroes_non_zero32(last)); @@ -391,15 +391,6 @@ td::uint64 BagOfCells::compute_sizes(int mode, int& r_size, int& o_size) { r_size = o_size = 0; return 0; } - if (!(mode & Mode::WithIndex)) { - if (rs > 2 || os > 4) { - rs = std::max(3, rs); - os = 8; - data_bytes_adj = data_bytes + (unsigned long long)int_refs * rs + hashes; - } else { - os = 4; - } - } r_size = rs; o_size = os; return data_bytes_adj; diff --git a/crypto/vm/cells/CellBuilder.cpp b/crypto/vm/cells/CellBuilder.cpp index 37940f718a..223959bdc4 100644 --- a/crypto/vm/cells/CellBuilder.cpp +++ b/crypto/vm/cells/CellBuilder.cpp @@ -527,9 +527,6 @@ int CellBuilder::serialize(unsigned char* buff, int buff_size) const { CellBuilder* CellBuilder::make_copy() const { CellBuilder* c = new CellBuilder(); - if (!c) { - throw CellWriteError(); - } c->bits = bits; std::memcpy(c->data, data, (bits + 7) >> 3); c->refs_cnt = refs_cnt; diff --git a/doc/tblkch.tex b/doc/tblkch.tex index 10de9cdc90..daf500653d 100644 --- a/doc/tblkch.tex +++ b/doc/tblkch.tex @@ -2000,36 +2000,46 @@ \section*{Introduction} \end{itemize} \nxsubpoint\label{sp:boc.ser.sch}\emb{TL-B scheme for serializing bags of cells} -Several TL-B constructors can be used to serialize bags of cells into octet (i.e., 8-bit byte) sequences: +Several TL-B constructors can be used to serialize bags of cells into octet (i.e., 8-bit byte) sequences. The only one that is currently used to serialize new bags of cell is \begin{verbatim} -serialized_boc_tiny cells:uint8 roots:uint8 absent:uint8 - tot_cells_size:(## 32) cell_data:(tot_cells_size * [ uint8 ]) - = BagOfCells; -serialized_boc_small cells:uint16 roots:uint16 absent:uint16 - tot_cells_size:(## 32) cell_data:(tot_cells_size * [ uint8 ]) - = BagOfCells; -serialized_boc_medium cells:uint24 roots:uint24 absent:uint24 - tot_cells_size:(## 64) cell_data:(tot_cells_size * [ uint8 ]) - = BagOfCells; -serialized_boc_large cells:uint32 roots:uint32 absent:uint32 - tot_cells_size:(## 64) cell_data:(tot_cells_size * [ uint8 ]) +serialized_boc#b5ee9c72 has_idx:(## 1) has_crc32c:(## 1) + has_cache_bits:(## 1) flags:(## 2) { flags = 0 } + size:(## 3) { size <= 4 } + off_bytes:(## 8) { off_bytes <= 8 } + cells:(##(size * 8)) + roots:(##(size * 8)) { roots >= 1 } + absent:(##(size * 8)) { roots + absent <= cells } + tot_cells_size:(##(off_bytes * 8)) + root_list:(roots * ##(size * 8)) + index:has_idx?(cells * ##(off_bytes * 8)) + cell_data:(tot_cells_size * [ uint8 ]) + crc32c:has_crc32c?uint32 = BagOfCells; \end{verbatim} -Field {\tt cells} is $n$, {\tt roots} is $k$, {\tt absent} is $l$, and {\tt tot\_cells\_size} is $L_n$ (the total size of the serialization of all cells in bytes). +Field {\tt cells} is $n$, {\tt roots} is $k$, {\tt absent} is $l$, and {\tt tot\_cells\_size} is $L_n$ (the total size of the serialization of all cells in bytes). If an index is present, parameters $s/8$ and $t/8$ are serialized separately as {\tt size} and {\tt off\_bytes}, respectively, and the flag {\tt has\_idx} is set. The index itself is contained in {\tt index}, present only if {\tt has\_idx} is set. The field {\tt root\_list} contains the (zero-based) indices of the root nodes of the bag of cells. -If an index is present, parameters $s/8$ and $t/8$ are serialized separately as 8-bit fields {\tt size} and {\tt off\_bytes}, respectively: +Two older constructors are still supported in the bag-of-cells deserialization functions: \begin{verbatim} -serialized_boc_idx size:(## 8) { size <= 4 } +serialized_boc_idx#68ff65f3 size:(## 8) { size <= 4 } off_bytes:(## 8) { off_bytes <= 8 } cells:(##(size * 8)) - roots:(##(size * 8)) + roots:(##(size * 8)) { roots = 1 } absent:(##(size * 8)) { roots + absent <= cells } tot_cells_size:(##(off_bytes * 8)) - index:(cells * [ ##(off_bytes * 8) ]) + index:(cells * ##(off_bytes * 8)) cell_data:(tot_cells_size * [ uint8 ]) = BagOfCells; + +serialized_boc_idx_crc32c#acc3a728 size:(## 8) { size <= 4 } + off_bytes:(## 8) { off_bytes <= 8 } + cells:(##(size * 8)) + roots:(##(size * 8)) { roots = 1 } + absent:(##(size * 8)) { roots + absent <= cells } + tot_cells_size:(##(off_bytes * 8)) + index:(cells * ##(off_bytes * 8)) + cell_data:(tot_cells_size * [ uint8 ]) + crc32c:uint32 = BagOfCells; \end{verbatim} -Finally, constructors {\tt serialized\_boc\_*\_crc32}, with the asterisk replaced by either {\tt tiny}, {\tt small}, {\tt medium}, {\tt large}, or {\tt idx}, are also introduced, with one extra field {\tt crc32c:uint32} added as the last field. \nxsubpoint\emb{Storing compiled TVM code in files} Notice that the above procedure for serializing bags of cells may be used to serialize compiled smart contracts and other TVM code. One must define a TL-B constructor similar to the following: diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index deae3fa970..c0dc53c1ff 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -38,7 +38,7 @@ unpackedAccountAddress workchain_id:int32 bounceable:Bool testnet:Bool addr:byte internal.transactionId lt:int64 hash:bytes = internal.TransactionId; raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState; -raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState; +raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId frozen_hash:bytes sync_utime:int53 = raw.AccountState; raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes message:bytes = raw.Message; raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector = raw.Transaction; raw.transactions transactions:vector previous_transaction_id:internal.transactionId = raw.Transactions; @@ -51,7 +51,7 @@ wallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.trans testGiver.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53= testGiver.AccountState; -uninited.accountState balance:int64 last_transaction_id:internal.transactionId sync_utime:int53 = uninited.AccountState; +uninited.accountState balance:int64 last_transaction_id:internal.transactionId frozen_hash:bytes sync_utime:int53 = uninited.AccountState; //generic.initialAccountStateRaw initital_account_state:raw.initialAccountState = generic.InitialAccountState; //generic.initialAccountStateTestWallet initital_account_state:testWallet.initialAccountState = generic.InitialAccountState; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 180f3521725fa8e4ac53b9db1d7e62587be7636c..1050a59cfde56bf149a42689b6fab05c8647c041 100644 GIT binary patch delta 230 zcmX?+Iw5U?04s}^>_hj>BCH*ZEL$0@Pi|homcauSn{1$@uvtfV21FdhoBTqgMiHc3 zpeV6iFEKeezcjBTxFoS8m4Sf)B!2UlkPqABk230$*NEouaHkdJSEc5~XCxM9OimCK znOq>jGI@=Z2E;}eu{*2^AR9$X^D^@?OHxzNOa#eIej}qZ`HiR!ZnMB{J|c4l>gEF) QDw7ZBi9oElp!tIX0Ak@%uK)l5 delta 185 zcmbP`b|Q6y04s~s=4R>5BCH*ZEG^ISGdC|_%iw{C@d)pLh)w1ZR+#)kq(%{>RG=ua zTrV*>IlnZoB)BB8B$a`I0VIC&n2-7$!kP&CSQ=!fEbb?c866JWUy#yUS?iq nNooq3W{@06FEd#01Q{K$Ua(VmWY0jHGC4rW0%Dwm)(;K( - make_object(nullptr, make_object(".")))) + sync_send(client, make_object(make_object( + nullptr, make_object("testdir")))) .ensure(); auto local_password = td::SecureString("local password"); auto mnemonic_password = td::SecureString("mnemonic password"); diff --git a/tonlib/tonlib/ExtClient.h b/tonlib/tonlib/ExtClient.h index 69af0c0bee..1547337948 100644 --- a/tonlib/tonlib/ExtClient.h +++ b/tonlib/tonlib/ExtClient.h @@ -61,15 +61,14 @@ class ExtClient { auto raw_query = ton::serialize_tl_object(&query, true); td::uint32 tag = td::Random::fast_uint32(); VLOG(lite_server) << "send query to liteserver: " << tag << " " << to_string(query); - td::BufferSlice liteserver_query = - ton::serialize_tl_object(ton::create_tl_object(std::move(raw_query)), true); - if (seq_no >= 0) { auto wait = ton::lite_api::liteServer_waitMasterchainSeqno(seq_no, 5000); VLOG(lite_server) << " with prefix " << to_string(wait); auto prefix = ton::serialize_tl_object(&wait, true); - liteserver_query = td::BufferSlice(PSLICE() << prefix.as_slice() << liteserver_query.as_slice()); + raw_query = td::BufferSlice(PSLICE() << prefix.as_slice() << raw_query.as_slice()); } + td::BufferSlice liteserver_query = + ton::serialize_tl_object(ton::create_tl_object(std::move(raw_query)), true); send_raw_query( std::move(liteserver_query), [promise = std::move(promise), tag](td::Result R) mutable { diff --git a/tonlib/tonlib/KeyStorage.cpp b/tonlib/tonlib/KeyStorage.cpp index fd6607775c..6e01f2f846 100644 --- a/tonlib/tonlib/KeyStorage.cpp +++ b/tonlib/tonlib/KeyStorage.cpp @@ -69,7 +69,7 @@ td::Result KeyStorage::export_decrypted_key(InputKey input_key) { if (r_encrypted_data.is_error()) { r_encrypted_data = kv_->get(to_file_name_old(input_key.key)); if (r_encrypted_data.is_ok()) { - LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> " + LOG(WARNING) << "Restore private key from deprecated location " << to_file_name_old(input_key.key) << " --> " << to_file_name(input_key.key); TRY_STATUS_PREFIX(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()), TonlibError::Internal()); kv_->erase(to_file_name_old(input_key.key)).ignore(); @@ -78,6 +78,21 @@ td::Result KeyStorage::export_decrypted_key(InputKey input_key) { TRY_RESULT_PREFIX(encrypted_data, std::move(r_encrypted_data), TonlibError::KeyUnknown()); EncryptedKey encrypted_key{std::move(encrypted_data), td::Ed25519::PublicKey(std::move(input_key.key.public_key)), std::move(input_key.key.secret)}; + { + auto r_decrypted_key = encrypted_key.decrypt(input_key.local_password.copy(), true, true); + if (r_decrypted_key.is_ok()) { + LOG(WARNING) << "Restore private from deprecated encryption " << to_file_name(input_key.key); + auto decrypted_key = r_decrypted_key.move_as_ok(); + auto key = Key{encrypted_key.public_key.as_octet_string(), encrypted_key.secret.copy()}; + auto new_encrypted_key = decrypted_key.encrypt(input_key.local_password.copy(), encrypted_key.secret); + CHECK(new_encrypted_key.public_key.as_octet_string() == encrypted_key.public_key.as_octet_string()); + CHECK(new_encrypted_key.secret == encrypted_key.secret); + CHECK(new_encrypted_key.decrypt(input_key.local_password.copy()).ok().private_key.as_octet_string() == + decrypted_key.private_key.as_octet_string()); + kv_->set(to_file_name(key), new_encrypted_key.encrypted_data); + return std::move(decrypted_key); + } + } TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(std::move(input_key.local_password)), TonlibError::KeyDecrypt()); return std::move(decrypted_key); @@ -139,14 +154,9 @@ td::Result KeyStorage::export_pem_key(InputKey input } td::Result KeyStorage::change_local_password(InputKey input_key, td::Slice new_local_password) { - auto new_secret = - DecryptedKey::change_local_password(input_key.key.secret, input_key.local_password, new_local_password); - Key res; - res.public_key = std::move(input_key.key.public_key); - res.secret = std::move(new_secret); - TRY_RESULT_PREFIX(value, kv_->get(to_file_name(input_key.key)), TonlibError::KeyUnknown()); - TRY_STATUS_PREFIX(kv_->add(to_file_name(res), value), TonlibError::Internal()); - return std::move(res); + auto old_name = to_file_name(input_key.key); + TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key))); + return save_key(std::move(decrypted_key), new_local_password); } td::Result KeyStorage::import_pem_key(td::Slice local_password, td::Slice key_password, diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index d71744f04d..796969241f 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -39,7 +39,7 @@ class KeyValueDir : public KeyValue { } td::Status erase(td::Slice key) override { - return td::unlink(key.str()); + return td::unlink(to_file_path(key.str())); } void foreach_key(std::function f) override { diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index fd15fed80c..d498aa7a25 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -32,7 +32,8 @@ namespace tonlib { // td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state) { return sb << td::tag("last_block", state.last_block_id.to_str()) - << td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime); + << td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime) + << td::tag("init_block", state.init_block_id.to_str()); } LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr callback) @@ -40,8 +41,7 @@ LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, t client_.set_client(client); state_.last_block_id = state_.last_key_block_id; - VLOG(last_block) << "check_init_block: skip - FIXME before release"; - check_init_block_state_ = QueryState::Done; + VLOG(last_block) << "State: " << state_; } void LastBlock::get_last_block(td::Promise promise) { @@ -87,7 +87,7 @@ void LastBlock::sync_loop() { } else { check_init_block_state_ = QueryState::Active; check_init_block_stats_.start(); - if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) { + if (state_.last_key_block_id.id.seqno >= config_.init_block_id.id.seqno) { VLOG(last_block) << "check_init_block: start - init_block -> last_block"; do_check_init_block(config_.init_block_id, state_.last_key_block_id); } else { @@ -162,10 +162,18 @@ void LastBlock::update_state(block::BlockProofChain& chain) { update_utime(chain.last_utime); } if (is_changed) { - callback_->on_state_changed(state_); + save_state(); } } +void LastBlock::save_state() { + if (check_init_block_state_ != QueryState::Done) { + VLOG(last_block) << "skip `save_state` because `check_init_block` is not finished"; + return; + } + callback_->on_state_changed(state_); +} + void LastBlock::on_block_proof( ton::BlockIdExt from, td::Result> r_block_proof) { @@ -209,6 +217,9 @@ void LastBlock::on_init_block_proof( if (chain->complete) { VLOG(last_block) << "check_init_block: done\n" << check_init_block_stats_; check_init_block_state_ = QueryState::Done; + if (update_init_block(config_.init_block_id)) { + save_state(); + } sync_loop(); } else { do_check_init_block(chain->to, to); @@ -291,6 +302,22 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) { return false; } +bool LastBlock::update_init_block(ton::BlockIdExt init_block_id) { + if (has_fatal_error()) { + return false; + } + if (!init_block_id.is_valid()) { + LOG(ERROR) << "Ignore invalid init block"; + return false; + } + if (state_.init_block_id != init_block_id) { + state_.init_block_id = init_block_id; + LOG(INFO) << "Update init block id: " << state_.init_block_id.to_str(); + return true; + } + return false; +} + void LastBlock::update_utime(td::int64 utime) { if (state_.utime < utime) { state_.utime = utime; diff --git a/tonlib/tonlib/LastBlock.h b/tonlib/tonlib/LastBlock.h index bb2a62ed4e..edd8c6f387 100644 --- a/tonlib/tonlib/LastBlock.h +++ b/tonlib/tonlib/LastBlock.h @@ -201,7 +201,9 @@ class LastBlock : public td::actor::Actor { bool update_mc_last_block(ton::BlockIdExt mc_block_id); bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id); void update_utime(td::int64 utime); + bool update_init_block(ton::BlockIdExt init_block_id); + void save_state(); void on_sync_ok(); void on_sync_error(td::Status status); void on_fatal_error(td::Status status); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 7989e18e03..63203df8a1 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -108,7 +108,6 @@ class GetTransactionHistory : public td::actor::Actor { void check(td::Status status) { if (status.is_error()) { - LOG(ERROR) << status; promise_.set_error(std::move(status)); stop(); } @@ -208,7 +207,7 @@ class GetRawAccountState : public td::actor::Actor { auto cell = res.info.root; std::ostringstream outp; block::gen::t_Account.print_ref(outp, cell); - LOG(ERROR) << outp.str(); + LOG(INFO) << outp.str(); if (cell.is_null()) { return res; } @@ -682,8 +681,9 @@ td::Result> to_raw_accountS .as_slice() .str(); } - return tonlib_api::make_object( - raw_state.balance, std::move(code), std::move(data), to_transaction_id(raw_state.info), raw_state.info.gen_utime); + return tonlib_api::make_object(raw_state.balance, std::move(code), std::move(data), + to_transaction_id(raw_state.info), raw_state.frozen_hash, + raw_state.info.gen_utime); } td::Result to_std_address_or_throw(td::Ref cs) { @@ -803,7 +803,7 @@ td::Result> to_raw_transacti std::ostringstream outp; block::gen::t_Transaction.print_ref(outp, info.transaction); - LOG(ERROR) << outp.str(); + LOG(INFO) << outp.str(); auto is_just = trans.r1.in_msg->prefetch_long(1); if (is_just == trans.r1.in_msg->fetch_long_eof) { @@ -917,7 +917,7 @@ td::Result> to_generic_ if (raw_state.code.is_null()) { return tonlib_api::make_object( tonlib_api::make_object(raw_state.balance, to_transaction_id(raw_state.info), - raw_state.info.gen_utime)); + raw_state.frozen_hash, raw_state.info.gen_utime)); } auto code_hash = raw_state.code->prefetch_ref()->get_hash(); @@ -953,7 +953,6 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, client_.send_query(ton::lite_api::liteServer_sendMessage(vm::std_boc_serialize(message).move_as_ok()), [promise = std::move(promise)](auto r_info) mutable { TRY_RESULT_PROMISE(promise, info, std::move(r_info)); - LOG(ERROR) << "info: " << to_string(info); promise.set_value(tonlib_api::make_object()); }); return td::Status::OK(); @@ -1042,7 +1041,6 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ return TonlibError::MessageTooLong(); } TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); - account_address.bounceable = false; TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( 0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); @@ -1116,7 +1114,6 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, TRY_RESULT_PREFIX(valid_until, td::narrow_cast_safe(request.valid_until_), TonlibError::InvalidField("valid_until", "overflow")); TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); - account_address.bounceable = false; TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( 0 /*zerochain*/, Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); @@ -1168,7 +1165,6 @@ td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& reque return TonlibError::MessageTooLong(); } TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); - account_address.bounceable = false; auto message = TestGiver::make_a_gift_message(request.seqno_, request.amount_, request.message_, account_address); auto message_hash = message->get_hash().as_slice().str(); td::Promise> new_promise = @@ -1245,7 +1241,7 @@ class GenericSendGrams : public TonlibQueryActor { block::StdAddress source_address_; tonlib_api::object_ptr destination_state_; - bool is_destination_bounce_{false}; + bool is_destination_bounceable_{false}; void check(td::Status status) { if (status.is_error()) { @@ -1263,7 +1259,7 @@ class GenericSendGrams : public TonlibQueryActor { return TonlibError::EmptyField("destination"); } TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_)); - is_destination_bounce_ = destination_address.bounceable; + is_destination_bounceable_ = destination_address.bounceable; if (!send_grams_.source_) { return TonlibError::EmptyField("destination"); @@ -1333,9 +1329,23 @@ class GenericSendGrams : public TonlibQueryActor { td::Status do_on_destination_state(td::Result> r_state) { TRY_RESULT(state, std::move(r_state)); destination_state_ = std::move(state); - if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounce_ && - !send_grams_.allow_send_to_uninited_) { - return TonlibError::DangerousTransaction("Transfer to uninited wallet"); + if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounceable_) { + //FIXME: after restoration of frozen accounts will be supported + if (!static_cast(*destination_state_) + .account_state_->frozen_hash_.empty()) { + return TonlibError::TransferToFrozen(); + //return TonlibError::DangerousTransaction("Transfer to frozen wallet"); + } + if (send_grams_.allow_send_to_uninited_) { + TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_)); + destination_address.bounceable = false; + auto new_destination_address = destination_address.rserialize(true); + LOG(INFO) << "Change destination address from bounceable to non-bounceable " + << send_grams_.destination_->account_address_ << " -> " << new_destination_address; + send_grams_.destination_->account_address_ = std::move(new_destination_address); + } else { + return TonlibError::DangerousTransaction("Transfer to uninited wallet"); + } } return do_loop(); } diff --git a/tonlib/tonlib/TonlibError.h b/tonlib/tonlib/TonlibError.h index e0b4d0b189..b521c13689 100644 --- a/tonlib/tonlib/TonlibError.h +++ b/tonlib/tonlib/TonlibError.h @@ -128,6 +128,9 @@ struct TonlibError { static td::Status NotEnoughFunds() { return td::Status::Error(500, "NOT_ENOUGH_FUNDS"); } + static td::Status TransferToFrozen() { + return td::Status::Error(500, "TRANSFER_TO_FROZEN"); + } static td::Status LiteServer(td::int32 code, td::Slice message) { auto f = [&](td::Slice code_description) { return LiteServer(code, code_description, message); }; diff --git a/tonlib/tonlib/keys/DecryptedKey.cpp b/tonlib/tonlib/keys/DecryptedKey.cpp index 746ca188c0..931b9f01f5 100644 --- a/tonlib/tonlib/keys/DecryptedKey.cpp +++ b/tonlib/tonlib/keys/DecryptedKey.cpp @@ -35,36 +35,15 @@ DecryptedKey::DecryptedKey(RawDecryptedKey key) : DecryptedKey(std::move(key.mnemonic_words), td::Ed25519::PrivateKey(key.private_key.copy())) { } -td::SecureString DecryptedKey::change_local_password(td::Slice secret_str, td::Slice old_local_password, - td::Slice new_local_password) { - CHECK(secret_str.size() == 32); - td::SecureString old_local_password_hash(32); - sha256(old_local_password, old_local_password_hash.as_mutable_slice()); - td::SecureString new_local_password_hash(32); - sha256(new_local_password, new_local_password_hash.as_mutable_slice()); - - td::SecureString new_secret(32); - for (size_t i = 0; i < new_secret.size(); i++) { - new_secret.as_mutable_slice()[i] = - secret_str[i] ^ old_local_password_hash.as_slice()[i] ^ new_local_password_hash.as_slice()[i]; - } - return new_secret; -} - EncryptedKey DecryptedKey::encrypt(td::Slice local_password, td::Slice old_secret) const { - LOG(ERROR) << "encrypt"; td::SecureString secret(32); if (old_secret.size() == td::as_slice(secret).size()) { secret.as_mutable_slice().copy_from(old_secret); } else { td::Random::secure_bytes(secret.as_mutable_slice()); } - td::SecureString local_password_hash(32); - sha256(local_password, local_password_hash.as_mutable_slice()); td::SecureString decrypted_secret(32); - for (size_t i = 0; i < decrypted_secret.size(); i++) { - decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i]; - } + hmac_sha256(secret, local_password, decrypted_secret.as_mutable_slice()); td::SecureString encryption_secret(64); pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", EncryptedKey::PBKDF_ITERATIONS, diff --git a/tonlib/tonlib/keys/DecryptedKey.h b/tonlib/tonlib/keys/DecryptedKey.h index 14086d0de4..bf53bc0215 100644 --- a/tonlib/tonlib/keys/DecryptedKey.h +++ b/tonlib/tonlib/keys/DecryptedKey.h @@ -56,8 +56,6 @@ struct DecryptedKey { std::vector mnemonic_words; td::Ed25519::PrivateKey private_key; - static td::SecureString change_local_password(td::Slice secret, td::Slice old_local_password, - td::Slice new_local_password); EncryptedKey encrypt(td::Slice local_password, td::Slice secret = {}) const; }; diff --git a/tonlib/tonlib/keys/EncryptedKey.cpp b/tonlib/tonlib/keys/EncryptedKey.cpp index 143677914f..65de8c8d19 100644 --- a/tonlib/tonlib/keys/EncryptedKey.cpp +++ b/tonlib/tonlib/keys/EncryptedKey.cpp @@ -24,15 +24,19 @@ #include "td/utils/crypto.h" namespace tonlib { -td::Result EncryptedKey::decrypt(td::Slice local_password, bool check_public_key) { +td::Result EncryptedKey::decrypt(td::Slice local_password, bool check_public_key, bool old) const { if (secret.size() != 32) { return td::Status::Error("Failed to decrypt key: invalid secret size"); } - td::SecureString local_password_hash(32); - sha256(local_password, local_password_hash.as_mutable_slice()); td::SecureString decrypted_secret(32); - for (size_t i = 0; i < 32; i++) { - decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i]; + if (old) { + td::SecureString local_password_hash(32); + sha256(local_password, local_password_hash.as_mutable_slice()); + for (size_t i = 0; i < 32; i++) { + decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i]; + } + } else { + hmac_sha256(secret, local_password, decrypted_secret.as_mutable_slice()); } td::SecureString encryption_secret(64); diff --git a/tonlib/tonlib/keys/EncryptedKey.h b/tonlib/tonlib/keys/EncryptedKey.h index 56aa661531..6eb1627008 100644 --- a/tonlib/tonlib/keys/EncryptedKey.h +++ b/tonlib/tonlib/keys/EncryptedKey.h @@ -32,7 +32,7 @@ struct EncryptedKey { td::Ed25519::PublicKey public_key; td::SecureString secret; - td::Result decrypt(td::Slice local_password, bool check_public_key = true); + td::Result decrypt(td::Slice local_password, bool check_public_key = true, bool old = false) const; }; } // namespace tonlib diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index a783d2aa37..8b3c94f840 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -186,11 +186,21 @@ class TonlibCli : public td::actor::Actor { if (cmd.empty()) { return; } + auto to_bool = [](td::Slice word, bool def = false) { + if (word.empty()) { + return def; + } + if (word == "0" || word == "FALSE" || word == "false") { + return false; + } + return true; + }; if (cmd == "help") { td::TerminalIO::out() << "help - show this help\n"; td::TerminalIO::out() << "genkey - generate new secret key\n"; td::TerminalIO::out() << "keys - show all stored keys\n"; td::TerminalIO::out() << "unpackaddress
- validate and parse address\n"; + td::TerminalIO::out() << "setbounceble
[] - change bounceble flag in address\n"; td::TerminalIO::out() << "importkey - import key\n"; td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n"; td::TerminalIO::out() << "exportkey [] - export key\n"; @@ -214,6 +224,8 @@ class TonlibCli : public td::actor::Actor { try_stop(); } else if (cmd == "keys") { dump_keys(); + } else if (cmd == "deletekey") { + //delete_key(parser.read_word()); } else if (cmd == "deletekeys") { delete_all_keys(); } else if (cmd == "exportkey") { @@ -223,15 +235,6 @@ class TonlibCli : public td::actor::Actor { } else if (cmd == "setconfig") { auto config = parser.read_word(); auto name = parser.read_word(); - auto to_bool = [](td::Slice word) { - if (word.empty()) { - return false; - } - if (word == "0" || word == "FALSE" || word == "false") { - return false; - } - return true; - }; auto use_callback = parser.read_word(); auto force = parser.read_word(); set_config(config, name, to_bool(use_callback), to_bool(force)); @@ -251,6 +254,10 @@ class TonlibCli : public td::actor::Actor { get_hints(parser.read_word()); } else if (cmd == "unpackaddress") { unpack_address(parser.read_word()); + } else if (cmd == "setbounceable") { + auto addr = parser.read_word(); + auto bounceable = parser.read_word(); + set_bounceable(addr, to_bool(bounceable, true)); } } @@ -325,6 +332,26 @@ class TonlibCli : public td::actor::Actor { }); } + void set_bounceable(td::Slice addr, bool bounceable) { + send_query(tonlib_api::make_object(addr.str()), + [addr = addr.str(), bounceable, this](auto r_parsed_addr) mutable { + if (r_parsed_addr.is_error()) { + LOG(ERROR) << "Failed to parse address: " << r_parsed_addr.error(); + return; + } + auto parsed_addr = r_parsed_addr.move_as_ok(); + parsed_addr->bounceable_ = bounceable; + this->send_query(tonlib_api::make_object(std::move(parsed_addr)), + [](auto r_addr) mutable { + if (r_addr.is_error()) { + LOG(ERROR) << "Failed to pack address"; + return; + } + td::TerminalIO::out() << r_addr.ok()->account_address_ << "\n"; + }); + }); + } + void generate_key(td::SecureString entropy = {}) { if (entropy.size() < 20) { td::TerminalIO::out() << "Enter some entropy"; @@ -527,6 +554,25 @@ class TonlibCli : public td::actor::Actor { return std::move(res); } + void delete_key(td::Slice key) { + auto r_key_i = to_key_i(key); + if (r_key_i.is_error()) { + td::TerminalIO::out() << "Unknown key id: [" << key << "]\n"; + return; + } + using tonlib_api::make_object; + auto key_i = r_key_i.move_as_ok(); + send_query(make_object( + make_object(keys_[key_i].public_key, keys_[key_i].secret.copy())), + + [key = key.str()](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't delete key id: [" << key << "] " << r_res.error() << "\n"; + return; + } + td::TerminalIO::out() << "Ok\n"; + }); + } void export_key(td::Slice key) { if (key.empty()) { dump_keys(); @@ -754,6 +800,11 @@ class TonlibCli : public td::actor::Actor { } void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message, bool allow_send_to_uninited) { + auto r_sz = td::to_integer_safe(message); + auto msg = message.str(); + if (r_sz.is_ok()) { + msg = std::string(r_sz.ok(), 'Z'); + } using tonlib_api::make_object; auto key = !from.secret.empty() ? make_object( @@ -761,7 +812,7 @@ class TonlibCli : public td::actor::Actor { : nullptr; send_query( make_object(std::move(key), std::move(from.address), std::move(to.address), - grams, 30, allow_send_to_uninited, message.str()), + grams, 60, allow_send_to_uninited, std::move(msg)), [this](auto r_res) { if (r_res.is_error()) { td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; @@ -881,6 +932,10 @@ int main(int argc, char* argv[]) { options.config = std::move(data); return td::Status::OK(); }); + p.add_option('N', "config-name", "set lite server config name", [&](td::Slice arg) { + options.name = arg.str(); + return td::Status::OK(); + }); p.add_option('n', "use-callbacks-for-network", "do not use this", [&]() { options.use_callbacks_for_network = true; return td::Status::OK(); diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 16bc873113..269b13e121 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -3099,7 +3099,8 @@ bool Collator::create_mc_state_extra() { CHECK(cb.store_long_bool(0x17, 8) && cb.append_cellslice_bool(block_create_stats_->get_root())); auto cs = vm::load_cell_slice_ref(cb.finalize()); state_extra.r1.block_create_stats = cs; - if (verify >= 1) { + if (verify >= 2) { + LOG(INFO) << "verifying new BlockCreateStats"; if (!block::gen::t_BlockCreateStats.validate_csr(cs)) { cs->print_rec(std::cerr); block::gen::t_BlockCreateStats.print(std::cerr, *cs); @@ -3154,10 +3155,12 @@ bool Collator::update_block_creator_count(td::ConstBitPtr key, unsigned shard_in int Collator::creator_count_outdated(td::ConstBitPtr key, vm::CellSlice& cs) { block::DiscountedCounter mc_cnt, shard_cnt; if (!(block::fetch_CreatorStats(cs, mc_cnt, shard_cnt) && cs.empty_ext())) { - return fatal_error("cannot unpack CreatorStats for "s + key.to_hex(256) + " from previous masterchain state"); + fatal_error("cannot unpack CreatorStats for "s + key.to_hex(256) + " from previous masterchain state"); + return -1; } if (!(mc_cnt.increase_by(0, now_) && shard_cnt.increase_by(0, now_))) { - return fatal_error("cannot amortize counters in CreatorStats for "s + key.to_hex(256)); + fatal_error("cannot amortize counters in CreatorStats for "s + key.to_hex(256)); + return -1; } if (!(mc_cnt.cnt65536 | shard_cnt.cnt65536)) { LOG(DEBUG) << "removing stale CreatorStats for " << key.to_hex(256); @@ -3178,17 +3181,42 @@ bool Collator::update_block_creator_stats() { return fatal_error("cannot update CreatorStats for "s + p.first.to_hex()); } } - if (!created_by_.is_zero() && !update_block_creator_count(created_by_.as_bits256().bits(), 0, 1)) { + auto has_creator = !created_by_.is_zero(); + if (has_creator && !update_block_creator_count(created_by_.as_bits256().bits(), 0, 1)) { return fatal_error("cannot update CreatorStats for "s + created_by_.as_bits256().to_hex()); } - if (!update_block_creator_count(td::Bits256::zero().bits(), block_create_total_, !created_by_.is_zero())) { + if ((has_creator || block_create_total_) && + !update_block_creator_count(td::Bits256::zero().bits(), block_create_total_, has_creator)) { return fatal_error("cannot update CreatorStats with zero index (representing the sum of other CreatorStats)"); } + // -> DEBUG + LOG(INFO) << "scanning for outdated CreatorStats entries"; + /* int cnt = block_create_stats_->filter([this](vm::CellSlice& cs, td::ConstBitPtr key, int key_len) { CHECK(key_len == 256); return creator_count_outdated(key, cs); }); - LOG(DEBUG) << "removed " << cnt << " stale CreatorStats entries"; + */ + // alternative version with partial scan + td::Bits256 key; + prng::rand_gen().rand_bytes(key.data(), 32); + int scanned, cnt = 0; + for (scanned = 0; scanned < 100; scanned++) { + auto cs = block_create_stats_->lookup_nearest_key(key.bits(), 256, true); + if (cs.is_null()) { + break; + } + auto res = creator_count_outdated(key.bits(), cs.write()); + if (!res) { + LOG(DEBUG) << "prunning CreatorStats for " << key.to_hex(); + block_create_stats_->lookup_delete(key); + ++cnt; + } else if (res < 0) { + return fatal_error("error scanning stale CreatorStats entries"); + } + } + // -> DEBUG + LOG(INFO) << "removed " << cnt << " stale CreatorStats entries out of " << scanned << " scanned"; return cnt >= 0; } diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 2cf588bd2a..23831af03a 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -192,7 +192,7 @@ void LiteQuery::perform_getMasterchainInfo(int mode) { } td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -230,7 +230,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -256,7 +256,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid, mode](td::Result> res) { + [ Self = actor_id(this), blkid, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -371,7 +371,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { } if (blkid.id.seqno) { td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -381,7 +381,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { }); } else { td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid, - [Self = actor_id(this), blkid](td::Result res) { + [ Self = actor_id(this), blkid ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -440,7 +440,7 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -466,7 +466,7 @@ bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_from_db_short, blkid, - [Self = actor_id(this), blkid, mode](td::Result> res) { + [ Self = actor_id(this), blkid, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : ")); @@ -488,7 +488,7 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -519,7 +519,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -541,7 +541,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -563,7 +563,7 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : ")); @@ -588,7 +588,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_zero_state, blkid, - [Self = actor_id(this), blkid](td::Result res) { + [ Self = actor_id(this), blkid ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : ")); @@ -632,7 +632,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, LOG(INFO) << "sending a get_top_masterchain_state_block query to manager"; td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this)](td::Result, BlockIdExt>> res) -> void { + [Self = actor_id(this)](td::Result, BlockIdExt>> res)->void { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1067,7 +1067,7 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) { << " " << trans_lt_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_by_lt_from_db, ton::extract_addr_prefix(acc_workchain_, acc_addr_), - trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result res) { + trans_lt_, [ Self = actor_id(this), remaining, manager = manager_ ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{}); } else { @@ -1141,7 +1141,7 @@ void LiteQuery::perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool e void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector param_list) { LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", ) liteserver query"; - set_continuation([this, mode, param_list = std::move(param_list)]() mutable { + set_continuation([ this, mode, param_list = std::move(param_list) ]() mutable { continue_getConfigParams(mode, std::move(param_list)); }); request_mc_block_data_state(blkid); @@ -1294,7 +1294,7 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni LOG(INFO) << "performing a lookupBlock(" << blkid.to_str() << ", " << mode << ", " << lt << ", " << utime << ") query"; auto P = td::PromiseCreator::lambda( - [Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result res) { + [ Self = actor_id(this), manager = manager_, mode = (mode >> 4) ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1446,58 +1446,83 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, return; } if (mode & 1) { - base_blk_id_ = (from.seqno() > to.seqno()) ? from : to; - td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, base_blk_id_, - [Self = actor_id(this), from, to, mode](td::Result> res) { - if (res.is_error()) { - td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); - } else { - td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, to, - mode, Ref(res.move_as_ok())); - } - }); + if (mode & 0x1000) { + BlockIdExt bblk = (from.seqno() > to.seqno()) ? from : to; + td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, bblk, + [ Self = actor_id(this), from, to, bblk, mode ](td::Result> res) { + if (res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); + } else { + td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, + to, mode, bblk, + Ref(res.move_as_ok())); + } + }); + } else { + td::actor::send_closure_later( + manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, + [ Self = actor_id(this), from, to, mode ](td::Result, BlockIdExt>> res) { + if (res.is_error()) { + td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); + } else { + auto pair = res.move_as_ok(); + td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, to, mode, pair.second, + Ref(std::move(pair.first))); + } + }); + } } else if (mode & 2) { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), from, mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), from, mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { auto pair = res.move_as_ok(); td::actor::send_closure_later(Self, &LiteQuery::continue_getBlockProof, from, pair.second, mode, - Ref(std::move(pair.first))); + pair.second, Ref(std::move(pair.first))); } }); } else { td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false, - [Self = actor_id(this), from, mode](td::Result res) { + [ Self = actor_id(this), from, mode ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { td::actor::send_closure_later(Self, &LiteQuery::perform_getBlockProof, from, - res.move_as_ok(), mode | 1); + res.move_as_ok(), mode | 0x1001); } }); } } -void LiteQuery::continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, +void LiteQuery::continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, BlockIdExt baseblk, Ref state) { + base_blk_id_ = baseblk; + if (!base_blk_id_.is_masterchain_ext()) { + fatal_error("reference masterchain block "s + base_blk_id_.to_str() + " for constructing a proof chain is invalid"); + return; + } if (!(mode & 1)) { - base_blk_id_ = to; if (!to.is_masterchain_ext()) { fatal_error("last masterchain block id "s + to.to_str() + " is invalid"); return; } - if (from.seqno() > to.seqno()) { - fatal_error("client knows block "s + from.to_str() + " newer than the latest masterchain block " + to.to_str()); - return; - } } if (state.is_null()) { fatal_error("obtained no valid masterchain state for block "s + base_blk_id_.to_str()); return; } + if (from.seqno() > base_blk_id_.seqno()) { + fatal_error("client knows block "s + from.to_str() + " newer than the reference masterchain block " + + base_blk_id_.to_str()); + return; + } + if (to.seqno() > base_blk_id_.seqno()) { + fatal_error("client knows block "s + to.to_str() + " newer than the reference masterchain block " + + base_blk_id_.to_str()); + return; + } mc_state0_ = Ref(state); if (base_blk_id_ != state->get_block_id()) { fatal_error("the state for "s + base_blk_id_.to_str() + " is in fact a state for different block " + @@ -1507,13 +1532,13 @@ void LiteQuery::continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, LOG(INFO) << "continuing getBlockProof(" << mode << ", " << from.to_str() << ", " << to.to_str() << ") query with a state for " << base_blk_id_.to_str(); if (!state->check_old_mc_block_id(from)) { - fatal_error("source masterchain block "s + from.to_str() + " is unknown from the perspective of newer block " + - base_blk_id_.to_str()); + fatal_error("proof source masterchain block "s + from.to_str() + + " is unknown from the perspective of reference block " + base_blk_id_.to_str()); return; } if (!state->check_old_mc_block_id(to)) { - fatal_error("destination masterchain block "s + to.to_str() + " is unknown from the perspective of newer block " + - base_blk_id_.to_str()); + fatal_error("proof destination masterchain block "s + to.to_str() + + " is unknown from the perspective of reference block " + base_blk_id_.to_str()); return; } chain_ = std::make_unique(from, to, mode); diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 580e086b4d..9118ec0a8c 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -111,9 +111,10 @@ class LiteQuery : public td::actor::Actor { void perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, UnixTime utime); void perform_listBlockTransactions(BlockIdExt blkid, int mode, int count, Bits256 account, LogicalTime lt); void finish_listBlockTransactions(int mode, int count); - void perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode); - void continue_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, Ref state); - bool construct_proof_chain(ton::BlockIdExt id); + void perform_getBlockProof(BlockIdExt from, BlockIdExt to, int mode); + void continue_getBlockProof(BlockIdExt from, BlockIdExt to, int mode, BlockIdExt baseblk, + Ref state); + bool construct_proof_chain(BlockIdExt id); bool construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdExt next); bool construct_proof_link_forward_cont(ton::BlockIdExt cur, ton::BlockIdExt next); bool construct_proof_link_back(ton::BlockIdExt cur, ton::BlockIdExt next); diff --git a/validator/manager.cpp b/validator/manager.cpp index 30467363e5..376b367e9e 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1880,10 +1880,12 @@ void ValidatorManagerImpl::update_shard_client_block_handle(BlockHandle handle, void ValidatorManagerImpl::shard_client_update(BlockSeqno seqno) { if (min_confirmed_masterchain_seqno_ < seqno) { min_confirmed_masterchain_seqno_ = seqno; + } else { + return; } while (shard_client_waiters_.size() > 0) { auto it = shard_client_waiters_.begin(); - if (it->first > seqno) { + if (it->first > min_confirmed_masterchain_seqno_) { break; } for (auto &y : it->second.waiting_) { From 7c595294b67dcdd7a6d020a8a5ff1c754480203b Mon Sep 17 00:00:00 2001 From: ton Date: Fri, 4 Oct 2019 21:17:47 +0400 Subject: [PATCH 007/667] updated tonlib --- crypto/fift/lib/Fift.fif | 28 ++++++++++++++++++++++ crypto/fift/lib/TonUtil.fif | 2 +- tl/generate/scheme/tonlib_api.tl | 6 +++++ tl/generate/scheme/tonlib_api.tlo | Bin 13072 -> 13576 bytes tonlib/test/offline.cpp | 28 ++++++++++++++++++++++ tonlib/tonlib/TonlibClient.cpp | 30 +++++++++++++++++++++++- tonlib/tonlib/TonlibClient.h | 4 ++++ tonlib/tonlib/keys/DecryptedKey.cpp | 8 +++---- tonlib/tonlib/keys/EncryptedKey.cpp | 10 ++++---- tonlib/tonlib/keys/SimpleEncryption.cpp | 6 +++++ tonlib/tonlib/keys/SimpleEncryption.h | 3 ++- tonlib/tonlib/tonlib-cli.cpp | 14 +++++++++-- 12 files changed, 125 insertions(+), 14 deletions(-) diff --git a/crypto/fift/lib/Fift.fif b/crypto/fift/lib/Fift.fif index 65fecb3116..f674be7976 100644 --- a/crypto/fift/lib/Fift.fif +++ b/crypto/fift/lib/Fift.fif @@ -39,6 +39,34 @@ // { dup abs <# #s rot sign #> nip } : (.) // { (.) type } : ._ // { ._ space } : . +{ dup 10 < { 48 } { 55 } cond + } : Digit +{ dup 10 < { 48 } { 87 } cond + } : digit +// x s b -- x' s' +{ -rot swap rot /mod Digit rot swap hold } : B# +{ -rot swap rot /mod digit rot swap hold } : b# +{ 16 B# } : X# +{ 16 b# } : x# +// x s b -- 0 s' +{ -rot { 2 pick B# over 0<= } until rot drop } : B#s +{ -rot { 2 pick b# over 0<= } until rot drop } : b#s +{ 16 B#s } : X#s +{ 16 b#s } : x#s +variable base +{ 10 base ! } : decimal +{ 16 base ! } : hex +{ 8 base ! } : octal +{ 2 base ! } : binary +{ base @ B# } : Base# +{ base @ b# } : base# +{ base @ B#s } : Base#s +{ base @ b#s } : base#s +// x w -- s +{ over abs <# rot 1- ' X# swap times X#s rot sign #> nip } : (0X.) +{ over abs <# rot 1- ' x# swap times x#s rot sign #> nip } : (0x.) +{ (0X.) type } : 0X._ +{ 0X._ space } : 0X. +{ (0x.) type } : 0x._ +{ 0x._ space } : 0x. { bl (-trailing) } : -trailing { char 0 (-trailing) } : -trailing0 { char " word 1 ' $+ } ::_ +" diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif index 2e2484ad26..9535322255 100644 --- a/crypto/fift/lib/TonUtil.fif +++ b/crypto/fift/lib/TonUtil.fif @@ -33,7 +33,7 @@ library TonUtil // TON Blockchain Fift Library } : parse-smc-addr // ( wc addr -- ) Show address in : form -{ swap ._ .":" x. } : .addr +{ swap ._ .":" 64 0x. } : .addr // ( wc addr flags -- ) Show address in base64url form { smca>$ type } : .Addr // ( wc addr fname -- ) Save address to file in 36-byte format diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index c0dc53c1ff..cf0ed2b837 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -85,6 +85,8 @@ logVerbosityLevel verbosity_level:int32 = LogVerbosityLevel; //@description Contains a list of available TDLib internal log tags @tags List of log tags logTags tags:vector = LogTags; +data bytes:secureBytes = Data; + ---functions--- init options:options = Ok; @@ -103,6 +105,10 @@ importPemKey local_password:secureBytes key_password:secureBytes exported_key:ex importEncryptedKey local_password:secureBytes key_password:secureBytes exported_encrypted_key:exportedEncryptedKey = Key; changeLocalPassword input_key:inputKey new_local_password:secureBytes = Key; +encrypt decrypted_data:secureBytes secret:secureBytes = Data; +decrypt encrypted_data:secureBytes secret:secureBytes = Data; +kdf password:secureBytes salt:secureBytes iterations:int32 = Data; + unpackAccountAddress account_address:string = UnpackedAccountAddress; packAccountAddress account_address:unpackedAccountAddress = AccountAddress; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 1050a59cfde56bf149a42689b6fab05c8647c041..1af5d79a941e63ba7697439dba37f678c9d5f5db 100644 GIT binary patch delta 298 zcmbP`){({gXtur;0~F{?4gd>Daj>1X#fS(7SDQi~_samlfOq&BB;J`rKm-|Q*(n^AjRYnUi|N@{XZ zWkCr{9}`p`Uuqs$JT)aAWb)+w@{*GacvQfKOtw&%qAlZ{Kbak>2tzZ}P;8nvA5gf# zCRtlNZ4PsGN*YWjGt^p+$&ISYtSrTeIVF=ns!4NmWtOBCC6;97=M_)h&nY_j4Ic-@ JA3f?V+yHcsZ2account_address_); } +TEST(Tonlib, EncryptionApi) { + using tonlib_api::make_object; + Client client; + + // init + sync_send(client, make_object( + make_object(nullptr, make_object(".")))) + .ensure(); + + std::string password = "hello world"; + std::string data = "very secret data"; + auto key = std::move( + sync_send(client, make_object(td::SecureString(password), td::SecureString("salt"), 100000)) + .move_as_ok() + ->bytes_); + auto encrypted = std::move( + sync_send(client, make_object(td::SecureString(data), key.copy())).move_as_ok()->bytes_); + auto decrypted = + std::move(sync_send(client, make_object(encrypted.copy(), key.copy())).move_as_ok()->bytes_); + ASSERT_EQ(data, decrypted); + + auto bad_key = std::move(sync_send(client, make_object(td::SecureString(password + "BAD"), + td::SecureString("salt"), 100000)) + .move_as_ok() + ->bytes_); + sync_send(client, make_object(encrypted.copy(), bad_key.copy())).ensure_error(); +} + TEST(Tonlib, KeysApi) { using tonlib_api::make_object; Client client; diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 63203df8a1..ccf44df640 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -28,6 +28,7 @@ #include "tonlib/TestGiver.h" #include "tonlib/utils.h" #include "tonlib/keys/Mnemonic.h" +#include "tonlib/keys/SimpleEncryption.h" #include "tonlib/TonlibError.h" @@ -444,7 +445,7 @@ tonlib_api::object_ptr TonlibClient::static_request( tonlib_api::object_ptr response; downcast_call(*function, [&response](auto& request) { response = TonlibClient::do_static_request(request); }); - VLOG(tonlib_query) << " answer static query " << to_string(function); + VLOG(tonlib_query) << " answer static query " << to_string(response); return response; } @@ -466,6 +467,9 @@ bool TonlibClient::is_static_request(td::int32 id) { case tonlib_api::setLogTagVerbosityLevel::ID: case tonlib_api::getLogTagVerbosityLevel::ID: case tonlib_api::addLogMessage::ID: + case tonlib_api::encrypt::ID: + case tonlib_api::decrypt::ID: + case tonlib_api::kdf::ID: return true; default: return false; @@ -1633,4 +1637,28 @@ tonlib_api::object_ptr TonlibClient::do_static_request(const return tonlib_api::make_object(); } +tonlib_api::object_ptr TonlibClient::do_static_request(const tonlib_api::encrypt& request) { + return tonlib_api::make_object( + SimpleEncryption::encrypt_data(request.decrypted_data_, request.secret_)); +} + +tonlib_api::object_ptr TonlibClient::do_static_request(const tonlib_api::decrypt& request) { + auto r_data = SimpleEncryption::decrypt_data(request.encrypted_data_, request.secret_); + if (r_data.is_ok()) { + return tonlib_api::make_object(r_data.move_as_ok()); + } else { + return status_to_tonlib_api(r_data.error().move_as_error_prefix(TonlibError::KeyDecrypt())); + } +} + +tonlib_api::object_ptr TonlibClient::do_static_request(const tonlib_api::kdf& request) { + auto max_iterations = 10000000; + if (request.iterations_ < 0 || request.iterations_ > max_iterations) { + return status_to_tonlib_api( + TonlibError::InvalidField("iterations", PSLICE() << "must be between 0 and " << max_iterations)); + } + return tonlib_api::make_object( + SimpleEncryption::kdf(request.password_, request.salt_, request.iterations_)); +} + } // namespace tonlib diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 649df52d74..e2ea77d1c6 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -117,6 +117,10 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::getLogTags& request); static object_ptr do_static_request(const tonlib_api::addLogMessage& request); + static object_ptr do_static_request(const tonlib_api::encrypt& request); + static object_ptr do_static_request(const tonlib_api::decrypt& request); + static object_ptr do_static_request(const tonlib_api::kdf& request); + template td::Status do_request(const T& request, P&& promise) { return td::Status::Error(400, "Function is unsupported"); diff --git a/tonlib/tonlib/keys/DecryptedKey.cpp b/tonlib/tonlib/keys/DecryptedKey.cpp index 931b9f01f5..7aadf63587 100644 --- a/tonlib/tonlib/keys/DecryptedKey.cpp +++ b/tonlib/tonlib/keys/DecryptedKey.cpp @@ -42,12 +42,10 @@ EncryptedKey DecryptedKey::encrypt(td::Slice local_password, td::Slice old_secre } else { td::Random::secure_bytes(secret.as_mutable_slice()); } - td::SecureString decrypted_secret(32); - hmac_sha256(secret, local_password, decrypted_secret.as_mutable_slice()); + td::SecureString decrypted_secret = SimpleEncryption::combine_secrets(secret, local_password); - td::SecureString encryption_secret(64); - pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", EncryptedKey::PBKDF_ITERATIONS, - encryption_secret.as_mutable_slice()); + td::SecureString encryption_secret = + SimpleEncryption::kdf(as_slice(decrypted_secret), "TON local key", EncryptedKey::PBKDF_ITERATIONS); std::vector mnemonic_words_copy; for (auto &w : mnemonic_words) { diff --git a/tonlib/tonlib/keys/EncryptedKey.cpp b/tonlib/tonlib/keys/EncryptedKey.cpp index 65de8c8d19..893b95871f 100644 --- a/tonlib/tonlib/keys/EncryptedKey.cpp +++ b/tonlib/tonlib/keys/EncryptedKey.cpp @@ -28,19 +28,21 @@ td::Result EncryptedKey::decrypt(td::Slice local_password, bool ch if (secret.size() != 32) { return td::Status::Error("Failed to decrypt key: invalid secret size"); } - td::SecureString decrypted_secret(32); + td::SecureString decrypted_secret; if (old) { + decrypted_secret = td::SecureString(32); td::SecureString local_password_hash(32); sha256(local_password, local_password_hash.as_mutable_slice()); for (size_t i = 0; i < 32; i++) { decrypted_secret.as_mutable_slice()[i] = secret.as_slice()[i] ^ local_password_hash.as_slice()[i]; } } else { - hmac_sha256(secret, local_password, decrypted_secret.as_mutable_slice()); + decrypted_secret = SimpleEncryption::combine_secrets(secret, local_password); } - td::SecureString encryption_secret(64); - pbkdf2_sha512(as_slice(decrypted_secret), "TON local key", PBKDF_ITERATIONS, encryption_secret.as_mutable_slice()); + td::SecureString encryption_secret = + SimpleEncryption::kdf(as_slice(decrypted_secret), "TON local key", EncryptedKey::PBKDF_ITERATIONS); + TRY_RESULT(decrypted_data, SimpleEncryption::decrypt_data(as_slice(encrypted_data), as_slice(encryption_secret))); RawDecryptedKey raw_decrypted_key; diff --git a/tonlib/tonlib/keys/SimpleEncryption.cpp b/tonlib/tonlib/keys/SimpleEncryption.cpp index 026a34292e..586070b090 100644 --- a/tonlib/tonlib/keys/SimpleEncryption.cpp +++ b/tonlib/tonlib/keys/SimpleEncryption.cpp @@ -50,6 +50,12 @@ td::SecureString SimpleEncryption::combine_secrets(td::Slice a, td::Slice b) { return res; } +td::SecureString SimpleEncryption::kdf(td::Slice secret, td::Slice password, int iterations) { + td::SecureString new_secret(64); + pbkdf2_sha512(secret, password, iterations, new_secret.as_mutable_slice()); + return new_secret; +} + td::SecureString SimpleEncryption::encrypt_data_with_prefix(td::Slice data, td::Slice secret) { CHECK(data.size() % 16 == 0); auto data_hash = sha256(data); diff --git a/tonlib/tonlib/keys/SimpleEncryption.h b/tonlib/tonlib/keys/SimpleEncryption.h index 981925b2c9..69d5629a72 100644 --- a/tonlib/tonlib/keys/SimpleEncryption.h +++ b/tonlib/tonlib/keys/SimpleEncryption.h @@ -27,13 +27,14 @@ class SimpleEncryption { public: static td::SecureString encrypt_data(td::Slice data, td::Slice secret); static td::Result decrypt_data(td::Slice encrypted_data, td::Slice secret); + static td::SecureString combine_secrets(td::Slice a, td::Slice b); + static td::SecureString kdf(td::Slice secret, td::Slice password, int iterations); private: static td::AesCbcState calc_aes_cbc_state_hash(td::Slice hash); static td::AesCbcState calc_aes_cbc_state_sha512(td::Slice seed); static td::SecureString gen_random_prefix(td::int64 data_size); - static td::SecureString combine_secrets(td::Slice a, td::Slice b); static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret); }; } // namespace tonlib diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 8b3c94f840..f681474e76 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -58,6 +58,9 @@ class TonlibCli : public td::actor::Actor { bool is_closing_{false}; td::uint32 ref_cnt_{1}; + td::int64 snd_bytes_{0}; + td::int64 rcv_bytes_{0}; + void start_up() override { class Cb : public td::TerminalIO::Callback { public: @@ -258,15 +261,21 @@ class TonlibCli : public td::actor::Actor { auto addr = parser.read_word(); auto bounceable = parser.read_word(); set_bounceable(addr, to_bool(bounceable, true)); + } else if (cmd == "netstats") { + dump_netstats(); } } + void dump_netstats() { + td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n"; + td::TerminalIO::out() << td::tag("rcv", td::format::as_size(rcv_bytes_)) << "\n"; + } void on_adnl_result(td::uint64 id, td::Result res) { using tonlib_api::make_object; if (res.is_ok()) { + rcv_bytes_ += res.ok().size(); send_query(make_object(id, res.move_as_ok().as_slice().str()), [](auto r_ok) { LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); }); - LOG(ERROR) << "!!!"; } else { send_query(make_object( id, make_object(res.error().code(), res.error().message().str())), @@ -279,6 +288,7 @@ class TonlibCli : public td::actor::Actor { if (result->get_id() == tonlib_api::updateSendLiteServerQuery::ID) { auto update = tonlib_api::move_object_as(std::move(result)); CHECK(!raw_client_.empty()); + snd_bytes_ += update->data_.size(); send_closure(raw_client_, &ton::adnl::AdnlExtClient::send_query, "query", td::BufferSlice(update->data_), td::Timestamp::in(5), [actor_id = actor_id(this), id = update->id_](td::Result res) { @@ -800,7 +810,7 @@ class TonlibCli : public td::actor::Actor { } void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message, bool allow_send_to_uninited) { - auto r_sz = td::to_integer_safe(message); + auto r_sz = td::to_integer_safe(message); auto msg = message.str(); if (r_sz.is_ok()) { msg = std::string(r_sz.ok(), 'Z'); From 29deff15c3cfbdfdb73cae37cde0831e9f079e01 Mon Sep 17 00:00:00 2001 From: ton Date: Sat, 5 Oct 2019 21:21:24 +0400 Subject: [PATCH 008/667] updated fift updated fift updated tonlib --- crypto/block/block.tlb | 2 +- crypto/block/transaction.cpp | 3 +- crypto/fift/lib/Fift.fif | 5 ++-- crypto/fift/lib/TonUtil.fif | 13 +++++++++ crypto/fift/words.cpp | 37 ++++++++++++++++++++++++- crypto/smartcont/gen-zerostate.fif | 2 +- crypto/smartcont/stdlib.fc | 15 ++++++++++ crypto/smartcont/wallet.fif | 14 +++++++--- crypto/test/fift/testvm2.fif | 2 +- crypto/vm/continuation.cpp | 27 ++++++++++++------ crypto/vm/continuation.h | 10 ++++--- doc/fiftbase.tex | 44 ++++++++++++++++-------------- test/regression-tests.ans | 4 +-- tonlib/test/offline.cpp | 8 +++--- 14 files changed, 137 insertions(+), 49 deletions(-) diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 5108294770..d4bba1a182 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -421,7 +421,7 @@ block_info#9bc7a987 version:uint32 prev_key_block_seqno:uint32 master_ref:not_master?^BlkMasterInfo prev_ref:^(BlkPrevInfo after_merge) - prev_vert_ref:vert_seq_no?^(BlkPrevInfo 0) + prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0) = BlockInfo; prev_blk_info$_ prev:ExtBlkRef = BlkPrevInfo 0; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 24fc731ee4..1e0084e943 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -739,7 +739,8 @@ td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const { } td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const { - return td::rshift(gas_price256 * gas_used, 16, 1); + return gas_used <= flat_gas_limit ? td::make_refint(flat_gas_price) + : td::rshift(gas_price256 * (gas_used - flat_gas_limit), 16, 1) + flat_gas_price; } bool Transaction::compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg) { diff --git a/crypto/fift/lib/Fift.fif b/crypto/fift/lib/Fift.fif index f674be7976..5430c359e7 100644 --- a/crypto/fift/lib/Fift.fif +++ b/crypto/fift/lib/Fift.fif @@ -42,8 +42,8 @@ { dup 10 < { 48 } { 55 } cond + } : Digit { dup 10 < { 48 } { 87 } cond + } : digit // x s b -- x' s' -{ -rot swap rot /mod Digit rot swap hold } : B# -{ -rot swap rot /mod digit rot swap hold } : b# +{ rot swap /mod Digit rot swap hold } : B# +{ rot swap /mod digit rot swap hold } : b# { 16 B# } : X# { 16 b# } : x# // x s b -- 0 s' @@ -110,3 +110,4 @@ variable base { null ' cons rot times } : list { true (atom) drop } : atom { bl word atom 1 'nop } ::_ ` +{ hole dup 1 { @ execute } does create } : recursive diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif index 9535322255..cb2fb89718 100644 --- a/crypto/fift/lib/TonUtil.fif +++ b/crypto/fift/lib/TonUtil.fif @@ -123,3 +123,16 @@ forget val, forget val@ forget .val { 0 lib+ } : private_lib // ( D c -- D' ) Add public library c to collection D { 1 lib+ } : public_lib + +// serialize simple transfers with long comments +// b B n -- b' +recursive append-long-bytes { + over Blen over <= { drop B, } { + B| -rot B, swap ref, + } cond +} swap ! +// b S n -- b' +{ swap $>B swap append-long-bytes } : append-long-string +// S -- c +{ +} : simple-transfer-body diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 87c269b78e..eda6796bb8 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -2149,7 +2149,23 @@ void interpret_run_vm(IntCtx& ctx, bool with_gas) { OstreamLogger ostream_logger(ctx.error_stream); auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); vm::GasLimits gas{gas_limit}; - int res = vm::run_vm_code(cs, ctx.stack, 3, &data, log, nullptr, &gas); + int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries()); + ctx.stack.push_smallint(res); + ctx.stack.push_cell(std::move(data)); + if (with_gas) { + ctx.stack.push_smallint(gas.gas_consumed()); + } +} + +void interpret_run_vm_c7(IntCtx& ctx, bool with_gas) { + long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty; + auto c7 = ctx.stack.pop_tuple(); + auto data = ctx.stack.pop_cell(); + auto cs = ctx.stack.pop_cellslice(); + OstreamLogger ostream_logger(ctx.error_stream); + auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); + vm::GasLimits gas{gas_limit}; + int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7)); ctx.stack.push_smallint(res); ctx.stack.push_cell(std::move(data)); if (with_gas) { @@ -2278,6 +2294,21 @@ void interpret_get_cmdline_arg(IntCtx& ctx) { } } +void interpret_getenv(vm::Stack& stack) { + auto str = stack.pop_string(); + auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr; + stack.push_string(value ? std::string{value} : ""); +} + +void interpret_getenv_exists(vm::Stack& stack) { + auto str = stack.pop_string(); + auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr; + if (value) { + stack.push_string(std::string{value}); + } + stack.push_bool((bool)value); +} + // x1 .. xn n 'w --> void interpret_execute_internal(IntCtx& ctx) { Ref word_def = pop_exec_token(ctx); @@ -2572,6 +2603,8 @@ void init_words_common(Dictionary& d) { d.def_ctx_word("file-exists? ", interpret_file_exists); // custom & crypto d.def_ctx_word("now ", interpret_now); + d.def_stack_word("getenv ", interpret_getenv); + d.def_stack_word("getenv? ", interpret_getenv_exists); d.def_stack_word("newkeypair ", interpret_new_keypair); d.def_stack_word("priv>pub ", interpret_priv_key_to_pub); d.def_stack_word("ed25519_sign ", interpret_ed25519_sign); @@ -2696,6 +2729,8 @@ void init_words_vm(Dictionary& d) { d.def_ctx_word("gasrunvmdict ", std::bind(interpret_run_vm_dict, _1, true)); d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, false)); d.def_ctx_word("gasrunvm ", std::bind(interpret_run_vm, _1, true)); + d.def_ctx_word("runvmctx ", std::bind(interpret_run_vm_c7, _1, false)); + d.def_ctx_word("gasrunvmctx ", std::bind(interpret_run_vm_c7, _1, true)); d.def_ctx_word("dbrunvm ", interpret_db_run_vm); d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel); } diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index e66bb4f572..0248722068 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -173,7 +173,7 @@ config.special! // gas_price gas_limit special_gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit flat_gas_limit flat_gas_price -- 1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 100 100000 config.gas_prices! -10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 0 0 config.mc_gas_prices! +10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 100 1000000 config.mc_gas_prices! // lump_price bit_price cell_price ihr_factor first_frac next_frac 1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices! 10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices! diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index fb9b49f56a..c6e296ff57 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -63,9 +63,14 @@ tuple parse_addr(slice s) asm "PARSEMSGADDR"; cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; (cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF"; +cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; +(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF"; cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF"; +cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETOPTREF"; (cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF"; +(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF"; (cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL"; +(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL"; (slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT"; (slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT"; (cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; @@ -80,10 +85,20 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va (cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB"; (cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB"; (cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; +(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB"; +(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB"; (cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; diff --git a/crypto/smartcont/wallet.fif b/crypto/smartcont/wallet.fif index 59dbf71c28..b25b4494a2 100644 --- a/crypto/smartcont/wallet.fif +++ b/crypto/smartcont/wallet.fif @@ -1,12 +1,17 @@ #!/usr/bin/env fift -s "TonUtil.fif" include -{ ."usage: " @' $0 type ." [-B ] []" cr +{ ."usage: " @' $0 type ." [-B ] [-C ] []" cr ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt } : usage -def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond - @' $# 2- =: $# } if } if +"" =: comment // comment for simple transfers +def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or + { @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6 + def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond + @' $# 2- =: $# + } if +} if $# dup 4 < swap 5 > or ' usage if true constant bounce @@ -17,13 +22,14 @@ $3 parse-int =: seqno $4 $>GR =: amount def? $5 { @' $5 } { "wallet-query" } cond constant savefile 3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +// "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment 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 } { } cond +def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond constant body-cell ."Transferring " amount .GR ."to account " diff --git a/crypto/test/fift/testvm2.fif b/crypto/test/fift/testvm2.fif index 523c6a7d27..7e91a87701 100644 --- a/crypto/test/fift/testvm2.fif +++ b/crypto/test/fift/testvm2.fif @@ -17,6 +17,6 @@ x{917720ED50D8} runvmcode drop .s 20 x{9720C11401C20CB0D8} runvmcode drop .s x{E3C03077F001A4} x{3020A8} |_ runvmdict drop .s x{71B0E30277F001A4} x{20A8} |_ runvmdict .s -17 x{30ED44D0D71131A020C8CB3FC9ED54} runvm .s +17 0 x{30ED44D0D71131A020C8CB3FC9ED54} runvm .s 100 19 7 x{92A9069620C004F2F571F321A0} runvmcode .s 19 0 x{92A9069620C004F2F571F321A0} runvmcode .s diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 24157d3b2a..7b059dcb59 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -349,7 +349,7 @@ VmState::VmState(Ref _code) } VmState::VmState(Ref _code, Ref _stack, int flags, Ref _data, VmLog log, - std::vector> _libraries) + std::vector> _libraries, Ref init_c7) : code(std::move(_code)) , stack(std::move(_stack)) , cp(-1) @@ -360,11 +360,14 @@ VmState::VmState(Ref _code, Ref _stack, int flags, Ref _ , libraries(std::move(_libraries)) { ensure_throw(init_cp(0)); set_c4(std::move(_data)); + if (init_c7.not_null()) { + set_c7(std::move(init_c7)); + } init_cregs(flags & 1, flags & 2); } VmState::VmState(Ref _code, Ref _stack, const GasLimits& gas, int flags, Ref _data, VmLog log, - std::vector> _libraries) + std::vector> _libraries, Ref init_c7) : code(std::move(_code)) , stack(std::move(_stack)) , cp(-1) @@ -376,6 +379,9 @@ VmState::VmState(Ref _code, Ref _stack, const GasLimits& gas, , libraries(std::move(_libraries)) { ensure_throw(init_cp(0)); set_c4(std::move(_data)); + if (init_c7.not_null()) { + set_c7(std::move(init_c7)); + } init_cregs(flags & 1, flags & 2); } @@ -755,10 +761,15 @@ ControlRegs* force_cregs(Ref& cont) { } int run_vm_code(Ref code, Ref& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, - GasLimits* gas_limits, std::vector> libraries) { - VmState vm{ - code, std::move(stack), gas_limits ? *gas_limits : GasLimits{}, flags, data_ptr ? *data_ptr : Ref{}, - log, std::move(libraries)}; + GasLimits* gas_limits, std::vector> libraries, Ref init_c7) { + VmState vm{code, + std::move(stack), + gas_limits ? *gas_limits : GasLimits{}, + flags, + data_ptr ? *data_ptr : Ref{}, + log, + std::move(libraries), + std::move(init_c7)}; int res = vm.run(); stack = vm.get_stack_ref(); if (res == -1 && data_ptr) { @@ -785,11 +796,11 @@ int run_vm_code(Ref code, Ref& stack, int flags, Ref* da } int run_vm_code(Ref code, Stack& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, - GasLimits* gas_limits, std::vector> libraries) { + GasLimits* gas_limits, std::vector> libraries, Ref init_c7) { Ref stk{true}; stk.unique_write().set_contents(std::move(stack)); stack.clear(); - int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries)); + int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7)); CHECK(stack.is_unique()); if (stk.is_null()) { stack.clear(); diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index 80f19e156d..1c5f8cd562 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -394,9 +394,9 @@ class VmState final : public VmStateInterface { VmState(); VmState(Ref _code); VmState(Ref _code, Ref _stack, int flags = 0, Ref _data = {}, VmLog log = {}, - std::vector> _libraries = {}); + std::vector> _libraries = {}, Ref init_c7 = {}); VmState(Ref _code, Ref _stack, const GasLimits& _gas, int flags = 0, Ref _data = {}, - VmLog log = {}, std::vector> _libraries = {}); + VmLog log = {}, std::vector> _libraries = {}, Ref init_c7 = {}); template VmState(Ref code_cell, Args&&... args) : VmState(convert_code_cell(std::move(code_cell)), std::forward(args)...) { @@ -573,9 +573,11 @@ class VmState final : public VmStateInterface { }; int run_vm_code(Ref _code, Ref& _stack, int flags = 0, Ref* data_ptr = 0, VmLog log = {}, - long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}); + long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, + Ref init_c7 = {}); int run_vm_code(Ref _code, Stack& _stack, int flags = 0, Ref* data_ptr = 0, VmLog log = {}, - long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}); + long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, + Ref init_c7 = {}); ControlData* force_cdata(Ref& cont); ControlRegs* force_cregs(Ref& cont); diff --git a/doc/fiftbase.tex b/doc/fiftbase.tex index 3347dd8626..a175f3a48c 100644 --- a/doc/fiftbase.tex +++ b/doc/fiftbase.tex @@ -1331,17 +1331,17 @@ \section*{Introduction} outputs ``{\tt -1 538fa7\dots 0f7d 0}'', meaning that the specified address is in workchain $-1$ (the masterchain of the TON Blockchain), and that the 256-bit address inside workchain $-1$ is 0x538\dots f7d. \mysubsection{Dictionary manipulation}\label{p:hashmap.ops} -Fift has several words for {\em hashmap\/} or {\em (TVM) dictionary\/} manipulation, corresponding to values of TL-B type {\tt HashmapE $n$ $X$} as described in~\cite[3.3]{TVM}. These (TVM) dictionaries are not to be confused with the Fift dictionary, which is a completely different thing. A dictionary of TL-B type {\tt HashmapE $n$ $X$} is essentially a key-value collection with distinct $n$-bit keys (where $0\leq n\leq 1023$) and values of an arbitrary TL-B type $X$. Dictionaries are represented by trees of cells (the complete layout may be found in \cite[3.3]{TVM}) and stored as values of type {\em Cell\/} or {\em Slice\/} in the Fift stack. +Fift has several words for {\em hashmap\/} or {\em (TVM) dictionary\/} manipulation, corresponding to values of TL-B type {\tt HashmapE $n$ $X$} as described in~\cite[3.3]{TVM}. These (TVM) dictionaries are not to be confused with the Fift dictionary, which is a completely different thing. A dictionary of TL-B type {\tt HashmapE $n$ $X$} is essentially a key-value collection with distinct $n$-bit keys (where $0\leq n\leq 1023$) and values of an arbitrary TL-B type $X$. Dictionaries are represented by trees of cells (the complete layout may be found in \cite[3.3]{TVM}) and stored as values of type {\em Cell\/} or {\em Slice\/} in the Fift stack. Sometimes empty dictionaries are represented by the {\em Null\/} value. \begin{itemize} -\item {\tt dictnew} ( -- $s$), pushes a {\em Slice\/} that represents a new empty dictionary. -\item {\tt idict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Slice}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success. Otherwise the unchanged dictionary $s$ and $0$ are returned. -\item {\tt idict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$. +\item {\tt dictnew} ( -- $D$), pushes a {\em Null\/} value that represents a new empty dictionary. +\item {\tt idict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $D$ (represented by a {\em Cell\/} or a {\em Null\/}) with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success. Otherwise the unchanged dictionary $D$ and $0$ are returned. +\item {\tt idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$. \item {\tt b>idict!}, {\tt b>idict!+}, variants of {\tt idict!} and {\tt idict!+} accepting the new value $v$ in a {\em Builder\/} instead of a {\em Slice}. \item {\tt udict!}, {\tt udict!+}, {\tt b>udict!}, {\tt b>udict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with an unsigned $n$-bit integer $x$ used as a key. -\item {\tt idict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), looks up the key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Slice\/}~$s$. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. -\item {\tt udict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with an {\em un}signed big-endian $n$-bit {\em Integer\/}~$x$ used as a key. -\item {\tt dictmap} ($s$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $s$ with $n$-bit keys. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary. -\item {\tt dictmerge} ($s$ $s'$ $n$ $e$ -- $s''$), combines two dictionaries $s$ and $s'$ with $n$-bit keys into one dictionary $s''$ with the same keys. If a key is present in only one of the dictionaries $s$ and $s'$, this key and the corresponding value are copied verbatim to the new dictionary $s''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $s$ and $s'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary. +\item {\tt idict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/}~$D$. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. +\item {\tt udict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with an {\em un}signed big-endian $n$-bit {\em Integer\/}~$x$ used as a key. +\item {\tt dictmap} ($D$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $D$ with $n$-bit keys. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary. +\item {\tt dictmerge} ($D$ $D'$ $n$ $e$ -- $D''$), combines two dictionaries $D$ and $D'$ with $n$-bit keys into one dictionary $D''$ with the same keys. If a key is present in only one of the dictionaries $D$ and $D'$, this key and the corresponding value are copied verbatim to the new dictionary $D''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $D$ and $D'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary. \end{itemize} Fift also offers some support for prefix dictionaries: \begin{itemize} @@ -1355,10 +1355,12 @@ \section*{Introduction} \begin{itemize} \item {\tt runvmcode} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$, thus executing code~$s$ in TVM. The original Fift stack (without $s$) is passed in its entirety as the initial stack of TVM. When TVM terminates, its resulting stack is used as the new Fift stack, with the exit code $x$ pushed at its top. If $x$ is non-zero, indicating that TVM has been terminated by an unhandled exception, the next stack entry from the top contains the parameter of this exception, and $x$ is the exception code. All other entries are removed from the stack in this case. \item {\tt runvmdict} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ similarly to {\tt runvmcode}, but also initializes the special register {\tt c3} with the same value, and pushes a zero into the initial TVM stack before the TVM execution begins. In a typical application {\em Slice\/}~$s$ consists of a subroutine selection code that uses the top-of-stack {\em Integer\/} to select the subroutine to be executed, thus enabling the definition and execution of several mutually-recursive subroutines (cf.~\cite[4.6]{TVM} and~\ptref{p:asm.prog}). The selector equal to zero corresponds to the {\tt main()} subroutine in a large TVM program. -\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and pushes a zero into the initial TVM stack similarly to {\tt runvmdict}, but also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage. +\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, similarly to {\tt runvmdict} (without pushing an extra zero to the initial TVM stack; if necessary, it can be pushed explicitly under $s$), and also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage. +\item {\tt runvmctx} (\dots $s$ $c$ $t$ -- \dots $x$ $c'$), a variant of {\tt runvm} that also initializes {\tt c7} (the ``context'') with {\em Tuple\/}~$t$. In this way the execution of a TVM smart contract inside TON Blockchain can be completely emulated, if the correct context is loaded into {\tt c7} (cf.~\cite[4.4.10]{TBC}). \item {\tt gasrunvmcode} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmcode} that accepts an extra {\em Integer\/} argument $z$ (the original gas limit) at the top of the stack, and returns the gas consumed by this TVM run as a new top-of-stack {\em Integer\/} value~$z'$. \item {\tt gasrunvmdict} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmdict}. \item {\tt gasrunvm} (\dots $s$ $c$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvm}. +\item {\tt gasrunvmctx} (\dots $s$ $c$ $t$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvmctx}. \end{itemize} For example, one can create an instance of TVM running some simple code as follows: \begin{verbatim} @@ -2039,9 +2041,9 @@ \section*{Introduction} \item {\tt csr.} ($s$ -- ), recursively prints a {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. On the first line, the data bits of $s$ are displayed in hexadecimal form embedded into an {\tt x\{\dots\}} construct similar to the one used for {\em Slice\/} literals (cf.~\ptref{p:slice.lit}). On the next lines, the cells referred to by $s$ are printed with larger indentation. \item {\tt \underline{def?} $\langle\textit{word-name\/}\rangle$} ( -- $?$), checks whether the word $\langle\textit{word-name\/}\rangle$ is defined at execution time, and returns $-1$ or $0$ accordingly. \item {\tt depth} ( -- $n$), returns the current depth (the total number of entries) of the Fift stack as an {\em Integer\/} $n\geq0$. -\item {\tt dictmap} ($s$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $s$ with $n$-bit keys, cf.~\ptref{p:hashmap.ops}. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary. -\item {\tt dictmerge} ($s$ $s'$ $n$ $e$ -- $s''$), combines two dictionaries $s$ and $s'$ with $n$-bit keys into one dictionary $s''$ with the same keys, cf.~\ptref{p:hashmap.ops}. If a key is present in only one of the dictionaries $s$ and $s'$, this key and the corresponding value are copied verbatim to the new dictionary $s''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $s$ and $s'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice\/}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary. -\item {\tt dictnew} ( -- $s$), pushes a {\em Slice\/} that represents a new empty dictionary, cf.~\ptref{p:hashmap.ops}. Equivalent to {\tt b\{0\}}. +\item {\tt dictmap} ($D$ $n$ $e$ -- $D'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $D$ with $n$-bit keys, cf.~\ptref{p:hashmap.ops}. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary. +\item {\tt dictmerge} ($D$ $D'$ $n$ $e$ -- $D''$), combines two dictionaries $D$ and $D'$ with $n$-bit keys into one dictionary $D''$ with the same keys, cf.~\ptref{p:hashmap.ops}. If a key is present in only one of the dictionaries $D$ and $D'$, this key and the corresponding value are copied verbatim to the new dictionary $D''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $D$ and $D'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice\/}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary. +\item {\tt dictnew} ( -- $D$), pushes the {\em Null\/} value that represents a new empty dictionary, cf.~\ptref{p:hashmap.ops}. Equivalent to {\tt null}. \item {\tt does} ($x_1$ \dots $x_n$ $n$ $e$ -- $e'$), creates a new execution token $e'$ that would push $n$ values $x_1$, \dots, $x_n$ into the stack and then execute $e$ when invoked, cf.~\ptref{p:wordlist.ops}. It is roughly equivalent to a combination of {\tt (\{)}, {\tt (compile)}, and {\tt (\})}. \item {\tt drop} ($x$ -- ), removes the top-of-stack entry, cf.~\ptref{p:stack.ops}. \item {\tt dup} ($x$ -- $x$ $x$), duplicates the top-of-stack entry, cf.~\ptref{p:stack.ops}. If the stack is empty, throws an exception. @@ -2062,8 +2064,9 @@ \section*{Introduction} \item {\tt first} ($t$ -- $x$), returns the first component of a {\em Tuple}, cf.~\ptref{p:tuples}. Equivalent to {\tt 0 []}. \item {\tt fits} ($x$ $y$ -- $?$), checks whether {\em Integer\/}~$x$ is a signed $y$-bit integer (i.e., whether $-2^{y-1}\leq x<2^{y-1}$ for $0\leq y\leq 1023$), and returns $-1$ or $0$ accordingly. \item {\tt forget} ( -- ), forgets (removes from the dictionary) the definition of the next word scanned from the input, cf.~\ptref{p:dict.create}. -\item {\tt gasrunvm} (\dots $s$ $c$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvm}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and pushes a zero into the initial TVM stack similarly to {\tt runvmdict}, but also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. Then starts the new TVM instance with the gas limit set to $z$. The actually consumed gas $z'$ is returned at the top of the final Fift stack, and the final value of {\tt c4} is returned immediately below the top of the final Fift stack as another {\em Cell\/}~$c'$. +\item {\tt gasrunvm} (\dots $s$ $c$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvm}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. Then starts the new TVM instance with the gas limit set to $z$. The actually consumed gas $z'$ is returned at the top of the final Fift stack, and the final value of {\tt c4} is returned immediately below the top of the final Fift stack as another {\em Cell\/}~$c'$. \item {\tt gasrunvmcode} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmcode}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ and with the gas limit set to $z$, thus executing code~$s$ in TVM. The original Fift stack (without $s$) is passed in its entirety as the initial stack of the new TVM instance. When TVM terminates, its resulting stack is used as the new Fift stack, with the exit code $x$ and the actually consumed gas $z'$ pushed at its top. If $x$ is non-zero, indicating that TVM has been terminated by an unhandled exception, the next stack entry from the top contains the parameter of this exception, and $x$ is the exception code. All other entries are removed from the stack in this case. +\item {\tt gasrunvmctx} (\dots $s$ $c$ $t$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvmctx}, cf.~\ptref{p:tvm.ops}. Differs from {\tt gasrunmv} in that it initializes {\tt c7} with {\em Tuple\/}~$t$. \item {\tt gasrunvmdict} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmdict}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ and sets the gas limit to $z$ similarly to {\tt gasrunvmcode}, but also initializes the special register {\tt c3} with the same value, and pushes a zero into the initial TVM stack before the TVM execution begins. The actually consumed gas is returned as an {\em Integer\/} $z'$. In a typical application {\em Slice\/}~$s$ consists of a subroutine selection code that uses the top-of-stack {\em Integer\/} to select the subroutine to be executed, thus enabling the definition and execution of several mutually-recursive subroutines (cf.~\cite[4.6]{TVM} and~\ptref{p:asm.prog}). The selector equal to zero corresponds to the {\tt main()} subroutine in a large TVM program. \item {\tt halt} ($x$ -- ), quits to the operating system similarly to {\tt bye}, but uses {\em Integer\/} $x$ as the exit code, cf.~\ptref{p:exit.fift}. \item {\tt hash} ($c$ -- $x$), a deprecated version of {\tt hashu}. Use {\tt hashu} or {\tt hashB} instead. @@ -2077,9 +2080,9 @@ \section*{Introduction} \item {\tt i@+} ($s$ $x$ -- $y$ $s'$), fetches a signed big-endian $x$-bit integer from the first $x$ bits of {\em Slice}~$s$ similarly to {\tt i@}, but returns the remainder of $s$ as well, cf.~\ptref{p:slice.ops}. \item {\tt i@?} ($s$ $x$ -- $y$ $-1$ or $0$), fetches a signed big-endian integer from a {\em Slice\/} similarly to {\tt i@}, but pushes integer $-1$ afterwards on success, cf.~\ptref{p:slice.ops}. If there are less than $x$ bits left in $s$, pushes integer $0$ to indicate failure. \item {\tt i@?+} ($s$ $x$ -- $y$ $s'$ $-1$ or $s$ $0$), fetches a signed big-endian integer from {\em Slice\/}~$s$ and computes the remainder of this {\em Slice\/} similarly to {\tt i@+}, but pushes $-1$ afterwards to indicate success, cf.~\ptref{p:slice.ops}. On failure, pushes the unchanged {\em Slice\/}~$s$ and $0$ to indicate failure. -\item {\tt idict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned. -\item {\tt idict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}. -\item {\tt idict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), looks up key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Slice\/}~$s$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. +\item {\tt idict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned. +\item {\tt idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}. +\item {\tt idict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/} or {\em Null\/}~$D$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. \item {\tt if} ($x$ $e$ -- ), executes execution token (i.e., a {\em WordDef\/}) $e$, but only if {\em Integer\/} $x$ is non-zero, cf.~\ptref{p:cond.ops}. \item {\tt ifnot} ($x$ $e$ -- ), executes execution token $e$, but only if {\em Integer\/} $x$ is zero, cf.~\ptref{p:cond.ops}. \item {\tt include} ($S$ -- ), loads and interprets a Fift source file from the path given by {\em String\/}~$S$, cf.~\ptref{p:asm.load}. If the filename $S$ does not begin with a slash, the Fift include search path, typically taken from the {\tt FIFTPATH} environment variable or the {\tt -I} command-line argument of the Fift interpreter (and equal to {\tt /usr/lib/fift} if both are absent), is used to locate~$S$. @@ -2116,9 +2119,10 @@ \section*{Introduction} \item {\tt reverse} ($x_1$ \dots $x_n$ $y_1$ \dots $y_m$ $n$ $m$ -- $x_n$ \dots $x_1$ $y_1$ \dots $y_m$), reverses the order of $n$ stack entries located immediately below the topmost $m$ elements, where both $0\leq m,n\leq 255$ are passed in the stack. \item {\tt roll} ($x_n$ \dots $x_0$ $n$ -- $x_{n-1}$ \dots $x_0$ $x_n$), rotates the top $n$ stack entries, where $n\geq0$ is also passed in the stack, cf.~\ptref{p:stack.ops}. In particular, {\tt 1 roll} is equivalent to {\tt swap}, and {\tt 2 roll} to {\tt rot}. \item {\tt rot} ($x$ $y$ $z$ -- $y$ $z$ $x$), rotates the three topmost stack entries. +\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$, cf.~\ptref{p:tvm.ops}. In contrast with {\tt runvmdict}, does not push an implicit zero into the initial TVM stack; if necessary, it can be explicitly passed under~$s$. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage. \item {\tt runvmcode} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$, thus executing code~$s$ in TVM, cf.~\ptref{p:tvm.ops}. The original Fift stack (without $s$) is passed in its entirety as the initial stack of the new TVM instance. When TVM terminates, its resulting stack is used as the new Fift stack, with the exit code $x$ pushed at its top. If $x$ is non-zero, indicating that TVM has been terminated by an unhandled exception, the next stack entry from the top contains the parameter of this exception, and $x$ is the exception code. All other entries are removed from the stack in this case. +\item {\tt runvmctx} (\dots $s$ $c$ $t$ -- \dots $x$ $c'$), a variant of {\tt runvm} that also initializes {\tt c7} (the ``context register'' of TVM) with {\em Tuple\/}~$t$, cf.~\ptref{p:tvm.ops}. \item {\tt runvmdict} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ similarly to {\tt runvmcode}, but also initializes the special register {\tt c3} with the same value, and pushes a zero into the initial TVM stack before start, cf.~\ptref{p:tvm.ops}. In a typical application {\em Slice\/}~$s$ consists of a subroutine selection code that uses the top-of-stack {\em Integer\/} to select the subroutine to be executed, thus enabling the definition and execution of several mutually-recursive subroutines (cf.~\cite[4.6]{TVM} and~\ptref{p:asm.prog}). The selector equal to zero corresponds to the {\tt main()} subroutine in a large TVM program. -\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and pushes a zero into the initial TVM stack similarly to {\tt runvmdict}, but also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$, cf.~\ptref{p:tvm.ops}. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage. \item {\tt s,} ($b$ $s$ -- $b'$), appends data bits and references taken from {\em Slice}~$s$ to {\em Builder}~$b$, cf.~\ptref{p:builder.ops}. \item {\tt s>} ($s$ -- ), throws an exception if {\em Slice\/}~$s$ is non-empty, cf.~\ptref{p:slice.ops}. It usually marks the end of the deserialization of a cell, checking whether there are any unprocessed data bits or references left. \item {\tt s>c} ($s$ -- $c$), creates a {\em Cell}~$c$ directly from a {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt }. @@ -2150,9 +2154,9 @@ \section*{Introduction} \item {\tt u@+} ($s$ $x$ -- $y$ $s'$), fetches an unsigned big-endian $x$-bit integer from the first $x$ bits of {\em Slice}~$s$ similarly to {\tt u@}, but returns the remainder of $s$ as well, cf.~\ptref{p:slice.ops}. \item {\tt u@?} ($s$ $x$ -- $y$ $-1$ or $0$), fetches an unsigned big-endian integer from a {\em Slice\/} similarly to {\tt u@}, but pushes integer $-1$ afterwards on success, cf.~\ptref{p:slice.ops}. If there are less than $x$ bits left in $s$, pushes integer $0$ to indicate failure. \item {\tt u@?+} ($s$ $x$ -- $y$ $s'$ $-1$ or $s$ $0$), fetches an unsigned big-endian integer from {\em Slice\/}~$s$ and computes the remainder of this {\em Slice\/} similarly to {\tt u@+}, but pushes $-1$ afterwards to indicate success, cf.~\ptref{p:slice.ops}. On failure, pushes the unchanged {\em Slice\/}~$s$ and $0$ to indicate failure. -\item {\tt udict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by big-endian unsigned $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned. -\item {\tt udict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt udict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}. -\item {\tt udict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), looks up key represented by unsigned big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Slice\/}~$s$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. +\item {\tt udict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by big-endian unsigned $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned. +\item {\tt udict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt udict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}. +\item {\tt udict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up key represented by unsigned big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/} or {\em Null\/}~$D$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. \item {\tt ufits} ($x$ $y$ -- $?$), checks whether {\em Integer\/}~$x$ is an unsigned $y$-bit integer (i.e., whether $0\leq x<2^y$ for $0\leq y\leq 1023$), and returns $-1$ or $0$ accordingly. \item {\tt uncons} ($l$ -- $h$ $t$), decomposes a non-empty list into its head and its tail, cf.~\ptref{p:lists}. Equivalent to {\tt unpair}. \item {\tt \underline{undef?} $\langle\textit{word-name\/}\rangle$} ( -- $?$), checks whether the word $\langle\textit{word-name\/}\rangle$ is undefined at execution time, and returns $-1$ or $0$ accordingly. diff --git a/test/regression-tests.ans b/test/regression-tests.ans index ba4b51d503..e93b086d56 100644 --- a/test/regression-tests.ans +++ b/test/regression-tests.ans @@ -6,9 +6,9 @@ Test_Fift_bug_div_default 1ac42861ce96b2896001c587f65e9afe1617db48859f19c2f4e306 Test_Fift_bug_newlize_default e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96 Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d -Test_Fift_testvm2_default 4dcb0f98fd689cbdd81a3a1f937a497dd64d27ecebdcd67cf28fd6888469d93d +Test_Fift_testvm2_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b Test_Fift_testvm3_default 3c1b77471c5fd914ed8b5f528b9faed618e278693f5030b953ff150e543864ae -Test_Fift_testvm4_default 4dcb0f98fd689cbdd81a3a1f937a497dd64d27ecebdcd67cf28fd6888469d93d +Test_Fift_testvm4_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b Test_Fift_testvm4a_default 523b561d6bf2f5ebb26a755e687bfbda8e33462c98e9978119755f79a086cf5e Test_Fift_testvm4b_default daf8567bd58f05c10bb6596cea33b63e1061fa02dd5560db18ff22f96736f0d5 Test_Fift_testvm4c_default 2bbd67831d90bceaae29546ee3a58c4d376c2e8fb6a5b8ea2eae3ab8787e063e diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index 04b55c7804..209b01d235 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -162,10 +162,10 @@ TEST(Tonlib, TestWallet) { fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet.fif")).ensure(); 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", "123", "321"}) - .move_as_ok(); + fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup), + {"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", + "321", "-C", "TEST"}) + .move_as_ok(); auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; auto gift_message = GenericAccount::create_ext_message( address, {}, TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", dest)); From f67f5d879ba0a395367a703dcd75f3b87c6ad535 Mon Sep 17 00:00:00 2001 From: ton Date: Mon, 7 Oct 2019 13:08:23 +0400 Subject: [PATCH 009/667] func: bugfix - bugfix in func - vertseqno support in validator/collator/topsharddescr --- crypto/block/block.cpp | 13 ++++++------- crypto/block/block.h | 2 +- crypto/block/mc-config.h | 5 ++++- crypto/func/codegen.cpp | 11 ++++++++--- validator/impl/collator-impl.h | 1 + validator/impl/collator.cpp | 19 ++++++++++++++----- validator/impl/top-shard-descr.cpp | 20 +++++++++++++++++++- validator/impl/top-shard-descr.hpp | 4 ++++ validator/impl/validate-query.cpp | 22 ++++++++++++++++++++-- validator/impl/validate-query.hpp | 1 + 10 files changed, 78 insertions(+), 20 deletions(-) diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index 7b8074bfbc..80723b5856 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -718,12 +718,9 @@ td::Status ShardState::unpack_state(ton::BlockIdExt blkid, Ref prev_st return td::Status::Error(-666, "shardchain state for "s + blkid.to_str() + " corresponds to incorrect workchain or shard " + shard1.to_str()); } - if (state.vert_seq_no) { - return td::Status::Error( - -666, "shardchain state for "s + blkid.to_str() + " has non-zero vert_seq_no, which is unsupported"); - } id_ = blkid; root_ = std::move(prev_state_root); + vert_seqno_ = state.vert_seq_no; before_split_ = state.before_split; account_dict_ = std::make_unique( vm::load_cell_slice(std::move(state.accounts)).prefetch_ref(), 256, block::tlb::aug_ShardAccounts); @@ -811,7 +808,7 @@ td::Status ShardState::unpack_out_msg_queue_info(Ref out_msg_queue_inf } out_msg_queue_ = std::make_unique(std::move(qinfo.out_queue), 352, block::tlb::aug_OutMsgQueue); - if (verbosity >= 3 * 0) { + if (verbosity >= 3 * 1) { LOG(DEBUG) << "unpacking ProcessedUpto of our previous block " << id_.to_str(); block::gen::t_ProcessedInfo.print(std::cerr, qinfo.proc_info); } @@ -953,14 +950,16 @@ td::Status ShardState::merge_with(ShardState& sib) { lt_ = std::max(lt_, sib.lt_); // 9. compute underload & overload history underload_history_ = overload_history_ = 0; + // 10. compute vert_seqno + vert_seqno_ = std::max(vert_seqno_, sib.vert_seqno_); // Anything else? add here // ... - // 10. compute new root + // 100. compute new root if (!block::gen::t_ShardState.cell_pack_split_state(root_, std::move(root_), std::move(sib.root_))) { return td::Status::Error(-667, "cannot construct a virtual split_state after a merge"); } - // 11. invalidate sibling, change id_ to the (virtual) common parent + // 101. invalidate sibling, change id_ to the (virtual) common parent sib.invalidate(); id_.id.shard = shard.shard; id_.file_hash.set_zero(); diff --git a/crypto/block/block.h b/crypto/block/block.h index 9fbeedf29f..604036d044 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -384,7 +384,7 @@ struct ShardState { int global_id_; ton::UnixTime utime_; ton::LogicalTime lt_; - ton::BlockSeqno mc_blk_seqno_, min_ref_mc_seqno_; + ton::BlockSeqno mc_blk_seqno_, min_ref_mc_seqno_, vert_seqno_; ton::BlockIdExt mc_blk_ref_; ton::LogicalTime mc_blk_lt_; bool before_split_{false}; diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index dc48ec0256..24351c0c71 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -556,7 +556,7 @@ class ConfigInfo : public Config, public ShardConfig { needAccountsRoot = 64, needPrevBlocks = 128 }; - int vert_seqno{-1}; + ton::BlockSeqno vert_seqno{~0U}; int global_id_{0}; ton::UnixTime utime{0}; ton::LogicalTime lt{0}; @@ -604,6 +604,9 @@ class ConfigInfo : public Config, public ShardConfig { Ref get_state_extra_root() const { return state_extra_root_; } + ton::BlockSeqno get_vert_seqno() const { + return vert_seqno; + } ton::CatchainSeqno get_shard_cc_seqno(ton::ShardIdFull shard) const; bool get_last_key_block(ton::BlockIdExt& blkid, ton::LogicalTime& blklt, bool strict = false) const; bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const; diff --git a/crypto/func/codegen.cpp b/crypto/func/codegen.cpp index 7e0d873ff6..6aa196c1a3 100644 --- a/crypto/func/codegen.cpp +++ b/crypto/func/codegen.cpp @@ -308,7 +308,7 @@ bool Op::generate_code_step(Stack& stack) { func->compile(stack.o, res, args); // compile res := f (args) } else { std::string name = sym::symbols.get_name(fun_ref->sym_idx); - stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size()); + stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size()); } stack.push_new_var(left[0]); return true; @@ -395,7 +395,7 @@ bool Op::generate_code_step(Stack& stack) { assert(stack.s[k + i].first == right1[i]); } if (cl == _CallInd) { - stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, -1, (int)right.size() - 1); + stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, (int)right.size(), (int)left.size()); } else { auto func = dynamic_cast(fun_ref->value); if (func) { @@ -407,7 +407,7 @@ bool Op::generate_code_step(Stack& stack) { func->compile(stack.o, res, args); // compile res := f (args) } else { std::string name = sym::symbols.get_name(fun_ref->sym_idx); - stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size()); + stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size()); } } stack.s.resize(k); @@ -421,6 +421,11 @@ bool Op::generate_code_step(Stack& stack) { if (block0->is_empty() && block1->is_empty()) { return true; } + if (!next->noreturn() && (block0->noreturn() != block1->noreturn())) { + // simple fix of unbalanced returns in if/else branches + // (to be replaced with a finer condition working in loop bodies) + throw src::ParseError{where, "`if` and `else` branches should both return or both not return"}; + } var_idx_t x = left[0]; stack.rearrange_top(x, var_info[x] && var_info[x]->is_last()); assert(stack[0] == x); diff --git a/validator/impl/collator-impl.h b/validator/impl/collator-impl.h index bff46e17b0..586c9e8bd2 100644 --- a/validator/impl/collator-impl.h +++ b/validator/impl/collator-impl.h @@ -129,6 +129,7 @@ class Collator final : public td::actor::Actor { ton::UnixTime prev_state_utime_; int global_id_{0}; ton::BlockSeqno min_ref_mc_seqno_{~0U}; + ton::BlockSeqno vert_seqno_{~0U}, prev_vert_seqno_{~0U}; ton::BlockIdExt prev_key_block_; ton::LogicalTime prev_key_block_lt_; bool accept_msgs_{true}; diff --git a/validator/impl/collator.cpp b/validator/impl/collator.cpp index 269b13e121..3f7983b260 100644 --- a/validator/impl/collator.cpp +++ b/validator/impl/collator.cpp @@ -518,6 +518,8 @@ bool Collator::unpack_last_mc_state() { prev_key_block_seqno_ = 0; } LOG(DEBUG) << "previous key block is " << prev_key_block_.to_str() << " (exists=" << prev_key_block_exists_ << ")"; + vert_seqno_ = config_->get_vert_seqno(); + LOG(DEBUG) << "vertical seqno (vert_seqno) is " << vert_seqno_; auto limits = config_->get_block_limits(is_masterchain()); if (limits.is_error()) { return fatal_error(limits.move_as_error()); @@ -686,6 +688,12 @@ bool Collator::unpack_one_last_state(block::ShardState& ss, BlockIdExt blkid, Re if (res.is_error()) { return fatal_error(std::move(res)); } + if (ss.vert_seqno_ > vert_seqno_) { + return fatal_error( + PSTRING() << "cannot create new block with vertical seqno " << vert_seqno_ + << " prescribed by the current masterchain configuration because the previous state of shard " + << ss.id_.to_str() << " has larger vertical seqno " << ss.vert_seqno_); + } return true; } @@ -712,8 +720,8 @@ bool Collator::split_last_state(block::ShardState& ss) { // SETS: account_dict, shard_libraries_, mc_state_extra // total_balance_ = old_total_balance_, total_validator_fees_ -// SETS: overload_history, underload_history -// SETS: prev_state_utime_, prev_state_lt_ +// SETS: overload_history_, underload_history_ +// SETS: prev_state_utime_, prev_state_lt_, prev_vert_seqno_ // SETS: out_msg_queue, processed_upto_, ihr_pending bool Collator::import_shard_state_data(block::ShardState& ss) { account_dict = std::move(ss.account_dict_); @@ -723,6 +731,7 @@ bool Collator::import_shard_state_data(block::ShardState& ss) { underload_history_ = ss.underload_history_; prev_state_utime_ = ss.utime_; prev_state_lt_ = ss.lt_; + prev_vert_seqno_ = ss.vert_seqno_; total_balance_ = old_total_balance_ = std::move(ss.total_balance_); value_flow_.from_prev_blk = old_total_balance_; total_validator_fees_ = std::move(ss.total_validator_fees_); @@ -3426,7 +3435,7 @@ bool Collator::create_shard_state() { && global_id_ // { global_id != 0 } && block::ShardId{shard}.serialize(cb) // shard_id:ShardIdent && cb.store_long_bool(new_block_seqno, 32) // seq_no:uint32 - && cb.store_long_bool(0, 32) // vert_seq_no:# + && cb.store_long_bool(vert_seqno_, 32) // vert_seq_no:# && cb.store_long_bool(now_, 32) // gen_utime:uint32 && cb.store_long_bool(max_lt, 64) // gen_lt:uint64 && update_processed_upto() // insert new ProcessedUpto @@ -3561,9 +3570,9 @@ bool Collator::create_block_info(Ref& block_info) { && cb.store_bool_bool(want_split_) // want_split:Bool && cb.store_bool_bool(want_merge_) // want_merge:Bool && cb.store_bool_bool(is_key_block_) // key_block:Bool - && cb.store_long_bool(0, 9) // flags:(## 9) + && cb.store_long_bool(0, 9) // vert_seqno_incr:(## 1) flags:(## 8) && cb.store_long_bool(new_block_seqno, 32) // seq_no:# - && cb.store_long_bool(0, 32) // vert_seq_no:# + && cb.store_long_bool(vert_seqno_, 32) // vert_seq_no:# && block::ShardId{shard}.serialize(cb) // shard:ShardIdent && cb.store_long_bool(now_, 32) // gen_utime:uint32 && cb.store_long_bool(start_lt, 64) // start_lt:uint64 diff --git a/validator/impl/top-shard-descr.cpp b/validator/impl/top-shard-descr.cpp index 6050779dd2..e4c1328bd3 100644 --- a/validator/impl/top-shard-descr.cpp +++ b/validator/impl/top-shard-descr.cpp @@ -100,6 +100,7 @@ td::Status ShardTopBlockDescrQ::unpack_one_proof(BlockIdExt& cur_id, Ref ShardTopBlockDescrQ::validate_internal(BlockIdExt last_mc_block_ BlockSeqno delta = chain_mc_blk_ids_[0].id.seqno - last_mc_block_id.id.seqno; // too new if ((mode & Mode::fail_new) || (delta > 8 && (mode & Mode::fail_too_new))) { - return td::Status::Error(-666, std::string{"ShardTopBlockDescr for "} + block_id_.to_str() + + return td::Status::Error(-666, "ShardTopBlockDescr for "s + block_id_.to_str() + " is too new for us: it refers to masterchain block " + chain_mc_blk_ids_[0].id.to_str() + " but we know only " + last_mc_block_id.to_str()); @@ -289,6 +295,18 @@ td::Result ShardTopBlockDescrQ::validate_internal(BlockIdExt last_mc_block_ return -1; // valid, but too new } auto config = state->get_config(); + if (config->get_vert_seqno() != vert_seqno_) { + if (vert_seqno_ < config->get_vert_seqno()) { + return td::Status::Error(-666, PSTRING() << "ShardTopBlockDescr for " << block_id_.to_str() + << " is too old: it has vertical seqno " << vert_seqno_ + << " but we already know about " << config->get_vert_seqno()); + } + if (mode & Mode::fail_new) { + return td::Status::Error(-666, PSTRING() << "ShardTopBlockDescr for " << block_id_.to_str() + << " is too new for us: it has vertical seqno " << vert_seqno_ + << " but we know only about " << config->get_vert_seqno()); + } + } BlockSeqno next_mc_seqno = ~BlockSeqno(0); for (const auto& mcid : chain_mc_blk_ids_) { if (mcid.id.seqno > next_mc_seqno) { diff --git a/validator/impl/top-shard-descr.hpp b/validator/impl/top-shard-descr.hpp index 8a82f0965f..c2e9cccc99 100644 --- a/validator/impl/top-shard-descr.hpp +++ b/validator/impl/top-shard-descr.hpp @@ -89,6 +89,9 @@ class ShardTopBlockDescrQ final : public ShardTopBlockDescrQBase { Ref get_root() const { return root_; } + BlockSeqno get_vert_seqno() const { + return vert_seqno_; + } ShardTopBlockDescrQ(td::BufferSlice data, bool is_fake = false) : ShardTopBlockDescrQBase(std::move(data)), is_fake_(is_fake) { } @@ -123,6 +126,7 @@ class ShardTopBlockDescrQ final : public ShardTopBlockDescrQBase { UnixTime gen_utime_{0}; CatchainSeqno catchain_seqno_{0}; td::uint32 validator_set_hash_{0}; + BlockSeqno vert_seqno_{~0U}; td::uint32 sig_count_; ValidatorWeight sig_weight_; Ref sig_root_; diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 1989287415..2350d171a5 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -378,7 +378,7 @@ bool ValidateQuery::init_parse() { block::gen::ExtBlkRef::Record mcref; // _ ExtBlkRef = BlkMasterInfo; 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) && block::gen::BlkPrevInfo{info.after_merge}.validate_ref(info.prev_ref) && (!info.not_master || tlb::unpack_cell(info.master_ref, mcref)) && tlb::unpack_cell(blk.extra, extra))) { return reject_query("cannot unpack block header"); @@ -393,6 +393,7 @@ bool ValidateQuery::init_parse() { return fatal_error("invalid Merkle update in block"); } global_id_ = blk.global_id; + vert_seqno_ = info.vert_seq_no; prev_state_hash_ = upd_cs.prefetch_ref(0)->get_hash(0).bits(); state_hash_ = upd_cs.prefetch_ref(1)->get_hash(0).bits(); start_lt_ = info.start_lt; @@ -428,6 +429,9 @@ bool ValidateQuery::init_parse() { if (is_key_block_ && !shard.is_masterchain()) { return reject_query("a non-masterchain block cannot be a key block"); } + if (info.vert_seqno_incr) { + return reject_query("new blocks cannot have vert_seqno_incr set"); + } if (info.after_merge != after_merge_) { return reject_query("after_merge value mismatch in block header"); } @@ -683,7 +687,13 @@ bool ValidateQuery::try_unpack_mc_state() { CHECK(!mc_seqno_ || new_shard_conf_->get_mc_hash().not_null()); } if (global_id_ != config_->get_global_blockchain_id()) { - return reject_query("blockchain global id mismatch"); + return reject_query(PSTRING() << "blockchain global id mismatch: new block has " << global_id_ + << " while the masterchain configuration expects " + << config_->get_global_blockchain_id()); + } + if (vert_seqno_ != config_->get_vert_seqno()) { + return reject_query(PSTRING() << "vertical seqno mismatch: new block has " << vert_seqno_ + << " while the masterchain configuration expects " << config_->get_vert_seqno()); } prev_key_block_exists_ = config_->get_last_key_block(prev_key_block_, prev_key_block_lt_); if (prev_key_block_exists_) { @@ -1102,6 +1112,10 @@ bool ValidateQuery::unpack_one_prev_state(block::ShardState& ss, BlockIdExt blki if (res.is_error()) { return fatal_error(std::move(res)); } + if (ss.vert_seqno_ > vert_seqno_) { + return reject_query(PSTRING() << "one of previous states " << ss.id_.to_str() << " has vertical seqno " + << ss.vert_seqno_ << " larger than that of the new block " << vert_seqno_); + } return true; } @@ -1148,6 +1162,10 @@ bool ValidateQuery::unpack_next_state() { return reject_query("new state refers to masterchain block "s + ns_.mc_blk_ref_.to_str() + " different from " + mc_blkid_.to_str() + " indicated in block header"); } + if (ns_.vert_seqno_ != vert_seqno_) { + return reject_query(PSTRING() << "new state has vertical seqno " << ns_.vert_seqno_ << " different from " + << vert_seqno_ << " declared in the new block header"); + } // ... return true; } diff --git a/validator/impl/validate-query.hpp b/validator/impl/validate-query.hpp index 14e40957aa..ed3f381005 100644 --- a/validator/impl/validate-query.hpp +++ b/validator/impl/validate-query.hpp @@ -176,6 +176,7 @@ class ValidateQuery : public td::actor::Actor { ton::LogicalTime max_shard_lt_{0}; int global_id_{0}; + ton::BlockSeqno vert_seqno_{~0U}; bool ihr_enabled_{false}; bool create_stats_enabled_{false}; ton::BlockSeqno prev_key_block_seqno_; From 38c3e39066a9c3b8a62b2f4cad54df4dc6307360 Mon Sep 17 00:00:00 2001 From: ton Date: Wed, 9 Oct 2019 20:00:54 +0400 Subject: [PATCH 010/667] func/fift: bugfixes --- .../blockchain-explorer-http.cpp | 29 +++++++----- crypto/fift/lib/Asm.fif | 42 ++++++++++++----- crypto/fift/utils.cpp | 18 ++++++-- crypto/fift/utils.h | 2 +- crypto/fift/words.cpp | 2 +- crypto/func/asmops.cpp | 6 +-- crypto/func/codegen.cpp | 11 +++-- crypto/func/func.cpp | 3 +- crypto/func/func.h | 15 +++++- crypto/func/gen-abscode.cpp | 46 +++++++++---------- crypto/func/keywords.cpp | 7 ++- crypto/func/parse-func.cpp | 20 +++++++- crypto/func/test/b3.fc | 20 ++++++++ crypto/func/test/w1.fc | 9 ++++ crypto/func/test/w2.fc | 14 ++++++ crypto/smartcont/highload-wallet-v2.fif | 2 +- crypto/smartcont/highload-wallet.fif | 2 +- crypto/smartcont/new-highload-wallet-v2.fif | 2 +- crypto/smartcont/new-highload-wallet.fif | 2 +- crypto/smartcont/new-pinger.fif | 2 +- crypto/smartcont/new-testgiver.fif | 2 +- crypto/smartcont/new-wallet-v2.fif | 2 +- crypto/smartcont/new-wallet.fif | 2 +- crypto/smartcont/recover-stake.fif | 2 +- crypto/smartcont/show-addr.fif | 2 +- crypto/smartcont/testgiver.fif | 2 +- crypto/smartcont/update-config-smc.fif | 2 +- crypto/smartcont/update-config.fif | 2 +- crypto/smartcont/update-elector-smc.fif | 2 +- crypto/smartcont/validator-elect-req.fif | 2 +- crypto/smartcont/validator-elect-signed.fif | 2 +- crypto/smartcont/wallet-v2.fif | 2 +- crypto/smartcont/wallet.fif | 2 +- crypto/test/fift.cpp | 20 ++++++++ crypto/test/fift/bug_ufits.fif | 1 + test/regression-tests.ans | 5 ++ tonlib/tonlib/KeyValue.cpp | 3 +- validator-engine/validator-engine.cpp | 3 ++ validator/full-node-shard.cpp | 12 ++--- validator/manager-init.cpp | 9 +++- validator/net/download-state.cpp | 14 +++--- 41 files changed, 248 insertions(+), 99 deletions(-) create mode 100644 crypto/func/test/b3.fc create mode 100644 crypto/func/test/w1.fc create mode 100644 crypto/func/test/w2.fc mode change 100644 => 100755 crypto/smartcont/highload-wallet-v2.fif mode change 100644 => 100755 crypto/smartcont/highload-wallet.fif mode change 100644 => 100755 crypto/smartcont/new-highload-wallet-v2.fif mode change 100644 => 100755 crypto/smartcont/new-highload-wallet.fif mode change 100644 => 100755 crypto/smartcont/new-pinger.fif mode change 100644 => 100755 crypto/smartcont/new-testgiver.fif mode change 100644 => 100755 crypto/smartcont/new-wallet-v2.fif mode change 100644 => 100755 crypto/smartcont/new-wallet.fif mode change 100644 => 100755 crypto/smartcont/recover-stake.fif mode change 100644 => 100755 crypto/smartcont/show-addr.fif mode change 100644 => 100755 crypto/smartcont/testgiver.fif mode change 100644 => 100755 crypto/smartcont/update-config-smc.fif mode change 100644 => 100755 crypto/smartcont/update-config.fif mode change 100644 => 100755 crypto/smartcont/update-elector-smc.fif mode change 100644 => 100755 crypto/smartcont/validator-elect-req.fif mode change 100644 => 100755 crypto/smartcont/validator-elect-signed.fif mode change 100644 => 100755 crypto/smartcont/wallet-v2.fif mode change 100644 => 100755 crypto/smartcont/wallet.fif create mode 100644 crypto/test/fift/bug_ufits.fif diff --git a/blockchain-explorer/blockchain-explorer-http.cpp b/blockchain-explorer/blockchain-explorer-http.cpp index 5d02c454df..43d63d91cd 100644 --- a/blockchain-explorer/blockchain-explorer-http.cpp +++ b/blockchain-explorer/blockchain-explorer-http.cpp @@ -268,8 +268,8 @@ HttpAnswer& HttpAnswer::operator<<(TransactionCell trans_c) { } *this << "
\n" << "\n" - << "" + << "" << "" << "" << "" @@ -381,8 +381,8 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) { *this << "
\n" << "
block" - << trans_c.block_id.id.to_str() << "
block" << trans_c.block_id.id.to_str() + << "
workchain" << trans_c.addr.workchain << "
account hex" << trans_c.addr.addr.to_hex() << "
account" << trans_c.addr.rserialize(true) << "
\n"; - *this << ""; + *this << ""; *this << ""; *this << ""; *this << ""; @@ -438,21 +438,24 @@ HttpAnswer& HttpAnswer::operator<<(BlockHeaderCell head_c) { << "\n" << "\n" << "\n" - << "\n" + << "\n" << "\n" << "\n" + << "\n" + << "\n" << "\n" << "\n" << "\n" << "\n" << "\n" << "\n" - << "\n" + << "\n" << "\n" - << "\n"; + << "\n" + << "\n" + << "\n" + << "\n"; for (auto id : prev) { *this << "\n"; } @@ -627,7 +630,8 @@ std::string HttpAnswer::header() { sb_->clear(); *this << "\n" << "" << title_ << "\n" - << "\n" + << "\n" << "\n" << "\n" << "\n" @@ -648,7 +652,8 @@ std::string HttpAnswer::header() { << "
" << ""; - *this << "
" + *this << "
" << "
" << "\n"; diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 69adeeeaa8..72b5d06bb2 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -756,6 +756,12 @@ x{EDF9} @Defop BOOLEVAL { PUSHINT c3 PREPAREVAR } cond } dup : PREPARE : PREPAREDICT // +// inline support +{ dup sbits { @addop } { + dup srefs 1- abort"exactly one reference expected in inline" + ref@ CALLREF } cond +} : INLINE +// // throwing and handling exceptions { dup 6 ufits { { s>c c } : }END> diff --git a/crypto/fift/utils.cpp b/crypto/fift/utils.cpp index 1c16b2cd4a..fdbd0c7eef 100644 --- a/crypto/fift/utils.cpp +++ b/crypto/fift/utils.cpp @@ -43,6 +43,9 @@ td::Result load_TonUtil_fif(std::string dir = "") { td::Result load_Lists_fif(std::string dir = "") { return load_source("Lists.fif", dir); } +td::Result load_Lisp_fif(std::string dir = "") { + return load_source("Lisp.fif", dir); +} class MemoryFileLoader : public fift::FileLoader { public: @@ -91,7 +94,8 @@ class MemoryFileLoader : public fift::FileLoader { }; td::Result create_source_lookup(std::string main, bool need_preamble = true, bool need_asm = true, - bool need_ton_util = true, std::string dir = "") { + bool need_ton_util = true, bool need_lisp = true, + std::string dir = "") { auto loader = std::make_unique(); loader->add_file("/main.fif", std::move(main)); if (need_preamble) { @@ -112,6 +116,10 @@ td::Result create_source_lookup(std::string main, bool need_ loader->add_file("/TonUtil.fif", std::move(f)); } } + if (need_lisp) { + TRY_RESULT(f, load_Lisp_fif(dir)); + loader->add_file("/Lisp.fif", std::move(f)); + } auto res = fift::SourceLookup(std::move(loader)); res.add_include_path("/"); return std::move(res); @@ -143,7 +151,7 @@ td::Result run_fift(fift::SourceLookup source_lookup, std::o } // namespace td::Result mem_run_fift(std::string source, std::vector args, std::string fift_dir) { std::stringstream ss; - TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, fift_dir)); + TRY_RESULT(source_lookup, create_source_lookup(source, true, true, true, true, fift_dir)); TRY_RESULT_ASSIGN(source_lookup, run_fift(std::move(source_lookup), &ss, true, std::move(args))); FiftOutput res; res.source_lookup = std::move(source_lookup); @@ -159,8 +167,8 @@ td::Result mem_run_fift(SourceLookup source_lookup, std::vector create_mem_source_lookup(std::string main, std::string fift_dir, bool need_preamble, - bool need_asm, bool need_ton_util) { - return create_source_lookup(main, need_preamble, need_asm, need_ton_util, fift_dir); + bool need_asm, bool need_ton_util, bool need_lisp) { + return create_source_lookup(main, need_preamble, need_asm, need_ton_util, need_lisp, fift_dir); } td::Result> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) { @@ -168,7 +176,7 @@ td::Result> compile_asm(td::Slice asm_code, std::string fift_d TRY_RESULT(source_lookup, create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n" << (is_raw ? "}>c" : "") << " boc>B \"res\" B>file", - true, true, true, fift_dir)); + true, true, true, false, fift_dir)); TRY_RESULT(res, run_fift(std::move(source_lookup), &ss)); TRY_RESULT(boc, res.read_file("res")); return vm::std_boc_deserialize(std::move(boc.data)); diff --git a/crypto/fift/utils.h b/crypto/fift/utils.h index e17eb8ae2c..0dcc629f91 100644 --- a/crypto/fift/utils.h +++ b/crypto/fift/utils.h @@ -28,7 +28,7 @@ struct FiftOutput { }; td::Result create_mem_source_lookup(std::string main, std::string fift_dir = "", bool need_preamble = true, bool need_asm = true, - bool need_ton_util = true); + bool need_ton_util = true, bool need_lisp = true); td::Result mem_run_fift(std::string source, std::vector args = {}, std::string fift_dir = ""); td::Result mem_run_fift(SourceLookup source_lookup, std::vector args); td::Result> compile_asm(td::Slice asm_code, std::string fift_dir = "", bool is_raw = true); diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index eda6796bb8..428914c589 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -2346,7 +2346,7 @@ void compile_one_literal(WordList& wlist, vm::StackEntry val) { auto x = std::move(val).as_int(); if (!x->signed_fits_bits(257)) { throw IntError{"invalid numeric literal"}; - } else if (x->signed_fits_bits(64)) { + } else if (x->signed_fits_bits(td::BigIntInfo::word_shift)) { wlist.push_back(Ref{true, std::bind(interpret_const, _1, x->to_long())}); } else { wlist.push_back(Ref{true, std::bind(interpret_big_const, _1, std::move(x))}); diff --git a/crypto/func/asmops.cpp b/crypto/func/asmops.cpp index afc164715c..df4443a4d2 100644 --- a/crypto/func/asmops.cpp +++ b/crypto/func/asmops.cpp @@ -193,21 +193,21 @@ void AsmOp::out(std::ostream& os) const { os << (b ? "SWAP" : "NOP"); break; } - os << "s" << a << " s" << b << " XCHG"; + os << SReg(a) << ' ' << SReg(b) << " XCHG"; break; case a_push: if (!(a & -2)) { os << (a ? "OVER" : "DUP"); break; } - os << "s" << a << " PUSH"; + os << SReg(a) << " PUSH"; break; case a_pop: if (!(a & -2)) { os << (a ? "NIP" : "DROP"); break; } - os << "s" << a << " POP"; + os << SReg(a) << " POP"; break; default: throw src::Fatal{"unknown assembler operation"}; diff --git a/crypto/func/codegen.cpp b/crypto/func/codegen.cpp index 6aa196c1a3..70ae929df3 100644 --- a/crypto/func/codegen.cpp +++ b/crypto/func/codegen.cpp @@ -406,8 +406,11 @@ bool Op::generate_code_step(Stack& stack) { } func->compile(stack.o, res, args); // compile res := f (args) } else { + auto fv = dynamic_cast(fun_ref->value); std::string name = sym::symbols.get_name(fun_ref->sym_idx); - stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size()); + bool is_inline = (fv && (fv->flags & 3)); + stack.o << AsmOp::Custom(name + (is_inline ? " INLINECALLDICT" : " CALLDICT"), (int)right.size(), + (int)left.size()); } } stack.s.resize(k); @@ -553,7 +556,7 @@ bool Op::generate_code_step(Stack& stack) { stack.opt_show(); stack.s.pop_back(); stack.modified(); - if (!next->is_empty()) { + if (true || !next->is_empty()) { stack.o << "REPEAT:<{"; stack.o.indent(); stack.forget_const(); @@ -601,7 +604,7 @@ bool Op::generate_code_step(Stack& stack) { case _Until: { // stack.drop_vars_except(block0->var_info); // stack.opt_show(); - if (!next->is_empty()) { + if (true || !next->is_empty()) { stack.o << "UNTIL:<{"; stack.o.indent(); stack.forget_const(); @@ -632,7 +635,7 @@ bool Op::generate_code_step(Stack& stack) { stack.drop_vars_except(block0->var_info); stack.opt_show(); StackLayout layout1 = stack.vars(); - bool next_empty = next->is_empty(); + bool next_empty = false && next->is_empty(); stack.o << "WHILE:<{"; stack.o.indent(); stack.forget_const(); diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index 223e6fcf95..1cd7bb5a1d 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -83,7 +83,8 @@ void generate_output_func(SymDef* func_sym) { if (verbosity >= 2) { std::cerr << "\n---------- resulting code for " << name << " -------------\n"; } - *outs << std::string(indent * 2, ' ') << name << " PROC:<{\n"; + bool inline_ref = (func_val->flags & 2); + *outs << std::string(indent * 2, ' ') << name << " PROC" << (inline_ref ? "REF" : "") << ":<{\n"; code.generate_code( *outs, (stack_layout_comments ? Stack::_StkCmt | Stack::_CptStkCmt : 0) | (opt_level < 2 ? Stack::_DisableOpt : 0), diff --git a/crypto/func/func.h b/crypto/func/func.h index 8fc881c88b..fe7b707cf3 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -79,6 +79,9 @@ enum Keyword { _RshiftLet, _RshiftRLet, _RshiftCLet, + _AndLet, + _OrLet, + _XorLet, _Int, _Cell, _Slice, @@ -90,6 +93,8 @@ enum Keyword { _Asm, _Impure, _Extern, + _Inline, + _InlineRef, _MethodId, _Operator, _Infix, @@ -632,6 +637,9 @@ struct CodeBlob { } bool import_params(FormalArgList arg_list); var_idx_t create_var(int cls, TypeExpr* var_type = 0, SymDef* sym = 0, const SrcLocation* loc = 0); + var_idx_t create_tmp_var(TypeExpr* var_type = 0, const SrcLocation* loc = 0) { + return create_var(TmpVar::_Tmp, var_type, nullptr, loc); + } int split_vars(bool strict = false); bool compute_used_code_vars(); bool compute_used_code_vars(std::unique_ptr& ops, const VarDescrList& var_info, bool edit) const; @@ -670,8 +678,9 @@ struct SymVal : sym::SymValBase { TypeExpr* sym_type; td::RefInt256 method_id; bool impure; + short flags; // +1 = inline, +2 = inline_ref SymVal(int _type, int _idx, TypeExpr* _stype = nullptr, bool _impure = false) - : sym::SymValBase(_type, _idx), sym_type(_stype), impure(_impure) { + : sym::SymValBase(_type, _idx), sym_type(_stype), impure(_impure), flags(0) { } ~SymVal() override = default; TypeExpr* get_type() const { @@ -802,6 +811,10 @@ struct Expr { int define_new_vars(CodeBlob& code); int predefine_vars(); std::vector pre_compile(CodeBlob& code) const; + var_idx_t new_tmp(CodeBlob& code) const; + std::vector new_tmp_vect(CodeBlob& code) const { + return {new_tmp(code)}; + } }; /* diff --git a/crypto/func/gen-abscode.cpp b/crypto/func/gen-abscode.cpp index ab2efed541..a3cc52042d 100644 --- a/crypto/func/gen-abscode.cpp +++ b/crypto/func/gen-abscode.cpp @@ -212,6 +212,10 @@ int Expr::predefine_vars() { return 0; } +var_idx_t Expr::new_tmp(CodeBlob& code) const { + return code.create_tmp_var(e_type, &here); +} + std::vector Expr::pre_compile(CodeBlob& code) const { switch (cls) { case _Tuple: { @@ -241,8 +245,7 @@ std::vector Expr::pre_compile(CodeBlob& code) const { res.insert(res.end(), add.cbegin(), add.cend()); } } - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; + auto rvect = new_tmp_vect(code); auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), sym); if (flags & _IsImpure) { op.flags |= Op::_Impure; @@ -256,57 +259,52 @@ std::vector Expr::pre_compile(CodeBlob& code) const { return {val}; case _VarApply: if (args[0]->cls == _Glob) { - std::vector res = args[1]->pre_compile(code); - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; + auto res = args[1]->pre_compile(code); + auto rvect = new_tmp_vect(code); auto& op = code.emplace_back(here, Op::_Call, rvect, std::move(res), args[0]->sym); if (args[0]->flags & _IsImpure) { op.flags |= Op::_Impure; } return rvect; } else { - std::vector res = args[1]->pre_compile(code); - std::vector tfunc = args[0]->pre_compile(code); + auto res = args[1]->pre_compile(code); + auto tfunc = args[0]->pre_compile(code); if (tfunc.size() != 1) { throw src::Fatal{"stack tuple used as a function"}; } res.push_back(tfunc[0]); - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; + auto rvect = new_tmp_vect(code); code.emplace_back(here, Op::_CallInd, rvect, std::move(res)); return rvect; } case _Const: { - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; + auto rvect = new_tmp_vect(code); code.emplace_back(here, Op::_IntConst, rvect, intval); return rvect; } case _Glob: { - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; + auto rvect = new_tmp_vect(code); code.emplace_back(here, Op::_GlobVar, rvect, std::vector{}, sym); return rvect; } case _Letop: { - std::vector right = args[1]->pre_compile(code); - std::vector left = args[0]->pre_compile(code); - code.emplace_back(here, Op::_Let, left, std::move(right)); - return left; + auto right = args[1]->pre_compile(code); + auto left = args[0]->pre_compile(code); + code.emplace_back(here, Op::_Let, std::move(left), right); + return right; } case _LetFirst: { - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector right = args[1]->pre_compile(code); - std::vector left = args[0]->pre_compile(code); - left.push_back(rv); + auto rvect = new_tmp_vect(code); + auto right = args[1]->pre_compile(code); + auto left = args[0]->pre_compile(code); + left.push_back(rvect[0]); code.emplace_back(here, Op::_Let, std::move(left), std::move(right)); - return std::vector{rv}; + return rvect; } case _CondExpr: { auto cond = args[0]->pre_compile(code); assert(cond.size() == 1); - var_idx_t rv = code.create_var(TmpVar::_Tmp, e_type, nullptr, &here); - std::vector rvect{rv}; + auto rvect = new_tmp_vect(code); Op& if_op = code.emplace_back(here, Op::_If, cond); code.push_set_cur(if_op.block0); code.emplace_back(here, Op::_Let, rvect, args[1]->pre_compile(code)); diff --git a/crypto/func/keywords.cpp b/crypto/func/keywords.cpp index bab30d32a6..b2b6120ad3 100644 --- a/crypto/func/keywords.cpp +++ b/crypto/func/keywords.cpp @@ -79,7 +79,10 @@ void define_keywords() { .add_keyword("<<=", Kw::_LshiftLet) .add_keyword(">>=", Kw::_RshiftLet) .add_keyword(">>~=", Kw::_RshiftRLet) - .add_keyword(">>^=", Kw::_RshiftCLet); + .add_keyword(">>^=", Kw::_RshiftCLet) + .add_keyword("&=", Kw::_AndLet) + .add_keyword("|=", Kw::_OrLet) + .add_keyword("^=", Kw::_XorLet); sym::symbols.add_keyword("return", Kw::_Return) .add_keyword("var", Kw::_Var) @@ -105,6 +108,8 @@ void define_keywords() { sym::symbols.add_keyword("extern", Kw::_Extern) .add_keyword("asm", Kw::_Asm) .add_keyword("impure", Kw::_Impure) + .add_keyword("inline", Kw::_Inline) + .add_keyword("inline_ref", Kw::_InlineRef) .add_keyword("method_id", Kw::_MethodId) .add_keyword("operator", Kw::_Operator) .add_keyword("infix", Kw::_Infix) diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index f605bd83ef..29c79283bf 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -428,7 +428,6 @@ Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) { res->flags = Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0); res->deduce_type(lex.cur()); if (modify) { - // FIXME (use _LetFirst instead of _Letop) auto tmp = res; res = new Expr{Expr::_LetFirst, {obj->copy(), tmp}}; res->here = loc; @@ -587,7 +586,8 @@ Expr* parse_expr10(Lexer& lex, CodeBlob& code, bool nv) { auto x = parse_expr13(lex, code, nv); int t = lex.tp(); if (t == _PlusLet || t == _MinusLet || t == _TimesLet || t == _DivLet || t == _DivRLet || t == _DivCLet || - t == _ModLet || t == _LshiftLet || t == _RshiftLet || t == _RshiftCLet || t == _RshiftRLet) { + t == _ModLet || t == _LshiftLet || t == _RshiftLet || t == _RshiftCLet || t == _RshiftRLet || t == _AndLet || + t == _OrLet || t == _XorLet) { x->chk_lvalue(lex.cur()); x->chk_rvalue(lex.cur()); sym_idx_t name = symbols.lookup_add(std::string{"^_"} + lex.cur().str + "_"); @@ -974,6 +974,11 @@ void parse_func_def(Lexer& lex) { if (impure) { lex.next(); } + int f = 0; + if (lex.tp() == _Inline || lex.tp() == _InlineRef) { + f = (lex.tp() == _Inline) ? 1 : 2; + lex.next(); + } td::RefInt256 method_id; std::string method_name; if (lex.tp() == _MethodId) { @@ -1071,6 +1076,17 @@ void parse_func_def(Lexer& lex) { lex.cur().error("integer method identifier for `"s + func_name.str + "` changed to a different value"); } } + if (f) { + auto val = dynamic_cast(func_sym->value); + if (!val) { + lex.cur().error("cannot set unknown function `"s + func_name.str + "` as an inline"); + } + if (!(val->flags & 3)) { + val->flags = (short)(val->flags | f); + } else if ((val->flags & 3) != f) { + lex.cur().error("inline mode for `"s + func_name.str + "` changed with respect to a previous declaration"); + } + } if (verbosity >= 1) { std::cerr << "new type of function " << func_name.str << " : " << func_type << std::endl; } diff --git a/crypto/func/test/b3.fc b/crypto/func/test/b3.fc new file mode 100644 index 0000000000..0e1e403474 --- /dev/null +++ b/crypto/func/test/b3.fc @@ -0,0 +1,20 @@ +;; inline test +_ unpack() inline { + var ds = get_data().begin_parse(); + var res = (ds~load_uint(8), ds~load_int(32), ds~load_int(32)); + return res; +} + +() pack(a, x, y) impure inline_ref { + set_data(begin_cell() + .store_uint(a, 8) + .store_int(x, 32) + .store_int(y, 32) + .end_cell()); +} + +() main() impure { + var (a, x, y) = unpack(); + x += y; + pack(a, x, y); +} diff --git a/crypto/func/test/w1.fc b/crypto/func/test/w1.fc new file mode 100644 index 0000000000..6efd4f3e19 --- /dev/null +++ b/crypto/func/test/w1.fc @@ -0,0 +1,9 @@ +(int, int) nested_if(int id) method_id { + dump_stack(); + if (id > 0) { + if (id > 10) { + return (2 * id, 3 * id); + } + } + return (5, 6); +} diff --git a/crypto/func/test/w2.fc b/crypto/func/test/w2.fc new file mode 100644 index 0000000000..3bbcd415ab --- /dev/null +++ b/crypto/func/test/w2.fc @@ -0,0 +1,14 @@ +_ f(cs) { + return (cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), + cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), + cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), + cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), + cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8)); +} + +_ main(cs) { + var (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, + x11, x12, x13, x14, x15, x16, x17, x18, x19) = f(cs); + return x0 + x1 + x2 + x3 + x4 + x5 + x6 + x7 + x8 + x9 + + x10 + x11 + x12 + x13 + x14 + x15 + x16 + x17 + x18 + x19; +} diff --git a/crypto/smartcont/highload-wallet-v2.fif b/crypto/smartcont/highload-wallet-v2.fif old mode 100644 new mode 100755 index 76f06ed590..b8f0be72e9 --- a/crypto/smartcont/highload-wallet-v2.fif +++ b/crypto/smartcont/highload-wallet-v2.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/highload-wallet.fif b/crypto/smartcont/highload-wallet.fif old mode 100644 new mode 100755 index ab69fdf368..e851016cc9 --- a/crypto/smartcont/highload-wallet.fif +++ b/crypto/smartcont/highload-wallet.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/new-highload-wallet-v2.fif b/crypto/smartcont/new-highload-wallet-v2.fif old mode 100644 new mode 100755 index df386cc72a..64f59831cf --- a/crypto/smartcont/new-highload-wallet-v2.fif +++ b/crypto/smartcont/new-highload-wallet-v2.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include "Asm.fif" include diff --git a/crypto/smartcont/new-highload-wallet.fif b/crypto/smartcont/new-highload-wallet.fif old mode 100644 new mode 100755 index 57be6a2311..9455827406 --- a/crypto/smartcont/new-highload-wallet.fif +++ b/crypto/smartcont/new-highload-wallet.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include "Asm.fif" include diff --git a/crypto/smartcont/new-pinger.fif b/crypto/smartcont/new-pinger.fif old mode 100644 new mode 100755 index 646bfdf68c..e7324280d3 --- a/crypto/smartcont/new-pinger.fif +++ b/crypto/smartcont/new-pinger.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include "Asm.fif" include diff --git a/crypto/smartcont/new-testgiver.fif b/crypto/smartcont/new-testgiver.fif old mode 100644 new mode 100755 index 504a81bdeb..ad9789fa2f --- a/crypto/smartcont/new-testgiver.fif +++ b/crypto/smartcont/new-testgiver.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include "Asm.fif" include diff --git a/crypto/smartcont/new-wallet-v2.fif b/crypto/smartcont/new-wallet-v2.fif old mode 100644 new mode 100755 index 39bb421bf9..f85dc12b47 --- a/crypto/smartcont/new-wallet-v2.fif +++ b/crypto/smartcont/new-wallet-v2.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include "Asm.fif" include diff --git a/crypto/smartcont/new-wallet.fif b/crypto/smartcont/new-wallet.fif old mode 100644 new mode 100755 index 9bdb0bdb38..ac8d4a0e3a --- a/crypto/smartcont/new-wallet.fif +++ b/crypto/smartcont/new-wallet.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include "Asm.fif" include diff --git a/crypto/smartcont/recover-stake.fif b/crypto/smartcont/recover-stake.fif old mode 100644 new mode 100755 index 573cb336a2..871b3855bc --- a/crypto/smartcont/recover-stake.fif +++ b/crypto/smartcont/recover-stake.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/show-addr.fif b/crypto/smartcont/show-addr.fif old mode 100644 new mode 100755 index 74536528f8..5d3094644f --- a/crypto/smartcont/show-addr.fif +++ b/crypto/smartcont/show-addr.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." " cr diff --git a/crypto/smartcont/testgiver.fif b/crypto/smartcont/testgiver.fif old mode 100644 new mode 100755 index fe812487a5..c48735aca1 --- a/crypto/smartcont/testgiver.fif +++ b/crypto/smartcont/testgiver.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/update-config-smc.fif b/crypto/smartcont/update-config-smc.fif old mode 100644 new mode 100755 index 22f01f0456..0a6d41fa0a --- a/crypto/smartcont/update-config-smc.fif +++ b/crypto/smartcont/update-config-smc.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/update-config.fif b/crypto/smartcont/update-config.fif old mode 100644 new mode 100755 index f58b5cf4f1..fcb8cf161b --- a/crypto/smartcont/update-config.fif +++ b/crypto/smartcont/update-config.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/update-elector-smc.fif b/crypto/smartcont/update-elector-smc.fif old mode 100644 new mode 100755 index 2531345e1e..318163ccde --- a/crypto/smartcont/update-elector-smc.fif +++ b/crypto/smartcont/update-elector-smc.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/validator-elect-req.fif b/crypto/smartcont/validator-elect-req.fif old mode 100644 new mode 100755 index c2aca4b5b6..215fe9c15c --- a/crypto/smartcont/validator-elect-req.fif +++ b/crypto/smartcont/validator-elect-req.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/validator-elect-signed.fif b/crypto/smartcont/validator-elect-signed.fif old mode 100644 new mode 100755 index 9755bfac67..55e3d9ddac --- a/crypto/smartcont/validator-elect-signed.fif +++ b/crypto/smartcont/validator-elect-signed.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr diff --git a/crypto/smartcont/wallet-v2.fif b/crypto/smartcont/wallet-v2.fif old mode 100644 new mode 100755 index 910f7d41f1..3d36643b45 --- a/crypto/smartcont/wallet-v2.fif +++ b/crypto/smartcont/wallet-v2.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." [-B ] []" cr diff --git a/crypto/smartcont/wallet.fif b/crypto/smartcont/wallet.fif old mode 100644 new mode 100755 index b25b4494a2..f4eb280255 --- a/crypto/smartcont/wallet.fif +++ b/crypto/smartcont/wallet.fif @@ -1,4 +1,4 @@ -#!/usr/bin/env fift -s +#!/usr/bin/fift -s "TonUtil.fif" include { ."usage: " @' $0 type ." [-B ] [-C ] []" cr diff --git a/crypto/test/fift.cpp b/crypto/test/fift.cpp index 59b912c34e..694dc7a83c 100644 --- a/crypto/test/fift.cpp +++ b/crypto/test/fift.cpp @@ -90,3 +90,23 @@ TEST(Fift, test) { TEST(Fift, bug_div) { run_fift("bug_div.fif"); } + +TEST(Fift, bug_ufits) { + run_fift("bug_ufits.fif"); +} + +TEST(Fift, test_dict) { + run_fift("testdict.fif"); +} + +TEST(Fift, test_fixed) { + run_fift("fixed.fif"); +} + +TEST(Fift, test_sort) { + run_fift("sort.fif"); +} + +TEST(Fift, test_sort2) { + run_fift("sort2.fif"); +} diff --git a/crypto/test/fift/bug_ufits.fif b/crypto/test/fift/bug_ufits.fif new file mode 100644 index 0000000000..6c607ea220 --- /dev/null +++ b/crypto/test/fift/bug_ufits.fif @@ -0,0 +1 @@ +100000000000000000 dup =: amount 56 ufits . amount 56 ufits . diff --git a/test/regression-tests.ans b/test/regression-tests.ans index e93b086d56..29e26328dc 100644 --- a/test/regression-tests.ans +++ b/test/regression-tests.ans @@ -4,8 +4,13 @@ Test_Bitstrings_main_default a8b08af3116923c4c2a14e138d168375abd0c059f2f780d3267 Test_Cells_simple_default 832502642fe4fe5db70de82681aedb7d54d7f3530e0069861fff405fe6f6cf23 Test_Fift_bug_div_default 1ac42861ce96b2896001c587f65e9afe1617db48859f19c2f4e3063a20ea60b0 Test_Fift_bug_newlize_default e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +Test_Fift_bug_ufits_default 51bf5a9f1ed7633a193f6fdd17a7a3af8e032dfe72a9669c85e8639aa8a7c195 Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96 Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d +Test_Fift_test_dict_default 480d22a6ec25a232febf4eec8ff64747573f79721327e7ff3b1aa7ea4921bbb4 +Test_Fift_test_fixed_default 278a19d56b773102caf5c1fe2997ea6c8d0d9e720eff8503feede6398a197eec +Test_Fift_test_sort2_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a +Test_Fift_test_sort_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a Test_Fift_testvm2_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b Test_Fift_testvm3_default 3c1b77471c5fd914ed8b5f528b9faed618e278693f5030b953ff150e543864ae Test_Fift_testvm4_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index 796969241f..ccf9a38372 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -2,6 +2,7 @@ #include "td/utils/filesystem.h" #include "td/utils/port/path.h" +#include "td/utils/PathView.h" #include #include @@ -51,7 +52,7 @@ class KeyValueDir : public KeyValue { return td::WalkPath::Action::SkipDir; } } else if (type == td::WalkPath::Type::NotDir) { - f(path); + f(td::PathView::relative(path, directory_)); } return td::WalkPath::Action::Continue; diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index a7fcff7e28..67171a1eae 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -29,6 +29,7 @@ #include "ton/ton-types.h" #include "ton/ton-tl.hpp" +#include "ton/ton-io.hpp" #include "common/errorlog.h" @@ -1004,9 +1005,11 @@ td::Status ValidatorEngine::load_global_config() { ton::BlockIdExt init_block; if (!conf.validator_->init_block_) { + LOG(INFO) << "no init block in config. using zero state"; init_block = zero_state; } else { init_block = ton::create_block_id(conf.validator_->init_block_); + LOG(INFO) << "found init block " << init_block; if (init_block.id.workchain != ton::masterchainId || init_block.id.shard != ton::shardIdAll) { return td::Status::Error(ton::ErrorCode::error, "[validator] section contains invalid [init_block]"); } diff --git a/validator/full-node-shard.cpp b/validator/full-node-shard.cpp index 130d344ef8..4c6caa2005 100644 --- a/validator/full-node-shard.cpp +++ b/validator/full-node-shard.cpp @@ -596,17 +596,17 @@ void FullNodeShardImpl::download_block(BlockIdExt id, td::uint32 priority, td::T void FullNodeShardImpl::download_zero_state(BlockIdExt id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) { - td::actor::create_actor("downloadstatereq", id, BlockIdExt{}, adnl_id_, overlay_id_, - adnl::AdnlNodeIdShort::zero(), priority, timeout, validator_manager_, rldp_, - overlays_, adnl_, client_, std::move(promise)) + td::actor::create_actor(PSTRING() << "downloadstatereq" << id.id.to_str(), id, BlockIdExt{}, adnl_id_, + overlay_id_, adnl::AdnlNodeIdShort::zero(), priority, timeout, + validator_manager_, rldp_, overlays_, adnl_, client_, std::move(promise)) .release(); } void FullNodeShardImpl::download_persistent_state(BlockIdExt id, BlockIdExt masterchain_block_id, td::uint32 priority, td::Timestamp timeout, td::Promise promise) { - td::actor::create_actor("downloadstatereq", id, masterchain_block_id, adnl_id_, overlay_id_, - adnl::AdnlNodeIdShort::zero(), priority, timeout, validator_manager_, rldp_, - overlays_, adnl_, client_, std::move(promise)) + td::actor::create_actor(PSTRING() << "downloadstatereq" << id.id.to_str(), id, masterchain_block_id, + adnl_id_, overlay_id_, adnl::AdnlNodeIdShort::zero(), priority, timeout, + validator_manager_, rldp_, overlays_, adnl_, client_, std::move(promise)) .release(); } diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index 3a8fe1fd5d..24f7c45d18 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -32,6 +32,7 @@ namespace ton { namespace validator { void ValidatorManagerMasterchainReiniter::start_up() { + LOG(INFO) << "init_block_id=" << block_id_; CHECK(block_id_.is_masterchain()); CHECK(block_id_.id.shard == shardIdAll); CHECK(block_id_.seqno() >= opts_->get_last_fork_masterchain_seqno()); @@ -83,7 +84,9 @@ void ValidatorManagerMasterchainReiniter::download_proof_link() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { LOG(WARNING) << "failed to download proof link: " << R.move_as_error(); - td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::download_proof_link); + delay_action( + [SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::download_proof_link); }, + td::Timestamp::in(1.0)); } else { td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::downloaded_proof_link, R.move_as_ok()); } @@ -208,6 +211,10 @@ void ValidatorManagerMasterchainReiniter::choose_masterchain_state() { p = key_blocks_[key_blocks_.size() - 2 - i]; } + LOG(INFO) << "key block candidate: seqno=" << h->id().seqno() + << " is_persistent=" << (!p || ValidatorManager::is_persistent_state(h->unix_time(), p->unix_time())) + << " ttl=" << ValidatorManager::persistent_state_ttl(h->unix_time()) + << " syncbefore=" << opts_->sync_blocks_before(); if (!p || ValidatorManager::is_persistent_state(h->unix_time(), p->unix_time())) { auto ttl = ValidatorManager::persistent_state_ttl(h->unix_time()); if (ttl > td::Clocks::system() + opts_->sync_blocks_before()) { diff --git a/validator/net/download-state.cpp b/validator/net/download-state.cpp index 17a684b8e4..3fe50247ce 100644 --- a/validator/net/download-state.cpp +++ b/validator/net/download-state.cpp @@ -172,12 +172,12 @@ void DownloadState::got_block_state_description(td::BufferSlice data) { create_serialize_tl_object(create_tl_block_id(block_id_)); if (client_.empty()) { td::actor::send_closure(overlays_, &overlay::Overlays::send_query_via, download_from_, local_id_, - overlay_id_, "download state", std::move(P), timeout_, std::move(query), - FullNode::max_state_size(), rldp_); + overlay_id_, "download state", std::move(P), td::Timestamp::in(3.0), + std::move(query), FullNode::max_state_size(), rldp_); } else { td::actor::send_closure(client_, &adnl::AdnlExtClient::send_query, "download state", create_serialize_tl_object_suffix(std::move(query)), - timeout_, std::move(P)); + td::Timestamp::in(3.0), std::move(P)); } })); } @@ -213,12 +213,12 @@ void DownloadState::got_block_state_part(td::BufferSlice data, td::uint32 reques create_tl_block_id(block_id_), create_tl_block_id(masterchain_block_id_), sum_, part_size); if (client_.empty()) { td::actor::send_closure(overlays_, &overlay::Overlays::send_query_via, download_from_, local_id_, overlay_id_, - "download state", std::move(P), timeout_, std::move(query), FullNode::max_state_size(), - rldp_); + "download state", std::move(P), td::Timestamp::in(10.0), std::move(query), + FullNode::max_state_size(), rldp_); } else { td::actor::send_closure(client_, &adnl::AdnlExtClient::send_query, "download state", - create_serialize_tl_object_suffix(std::move(query)), timeout_, - std::move(P)); + create_serialize_tl_object_suffix(std::move(query)), + td::Timestamp::in(10.0), std::move(P)); } } From b978e27b2f2ca6e4403843b9d6bf3f407bf63499 Mon Sep 17 00:00:00 2001 From: ton Date: Mon, 14 Oct 2019 11:13:23 +0400 Subject: [PATCH 011/667] updated smartcontract code - updated smartcontract code - fixed bug in liteserver listening socket - updated documentation --- adnl/adnl-ext-client.cpp | 3 + adnl/adnl-ext-server.cpp | 4 +- adnl/adnl-ext-server.hpp | 68 +- adnl/adnl-query.h | 4 + crypto/CMakeLists.txt | 35 +- crypto/block/block.h | 9 +- crypto/block/check-proof.cpp | 5 +- crypto/fift/lib/Asm.fif | 2 + crypto/fift/lib/TonUtil.fif | 12 + crypto/fift/words.cpp | 50 + crypto/func/abscode.cpp | 3 + crypto/func/analyzer.cpp | 35 +- crypto/func/builtins.cpp | 9 +- crypto/func/func.cpp | 27 +- crypto/func/func.h | 5 + crypto/func/test/w3.fc | 13 + crypto/func/test/w4.fc | 13 + crypto/func/test/w5.fc | 14 + crypto/func/test/w6.fc | 12 + crypto/func/test/w7.fc | 14 + crypto/smartcont/CreateState.fif | 45 + crypto/smartcont/asm-to-cpp.fif | 31 + crypto/smartcont/auto/config-code.cpp | 1 + crypto/smartcont/auto/config-code.fif | 323 ++++ crypto/smartcont/auto/elector-code.cpp | 1 + crypto/smartcont/auto/elector-code.fif | 1712 +++++++++++++++++ .../smartcont/auto/highload-wallet-code.cpp | 1 + .../smartcont/auto/highload-wallet-code.fif | 79 + .../auto/highload-wallet-v2-code.cpp | 1 + .../auto/highload-wallet-v2-code.fif | 134 ++ crypto/smartcont/auto/simple-wallet-code.cpp | 1 + crypto/smartcont/auto/simple-wallet-code.fif | 45 + crypto/smartcont/auto/wallet-code.cpp | 1 + crypto/smartcont/auto/wallet-code.fif | 59 + crypto/smartcont/gen-zerostate-test.fif | 2 +- crypto/smartcont/gen-zerostate.fif | 7 +- crypto/smartcont/new-highload-wallet-v2.fif | 4 +- crypto/smartcont/new-highload-wallet.fif | 4 +- crypto/smartcont/new-restricted-wallet.fif | 33 + crypto/smartcont/new-restricted-wallet2.fif | 47 + crypto/smartcont/new-wallet-v2.fif | 2 +- crypto/smartcont/restricted-wallet-code.fc | 67 + crypto/smartcont/restricted-wallet2-code.fc | 69 + crypto/smartcont/stdlib.fc | 5 + crypto/smartcont/update-config-smc.fif | 4 +- crypto/smartcont/update-elector-smc.fif | 4 +- doc/tvm.tex | 12 +- lite-client/lite-client.cpp | 13 +- lite-client/lite-client.h | 4 +- tdactor/td/actor/core/ActorExecutor.cpp | 2 +- tdactor/td/actor/core/Scheduler.cpp | 1 + tdutils/td/utils/CancellationToken.h | 5 + tl/generate/scheme/tonlib_api.tl | 6 + tl/generate/scheme/tonlib_api.tlo | Bin 13576 -> 13984 bytes tonlib/tonlib/Client.cpp | 8 +- tonlib/tonlib/ExtClient.cpp | 4 + tonlib/tonlib/ExtClient.h | 1 + tonlib/tonlib/LastBlock.cpp | 59 +- tonlib/tonlib/LastBlock.h | 31 +- tonlib/tonlib/TonlibClient.cpp | 65 +- tonlib/tonlib/TonlibClient.h | 8 + tonlib/tonlib/tonlib-cli.cpp | 17 +- validator/manager-init.cpp | 11 +- 63 files changed, 3185 insertions(+), 81 deletions(-) create mode 100644 crypto/func/test/w3.fc create mode 100644 crypto/func/test/w4.fc create mode 100644 crypto/func/test/w5.fc create mode 100644 crypto/func/test/w6.fc create mode 100644 crypto/func/test/w7.fc create mode 100755 crypto/smartcont/asm-to-cpp.fif create mode 100644 crypto/smartcont/auto/config-code.cpp create mode 100644 crypto/smartcont/auto/config-code.fif create mode 100644 crypto/smartcont/auto/elector-code.cpp create mode 100644 crypto/smartcont/auto/elector-code.fif create mode 100644 crypto/smartcont/auto/highload-wallet-code.cpp create mode 100644 crypto/smartcont/auto/highload-wallet-code.fif create mode 100644 crypto/smartcont/auto/highload-wallet-v2-code.cpp create mode 100644 crypto/smartcont/auto/highload-wallet-v2-code.fif create mode 100644 crypto/smartcont/auto/simple-wallet-code.cpp create mode 100644 crypto/smartcont/auto/simple-wallet-code.fif create mode 100644 crypto/smartcont/auto/wallet-code.cpp create mode 100644 crypto/smartcont/auto/wallet-code.fif create mode 100644 crypto/smartcont/new-restricted-wallet.fif create mode 100644 crypto/smartcont/new-restricted-wallet2.fif create mode 100644 crypto/smartcont/restricted-wallet-code.fc create mode 100644 crypto/smartcont/restricted-wallet2-code.fc diff --git a/adnl/adnl-ext-client.cpp b/adnl/adnl-ext-client.cpp index d7a39d417e..1d10283b22 100644 --- a/adnl/adnl-ext-client.cpp +++ b/adnl/adnl-ext-client.cpp @@ -62,6 +62,9 @@ void AdnlExtClientImpl::hangup() { conn_ = {}; is_closing_ = true; ref_cnt_--; + for (auto &it : out_queries_) { + td::actor::ActorOwn<>(it.second); // send hangup + } try_stop(); } diff --git a/adnl/adnl-ext-server.cpp b/adnl/adnl-ext-server.cpp index 1fab1143c8..168e9839cd 100644 --- a/adnl/adnl-ext-server.cpp +++ b/adnl/adnl-ext-server.cpp @@ -146,8 +146,8 @@ void AdnlExtServerImpl::add_tcp_port(td::uint16 port) { } }; - auto act = td::actor::create_actor(td::actor::ActorOptions().with_name("listener").with_poll(), port, - std::make_unique(actor_id(this))); + auto act = td::actor::create_actor(td::actor::ActorOptions().with_name("listener").with_poll(), + port, std::make_unique(actor_id(this))); listeners_.emplace(port, std::move(act)); } diff --git a/adnl/adnl-ext-server.hpp b/adnl/adnl-ext-server.hpp index 3cc32f251e..f7912c8511 100644 --- a/adnl/adnl-ext-server.hpp +++ b/adnl/adnl-ext-server.hpp @@ -32,6 +32,69 @@ namespace ton { namespace adnl { +class TcpInfiniteListener : public td::actor::Actor { + public: + TcpInfiniteListener(td::int32 port, std::unique_ptr callback) + : port_(port), callback_(std::move(callback)) { + } + + private: + td::int32 port_; + std::unique_ptr callback_; + td::actor::ActorOwn tcp_listener_; + td::int32 refcnt_{0}; + bool close_flag_{false}; + + void start_up() override { + loop(); + } + + void hangup() override { + close_flag_ = true; + tcp_listener_.reset(); + if (refcnt_ == 0) { + stop(); + } + } + + void loop() override { + if (!tcp_listener_.empty()) { + return; + } + class Callback : public td::TcpListener::Callback { + public: + Callback(td::actor::ActorShared parent) : parent_(std::move(parent)) { + } + void accept(td::SocketFd fd) override { + td::actor::send_closure(parent_, &TcpInfiniteListener::accept, std::move(fd)); + } + + private: + td::actor::ActorShared parent_; + }; + refcnt_++; + tcp_listener_ = td::actor::create_actor( + td::actor::ActorOptions().with_name(PSLICE() << "TcpListener" << td::tag("port", port_)).with_poll(), port_, + std::make_unique(actor_shared(this))); + } + + void accept(td::SocketFd fd) { + callback_->accept(std::move(fd)); + } + + void hangup_shared() override { + refcnt_--; + tcp_listener_.reset(); + if (close_flag_) { + if (refcnt_ == 0) { + stop(); + } + } else { + alarm_timestamp() = td::Timestamp::in(5 /*5 seconds*/); + } + } +}; + class AdnlExtServerImpl; class AdnlInboundConnection : public AdnlExtConnection { @@ -69,6 +132,9 @@ class AdnlExtServerImpl : public AdnlExtServer { ports_.clear(); } + void reopen_port() { + } + AdnlExtServerImpl(td::actor::ActorId adnl, std::vector ids, std::vector ports) : peer_table_(adnl) { @@ -84,7 +150,7 @@ class AdnlExtServerImpl : public AdnlExtServer { td::actor::ActorId peer_table_; std::set local_ids_; std::set ports_; - std::map> listeners_; + std::map> listeners_; }; } // namespace adnl diff --git a/adnl/adnl-query.h b/adnl/adnl-query.h index e900c69987..3c0ebf065f 100644 --- a/adnl/adnl-query.h +++ b/adnl/adnl-query.h @@ -20,6 +20,7 @@ #include "td/actor/actor.h" #include "common/bitstring.h" +#include "common/errorcode.h" #include "td/utils/buffer.h" #include @@ -52,6 +53,9 @@ class AdnlQuery : public td::actor::Actor { } void tear_down() override { destroy_(id_); + if (promise_) { + promise_.set_error(td::Status::Error(ErrorCode::cancelled, "Cancelled")); + } } private: diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index acf88eeef6..dc7d7cf14c 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -279,6 +279,7 @@ if (TON_USE_ASAN AND NOT WIN32) set(TURN_OFF_LSAN export LSAN_OPTIONS=detect_leaks=0) endif() +file(MAKE_DIRECTORY smartcont/auto) if (NOT CMAKE_CROSSCOMPILING) set(GENERATE_TLB_CMD tlbc) add_custom_command( @@ -295,29 +296,41 @@ if (NOT CMAKE_CROSSCOMPILING) add_custom_target(gen_fif ALL) function(GenFif) set(options ) - set(oneValueArgs DEST) + set(oneValueArgs DEST NAME) set(multiValueArgs SOURCE) set(FUNC_LIB_SOURCE smartcont/stdlib.fc) cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) string(REGEX REPLACE "[^a-zA-Z_]" "_" ID ${ARG_DEST}) + set(ARG_DEST_FIF "${ARG_DEST}.fif") add_custom_command( - COMMENT "Generate ${ARG_DEST}" + COMMENT "Generate ${ARG_DEST_FIF}" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - COMMAND func -PS -o ${ARG_DEST} ${FUNC_LIB_SOURCE} ${ARG_SOURCE} + COMMAND func -PS -o ${ARG_DEST_FIF} ${FUNC_LIB_SOURCE} ${ARG_SOURCE} MAIN_DEPENDENCY ${ARG_SOURCE} DEPENDS func ${FUNC_LIB_SOURCE} - OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_DEST} + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_DEST_FIF} ) - add_custom_target(gen_fif_${ID} DEPENDS ${ARG_DEST}) + set(ARG_DEST_CPP "${ARG_DEST}.cpp") + add_custom_command( + COMMENT "Generate ${ARG_DEST_CPP}" + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND fift -Ifift/lib:smartcont -s asm-to-cpp.fif ${ARG_DEST_FIF} ${ARG_DEST_CPP} ${ARG_NAME} + MAIN_DEPENDENCY ${ARG_SOURCE} + DEPENDS fift ${ARG_DEST_FIF} smartcont/asm-to-cpp.fif + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_DEST_CPP} + ) + add_custom_target(gen_fif_${ID} DEPENDS ${ARG_DEST_FIF} ${ARG_DEST_CPP}) add_dependencies(gen_fif gen_fif_${ID}) endfunction() - GenFif(DEST smartcont/config-code.fif SOURCE smartcont/config-code.fc) - GenFif(DEST smartcont/wallet-code.fif SOURCE smartcont/wallet-code.fc) - GenFif(DEST smartcont/simple-wallet-code.fif SOURCE smartcont/simple-wallet-code.fc) - GenFif(DEST smartcont/highload-wallet-code.fif SOURCE smartcont/highload-wallet-code.fc) - GenFif(DEST smartcont/highload-wallet-v2-code.fif SOURCE smartcont/highload-wallet-v2-code.fc) - GenFif(DEST smartcont/elector-code.fif SOURCE smartcont/elector-code.fc) + GenFif(DEST smartcont/auto/config-code SOURCE smartcont/config-code.fc NAME config) + GenFif(DEST smartcont/auto/wallet-code SOURCE smartcont/wallet-code.fc NAME wallet) + GenFif(DEST smartcont/auto/simple-wallet-code SOURCE smartcont/simple-wallet-code.fc NAME simple-wallet) + GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet) + GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2) + GenFif(DEST smartcont/auto/elector-code SOURCE smartcont/elector-code.fc NAME elector-code) + GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet) + GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2) endif() add_executable(create-state block/create-state.cpp) diff --git a/crypto/block/block.h b/crypto/block/block.h index 604036d044..fd9a724f26 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -26,6 +26,7 @@ #include #include "tl/tlblib.hpp" #include "td/utils/bits.h" +#include "td/utils/CancellationToken.h" #include "td/utils/StringBuilder.h" #include "ton/ton-types.h" @@ -162,12 +163,12 @@ struct MsgProcessedUpto { MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash) : shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) { } - bool operator<(const MsgProcessedUpto& other) const & { + bool operator<(const MsgProcessedUpto& other) const& { return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno); } - bool contains(const MsgProcessedUpto& other) const &; + bool contains(const MsgProcessedUpto& other) const&; bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash, - ton::BlockSeqno other_mc_seqno) const &; + ton::BlockSeqno other_mc_seqno) const&; // NB: this is for checking whether we have already imported an internal message bool already_processed(const EnqueuedMsgDescr& msg) const; }; @@ -573,7 +574,7 @@ struct BlockProofChain { bool last_link_incomplete() const { return !links.empty() && last_link().incomplete(); } - td::Status validate(); + td::Status validate(td::CancellationToken cancellation_token = {}); }; int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard); diff --git a/crypto/block/check-proof.cpp b/crypto/block/check-proof.cpp index c459b1b458..98d2c1cf23 100644 --- a/crypto/block/check-proof.cpp +++ b/crypto/block/check-proof.cpp @@ -424,7 +424,7 @@ td::Status BlockProofLink::validate(td::uint32* save_utime) const { } } -td::Status BlockProofChain::validate() { +td::Status BlockProofChain::validate(td::CancellationToken cancellation_token) { valid = false; has_key_block = false; has_utime = false; @@ -450,6 +450,9 @@ td::Status BlockProofChain::validate() { << link.from.to_str() << " but the previous link ends at different block " << cur.to_str()); } + if (cancellation_token) { + return td::Status::Error("Cancelled"); + } auto err = link.validate(&last_utime); if (err.is_error()) { return td::Status::Error(PSTRING() << "link #" << i << " in BlockProofChain is invalid: " << err.to_string()); diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 72b5d06bb2..6854717378 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -975,6 +975,8 @@ x{F82} @Defop(4u) GETPARAM x{F823} @Defop NOW x{F824} @Defop BLOCKLT x{F825} @Defop LTIME +x{F826} @Defop BALANCE +x{F827} @Defop RANDSEED x{F828} @Defop MYADDR x{F829} @Defop CONFIGROOT x{F830} @Defop CONFIGDICT diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif index cb2fb89718..44f1ff1cfd 100644 --- a/crypto/fift/lib/TonUtil.fif +++ b/crypto/fift/lib/TonUtil.fif @@ -136,3 +136,15 @@ recursive append-long-bytes { // S -- c { } : simple-transfer-body + +// ( S -- x ) parse public key +{ dup $len 48 <> abort"public key must be 48 characters long" + base64>B dup Blen 36 <> abort"public key must be 48 characters long" + 34 B| 16 B>u@ over crc16 <> abort"crc16 mismatch in public key" + 16 B>u@+ 0x3ee6 <> abort"invalid tag in public key" + 256 B>u@ +} : parse-pubkey +{ bl word parse-pubkey 1 'nop } ::_ PK' +// ( x -- S ) serialize public key +{ 256 u>B B{3ee6} swap B+ dup crc16 16 u>B B+ B>base64 } : pubkey>$ +{ pubkey>$ type } : .pubkey diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 428914c589..2c1a3083d6 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -614,6 +614,43 @@ void interpret_str_reverse(vm::Stack& stack) { stack.push_string(std::move(s)); } +void interpret_utf8_str_len(vm::Stack& stack) { + std::string s = stack.pop_string(); + long long cnt = 0; + for (char c : s) { + if ((c & 0xc0) != 0x80) { + cnt++; + } + } + stack.push_smallint(cnt); +} + +void interpret_utf8_str_split(vm::Stack& stack) { + stack.check_underflow(2); + unsigned c = stack.pop_smallint_range(0xffff); + std::string s = stack.pop_string(); + if (c > s.size()) { + throw IntError{"not enough utf8 characters for cutting"}; + } + auto it = s.begin(); + for (; it < s.end(); ++it) { + if ((*it & 0xc0) != 0x80) { + if (!c) { + stack.push_string(std::string{s.begin(), it}); + stack.push_string(std::string{it, s.end()}); + return; + } + --c; + } + } + if (!c) { + stack.push_string(std::move(s)); + stack.push_string(std::string{}); + } else { + throw IntError{"not enough utf8 characters for cutting"}; + } +} + void interpret_str_remove_trailing_int(vm::Stack& stack, int arg) { char x = (char)(arg ? arg : stack.pop_long_range(127)); std::string s = stack.pop_string(); @@ -1797,6 +1834,16 @@ void interpret_char(IntCtx& ctx) { push_argcount(ctx, 1); } +void interpret_char_internal(vm::Stack& stack) { + auto s = stack.pop_string(); + int len = (s.size() < 10 ? (int)s.size() : 10); + int code = str_utf8_code(s.c_str(), len); + if (code < 0 || s.size() != (unsigned)len) { + throw IntError{"exactly one character expected"}; + } + stack.push_smallint(code); +} + int parse_number(std::string s, td::RefInt256& num, td::RefInt256& denom, bool allow_frac = true, bool throw_error = false) { if (allow_frac) { @@ -2496,6 +2543,7 @@ void init_words_common(Dictionary& d) { // char/string manipulation d.def_active_word("\"", interpret_quote_str); d.def_active_word("char ", interpret_char); + d.def_stack_word("(char) ", interpret_char_internal); d.def_ctx_word("emit ", interpret_emit); d.def_ctx_word("space ", std::bind(interpret_emit_const, _1, ' ')); d.def_ctx_word("cr ", std::bind(interpret_emit_const, _1, '\n')); @@ -2515,6 +2563,8 @@ void init_words_common(Dictionary& d) { d.def_stack_word("-trailing0 ", std::bind(interpret_str_remove_trailing_int, _1, '0')); d.def_stack_word("$len ", interpret_str_len); d.def_stack_word("Blen ", interpret_bytes_len); + d.def_stack_word("$Len ", interpret_utf8_str_len); + d.def_stack_word("$Split ", interpret_utf8_str_split); d.def_ctx_word("Bx. ", std::bind(interpret_bytes_hex_print_raw, _1, true)); d.def_stack_word("B>X ", std::bind(interpret_bytes_to_hex, _1, true)); d.def_stack_word("B>x ", std::bind(interpret_bytes_to_hex, _1, false)); diff --git a/crypto/func/abscode.cpp b/crypto/func/abscode.cpp index 1bc4f0d9db..8ff204f13c 100644 --- a/crypto/func/abscode.cpp +++ b/crypto/func/abscode.cpp @@ -205,6 +205,9 @@ void VarDescr::clear_value() { } void VarDescrList::show(std::ostream& os) const { + if (unreachable) { + os << " "; + } os << "["; for (const auto& v : list) { os << ' ' << v; diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index 4d2e790298..a80bc74e23 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -272,6 +272,12 @@ VarDescrList& VarDescrList::operator+=(const VarDescrList& y) { } VarDescrList VarDescrList::operator|(const VarDescrList& y) const { + if (y.unreachable) { + return *this; + } + if (unreachable) { + return y; + } VarDescrList res; auto it1 = list.cbegin(); auto it2 = y.list.cbegin(); @@ -289,7 +295,11 @@ VarDescrList VarDescrList::operator|(const VarDescrList& y) const { } VarDescrList& VarDescrList::operator|=(const VarDescrList& y) { - return *this = *this | y; + if (y.unreachable) { + return *this; + } else { + return *this = *this | y; + } } VarDescrList& VarDescrList::operator&=(const VarDescrList& values) { @@ -299,16 +309,22 @@ VarDescrList& VarDescrList::operator&=(const VarDescrList& values) { *item &= vd; } } + unreachable |= values.unreachable; return *this; } VarDescrList& VarDescrList::import_values(const VarDescrList& values) { - for (const VarDescr& vd : values.list) { - VarDescr* item = operator[](vd.idx); - if (item) { - item->set_value(vd); + if (values.unreachable) { + set_unreachable(); + } else + for (auto& vd : list) { + auto new_vd = values[vd.idx]; + if (new_vd) { + vd.set_value(*new_vd); + } else { + vd.clear_value(); + } } - } return *this; } @@ -658,8 +674,11 @@ void Op::prepare_args(VarDescrList values) { const VarDescr* val = values[right[i]]; if (val) { args[i].set_value(*val); - args[i].clear_unused(); + // args[i].clear_unused(); + } else { + args[i].clear_value(); } + args[i].clear_unused(); } } @@ -670,7 +689,7 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { case _Import: break; case _Return: - values.list.clear(); + values.set_unreachable(); break; case _IntConst: { values.add_newval(left[0]).set_const(int_const); diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 5010a2564b..f9434b5b80 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -668,6 +668,7 @@ AsmOp compile_cmp_int(std::vector& res, std::vector& args, i return mode == 7 ? push_const(r.int_const) : AsmOp::BoolConst(v != 0); } int v = compute_compare(x, y, mode); + // std::cerr << "compute_compare(" << x << ", " << y << ", " << mode << ") = " << v << std::endl; assert(v); if (!(v & (v - 1))) { r.set_const(v - (v >> 2) - 2); @@ -685,6 +686,7 @@ AsmOp compile_cmp_int(std::vector& res, std::vector& args, i if (v & 4) { r.val &= VarDescr::ConstOne; } + // std::cerr << "result: " << r << std::endl; static const char* cmp_int_names[] = {"", "GTINT", "EQINT", "GTINT", "LESSINT", "NEQINT", "LESSINT"}; static const char* cmp_names[] = {"", "GREATER", "EQUAL", "GEQ", "LESS", "NEQ", "LEQ", "CMP"}; static int cmp_int_delta[] = {0, 0, 0, -1, 0, 0, 1}; @@ -911,6 +913,7 @@ void define_builtins() { define_builtin_func("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false)); // define_builtin_func("null", Null, AsmOp::Const("PUSHNULL")); define_builtin_func("nil", Tuple, AsmOp::Const("PUSHNULL")); + define_builtin_func("Nil", Tuple, AsmOp::Const("NIL")); define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null); define_builtin_func("cons", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor(X, Tuple), Tuple)), AsmOp::Custom("CONS", 2, 1)); @@ -932,6 +935,10 @@ void define_builtins() { AsmOp::Custom("4 TUPLE", 4, 1)); define_builtin_func("untuple4", TypeExpr::new_forall({X, Y, Z, T}, TypeExpr::new_map(Tuple, XYZT)), AsmOp::Custom("4 UNTUPLE", 1, 4)); + define_builtin_func("first", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("FIRST", 1, 1)); + define_builtin_func("second", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("SECOND", 1, 1)); + define_builtin_func("third", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("THIRD", 1, 1)); + define_builtin_func("fourth", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("3 INDEX", 1, 1)); define_builtin_func("throw", impure_un_op, compile_throw, true); define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); define_builtin_func("throw_unless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true); @@ -947,7 +954,7 @@ void define_builtins() { define_builtin_func("preload_bits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false)); define_builtin_func("int_at", TypeExpr::new_map(TupleInt, Int), compile_tuple_at); define_builtin_func("cell_at", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); - define_builtin_func("slice_at", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); + define_builtin_func("slice_at", TypeExpr::new_map(TupleInt, Slice), compile_tuple_at); define_builtin_func("tuple_at", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at); define_builtin_func("at", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at); define_builtin_func("touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, X)), AsmOp::Nop()); diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index 1cd7bb5a1d..2b98c332bd 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -60,21 +60,36 @@ void generate_output_func(SymDef* func_sym) { code.print(std::cerr, 9); } code.simplify_var_types(); - // std::cerr << "after simplify_var_types: \n"; code.print(std::cerr, 0); + if (verbosity >= 5) { + std::cerr << "after simplify_var_types: \n"; + code.print(std::cerr, 0); + } code.prune_unreachable_code(); - // std::cerr << "after prune_unreachable: \n"; code.print(std::cerr, 0); + if (verbosity >= 5) { + std::cerr << "after prune_unreachable: \n"; + code.print(std::cerr, 0); + } code.split_vars(true); - // std::cerr << "after split_vars: \n"; code.print(std::cerr, 0); + if (verbosity >= 5) { + std::cerr << "after split_vars: \n"; + code.print(std::cerr, 0); + } for (int i = 0; i < 8; i++) { code.compute_used_code_vars(); - if (verbosity >= 5) { + if (verbosity >= 4) { std::cerr << "after compute_used_vars: \n"; code.print(std::cerr, 6); } code.fwd_analyze(); - // std::cerr << "after fwd_analyze: \n"; code.print(std::cerr, 6); + if (verbosity >= 5) { + std::cerr << "after fwd_analyze: \n"; + code.print(std::cerr, 6); + } code.prune_unreachable_code(); - // std::cerr << "after prune_unreachable: \n"; code.print(std::cerr, 6); + if (verbosity >= 5) { + std::cerr << "after prune_unreachable: \n"; + code.print(std::cerr, 6); + } } code.mark_noreturn(); if (verbosity >= 3) { diff --git a/crypto/func/func.h b/crypto/func/func.h index fe7b707cf3..77d1efd7b3 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -397,6 +397,7 @@ inline std::ostream& operator<<(std::ostream& os, const VarDescr& vd) { struct VarDescrList { std::vector list; + bool unreachable{false}; VarDescrList() : list() { } VarDescrList(const std::vector& _list) : list(_list) { @@ -430,6 +431,10 @@ struct VarDescrList { VarDescrList operator|(const VarDescrList& y) const; VarDescrList& operator|=(const VarDescrList& values); void show(std::ostream& os) const; + void set_unreachable() { + list.clear(); + unreachable = true; + } }; inline std::ostream& operator<<(std::ostream& os, const VarDescrList& values) { diff --git a/crypto/func/test/w3.fc b/crypto/func/test/w3.fc new file mode 100644 index 0000000000..0de8e551f6 --- /dev/null +++ b/crypto/func/test/w3.fc @@ -0,0 +1,13 @@ +() main(cs) impure { + int i = 0; + if (cs.slice_refs()) { + do { + i = i + 1; + } until(i > 10); + } + set_data(begin_cell() + .store_uint(1, 32) + .store_uint(2, 32) + .store_uint(i, 32) + .end_cell()); +} diff --git a/crypto/func/test/w4.fc b/crypto/func/test/w4.fc new file mode 100644 index 0000000000..1c0e2cefe1 --- /dev/null +++ b/crypto/func/test/w4.fc @@ -0,0 +1,13 @@ +_ main(cell dict, int t3) { + int index = -1; + do { + (index, slice value, int found) = dict.udict_get_next?(32, index); + if (found) { + (int temp1, int temp2) = (value~load_uint(16), value~load_uint(32)); + if (t3 > temp2) { + dict~udict_delete_get?(32, index); + } + } + } until ( ~ found); + return dict; +} diff --git a/crypto/func/test/w5.fc b/crypto/func/test/w5.fc new file mode 100644 index 0000000000..330a505042 --- /dev/null +++ b/crypto/func/test/w5.fc @@ -0,0 +1,14 @@ +(cell) recv_external(slice in_msg) impure { + cell mydict = new_dict(); + builder r = begin_cell().store_int(10, 16); + mydict~udict_set_builder(32, 1, r ); + int index = -1; + do { + (var index, var value, var found) = mydict.udict_get_next?(32, index); + } until ( ~ found ); + return mydict; +} + +() recv_internal() impure { + ;; Nothing +} diff --git a/crypto/func/test/w6.fc b/crypto/func/test/w6.fc new file mode 100644 index 0000000000..37251a58e8 --- /dev/null +++ b/crypto/func/test/w6.fc @@ -0,0 +1,12 @@ +int test(int x) method_id { + int i = 0; + ;; int f = false; + do { + i = i + 1; + if (i > 5) { + return 1; + } + int f = (i * i == 64); + } until (f); + return -1; +} diff --git a/crypto/func/test/w7.fc b/crypto/func/test/w7.fc new file mode 100644 index 0000000000..dddfd627b2 --- /dev/null +++ b/crypto/func/test/w7.fc @@ -0,0 +1,14 @@ +int test(int y) { + int x = 1; + if (y > 0) { + return 1; + } + return x > 0; +} + +int f(int y) { + if (y > 0) { + return 1; + } + return 2; +} diff --git a/crypto/smartcont/CreateState.fif b/crypto/smartcont/CreateState.fif index d701ecdc69..d3c05c00c9 100644 --- a/crypto/smartcont/CreateState.fif +++ b/crypto/smartcont/CreateState.fif @@ -182,3 +182,48 @@ dictnew constant special-dict -2 constant run_ticktock -3 constant split_prepare -4 constant split_install + +// public key conversion + + +// restricted wallet creation +"auto/restricted-wallet-code.fif" include =: RWCode1 +"auto/restricted-wallet2-code.fif" include =: RWCode2 + +// pubkey amount -- +{ over ."Key " pubkey>$ type ." -> " + RWCode1 // code + // data + empty_cell // libs + 3 roll // balance + 0 // split_depth + 0 // ticktock + 2 // mode: create + register_smc + Masterchain 6 .Addr cr +} : create-wallet1 + +// D x t -- D' +{ idict! not abort"cannot add value" +} : rdict-entry +// balance -- dict +{ dictnew + over -32768 rdict-entry + over 3/4 */ 92 rdict-entry + over 1/2 */ 183 rdict-entry + swap 1/4 */ 366 rdict-entry + 0 548 rdict-entry +} : make-rdict + +// pubkey amount -- +{ over ."Key " pubkey>$ type ." -> " + RWCode2 // code + // data + empty_cell // libs + 3 roll // balance + 0 // split_depth + 0 // ticktock + 2 // mode: create + register_smc + Masterchain 6 .Addr cr +} : create-wallet2 diff --git a/crypto/smartcont/asm-to-cpp.fif b/crypto/smartcont/asm-to-cpp.fif new file mode 100755 index 0000000000..21ff34bb2d --- /dev/null +++ b/crypto/smartcont/asm-to-cpp.fif @@ -0,0 +1,31 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." " cr + ."Compiles asm from file and stores serialization in a way prepared for usage from c++ to " cr 1 halt +} : usage + + +$# 3 - ' usage if + + +$1 =: source +$2 =: destination +$3 =: name + +."Include source from " source type cr +source include + +boc>B +."Convert boc to base64" cr +B>base64 + +{ char " chr tuck $+ $+ } : wrp +wrp // "" +"with_tvm_code(" name wrp $+ +", " swap ");" 10 hold $+ $+ // "with_tm_code("");\n" + +$>B + +."Store tvm code to " destination type cr +destination B>file diff --git a/crypto/smartcont/auto/config-code.cpp b/crypto/smartcont/auto/config-code.cpp new file mode 100644 index 0000000000..4b04b77b54 --- /dev/null +++ b/crypto/smartcont/auto/config-code.cpp @@ -0,0 +1 @@ +with_tvm_code("config", "te6ccgECEwEAAhQAART/APSkE/Sg8sgLAQIBIAIDAgLPBAUCAvEPEAIBIAYHAgEgDA0CASAICQIBIAoLAOkAdDTA/pAMCD6RAGkA3GwE7EjxwCxkl8D4ALTH9M/IoIQTlZTVLqOPTIB1NFx+DMgbpIwf5TQ1wv/4gNwBLqbMiHwAiH4I7wCvLCRAuKdgCRY8AGCEO52T0vwBOAxghDudk9v8AXgECRfBCDAAAGDHrCx8qWAAJTtRNDUUDOAIPQVyMwBzxbJ7VSAAMzQ0wcBwBHyidMf0x/TD9MPMCDCAPKJvvKJgACscIAYyMsFUAXPFhTLbssfyz/JAfsAgAgEgDg4AW0cfgz0NcL//gjghBOQ29kcIIAxP/IyxAUy/+DHfoCE8tqEssfyz8BzxbJcPsAgACSAQPADgAK0MO1E0NSAIIAkUzH0aiBukxAjW444INAg10nCJ44p0wfXCx8BwBH4IxK+sI4VAYAiVBBE9G5UUyH0bjCAJEAT9FowkxAjW+KUEDRfA+LiyMwBzxbJ7VSABASARAf6DCNcYINMf0x/TH/gjErnyY+1E0NTTH9P/0VFBuvKhBfkBVBBj+RDyovgAIoIQQ2ZQIbqcMgHTH9TRQBSAIPQVjjcighBOQ29kupYyAdTR+wSOJCKCEFBiSyG6lTEx0//RjhMighBOQ+8FupQyAfAGlDAB8mDi4uIC4gGkyBLMEgAOyx/L/8ntVA=="); diff --git a/crypto/smartcont/auto/config-code.fif b/crypto/smartcont/auto/config-code.fif new file mode 100644 index 0000000000..1a4084fafe --- /dev/null +++ b/crypto/smartcont/auto/config-code.fif @@ -0,0 +1,323 @@ +// automatically generated from `smartcont/stdlib.fc` `smartcont/config-code.fc` +PROGRAM{ + DECLPROC set_conf_param + DECLPROC check_validator_set + DECLPROC send_answer + DECLPROC send_confirmation + DECLPROC send_error + DECLPROC recv_internal + DECLPROC change_elector_code + DECLPROC recv_external + DECLPROC run_ticktock + set_conf_param PROC:<{ + // index value + c4 PUSH // index value _3 + CTOS // index value cs + LDREF // index value cfg_dict cs + s3 s3 XCHG2 + 32 PUSHINT // cs value index cfg_dict _9=32 + DICTISETREF // cs cfg_dict + NEWC // cs cfg_dict _11 + STREF // cs _12 + SWAP // _12 cs + STSLICER // _13 + ENDC // _14 + c4 POP + }> + check_validator_set PROC:<{ + // vset + CTOS // cs + 8 LDU // _4 cs + SWAP // cs _4 + 17 EQINT // cs _8 + 9 THROWIFNOT + 32 LDU // utime_since cs + 32 LDU // utime_since utime_until cs + 16 LDU // utime_since utime_until total cs + 16 LDU // utime_since utime_until total _42 _41 + DROP // utime_since utime_until total main + DUP // utime_since utime_until total main main + 0 GTINT // utime_since utime_until total main _28 + 9 THROWIFNOT + GEQ // utime_since utime_until _31 + 9 THROWIFNOT + }> + send_answer PROC:<{ + // addr query_id ans_tag mode + 0 PUSHINT // addr query_id ans_tag mode _4=0 + 24 PUSHINT // addr query_id ans_tag mode _4=0 _5=24 + NEWC // addr query_id ans_tag mode _4=0 _5=24 _6 + 6 STU // addr query_id ans_tag mode _4=0 _8 + s0 s5 XCHG2 // _4=0 query_id ans_tag mode _8 addr + STSLICER // _4=0 query_id ans_tag mode _9 + s1 s4 XCHG // mode query_id ans_tag _4=0 _9 + 111 STU // mode query_id ans_tag _23 + 32 STU // mode query_id _25 + 64 STU // mode _27 + ENDC // mode _28 + SWAP // _28 mode + SENDRAWMSG + }> + send_confirmation PROC:<{ + // addr query_id ans_tag + 64 PUSHINT // addr query_id ans_tag _3=64 + send_answer CALLDICT + }> + send_error PROC:<{ + // addr query_id ans_tag + 64 PUSHINT // addr query_id ans_tag _3=64 + send_answer CALLDICT + }> + recv_internal PROC:<{ + // in_msg_cell in_msg + SWAP // in_msg in_msg_cell + CTOS // in_msg cs + 4 LDU // in_msg flags cs + LDMSGADDR // in_msg flags _74 _73 + DROP // in_msg flags s_addr + DUP // in_msg flags s_addr s_addr + REWRITESTDADDR // in_msg flags s_addr src_wc src_addr + SWAP // in_msg flags s_addr src_addr src_wc + INC // in_msg flags s_addr src_addr _15 + s0 s3 XCHG + 1 PUSHINT // in_msg _15 s_addr src_addr flags _16=1 + AND // in_msg _15 s_addr src_addr _17 + s1 s3 XCHG // in_msg src_addr s_addr _15 _17 + OR // in_msg src_addr s_addr _18 + s3 PUSH // in_msg src_addr s_addr _18 in_msg + SEMPTY // in_msg src_addr s_addr _18 _19 + OR // in_msg src_addr s_addr _20 + IFJMP:<{ // in_msg src_addr s_addr + 3 BLKDROP // + }> // in_msg src_addr s_addr + s0 s2 XCHG // s_addr src_addr in_msg + 32 LDU // s_addr src_addr tag in_msg + 64 LDU // s_addr src_addr tag query_id in_msg + s2 PUSH + 1314280276 PUSHINT // s_addr src_addr tag query_id in_msg tag _29=1314280276 + EQUAL // s_addr src_addr tag query_id in_msg _30 + IFJMP:<{ // s_addr src_addr tag query_id in_msg + s2 POP // s_addr src_addr in_msg query_id + SWAP // s_addr src_addr query_id in_msg + LDREF // s_addr src_addr query_id vset in_msg + ENDS + 1 PUSHINT // s_addr src_addr query_id vset _36=1 + CONFIGOPTPARAM // s_addr src_addr query_id vset elector_param + DUP // s_addr src_addr query_id vset elector_param elector_param + ISNULL // s_addr src_addr query_id vset elector_param _39 + IF:<{ // s_addr src_addr query_id vset elector_param + DROP // s_addr src_addr query_id vset + -1 PUSHINT // s_addr src_addr query_id vset _40=-1 + }>ELSE<{ // s_addr src_addr query_id vset elector_param + CTOS // s_addr src_addr query_id vset _42 + 256 PLDU // s_addr src_addr query_id vset _40 + }> // s_addr src_addr query_id vset elector_addr + s0 s3 XCHG + FALSE + s0 s4 XCHG // s_addr ok query_id vset src_addr elector_addr + EQUAL // s_addr ok query_id vset _47 + IF:<{ // s_addr ok query_id vset + s2 POP // s_addr vset query_id + OVER // s_addr vset query_id vset + check_validator_set CALLDICT // s_addr vset query_id t_since t_until + OVER + NOW // s_addr vset query_id t_since t_until t_since t + GREATER // s_addr vset query_id t_since t_until _53 + s0 s2 XCHG // s_addr vset query_id _53 t_until t_since + GREATER // s_addr vset query_id _53 _54 + AND // s_addr vset query_id ok + }>ELSE<{ + s0 s2 XCHG // s_addr vset query_id ok + }> + IFJMP:<{ // s_addr vset query_id + 36 PUSHINT // s_addr vset query_id _56=36 + ROT // s_addr query_id _56=36 vset + set_conf_param CALLDICT + 4000730955 PUSHINT // s_addr query_id _58=4000730955 + send_confirmation CALLDICT + }> // s_addr vset query_id + NIP // s_addr query_id + 4000730991 PUSHINT // s_addr query_id _60=4000730991 + send_error CALLDICT + }> // s_addr src_addr tag query_id in_msg + s2 s4 XCHG + 4 BLKDROP // tag + DUP // tag tag + 0 EQINT // tag _64 + SWAP + 31 PUSHPOW2 // _64 tag _67 + AND // _64 _68 + OR // _69 + 37 THROWIFNOT + }> + change_elector_code PROC:<{ + // cs + 1 PUSHINT // cs _2=1 + CONFIGOPTPARAM // cs _3 + CTOS // cs _4 + 256 PLDU // cs dest_addr + NOW // cs dest_addr query_id + 1313042276 PUSHINT // cs dest_addr query_id _9=1313042276 + 0 PUSHINT // cs dest_addr query_id _9=1313042276 _10=0 + 50431 PUSHINT // cs dest_addr query_id _9=1313042276 _10=0 _11=50431 + NEWC // cs dest_addr query_id _9=1313042276 _10=0 _11=50431 _12 + 17 STU // cs dest_addr query_id _9=1313042276 _10=0 _14 + s1 s4 XCHG // cs _10=0 query_id _9=1313042276 dest_addr _14 + 256 STU // cs _10=0 query_id _9=1313042276 _16 + 30 PUSHPOW2 // cs _10=0 query_id _9=1313042276 _16 _19 + STGRAMS // cs _10=0 query_id _9=1313042276 _20 + s1 s3 XCHG // cs _9=1313042276 query_id _10=0 _20 + 107 STU // cs _9=1313042276 query_id _34 + s1 s2 XCHG // cs query_id _9=1313042276 _34 + 32 STU // cs query_id _36 + 64 STU // cs _38 + SWAP // _38 cs + STSLICER // _39 + ENDC // _40 + 0 PUSHINT // _40 _41=0 + SENDRAWMSG + }> + recv_external PROC:<{ + // in_msg + 9 PUSHPOW2 // in_msg _3=512 + LDSLICEX // signature in_msg + DUP // signature in_msg cs + 32 LDU // signature in_msg action cs + 32 LDU // signature in_msg action msg_seqno cs + 32 LDU // signature in_msg action msg_seqno valid_until cs + NOW // signature in_msg action msg_seqno valid_until cs _19 + s1 s2 XCHG // signature in_msg action msg_seqno cs valid_until _19 + LESS // signature in_msg action msg_seqno cs _20 + 35 THROWIF + c4 PUSH // signature in_msg action msg_seqno cs _23 + CTOS // signature in_msg action msg_seqno cs cs2 + LDREF // signature in_msg action msg_seqno cs cfg_dict cs2 + 32 LDU // signature in_msg action msg_seqno cs cfg_dict stored_seqno cs2 + 256 LDU // signature in_msg action msg_seqno cs cfg_dict stored_seqno public_key cs2 + ENDS + s4 s1 XCPU // signature in_msg action public_key cs cfg_dict stored_seqno msg_seqno stored_seqno + EQUAL // signature in_msg action public_key cs cfg_dict stored_seqno _38 + 33 THROWIFNOT + s0 s5 XCHG // signature stored_seqno action public_key cs cfg_dict in_msg + HASHSU // signature stored_seqno action public_key cs cfg_dict _41 + s0 s6 s3 XC2PU // cfg_dict stored_seqno action public_key cs _41 signature public_key + CHKSIGNU // cfg_dict stored_seqno action public_key cs _42 + 34 THROWIFNOT + ACCEPT + s2 PUSH + 1130778657 PUSHINT // cfg_dict stored_seqno action public_key cs action _45=1130778657 + EQUAL // cfg_dict stored_seqno action public_key cs _46 + IF:<{ // cfg_dict stored_seqno action public_key cs + s2 POP // cfg_dict stored_seqno cs public_key + SWAP // cfg_dict stored_seqno public_key cs + 32 LDU // cfg_dict stored_seqno public_key param_index cs + LDREF // cfg_dict stored_seqno public_key param_index param_value cs + ENDS + s0 s1 s4 XCHG3 + 32 PUSHINT // public_key stored_seqno param_value param_index cfg_dict _56=32 + DICTISETREF // public_key stored_seqno cfg_dict + }>ELSE<{ // cfg_dict stored_seqno action public_key cs + s2 PUSH + 1313042276 PUSHINT // cfg_dict stored_seqno action public_key cs action _58=1313042276 + EQUAL // cfg_dict stored_seqno action public_key cs _59 + IF:<{ // cfg_dict stored_seqno action public_key cs + s2 POP // cfg_dict stored_seqno cs public_key + SWAP // cfg_dict stored_seqno public_key cs + LDREF // cfg_dict stored_seqno public_key new_code cs + ENDS + SETCODE + }>ELSE<{ // cfg_dict stored_seqno action public_key cs + s2 PUSH + 1348619041 PUSHINT // cfg_dict stored_seqno action public_key cs action _65=1348619041 + EQUAL // cfg_dict stored_seqno action public_key cs _66 + IF:<{ // cfg_dict stored_seqno action public_key cs + NIP + NIP // cfg_dict stored_seqno cs + 256 LDU // cfg_dict stored_seqno public_key cs + ENDS + }>ELSE<{ // cfg_dict stored_seqno action public_key cs + s2 PUSH + 1313074949 PUSHINT // cfg_dict stored_seqno action public_key cs action _71=1313074949 + EQUAL // cfg_dict stored_seqno action public_key cs _72 + IF:<{ // cfg_dict stored_seqno action public_key cs + s2 POP // cfg_dict stored_seqno cs public_key + SWAP // cfg_dict stored_seqno public_key cs + change_elector_code CALLDICT + }>ELSE<{ // cfg_dict stored_seqno action public_key cs + DROP // cfg_dict stored_seqno action public_key + SWAP // cfg_dict stored_seqno public_key action + 32 THROWIF + }> + }> + }> + s0 s2 XCHG // public_key stored_seqno cfg_dict + }> + SWAP // public_key cfg_dict stored_seqno + INC // public_key cfg_dict _77 + NEWC // public_key cfg_dict _77 _78 + s1 s2 XCHG // public_key _77 cfg_dict _78 + STREF // public_key _77 _79 + 32 STU // public_key _81 + 256 STU // _83 + ENDC // _84 + c4 POP + }> + run_ticktock PROC:<{ + // is_tock + DROP // + c4 PUSH // _2 + CTOS // cs + LDREF // cfg_dict cs + 32 PUSHINT // cfg_dict cs kl=32 + 36 PUSHINT // cfg_dict cs kl=32 _10=36 + s3 s1 PUSH2 // cfg_dict cs kl=32 _10=36 cfg_dict kl=32 + DICTIGETOPTREF // cfg_dict cs kl=32 next_vset + DUP // cfg_dict cs kl=32 next_vset next_vset + ISNULL // cfg_dict cs kl=32 next_vset _12 + IFNOT:<{ // cfg_dict cs kl=32 next_vset + DUP // cfg_dict cs kl=32 next_vset next_vset + CTOS // cfg_dict cs kl=32 next_vset ds + DUP // cfg_dict cs kl=32 next_vset ds ds + SBITS // cfg_dict cs kl=32 next_vset ds _15 + 39 GTINT // cfg_dict cs kl=32 next_vset ds _17 + IF:<{ // cfg_dict cs kl=32 next_vset ds + 8 LDU // cfg_dict cs kl=32 next_vset tag ds + 32 PLDU // cfg_dict cs kl=32 next_vset tag since + SWAP // cfg_dict cs kl=32 next_vset since tag + 17 EQINT // cfg_dict cs kl=32 next_vset since _26 + NOW // cfg_dict cs kl=32 next_vset since _26 _27 + s1 s2 XCHG // cfg_dict cs kl=32 next_vset _26 since _27 + GEQ // cfg_dict cs kl=32 next_vset _26 _28 + AND // cfg_dict cs kl=32 next_vset _29 + IF:<{ // cfg_dict cs kl=32 next_vset + SWAP + 34 PUSHINT + s0 s4 s4 XC2PU // kl=32 cs next_vset _32=34 cfg_dict kl=32 + DICTISETGETOPTREF // kl=32 cs cfg_dict cur_vset + s3 s1 s0 PUXCPU // kl=32 cs cur_vset _35=32 cfg_dict kl=32 + DICTISETGETOPTREF // kl=32 cs _51 _52 + DROP // kl=32 cs cfg_dict + 36 PUSHINT // kl=32 cs cfg_dict _38=36 + s0 s1 s3 XCHG3 // cs _38=36 cfg_dict kl=32 + DICTIDEL // cs _53 _54 + DROP // cs cfg_dict + }>ELSE<{ + s2 s3 XCHG + 2DROP // cs cfg_dict + }> + }>ELSE<{ + s3 s4 XCHG + 3 BLKDROP // cs cfg_dict + }> + }>ELSE<{ + s2 s3 XCHG + 2DROP // cs cfg_dict + }> + NEWC // cs cfg_dict _40 + STREF // cs _41 + SWAP // _41 cs + STSLICER // _42 + ENDC // _43 + c4 POP + }> +}END>c diff --git a/crypto/smartcont/auto/elector-code.cpp b/crypto/smartcont/auto/elector-code.cpp new file mode 100644 index 0000000000..eb520f79ef --- /dev/null +++ b/crypto/smartcont/auto/elector-code.cpp @@ -0,0 +1 @@ +with_tvm_code("elector-code", "te6ccgECVAEACrkAART/APSkE/Sg8sgLAQIBIAIDAgFIBAUAUaX//xh2omh6AnoCETdKrPgN+SBOKjgQ+At5ICz4DPkgcXgL+SB4DRhAAgLFBgcCASAKCwIBywgJACyqgjGCEE5Db2SCEM5Db2RwQzCAQPAGAgEgEBECASAzNAIDeqAMDQIBbg4PACSqB+1E0PQFIG6SMHCU0NcLH+IAQqss7UTQ9AUgbpJbcODwAxAmXwaDB/QOb6GT+gAwkjBw4gBzsKV7UTQ9AUgbpIwbeDwAxAmXwZthP+OHiKDB/R+b6FvoSCcAvoAMFIQbwJQA28Cklow4gGzEuYwMYAAzs+A7UTQ9AQx9AQwgwf0Dm+hk/oAMJIwcOKACASASEwIBICMkAgEgFBUCASAdHgIBIBYXAgEgGxwBASAYACE7UTQ9AT0BPQE+gDTH9P/0YAL8AdDTAwFxsJJfA+D6QDAhxwCUMQHwEOAB0x8hwACUWwHwEODTPyKCEE5zdEu6lTIQI/AL4DQhghBHZXQkupMT8BHgIYIQTkNvZLqOGVRSRPASloIQzkNvZJKEH+JwEDRDMIBA8AbgIYIQ7nZPS7oighDudk9vulIQsYrgMDMgGRoADDIQIwHwDwAkgx6wm4QfcBA0QzCAQPAG4V8DACkyBb0ABT0ABL0AAH6Assfy//J7VSAAITQ0x/TH/oA+gD0BNIA0gDRgAgEgHyACASAhIgArMgXyx8Vyx9QA/oCAfoC9ADKAMoAyYAAdIAP+DPQ0h/SH9If1wofgAEUcIAYyMsFUAfPFlj6AhXLahPLH8s/IcL/kssfkTHiyQH7AIAAbIIQ7m9FTHBDMIBA8AaACASAlJgIBIC0uAgEgJygCASApKgAjIIQ83RITIIQO5rKAEMwcvAGgAEcghBOVlNUcIIAxP/IyxAVy/+DHfoCFMtqE8sfEss/zMlx+wCAAMxTEoMH9A5voZT6ADCgkTDiyAH6AgKDB/RDgAQEgKwH+I/pE7UTQ9AQhbgSkFLGXEDVfBXDwB+AE0//TH9Mf0//UAdCDCNcZAdGCEGVMUHTIyx9SQMsfUjDLH1Jgy/9SIMv/ydBRFfkRlxBoXwhx8AfhIYMPuZcQaF8IdvAH4AfwAzEF/iANghA7msoAoSCqCyO5mBC8XwwxcvAH4FEioCwA/lF9vZcQrF8Mc/AH4ASXEJtfC3DwB+BTY4MH9A5voSCfMPoAWaAB0z8x0/8wUoC9kTHilxCbXwt08AfgUwG5lxCbXwt18AfgIPKs+AD4I8hY+gLLHxTLHxbL/xjL/0A3gwf0Q3AQVxA0ECMQJnDwBMj0AAHPFsntVCCTcPAI4FsCASAvMAIBIDEyAHkcCB/jjAkgwf0fG+hb6EgjhwC0//TPzH6ANIA0ZQxUTOgl1QYiPAKBwPiUEOgk1BCMOIBsxPmMDMBuvK7gAKEcFMAf447JoMH9HxvoW+hII4nAtP/0z8x+gDSANGUMVEzoI4RVHcIqYRRZqBSF6BLsPAKCQPiUFOgk1BSMOIBsxTmMDUDulMhu7DyuxKgAaGAAVwBgCD0Zm+hkjBw4dM/MdP/MfQE+gD6APQEMdEgwgCUEDTwDZUwECPwDOISgAKMMgL6RHD4M9DXC//tRND0BASkWr2xIW6xkl8E4PADBV8FUhW9BLMUsZJfA+D4AAGRW44d9AT0BPoAQzTwDnDIygAT9AD0AFmg+gIBzxbJ7VTigAgEgNTYCAUhOTwIBIDc4AgEgPT4CASA5OgIBIDs8AKU8AEH+kQBpLEhwACxlwWgVRMB8ALgUwKAIPQOb6GOJ9M/0//0BPoA+gALoMgVyz8Ty//0AAH6AgH6AlAGzxYBgCBURkT0Q5QwBaAB4hA1QUPwAoADZDEh+kQBpJ8wghD////+cBA0E4BA8Abg7UTQ9AT0BFAzgwf0Zm+hjhBfBIIQ/////nAQNBOAQPAG4TYF+gDRyBL0ABX0AAHPFsntVIIQ+W9zJHCAGMjLBVAEzxZQBPoCEstqEssfyz/JgED7AIABpHD4MyBuk18EcODQ1wv/I/pEAaQCvbGTXwNw4PgAAdQh+wQgxwCSXwSZAe1TAfEGgvIA4n+AAAxwgAgEgP0ACASBJSgA1HACjhMCbyIhbxACbxEkqKsPErYIEqBY5DAxgAQEgQQP+gBD4M9DTD9MPMdMP0XG2CXBtf45GKYMH9HxvoW+hII4wAvoA0x/TH9P/0//RA6PIFct/FMofUkDL/8nQURq2CMjLHxPL/8v/QBSBAaD0QQOklBA0WjDiAbNDMOYwNFi2CFMBuZZfB21tcCDgbYrmMzSlXJJvEeRwIIrmMjI0NEJDRABqA4EBoPSSb6FvoSCOIAHTf1EZtggB0x8x1wv/A9Mf0/8x1wv/QTAUbwRQBW8Ck0YWW+IEsxQASgJvIgFvEASkU0i+jhBUZQbwFFMCvJMyMiKSEzDik1A0MOJTNr4BXiLAAFJDuRKxll8EbW1wIOAg/iAipZJvEeRvEG8QcCBtbYrmMzMzNVIUuvKxECMBRQL+BW8iAW8kUxyDB/QOb6HyvfoAMdM/MdcL/1OLuY5XUTmoqw9SQLYIUUShJKo7LakEUYWgghCOgSeKI5KAc5KAU+LIywfLH1JAy/9SkMs/I5MTy/+TUAMw4gGAEFRKmfRDcCTIy/8Zyz9QBfoCF8oAQBmDB/RDlgoQRgNfA+IjikZHAAhLc/AKARiK4gOkKW4QShBGREBIAAZLc1sBASBLAa08AGAIvgz+QBTAbqTXwdw4CKOL1MkgCD0Dm+hjh/THzEg0x/T/zBQBLryufgjUAOgyMsfWM8WQASAIPRDlBUTXwPik0QUW+J/iuYgbpIwcJDiUAPwAn+BNAf4B8AM0+CMluZNfCHDgcPgzbpRfCPAT4IAR+DPQ+gD6APoA0x/RU2G5lF8M8BPgBJRfC/AT4AaTXwpw4BA4KFEyUHfwFSDAACCzKgYQWhBJEDhMzPAEIo4TMjIyMjLIEvQA9AABzxbJ7VTwE+HwBTL4IwGgpsQotgmAEPgz0IAQTADI1yHXCw9SYLYIUxOggBHIywdSMMsfyx8Xyw8Wyw8J0BnPFslw+DPQ1wv/UxjwCQn0BHAL+QBQY6ApoMjLHxnLH8v/FfQAUAP6AnD6AhbKAEA0gCD0Q8gT9AD0APQAAc8Wye1UfwCkIYAg9HxvoW+hII4/AtM/0/9TF7qOLTQD9AT6APoAKKsCUZmhUCmgyBXLPxbL/xL0AAH6AgH6AljPFgGAIFRBRPRDcJYQNRA0XwPik0MDMOKzEgIBIFBRAgEgUlMAGQhbpJbcJUB+QABuuKAAlQB8AMFXwWTXwNw4QL0BFExgCD0Dm+hk18EcOGAQNch1wv/gCL4MyHwGIAk+DNY8BixjhNwyMoAEvQA9AABzxbJ7VTwFzB/4F8DcIACTPABf44+JIAg9HxvoW+hII4kAtMfMPgju1MUvbCOETFUFUTwDhSgVHYTVHNY8AJwlEYVUETilhA3FkUFMOKzECYQRRTmBl8GbrOAA4QxgCT4M26SW3Dhcfgz0NcL//go+kQBpAK9sZJbcOCAIvgzIG6TXwNw4PAFMDIC0IAo1yHXCx/4I1EToVy5k18GcOBcocE8kTGRMOKAEfgz0PoAMAOg/iBSAqFwbXAQRRA0cPAEyPQA9AABzxbJ7VR/g"); diff --git a/crypto/smartcont/auto/elector-code.fif b/crypto/smartcont/auto/elector-code.fif new file mode 100644 index 0000000000..d964063b2e --- /dev/null +++ b/crypto/smartcont/auto/elector-code.fif @@ -0,0 +1,1712 @@ +// automatically generated from `smartcont/stdlib.fc` `smartcont/elector-code.fc` +PROGRAM{ + DECLPROC load_data + DECLPROC store_data + DECLPROC unpack_elect + DECLPROC pack_elect + DECLPROC get_validator_conf + DECLPROC send_message_back + DECLPROC return_stake + DECLPROC send_confirmation + DECLPROC send_validator_set_to_config + DECLPROC ~credit_to + DECLPROC process_new_stake + DECLPROC unfreeze_without_bonuses + DECLPROC unfreeze_with_bonuses + DECLPROC unfreeze_all + DECLPROC config_set_confirmed + DECLPROC process_simple_transfer + DECLPROC recover_stake + 1666 DECLMETHOD after_code_upgrade + DECLPROC upgrade_code + DECLPROC recv_internal + DECLPROC postpone_elections + DECLPROC compute_total_stake + DECLPROC try_elect + DECLPROC conduct_elections + DECLPROC update_active_vset_id + DECLPROC cell_hash_eq? + DECLPROC validator_set_installed + DECLPROC check_unfreeze + DECLPROC announce_new_elections + DECLPROC run_ticktock + 86535 DECLMETHOD active_election_id + 87852 DECLMETHOD participates_in + 123541 DECLMETHOD participant_list + 130944 DECLMETHOD compute_returned_stake + load_data PROC:<{ + // + c4 PUSH // _1 + CTOS // cs + LDDICT // _4 cs + LDDICT // _4 _6 cs + LDDICT // _4 _6 _8 cs + LDGRAMS // _4 _6 _8 _10 cs + 32 LDU // _4 _6 _8 _10 _12 cs + 256 LDU // res res res res res res cs + ENDS + }> + store_data PROC:<{ + // elect credits past_elect grams active_id active_hash + NEWC // elect credits past_elect grams active_id active_hash _6 + s1 s6 XCHG // active_hash credits past_elect grams active_id elect _6 + STDICT // active_hash credits past_elect grams active_id _7 + s1 s4 XCHG // active_hash active_id past_elect grams credits _7 + STDICT // active_hash active_id past_elect grams _8 + s1 s2 XCHG // active_hash active_id grams past_elect _8 + STDICT // active_hash active_id grams _9 + SWAP // active_hash active_id _9 grams + STGRAMS // active_hash active_id _10 + 32 STU // active_hash _12 + 256 STU // _14 + ENDC // _15 + c4 POP + }> + unpack_elect PROC:<{ + // elect + CTOS // es + 32 LDU // _4 es + 32 LDU // _4 _7 es + LDGRAMS // _4 _7 _10 es + LDGRAMS // _4 _7 _10 _12 es + LDDICT // _4 _7 _10 _12 _14 es + 1 LDI // _4 _7 _10 _12 _14 _16 es + 1 LDI // res res res res res res res es + ENDS + }> + pack_elect PROC:<{ + // elect_at elect_close min_stake total_stake members failed finished + NEWC // elect_at elect_close min_stake total_stake members failed finished _7 + s1 s7 XCHG // finished elect_close min_stake total_stake members failed elect_at _7 + 32 STU // finished elect_close min_stake total_stake members failed _9 + s1 s5 XCHG // finished failed min_stake total_stake members elect_close _9 + 32 STU // finished failed min_stake total_stake members _11 + s0 s3 XCHG2 // finished failed members total_stake _11 min_stake + STGRAMS // finished failed members total_stake _12 + SWAP // finished failed members _12 total_stake + STGRAMS // finished failed members _13 + STDICT // finished failed _14 + 1 STI // finished _16 + 1 STI // _18 + ENDC // _19 + }> + get_validator_conf PROC:<{ + // + 15 PUSHINT // _1=15 + CONFIGOPTPARAM // _2 + CTOS // cs + 32 LDI // _4 cs + 32 LDI // _4 _7 cs + 32 LDI // _4 _7 _10 cs + 32 PLDI // _4 _7 _10 _14 + }> + send_message_back PROC:<{ + // addr ans_tag query_id body grams mode + 0 PUSHINT // addr ans_tag query_id body grams mode _7=0 + 24 PUSHINT // addr ans_tag query_id body grams mode _7=0 _8=24 + NEWC // addr ans_tag query_id body grams mode _7=0 _8=24 _9 + 6 STU // addr ans_tag query_id body grams mode _7=0 _11 + s0 s7 XCHG2 // _7=0 ans_tag query_id body grams mode _11 addr + STSLICER // _7=0 ans_tag query_id body grams mode _12 + ROT // _7=0 ans_tag query_id body mode _12 grams + STGRAMS // _7=0 ans_tag query_id body mode _13 + s1 s5 XCHG // mode ans_tag query_id body _7=0 _13 + 107 STU // mode ans_tag query_id body _27 + s1 s3 XCHG // mode body query_id ans_tag _27 + 32 STU // mode body query_id _29 + 64 STU // mode body msg + OVER // mode body msg body + -1 GTINT // mode body msg _33 + IF:<{ // mode body msg + 32 STU // mode msg + }>ELSE<{ + NIP // mode msg + }> + ENDC // mode _37 + SWAP // _37 mode + SENDRAWMSG + }> + return_stake PROC:<{ + // addr query_id reason + 4000269644 PUSHINT // addr query_id reason _3=4000269644 + 0 PUSHINT // addr query_id reason _3=4000269644 _4=0 + s3 s3 s0 XCHG3 + 64 PUSHINT // addr _3=4000269644 query_id reason _4=0 _5=64 + send_message_back CALLDICT + }> + send_confirmation PROC:<{ + // addr query_id comment + 4084484172 PUSHINT // addr query_id comment _3=4084484172 + 1000000000 PUSHINT // addr query_id comment _3=4084484172 _4=1000000000 + s3 s3 s0 XCHG3 + 2 PUSHINT // addr _3=4084484172 query_id comment _4=1000000000 _5=2 + send_message_back CALLDICT + }> + send_validator_set_to_config PROC:<{ + // config_addr vset query_id + 1314280276 PUSHINT // config_addr vset query_id _4=1314280276 + 0 PUSHINT // config_addr vset query_id _4=1314280276 _5=0 + 50431 PUSHINT // config_addr vset query_id _4=1314280276 _5=0 _6=50431 + NEWC // config_addr vset query_id _4=1314280276 _5=0 _6=50431 _7 + 17 STU // config_addr vset query_id _4=1314280276 _5=0 _9 + s1 s5 XCHG // _5=0 vset query_id _4=1314280276 config_addr _9 + 256 STU // _5=0 vset query_id _4=1314280276 _11 + 30 PUSHPOW2 // _5=0 vset query_id _4=1314280276 _11 _14 + STGRAMS // _5=0 vset query_id _4=1314280276 _15 + s1 s4 XCHG // _4=1314280276 vset query_id _5=0 _15 + 107 STU // _4=1314280276 vset query_id _29 + s1 s3 XCHG // query_id vset _4=1314280276 _29 + 32 STU // query_id vset _31 + s1 s2 XCHG // vset query_id _31 + 64 STU // vset _33 + STREF // msg + ENDC // _35 + 1 PUSHINT // _35 _36=1 + SENDRAWMSG + }> + ~credit_to PROC:<{ + // credits addr amount + s1 s2 PUSH2 + 8 PUSHPOW2 // credits addr amount addr credits _5=256 + DICTUGET + NULLSWAPIFNOT // credits addr amount val f + IF:<{ // credits addr amount val + LDGRAMS // credits addr amount _18 _17 + DROP // credits addr amount _7 + ADD // credits addr amount + }>ELSE<{ + DROP // credits addr amount + }> + NEWC // credits addr amount _11 + SWAP // credits addr _11 amount + STGRAMS // credits addr _12 + s0 s2 XCHG + 8 PUSHPOW2 // _12 addr credits _13=256 + DICTUSETB // credits + }> + process_new_stake PROC:<{ + // s_addr msg_value cs query_id + s3 PUSH // s_addr msg_value cs query_id s_addr + REWRITESTDADDR // s_addr msg_value cs query_id src_wc src_addr + c4 PUSH // s_addr msg_value cs query_id src_wc src_addr _8 + CTOS // s_addr msg_value cs query_id src_wc src_addr ds + LDDICT // s_addr msg_value cs query_id src_wc src_addr elect ds + OVER // s_addr msg_value cs query_id src_wc src_addr elect ds elect + ISNULL // s_addr msg_value cs query_id src_wc src_addr elect ds _13 + s0 s4 XCHG // s_addr msg_value cs query_id _13 src_addr elect ds src_wc + INC // s_addr msg_value cs query_id _13 src_addr elect ds _15 + s1 s4 XCHG // s_addr msg_value cs query_id ds src_addr elect _13 _15 + OR // s_addr msg_value cs query_id ds src_addr elect _16 + IFJMP:<{ // s_addr msg_value cs query_id ds src_addr elect + s3 s5 XCHG + 5 BLKDROP // s_addr query_id + 0 PUSHINT // s_addr query_id _17=0 + return_stake CALLDICT + }> // s_addr msg_value cs query_id ds src_addr elect + s0 s4 XCHG // s_addr msg_value elect query_id ds src_addr cs + 256 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey cs + 32 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at cs + 32 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor cs + 256 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs + LDREF // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _36 cs + SWAP // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs _36 + CTOS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs _38 + 9 PUSHPOW2 // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs _38 _39=512 + PLDSLICEX // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs signature + SWAP // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature cs + ENDS + 1699500148 PUSHINT // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _42=1699500148 + NEWC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _42=1699500148 _43 + 32 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _45 + s4 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature stake_at _45 + 32 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _47 + s3 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature max_factor _47 + 32 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _49 + s6 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature src_addr _49 + 256 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _51 + s2 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature adnl_addr _51 + 256 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _53 + ENDC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _54 + CTOS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _55 + s1 s5 XCPU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _55 signature validator_pubkey + CHKSIGNS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _56 + IFNOTJMP:<{ // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr + s6 s8 XCHG + 8 BLKDROP // s_addr query_id + 1 PUSHINT // s_addr query_id _57=1 + return_stake CALLDICT + }> // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr + OVER + 16 PUSHPOW2 // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr max_factor _59=65536 + LESS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _60 + IFJMP:<{ // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr + s6 s8 XCHG + 8 BLKDROP // s_addr query_id + 6 PUSHINT // s_addr query_id _61=6 + return_stake CALLDICT + }> // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr + s0 s7 XCHG // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor elect + unpack_elect CALLDICT // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor _146 _147 _148 _149 _150 _151 _152 + NIP // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor elect_at elect_close min_stake total_stake members finished + s0 s5 XCHG // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members elect_at + s0 DUMP // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members elect_at + s0 s13 XCHG + 1000000000 PUSHINT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _73=1000000000 + SUB // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value + DUP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value msg_value + 12 LSHIFT# // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _76 + s3 PUSH // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _76 total_stake + LESS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _77 + IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value + s11 s12 XCHG + 12 BLKDROP + NIP // s_addr query_id + 2 PUSHINT // s_addr query_id _78=2 + return_stake CALLDICT + }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value + s2 s2 XCPU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake msg_value members total_stake msg_value + ADD // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake msg_value members total_stake + s7 s13 XCPU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members stake_at elect_at + NEQ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members _81 + IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members + s10 s12 XCHG + 12 BLKDROP // s_addr query_id + 3 PUSHINT // s_addr query_id _82=3 + return_stake CALLDICT + }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members + s0 s4 XCHG // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value finished + IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value + s9 s11 XCHG + 11 BLKDROP // s_addr query_id + 0 PUSHINT // s_addr query_id _84=0 + return_stake CALLDICT + }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value + s6 s3 PUSH2 + 8 PUSHPOW2 // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value validator_pubkey members _88=256 + DICTUGET + NULLSWAPIFNOT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem found + DUP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem found found + IF:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem found + DROP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem + LDGRAMS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _90 mem + -ROT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake mem msg_value _90 + ADD // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake mem msg_value + SWAP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem + 64 LDU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _158 _157 + NIP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem + 256 LDU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _160 _159 + DROP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _96 + s8 s(-1) PUXC // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value src_addr _96 + NEQ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value found + }>ELSE<{ + NIP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value found + }> + IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value + s9 s11 XCHG + 11 BLKDROP // s_addr query_id + 4 PUSHINT // s_addr query_id _100=4 + return_stake CALLDICT + }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value + s0 s1 PUSH2 // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value msg_value min_stake + LESS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _102 + IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value + s9 s11 XCHG + 11 BLKDROP // s_addr query_id + 5 PUSHINT // s_addr query_id _103=5 + return_stake CALLDICT + }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value + DUP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value msg_value + 44 THROWIFNOT + ACCEPT + NOW // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _109 + NEWC // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _109 _110 + ROT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake _109 _110 msg_value + STGRAMS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake _109 _111 + 32 STU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake _113 + s1 s4 XCHG // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake min_stake members elect_close max_factor _113 + 32 STU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake min_stake members elect_close _115 + s1 s6 XCHG // s_addr elect_at adnl_addr query_id ds elect_close validator_pubkey total_stake min_stake members src_addr _115 + 256 STU // s_addr elect_at adnl_addr query_id ds elect_close validator_pubkey total_stake min_stake members _117 + s1 s8 XCHG // s_addr elect_at members query_id ds elect_close validator_pubkey total_stake min_stake adnl_addr _117 + 256 STU // s_addr elect_at members query_id ds elect_close validator_pubkey total_stake min_stake _119 + s0 s3 s7 XCHG3 + 8 PUSHPOW2 // s_addr elect_at total_stake query_id ds elect_close min_stake _119 validator_pubkey members _120=256 + DICTUSETB // s_addr elect_at total_stake query_id ds elect_close min_stake members + FALSE // s_addr elect_at total_stake query_id ds elect_close min_stake members _122 + s5 s7 XCHG + s3 s4 XCHG + s2 s3 XCHG + s2 s6 XCHG + FALSE // s_addr query_id ds elect_at elect_close min_stake total_stake members _122 _123 + pack_elect CALLDICT // s_addr query_id ds elect + NEWC // s_addr query_id ds elect _125 + STDICT // s_addr query_id ds _126 + SWAP // s_addr query_id _126 ds + STSLICER // s_addr query_id _127 + ENDC // s_addr query_id _128 + c4 POP + DUP // s_addr query_id query_id + IFJMP:<{ // s_addr query_id + 0 PUSHINT // s_addr query_id _130=0 + send_confirmation CALLDICT + }> // s_addr query_id + 2DROP // + }> + unfreeze_without_bonuses PROC:<{ + // credits freeze_dict tot_stakes + 0 PUSHINT // credits freeze_dict tot_stakes _5=0 + DUP // credits freeze_dict tot_stakes total=0 recovered=0 + -1 PUSHINT // credits freeze_dict tot_stakes total=0 recovered=0 pubkey=-1 + UNTIL:<{ + s4 PUSH + 8 PUSHPOW2 // credits freeze_dict tot_stakes total recovered pubkey freeze_dict _10=256 + DICTUGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT // credits freeze_dict tot_stakes total recovered cs pubkey f + DUP // credits freeze_dict tot_stakes total recovered cs pubkey f f + IF:<{ // credits freeze_dict tot_stakes total recovered cs pubkey f + s0 s2 XCHG // credits freeze_dict tot_stakes total recovered f pubkey cs + 256 LDU // credits freeze_dict tot_stakes total recovered f pubkey _16 cs + 64 LDU // credits freeze_dict tot_stakes total recovered f pubkey _16 _42 _41 + NIP // credits freeze_dict tot_stakes total recovered f pubkey _16 cs + LDGRAMS // credits freeze_dict tot_stakes total recovered f pubkey _16 _22 cs + 1 LDI // credits freeze_dict tot_stakes total recovered f pubkey addr stake banned cs + ENDS + IF:<{ // credits freeze_dict tot_stakes total recovered f pubkey addr stake + NIP // credits freeze_dict tot_stakes total recovered f pubkey stake + s3 s3 XCPU // credits freeze_dict tot_stakes total stake f pubkey recovered stake + ADD // credits freeze_dict tot_stakes total stake f pubkey recovered + }>ELSE<{ // credits freeze_dict tot_stakes total recovered f pubkey addr stake + s8 s8 s8 XC2PU // stake freeze_dict tot_stakes total recovered f pubkey credits addr stake + ~credit_to CALLDICT // stake freeze_dict tot_stakes total recovered f pubkey credits + s0 s7 XCHG + s0 s3 XCHG // credits freeze_dict tot_stakes total stake f pubkey recovered + }> + s4 s3 XCHG2 // credits freeze_dict tot_stakes pubkey recovered f total stake + ADD // credits freeze_dict tot_stakes pubkey recovered f total + }>ELSE<{ + s4 s2 XCHG2 + DROP // credits freeze_dict tot_stakes pubkey recovered f total + }> + SWAP // credits freeze_dict tot_stakes pubkey recovered total f + NOT // credits freeze_dict tot_stakes pubkey recovered total _32 + s1 s3 XCHG // credits freeze_dict tot_stakes total recovered pubkey _32 + }> // credits freeze_dict tot_stakes total recovered pubkey + DROP + s3 POP // credits recovered tot_stakes total + SWAP // credits recovered total tot_stakes + EQUAL // credits recovered _34 + 59 THROWIFNOT + }> + unfreeze_with_bonuses PROC:<{ + // credits freeze_dict tot_stakes tot_bonuses + 0 PUSHINT // credits freeze_dict tot_stakes tot_bonuses _7=0 + s0 s0 PUSH2 // credits freeze_dict tot_stakes tot_bonuses total=0 returned_bonuses=0 recovered=0 + -1 PUSHINT // credits freeze_dict tot_stakes tot_bonuses total=0 returned_bonuses=0 recovered=0 pubkey=-1 + UNTIL:<{ + s6 PUSH + 8 PUSHPOW2 // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered pubkey freeze_dict _12=256 + DICTUGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered cs pubkey f + DUP // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered cs pubkey f f + IF:<{ // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered cs pubkey f + s0 s2 XCHG // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey cs + 256 LDU // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 cs + 64 LDU // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 _52 _51 + NIP // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 cs + LDGRAMS // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 _24 cs + 1 LDI // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake banned cs + ENDS + IF:<{ // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake + NIP // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey stake + s3 s3 XCPU // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses stake f pubkey recovered stake + ADD // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses stake f pubkey recovered + }>ELSE<{ // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake + s7 s0 s8 PUSH3 // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake tot_bonuses stake tot_stakes + MULDIV // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake bonus + s6 s6 XCPU // credits freeze_dict tot_stakes tot_bonuses total bonus recovered f pubkey addr stake returned_bonuses bonus + ADD // credits freeze_dict tot_stakes tot_bonuses total bonus recovered f pubkey addr stake returned_bonuses + s1 s6 PUXC // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake stake bonus + ADD // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake _35 + s11 s11 s0 XCHG3 // stake freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey credits addr _35 + ~credit_to CALLDICT // stake freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey credits + s0 s9 XCHG + s0 s3 XCHG // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses stake f pubkey recovered + }> + s5 s3 XCHG2 // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered f total stake + ADD // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered f total + }>ELSE<{ + s5 s2 XCHG2 + DROP // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered f total + }> + SWAP // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered total f + NOT // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered total _38 + s1 s4 XCHG // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered pubkey _38 + }> // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered pubkey + DROP + s5 POP // credits recovered tot_stakes tot_bonuses total returned_bonuses + s0 s3 XCHG // credits recovered returned_bonuses tot_bonuses total tot_stakes + EQUAL // credits recovered returned_bonuses tot_bonuses _40 + s2 s1 PUSH2 // credits recovered returned_bonuses tot_bonuses _40 returned_bonuses tot_bonuses + LEQ // credits recovered returned_bonuses tot_bonuses _40 _41 + AND // credits recovered returned_bonuses tot_bonuses _42 + 59 THROWIFNOT + s1 s2 XCHG // credits returned_bonuses recovered tot_bonuses + ADD // credits returned_bonuses _44 + SWAP // credits _44 returned_bonuses + SUB // credits _45 + }> + unfreeze_all PROC:<{ + // credits past_elections elect_id + SWAP + 32 PUSHINT // credits elect_id past_elections _6=32 + DICTUDELGET + NULLSWAPIFNOT // credits past_elections fs f + IFNOTJMP:<{ // credits past_elections fs + DROP // credits past_elections + 0 PUSHINT // credits past_elections _8=0 + }> // credits past_elections fs + 64 LDU // credits past_elections _44 _43 + NIP // credits past_elections fs + 256 LDU // credits past_elections _46 _45 + NIP // credits past_elections fs + LDDICT // credits past_elections _21 fs + LDGRAMS // credits past_elections _21 _23 fs + LDGRAMS // credits past_elections _21 _23 _25 fs + LDDICT // credits past_elections _21 _23 _25 _54 _53 + NIP // credits past_elections fdict tot_stakes bonuses fs + ENDS + DUP // credits past_elections fdict tot_stakes bonuses bonuses + 0 GTINT // credits past_elections fdict tot_stakes bonuses _32 + IF:<{ // credits past_elections fdict tot_stakes bonuses + s3 s4 XCHG // past_elections credits fdict tot_stakes bonuses + unfreeze_with_bonuses CALLDICT // past_elections credits _33 + }>ELSE<{ // credits past_elections fdict tot_stakes bonuses + DROP // credits past_elections fdict tot_stakes + s2 s3 XCHG // past_elections credits fdict tot_stakes + unfreeze_without_bonuses CALLDICT // past_elections credits _33 + }> // past_elections credits unused_prizes + s1 s2 XCHG // credits past_elections unused_prizes + }> + config_set_confirmed PROC:<{ + // s_addr cs query_id ok + s2 POP // s_addr ok query_id + s0 s2 XCHG // query_id ok s_addr + REWRITESTDADDR // query_id ok src_wc src_addr + 0 PUSHINT // query_id ok src_wc src_addr _8=0 + CONFIGOPTPARAM // query_id ok src_wc src_addr _9 + CTOS // query_id ok src_wc src_addr _10 + 256 PLDU // query_id ok src_wc src_addr config_addr + c4 PUSH // query_id ok src_wc src_addr config_addr _14 + CTOS // query_id ok src_wc src_addr config_addr ds + LDDICT // query_id ok src_wc src_addr config_addr elect ds + s0 s4 XCHG // query_id ok ds src_addr config_addr elect src_wc + INC // query_id ok ds src_addr config_addr elect _20 + 2SWAP // query_id ok ds elect _20 src_addr config_addr + NEQ // query_id ok ds elect _20 _21 + OR // query_id ok ds elect _22 + OVER // query_id ok ds elect _22 elect + ISNULL // query_id ok ds elect _22 _23 + OR // query_id ok ds elect _24 + IFJMP:<{ // query_id ok ds elect + 4 BLKDROP // + }> // query_id ok ds elect + unpack_elect CALLDICT // query_id ok ds _63 _64 _65 _66 _67 _68 _69 + s0 s5 XCHG + 5 BLKDROP // query_id ok ds elect_at finished + s1 s4 PUXC // finished ok ds elect_at elect_at query_id + NEQ // finished ok ds elect_at _33 + s0 s4 XCHG // _33 ok ds elect_at finished + NOT // _33 ok ds elect_at _34 + s1 s4 XCHG // elect_at ok ds _33 _34 + OR // elect_at ok ds _35 + IFJMP:<{ // elect_at ok ds + 3 BLKDROP // + }> // elect_at ok ds + ACCEPT + SWAP // elect_at ds ok + IFNOT:<{ // elect_at ds + LDDICT // elect_at _40 ds + LDDICT // elect_at _40 _42 ds + LDGRAMS // elect_at credits past_elections grams ds + s3 s3 s4 XCHG3 // ds grams credits past_elections elect_at + unfreeze_all CALLDICT // ds grams credits past_elections unused_prizes + FALSE // ds grams credits past_elections unused_prizes _48 + NEWC // ds grams credits past_elections unused_prizes _48 _49 + 1 STI // ds grams credits past_elections unused_prizes _51 + s1 s3 XCHG // ds grams unused_prizes past_elections credits _51 + STDICT // ds grams unused_prizes past_elections _52 + STDICT // ds grams unused_prizes _53 + -ROT // ds _53 grams unused_prizes + ADD // ds _53 _54 + STGRAMS // ds _55 + SWAP // _55 ds + STSLICER // _56 + ENDC // _57 + c4 POP + }>ELSE<{ + 2DROP // + }> + }> + process_simple_transfer PROC:<{ + // s_addr msg_value + load_data CALLDICT // s_addr msg_value elect credits past_elect grams active_id active_hash + s0 s7 XCHG // active_hash msg_value elect credits past_elect grams active_id s_addr + REWRITESTDADDR // active_hash msg_value elect credits past_elect grams active_id src_wc src_addr + SWAP // active_hash msg_value elect credits past_elect grams active_id src_addr src_wc + INC // active_hash msg_value elect credits past_elect grams active_id src_addr _13 + OR // active_hash msg_value elect credits past_elect grams active_id _14 + OVER // active_hash msg_value elect credits past_elect grams active_id _14 active_id + 0 EQINT // active_hash msg_value elect credits past_elect grams active_id _14 _16 + OR // active_hash msg_value elect credits past_elect grams active_id _17 + IFJMP:<{ // active_hash msg_value elect credits past_elect grams active_id + s0 s5 XCHG // active_hash active_id elect credits past_elect grams msg_value + ADD // active_hash active_id elect credits past_elect grams + 2 4 BLKSWAP + SWAP // elect credits past_elect grams active_id active_hash + store_data CALLDICT + }> // active_hash msg_value elect credits past_elect grams active_id + s0 s2 PUSH2 + 32 PUSHINT // active_hash msg_value elect credits past_elect grams active_id active_id past_elect _22=32 + DICTUGET + NULLSWAPIFNOT // active_hash msg_value elect credits past_elect grams active_id fs f + IF:<{ // active_hash msg_value elect credits past_elect grams active_id fs + 64 LDU // active_hash msg_value elect credits past_elect grams active_id _30 fs + 256 LDU // active_hash msg_value elect credits past_elect grams active_id _30 _33 fs + LDDICT // active_hash msg_value elect credits past_elect grams active_id _30 _33 _36 fs + LDGRAMS // active_hash msg_value elect credits past_elect grams active_id _30 _33 _36 _38 fs + LDGRAMS // active_hash msg_value elect credits past_elect grams active_id data hash dict total_stake bonuses fs + s0 s11 XCHG // active_hash fs elect credits past_elect grams active_id data hash dict total_stake bonuses msg_value + ADD // active_hash fs elect credits past_elect grams active_id data hash dict total_stake bonuses + NEWC // active_hash fs elect credits past_elect grams active_id data hash dict total_stake bonuses _44 + s1 s5 XCHG // active_hash fs elect credits past_elect grams active_id bonuses hash dict total_stake data _44 + 64 STU // active_hash fs elect credits past_elect grams active_id bonuses hash dict total_stake _46 + s1 s3 XCHG // active_hash fs elect credits past_elect grams active_id bonuses total_stake dict hash _46 + 256 STU // active_hash fs elect credits past_elect grams active_id bonuses total_stake dict _48 + STDICT // active_hash fs elect credits past_elect grams active_id bonuses total_stake _49 + SWAP // active_hash fs elect credits past_elect grams active_id bonuses _49 total_stake + STGRAMS // active_hash fs elect credits past_elect grams active_id bonuses _50 + SWAP // active_hash fs elect credits past_elect grams active_id _50 bonuses + STGRAMS // active_hash fs elect credits past_elect grams active_id _51 + s0 s6 XCHG2 // active_hash active_id elect credits past_elect grams _51 fs + STSLICER // active_hash active_id elect credits past_elect grams _52 + SWAP + 32 PUSHINT + s6 s3 s3 PUXC2 // active_hash active_id elect credits grams _52 active_id past_elect _53=32 + DICTUSETB // active_hash active_id elect credits grams past_elect + }>ELSE<{ // active_hash msg_value elect credits past_elect grams active_id fs + DROP // active_hash msg_value elect credits past_elect grams active_id + s0 s5 XCHG // active_hash active_id elect credits past_elect grams msg_value + ADD // active_hash active_id elect credits past_elect grams + SWAP // active_hash active_id elect credits grams past_elect + }> + s3 s5 XCHG + s1 s4 s3 XCHG3 // elect credits past_elect grams active_id active_hash + store_data CALLDICT + }> + recover_stake PROC:<{ + // op s_addr cs query_id + NIP // op s_addr query_id + OVER // op s_addr query_id s_addr + REWRITESTDADDR // op s_addr query_id src_wc src_addr + SWAP // op s_addr query_id src_addr src_wc + INC // op s_addr query_id src_addr _8 + IFJMP:<{ // op s_addr query_id src_addr + DROP // op s_addr query_id + 4294967294 PUSHINT // op s_addr query_id _9=4294967294 + 0 PUSHINT // op s_addr query_id _9=4294967294 _10=0 + s3 s4 XCHG + s1 s3 XCHG + 64 PUSHINT // s_addr _9=4294967294 query_id op _10=0 _11=64 + send_message_back CALLDICT + }> // op s_addr query_id src_addr + c4 PUSH // op s_addr query_id src_addr _14 + CTOS // op s_addr query_id src_addr ds + LDDICT // op s_addr query_id src_addr _18 ds + LDDICT // op s_addr query_id src_addr elect credits ds + s3 s3 XCHG2 + 8 PUSHPOW2 // op s_addr query_id ds elect src_addr credits _25=256 + DICTUDELGET + NULLSWAPIFNOT // op s_addr query_id ds elect credits cs f + IFNOTJMP:<{ // op s_addr query_id ds elect credits cs + 4 BLKDROP // op s_addr query_id + 4294967294 PUSHINT // op s_addr query_id _27=4294967294 + 0 PUSHINT // op s_addr query_id _27=4294967294 _28=0 + s3 s4 XCHG + s1 s3 XCHG + 64 PUSHINT // s_addr _27=4294967294 query_id op _28=0 _29=64 + send_message_back CALLDICT + }> // op s_addr query_id ds elect credits cs + s6 POP // cs s_addr query_id ds elect credits + s0 s5 XCHG // credits s_addr query_id ds elect cs + LDGRAMS // credits s_addr query_id ds elect amount cs + ENDS + NEWC // credits s_addr query_id ds elect amount _35 + s1 s2 XCHG // credits s_addr query_id ds amount elect _35 + STDICT // credits s_addr query_id ds amount _36 + s1 s5 XCHG // amount s_addr query_id ds credits _36 + STDICT // amount s_addr query_id ds _37 + SWAP // amount s_addr query_id _37 ds + STSLICER // amount s_addr query_id _38 + ENDC // amount s_addr query_id _39 + c4 POP + 4184830756 PUSHINT // amount s_addr query_id _41=4184830756 + 0 PUSHINT // amount s_addr query_id _41=4184830756 _42=0 + 24 PUSHINT // amount s_addr query_id _41=4184830756 _42=0 _43=24 + NEWC // amount s_addr query_id _41=4184830756 _42=0 _43=24 _44 + 6 STU // amount s_addr query_id _41=4184830756 _42=0 _46 + s0 s4 XCHG2 // amount _42=0 query_id _41=4184830756 _46 s_addr + STSLICER // amount _42=0 query_id _41=4184830756 _47 + s0 s4 XCHG2 // _41=4184830756 _42=0 query_id _47 amount + STGRAMS // _41=4184830756 _42=0 query_id _48 + s1 s2 XCHG // _41=4184830756 query_id _42=0 _48 + 107 STU // _41=4184830756 query_id _62 + s1 s2 XCHG // query_id _41=4184830756 _62 + 32 STU // query_id _64 + 64 STU // _66 + ENDC // _67 + 64 PUSHINT // _67 _68=64 + SENDRAWMSG + }> + after_code_upgrade PROC:<{ + // s_addr cs query_id + NIP // s_addr query_id + 1313042276 PUSHINT // s_addr query_id op=1313042276 + 3460525924 PUSHINT // s_addr query_id op=1313042276 _5=3460525924 + 0 PUSHINT // s_addr query_id op=1313042276 _5=3460525924 _6=0 + s3 s3 s0 XCHG3 + 64 PUSHINT // s_addr _5=3460525924 query_id op=1313042276 _6=0 _7=64 + send_message_back CALLDICT + }> + upgrade_code PROC:<{ + // s_addr cs query_id + 0 PUSHINT // s_addr cs query_id _4=0 + CONFIGOPTPARAM // s_addr cs query_id c_addr + DUP // s_addr cs query_id c_addr c_addr + ISNULL // s_addr cs query_id c_addr _6 + IFJMP:<{ // s_addr cs query_id c_addr + 4 BLKDROP // + FALSE // _7 + }> // s_addr cs query_id c_addr + CTOS // s_addr cs query_id _9 + 256 PLDU // s_addr cs query_id config_addr + s3 PUSH // s_addr cs query_id config_addr s_addr + REWRITESTDADDR // s_addr cs query_id config_addr src_wc src_addr + SWAP // s_addr cs query_id config_addr src_addr src_wc + INC // s_addr cs query_id config_addr src_addr _16 + s0 s2 XCHG // s_addr cs query_id _16 src_addr config_addr + NEQ // s_addr cs query_id _16 _17 + OR // s_addr cs query_id _18 + IFJMP:<{ // s_addr cs query_id + 3 BLKDROP // + FALSE // _19 + }> // s_addr cs query_id + ACCEPT + SWAP // s_addr query_id cs + LDREF // s_addr query_id code cs + OVER // s_addr query_id code cs code + SETCODE + DUP // s_addr query_id code cs cs + SEMPTY // s_addr query_id code cs _25 + IFNOT:<{ // s_addr query_id code cs + SWAP // s_addr query_id cs code + c3 POP + SWAP // s_addr cs query_id + after_code_upgrade CALLDICT + 0 THROW + }>ELSE<{ + 4 BLKDROP // + }> + TRUE // _30 + }> + recv_internal PROC:<{ + // msg_value in_msg_cell in_msg + SWAP // msg_value in_msg in_msg_cell + CTOS // msg_value in_msg cs + 4 LDU // msg_value in_msg flags cs + SWAP + 1 PUSHINT // msg_value in_msg cs flags _9=1 + AND // msg_value in_msg cs _10 + IFJMP:<{ // msg_value in_msg cs + 3 BLKDROP // + }> // msg_value in_msg cs + LDMSGADDR // msg_value in_msg _61 _60 + DROP // msg_value in_msg s_addr + OVER // msg_value in_msg s_addr in_msg + SEMPTY // msg_value in_msg s_addr _14 + IFJMP:<{ // msg_value in_msg s_addr + NIP // msg_value s_addr + SWAP // s_addr msg_value + process_simple_transfer CALLDICT + }> // msg_value in_msg s_addr + SWAP // msg_value s_addr in_msg + 32 LDU // msg_value s_addr op in_msg + OVER // msg_value s_addr op in_msg op + 0 EQINT // msg_value s_addr op in_msg _21 + IFJMP:<{ // msg_value s_addr op in_msg + 2DROP // msg_value s_addr + SWAP // s_addr msg_value + process_simple_transfer CALLDICT + }> // msg_value s_addr op in_msg + 64 LDU // msg_value s_addr op query_id in_msg + s2 PUSH + 1316189259 PUSHINT // msg_value s_addr op query_id in_msg op _27=1316189259 + EQUAL // msg_value s_addr op query_id in_msg _28 + IFJMP:<{ // msg_value s_addr op query_id in_msg + s2 POP // msg_value s_addr in_msg query_id + s2 s3 XCHG // s_addr msg_value in_msg query_id + process_new_stake CALLDICT + }> // msg_value s_addr op query_id in_msg + s4 POP // in_msg s_addr op query_id + OVER + 1197831204 PUSHINT // in_msg s_addr op query_id op _30=1197831204 + EQUAL // in_msg s_addr op query_id _31 + IFJMP:<{ // in_msg s_addr op query_id + s1 s3 XCHG // op s_addr in_msg query_id + recover_stake CALLDICT + }> // in_msg s_addr op query_id + OVER + 1313042276 PUSHINT // in_msg s_addr op query_id op _33=1313042276 + EQUAL // in_msg s_addr op query_id _34 + IFJMP:<{ // in_msg s_addr op query_id + s2 s3 s3 PUXCPU // query_id s_addr op s_addr in_msg query_id + upgrade_code CALLDICT // query_id s_addr op ok + IF:<{ // query_id s_addr op + 3460525924 PUSHINT // query_id s_addr op _37=3460525924 + }>ELSE<{ // query_id s_addr op + 32 PUSHPOW2DEC // query_id s_addr op _37=4294967295 + }> + 0 PUSHINT // query_id s_addr op _37 _40=0 + s3 s4 XCHG + s3 s3 s0 XCHG3 + 64 PUSHINT // s_addr _37 query_id op _40=0 _41=64 + send_message_back CALLDICT + }> // in_msg s_addr op query_id + OVER + 4000730955 PUSHINT // in_msg s_addr op query_id op _44=4000730955 + EQUAL // in_msg s_addr op query_id cfg_ok + s2 PUSH + 4000730991 PUSHINT // in_msg s_addr op query_id cfg_ok op _46=4000730991 + EQUAL // in_msg s_addr op query_id cfg_ok _47 + s1 s(-1) PUXC // in_msg s_addr op query_id cfg_ok cfg_ok _47 + OR // in_msg s_addr op query_id cfg_ok _48 + IFJMP:<{ // in_msg s_addr op query_id cfg_ok + s2 POP // in_msg s_addr cfg_ok query_id + s2 s3 XCHG + SWAP // s_addr in_msg query_id cfg_ok + config_set_confirmed CALLDICT + }> // in_msg s_addr op query_id cfg_ok + DROP + s3 POP // query_id s_addr op + DUP + 31 PUSHPOW2 // query_id s_addr op op _52 + AND // query_id s_addr op _53 + IFNOTJMP:<{ // query_id s_addr op + 32 PUSHPOW2DEC // query_id s_addr op _54=4294967295 + 0 PUSHINT // query_id s_addr op _54=4294967295 _55=0 + s3 s4 XCHG + s3 s3 s0 XCHG3 + 64 PUSHINT // s_addr _54=4294967295 query_id op _55=0 _56=64 + send_message_back CALLDICT + }> // query_id s_addr op + 3 BLKDROP // + }> + postpone_elections PROC:<{ + // + FALSE // _0 + }> + compute_total_stake PROC:<{ + // l n m_stake + 0 PUSHINT // l n m_stake tot_stake=0 + s0 s2 XCHG // l tot_stake=0 m_stake n + REPEAT:<{ // l tot_stake m_stake + s0 s2 XCHG // m_stake tot_stake l + UNCONS // m_stake tot_stake h l + OVER // m_stake tot_stake h l h + 0 INDEX // m_stake tot_stake h l stake + s0 s2 XCHG // m_stake tot_stake stake l h + 1 INDEX // m_stake tot_stake stake l max_f + s4 PUSH // m_stake tot_stake stake l max_f m_stake + MUL // m_stake tot_stake stake l _13 + 16 RSHIFT# // m_stake tot_stake stake l _15 + s1 s2 XCHG // m_stake tot_stake l stake _15 + MIN // m_stake tot_stake l stake + s1 s2 XCHG // m_stake l tot_stake stake + ADD // m_stake l tot_stake + ROT // l tot_stake m_stake + }> + DROP + NIP // tot_stake + }> + try_elect PROC:<{ + // credits members min_stake max_stake min_total_stake max_stake_factor + 16 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor _7=16 + CONFIGOPTPARAM // credits members min_stake max_stake min_total_stake max_stake_factor _8 + CTOS // credits members min_stake max_stake min_total_stake max_stake_factor cs + 16 LDU // credits members min_stake max_stake min_total_stake max_stake_factor _13 cs + 16 LDU // credits members min_stake max_stake min_total_stake max_stake_factor _13 _253 _252 + NIP // credits members min_stake max_stake min_total_stake max_stake_factor _13 cs + 16 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators cs + ENDS + 1 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators _23=1 + MAX // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators + 0 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n=0 + NEWDICT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n=0 sdict + -1 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n=0 sdict pubkey=-1 + UNTIL:<{ + s9 PUSH + 8 PUSHPOW2 // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict pubkey members _33=256 + DICTUGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict cs pubkey f + DUP // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict cs pubkey f f + IF:<{ // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict cs pubkey f + s0 s2 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey cs + LDGRAMS // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 cs + 32 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 _42 cs + 32 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 _42 _45 cs + 256 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 _42 _45 _48 cs + 256 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake time max_factor addr adnl_addr cs + ENDS + s0 s3 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake adnl_addr max_factor addr time + NEGATE // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake adnl_addr max_factor addr _56 + NEWC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake adnl_addr max_factor addr _56 _57 + s1 s5 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _56 adnl_addr max_factor addr stake _57 + 128 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _56 adnl_addr max_factor addr _59 + s1 s4 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _56 _59 + 32 STI // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _61 + s4 s(-1) PUXC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor pubkey _61 + 256 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _63 + ENDC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _64 + CTOS // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor key + s1 s10 XCPU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key max_factor max_stake_factor + MIN // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key _67 + NEWC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key _67 _68 + 32 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key _70 + s1 s3 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey key adnl_addr addr _70 + 256 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey key adnl_addr _72 + 256 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey key _74 + s0 s1 s4 XCHG3 + 416 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n pubkey f _74 key sdict _79 + DICTSETB // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n pubkey f sdict + s0 s3 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey f n + INC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey f n + }>ELSE<{ + s3 s4 XCHG + 2SWAP + DROP // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey f n + }> + SWAP // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey n f + NOT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey n _83 + s3 s3 s0 XCHG3 // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict pubkey _83 + }> // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict pubkey + DROP + s4 POP // credits members min_stake max_stake min_total_stake sdict max_validators min_validators n + ROT // credits members min_stake max_stake min_total_stake sdict min_validators n max_validators + MIN // credits members min_stake max_stake min_total_stake sdict min_validators n + s0 s1 PUSH2 // credits members min_stake max_stake min_total_stake sdict min_validators n n min_validators + LESS // credits members min_stake max_stake min_total_stake sdict min_validators n _85 + IFJMP:<{ // credits members min_stake max_stake min_total_stake sdict min_validators n + 7 BLKDROP // credits + NEWDICT // credits _86 + NEWDICT // credits _86 _87 + 0 PUSHINT // credits _86 _87 _88=0 + DUP // credits _86 _87 _88=0 _89=0 + }> // credits members min_stake max_stake min_total_stake sdict min_validators n + PUSHNULL // credits members min_stake max_stake min_total_stake sdict min_validators n l + UNTIL:<{ + s0 s3 XCHG + 416 PUSHINT // credits members min_stake max_stake min_total_stake l min_validators n sdict _100 + DICTREMMIN + NULLSWAPIFNOT + NULLSWAPIFNOT // credits members min_stake max_stake min_total_stake l min_validators n sdict cs key f + DUP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs key f f + IF:<{ // credits members min_stake max_stake min_total_stake l min_validators n sdict cs key f + SWAP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f key + 128 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _105 key + s1 s9 XCPU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f key _105 max_stake + MIN // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f key _108 + SWAP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _108 key + 32 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _108 _279 _278 + NIP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _108 key + 256 PLDU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f stake pubkey + s0 s3 XCHG // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake cs + 32 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake _117 cs + 256 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake _117 _283 _282 + NIP // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake _117 cs + 256 PLDU // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake max_f adnl_addr + s1 s3 s0 XCHG3 + s1 s4 XCHG // credits members min_stake max_stake min_total_stake l min_validators n sdict f stake max_f pubkey adnl_addr + 4 TUPLE // credits members min_stake max_stake min_total_stake l min_validators n sdict f _125 + s0 s5 XCHG2 // credits members min_stake max_stake min_total_stake f min_validators n sdict _125 l + CONS // credits members min_stake max_stake min_total_stake f min_validators n sdict l + }>ELSE<{ + s6 s1 s6 XCHG3 + 2DROP // credits members min_stake max_stake min_total_stake f min_validators n sdict l + }> + s0 s4 XCHG // credits members min_stake max_stake min_total_stake l min_validators n sdict f + NOT // credits members min_stake max_stake min_total_stake l min_validators n sdict _127 + s1 s4 XCHG // credits members min_stake max_stake min_total_stake sdict min_validators n l _127 + }> // credits members min_stake max_stake min_total_stake sdict min_validators n l + s3 POP + s4 POP // credits members min_stake n min_total_stake l min_validators + DEC // credits members min_stake n min_total_stake l i + 2DUP // credits members min_stake n min_total_stake l i l1 i + REPEAT:<{ // credits members min_stake n min_total_stake l i l1 + CDR // credits members min_stake n min_total_stake l i l1 + }> + 0 PUSHINT // credits members min_stake n min_total_stake l i l1 _135=0 + DUP // credits members min_stake n min_total_stake l i l1 best_stake=0 m=0 + UNTIL:<{ + s0 s2 XCHG // credits members min_stake n min_total_stake l i m best_stake l1 + UNCONS // credits members min_stake n min_total_stake l i m best_stake _138 l1 + SWAP // credits members min_stake n min_total_stake l i m best_stake l1 _138 + 0 INDEX // credits members min_stake n min_total_stake l i m best_stake l1 stake + s0 s4 XCHG // credits members min_stake n min_total_stake l stake m best_stake l1 i + INC // credits members min_stake n min_total_stake l stake m best_stake l1 i + s4 s8 PUSH2 // credits members min_stake n min_total_stake l stake m best_stake l1 i stake min_stake + GEQ // credits members min_stake n min_total_stake l stake m best_stake l1 i _144 + IF:<{ // credits members min_stake n min_total_stake l stake m best_stake l1 i + s5 s(-1) s4 PU2XC // credits members min_stake n min_total_stake l i m best_stake l1 l i stake + compute_total_stake CALLDICT // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake + s0 s2 PUSH2 // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake tot_stake best_stake + GREATER // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake _147 + IF:<{ // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake + s2 POP + s2 POP // credits members min_stake n min_total_stake l i l1 tot_stake + s2 PUSH // credits members min_stake n min_total_stake l i l1 best_stake m + }>ELSE<{ + s1 s3 XCHG + DROP // credits members min_stake n min_total_stake l i l1 best_stake m + }> + }>ELSE<{ + s3 s4 XCHG2 + DROP // credits members min_stake n min_total_stake l i l1 best_stake m + }> + s3 s6 PUSH2 // credits members min_stake n min_total_stake l i l1 best_stake m i n + GEQ // credits members min_stake n min_total_stake l i l1 best_stake m _148 + }> // credits members min_stake n min_total_stake l i l1 best_stake m + s2 POP + s2 POP + s4 POP + s4 POP // credits members best_stake m min_total_stake l + s2 PUSH // credits members best_stake m min_total_stake l m + 0 EQINT // credits members best_stake m min_total_stake l _150 + s4 s2 PUXC // credits members best_stake m _150 l best_stake min_total_stake + LESS // credits members best_stake m _150 l _151 + s1 s2 XCHG // credits members best_stake m l _150 _151 + OR // credits members best_stake m l _152 + IFJMP:<{ // credits members best_stake m l + 4 BLKDROP // credits + NEWDICT // credits _153 + NEWDICT // credits _153 _154 + 0 PUSHINT // credits _153 _154 _155=0 + DUP // credits _153 _154 _155=0 _156=0 + }> // credits members best_stake m l + DUP // credits members best_stake m l l1 + s0 DUMP // credits members best_stake m l l1 + s2 PUSH // credits members best_stake m l l1 m + DEC // credits members best_stake m l l1 _161 + REPEAT:<{ // credits members best_stake m l l1 + CDR // credits members best_stake m l l1 + }> + CAR // credits members best_stake m l _164 + 0 INDEX // credits members best_stake m l m_stake + 0 PUSHINT // credits members best_stake m l m_stake i=0 + DUP // credits members best_stake m l m_stake i=0 tot_stake=0 + NEWDICT // credits members best_stake m l m_stake i=0 tot_stake=0 vset + NEWDICT // credits members best_stake m l m_stake i=0 tot_stake=0 vset frozen + UNTIL:<{ + s0 s5 XCHG // credits members best_stake m frozen m_stake i tot_stake vset l + UNCONS // credits members best_stake m frozen m_stake i tot_stake vset _179 l + SWAP // credits members best_stake m frozen m_stake i tot_stake vset l _179 + 4 UNTUPLE // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr + s1 s12 PUSH2 + 8 PUSHPOW2 // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr pubkey members _184=256 + DICTUGET + NULLSWAPIFNOT // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr val f + 61 THROWIFNOT + LDGRAMS // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr _295 _294 + NIP // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr val + 64 LDU // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr _297 _296 + NIP // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr val + 256 PLDU // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr + s8 s11 PUSH2 // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr i m + LESS // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr _198 + IF:<{ // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr + s3 s9 XCPU // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr max_f m_stake + MUL // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr _200 + 16 RSHIFT# // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr _202 + s4 s(-1) PUXC // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr stake _202 + MIN // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr true_stake + s4 s4 XCPU // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake true_stake + SUB // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake + s4 PUSH // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake true_stake + 60 LSHIFT# // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake _207 + s13 PUSH // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake _207 best_stake + DIV // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake weight + s8 s5 XCPU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake true_stake + ADD // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake + 2390828938 PUSHINT // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 + s3 PUSH // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 adnl_addr + IF:<{ // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 + 115 PUSHINT // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _212=115 + }>ELSE<{ // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 + 83 PUSHINT // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _212=83 + }> + NEWC // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _212 _215 + 8 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _217 + 32 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _219 + s4 s(-1) PUXC // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake pubkey _219 + 256 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _221 + s9 s(-1) PUXC // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake weight _221 + 64 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake vinfo + s3 PUSH // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake vinfo adnl_addr + IF:<{ // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake vinfo + s1 s3 XCHG // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey tot_stake stake adnl_addr vinfo + 256 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey tot_stake stake vinfo + }>ELSE<{ + s0 s3 XCHG2 + DROP // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey tot_stake stake vinfo + }> + SWAP + 16 PUSHINT + s10 s8 s8 PUXC2 // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vinfo i vset _228=16 + DICTUSETB // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset + FALSE // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset _231 + s4 PUSH + NEWC // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset _231 src_addr _232 + 256 STU // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset _231 _234 + s1 s9 XCHG // credits members best_stake m frozen m_stake i _231 stake l true_stake src_addr pubkey tot_stake vset weight _234 + 64 STU // credits members best_stake m frozen m_stake i _231 stake l true_stake src_addr pubkey tot_stake vset _236 + s0 s5 XCHG2 // credits members best_stake m frozen m_stake i _231 stake l vset src_addr pubkey tot_stake _236 true_stake + STGRAMS // credits members best_stake m frozen m_stake i _231 stake l vset src_addr pubkey tot_stake _237 + s1 s7 XCHG // credits members best_stake m frozen m_stake i tot_stake stake l vset src_addr pubkey _231 _237 + 1 STI // credits members best_stake m frozen m_stake i tot_stake stake l vset src_addr pubkey _239 + s0 s1 s9 XCHG3 + 8 PUSHPOW2 // credits members best_stake m src_addr m_stake i tot_stake stake l vset _239 pubkey frozen _240=256 + DICTUSETB // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen + }>ELSE<{ + s0 s10 XCHG + s4 s6 XCHG + s0 s3 XCHG + 3 BLKDROP // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen + }> + s3 PUSH // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen stake + IF:<{ // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen + s11 s7 s3 XCHG3 // l members best_stake m vset m_stake i tot_stake frozen credits src_addr stake + ~credit_to CALLDICT // l members best_stake m vset m_stake i tot_stake frozen credits + }>ELSE<{ + s11 s7 s3 XCHG3 + 2DROP // l members best_stake m vset m_stake i tot_stake frozen credits + }> + s0 s3 XCHG // l members best_stake m vset m_stake credits tot_stake frozen i + INC // l members best_stake m vset m_stake credits tot_stake frozen i + s9 PUSH // l members best_stake m vset m_stake credits tot_stake frozen i l + ISNULL // l members best_stake m vset m_stake credits tot_stake frozen i _246 + s4 s10 XCHG + s4 s6 XCHG + s4 s4 s0 XCHG3 // credits members best_stake m l m_stake i tot_stake vset frozen _246 + }> // credits members best_stake m l m_stake i tot_stake vset frozen + s3 POP + s3 POP + s3 POP + s5 POP // credits frozen best_stake m tot_stake vset + s1 s3 PUXC // credits frozen vset m tot_stake tot_stake best_stake + EQUAL // credits frozen vset m tot_stake _248 + 49 THROWIFNOT + s2 s3 XCHG + SWAP // credits vset frozen tot_stake m + }> + conduct_elections PROC:<{ + // ds elect credits + SWAP // ds credits elect + unpack_elect CALLDICT // ds credits _130 _131 _132 _133 _134 _135 _136 + s4 POP // ds credits elect_at elect_close finished total_stake members failed + NOW // ds credits elect_at elect_close finished total_stake members failed _11 + s5 PUSH // ds credits elect_at elect_close finished total_stake members failed _11 elect_close + LESS // ds credits elect_at elect_close finished total_stake members failed _12 + IFJMP:<{ // ds credits elect_at elect_close finished total_stake members failed + 8 BLKDROP // + FALSE // _13 + }> // ds credits elect_at elect_close finished total_stake members failed + 0 PUSHINT // ds credits elect_at elect_close finished total_stake members failed _14=0 + CONFIGOPTPARAM // ds credits elect_at elect_close finished total_stake members failed _15 + ISNULL // ds credits elect_at elect_close finished total_stake members failed _16 + IFJMP:<{ // ds credits elect_at elect_close finished total_stake members failed + 8 BLKDROP // + postpone_elections CALLDICT // _17 + }> // ds credits elect_at elect_close finished total_stake members failed + 17 PUSHINT // ds credits elect_at elect_close finished total_stake members failed _19=17 + CONFIGOPTPARAM // ds credits elect_at elect_close finished total_stake members failed _20 + CTOS // ds credits elect_at elect_close finished total_stake members failed cs + LDGRAMS // ds credits elect_at elect_close finished total_stake members failed min_stake cs + LDGRAMS // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake cs + LDGRAMS // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake cs + 32 LDU // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor cs + ENDS + s6 s1 PUSH2 // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor total_stake min_total_stake + LESS // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor _35 + IFJMP:<{ // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor + 12 BLKDROP // + postpone_elections CALLDICT // _36 + }> // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor + s0 s4 XCHG // ds credits elect_at elect_close finished total_stake members max_stake_factor min_stake max_stake min_total_stake failed + IFJMP:<{ // ds credits elect_at elect_close finished total_stake members max_stake_factor min_stake max_stake min_total_stake + 11 BLKDROP // + postpone_elections CALLDICT // _37 + }> // ds credits elect_at elect_close finished total_stake members max_stake_factor min_stake max_stake min_total_stake + s0 s6 XCHG // ds credits elect_at elect_close min_total_stake total_stake members max_stake_factor min_stake max_stake finished + IFJMP:<{ // ds credits elect_at elect_close min_total_stake total_stake members max_stake_factor min_stake max_stake + 10 BLKDROP // + FALSE // _38 + }> // ds credits elect_at elect_close min_total_stake total_stake members max_stake_factor min_stake max_stake + s3 s8 XCHG + s8 PUSH + s3 s2 XCPU + s7 s7 XCHG2 // ds members elect_at elect_close min_stake total_stake credits members min_stake max_stake min_total_stake max_stake_factor + try_elect CALLDICT // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt + DUP // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt cnt + 0 EQINT // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt failed + DUP // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt failed failed + NOT // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt failed finished + s10 PUSH + s0 s6 XCHG + s5 s10 XCHG + s4 s9 XCHG + s3 s8 XCHG + s12 s12 s12 XCHG3 // ds vdict elect_at frozen total_stakes cnt credits elect_at elect_close min_stake total_stake members failed finished + pack_elect CALLDICT // ds vdict elect_at frozen total_stakes cnt credits elect + s2 PUSH // ds vdict elect_at frozen total_stakes cnt credits elect cnt + IFNOTJMP:<{ // ds vdict elect_at frozen total_stakes cnt credits elect + s2 POP + s2 POP + s2 POP + s2 POP + s2 POP // ds elect credits + NEWC // ds elect credits _48 + s1 s2 XCHG // ds credits elect _48 + STDICT // ds credits _49 + STDICT // ds _50 + SWAP // _50 ds + STSLICER // _51 + ENDC // _52 + c4 POP + postpone_elections CALLDICT // _54 + }> // ds vdict elect_at frozen total_stakes cnt credits elect + get_validator_conf CALLDICT // ds vdict elect_at frozen total_stakes cnt credits elect _150 _151 _152 _153 + s2 POP // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held elect_end_before + NOW // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held elect_end_before _61 + SWAP // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _61 elect_end_before + ADD // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _62 + -60 ADDCONST // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _64 + s8 PUSH // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _64 elect_at + MAX // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start + 16 PUSHINT // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _67=16 + CONFIGOPTPARAM // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _68 + CTOS // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _69 + 16 PUSHINT // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _69 _70=16 + SDSKIPFIRST // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _71 + 16 PLDU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start main_validators + s6 s(-1) PUXC // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start cnt main_validators + MIN // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 + s1 s3 PUSH2 // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 start elect_for + ADD // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 + 17 PUSHINT // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _77=17 + NEWC // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _77=17 _78 + 8 STU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _80 + s3 s(-1) PUXC // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 start _80 + 32 STU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _82 + 32 STU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _84 + s1 s7 XCHG // ds vdict elect_at frozen total_stakes _75 credits elect elect_for stake_held start cnt _84 + 16 STU // ds vdict elect_at frozen total_stakes _75 credits elect elect_for stake_held start _86 + s1 s6 XCHG // ds vdict elect_at frozen total_stakes start credits elect elect_for stake_held _75 _86 + 16 STU // ds vdict elect_at frozen total_stakes start credits elect elect_for stake_held _88 + s0 s9 XCHG // ds _88 elect_at frozen total_stakes start credits elect elect_for stake_held vdict + CTOS // ds _88 elect_at frozen total_stakes start credits elect elect_for stake_held _89 + s1 s9 XCHG // ds stake_held elect_at frozen total_stakes start credits elect elect_for _88 _89 + STSLICER // ds stake_held elect_at frozen total_stakes start credits elect elect_for _90 + ENDC // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset + 0 PUSHINT // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset _93=0 + CONFIGOPTPARAM // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset _94 + CTOS // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset _95 + 256 PLDU // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset config_addr + s1 s8 PUSH2 // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset config_addr vset elect_at + send_validator_set_to_config CALLDICT + s0 s9 XCHG // vset stake_held elect_at frozen total_stakes start credits elect elect_for ds + LDDICT // vset stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds + FALSE // vset stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds _103 + s0 s11 XCHG // _103 stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds vset + HASHCU // _103 stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds _104 + s6 s3 XCHG2 // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect start elect_for + ADD // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _105 + s9 PUSH // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _105 stake_held + ADD // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _106 + NEWC // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _106 _107 + 32 STU // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _109 + s1 s9 XCHG // _103 past_elect elect_at frozen total_stakes ds credits elect _104 stake_held _109 + 32 STU // _103 past_elect elect_at frozen total_stakes ds credits elect _104 _111 + 256 STU // _103 past_elect elect_at frozen total_stakes ds credits elect _113 + s1 s5 XCHG // _103 past_elect elect_at elect total_stakes ds credits frozen _113 + STDICT // _103 past_elect elect_at elect total_stakes ds credits _114 + s0 s3 XCHG2 // _103 past_elect elect_at elect credits ds _114 total_stakes + STGRAMS // _103 past_elect elect_at elect credits ds _115 + 0 PUSHINT // _103 past_elect elect_at elect credits ds _115 _116=0 + STGRAMS // _103 past_elect elect_at elect credits ds _117 + s1 s6 XCHG // ds past_elect elect_at elect credits _103 _117 + 1 STI // ds past_elect elect_at elect credits _119 + s0 s3 s4 XCHG3 + 32 PUSHINT // ds elect credits _119 elect_at past_elect _120=32 + DICTUSETB // ds elect credits past_elect + NEWC // ds elect credits past_elect _122 + s1 s3 XCHG // ds past_elect credits elect _122 + STDICT // ds past_elect credits _123 + STDICT // ds past_elect _124 + STDICT // ds _125 + SWAP // _125 ds + STSLICER // _126 + ENDC // _127 + c4 POP + TRUE // _129 + }> + update_active_vset_id PROC:<{ + // + load_data CALLDICT // elect credits past_elect grams active_id active_hash + 34 PUSHINT // elect credits past_elect grams active_id active_hash _8=34 + CONFIGOPTPARAM // elect credits past_elect grams active_id active_hash _9 + HASHCU // elect credits past_elect grams active_id active_hash cur_hash + s0 s1 PUSH2 // elect credits past_elect grams active_id active_hash cur_hash cur_hash active_hash + EQUAL // elect credits past_elect grams active_id active_hash cur_hash _11 + IFJMP:<{ // elect credits past_elect grams active_id active_hash cur_hash + 7 BLKDROP // + FALSE // _12 + }> // elect credits past_elect grams active_id active_hash cur_hash + s2 PUSH // elect credits past_elect grams active_id active_hash cur_hash active_id + IF:<{ // elect credits past_elect grams active_id active_hash cur_hash + s2 s4 PUSH2 + 32 PUSHINT // elect credits past_elect grams active_id active_hash cur_hash active_id past_elect _15=32 + DICTUGET + NULLSWAPIFNOT // elect credits past_elect grams active_id active_hash cur_hash fs f + IF:<{ // elect credits past_elect grams active_id active_hash cur_hash fs + 32 LDU // elect credits past_elect grams active_id active_hash cur_hash _99 _98 + NIP // elect credits past_elect grams active_id active_hash cur_hash fs + DUP // elect credits past_elect grams active_id active_hash cur_hash fs0 fs + 32 LDU // elect credits past_elect grams active_id active_hash cur_hash fs0 _24 fs + 256 LDU // elect credits past_elect grams active_id active_hash cur_hash fs0 _24 _103 _102 + DROP // elect credits past_elect grams active_id active_hash cur_hash fs0 stake_held hash + s0 s4 XCHG2 // elect credits past_elect grams active_id stake_held cur_hash fs0 hash active_hash + EQUAL // elect credits past_elect grams active_id stake_held cur_hash fs0 _31 + 57 THROWIFNOT + NOW // elect credits past_elect grams active_id stake_held cur_hash fs0 _33 + s0 s3 XCHG2 // elect credits past_elect grams active_id fs0 cur_hash _33 stake_held + ADD // elect credits past_elect grams active_id fs0 cur_hash unfreeze_time + NEWC // elect credits past_elect grams active_id fs0 cur_hash unfreeze_time _36 + 32 STU // elect credits past_elect grams active_id fs0 cur_hash _38 + ROT // elect credits past_elect grams active_id cur_hash _38 fs0 + STSLICER // elect credits past_elect grams active_id cur_hash _39 + s0 s0 s4 XCHG3 + 32 PUSHINT // elect credits cur_hash grams _39 active_id past_elect _40=32 + DICTUSETB // elect credits cur_hash grams past_elect + }>ELSE<{ + s1 s5 XCHG + s1 s3 XCHG + 3 BLKDROP // elect credits cur_hash grams past_elect + }> + }>ELSE<{ + s4 s1 s4 XCHG3 + 2DROP // elect credits cur_hash grams past_elect + }> + -1 PUSHINT // elect credits cur_hash grams past_elect id=-1 + UNTIL:<{ + OVER + 32 PUSHINT // elect credits cur_hash grams past_elect id past_elect _46=32 + DICTUGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT // elect credits cur_hash grams past_elect fs id f + DUP // elect credits cur_hash grams past_elect fs id f f + IF:<{ // elect credits cur_hash grams past_elect fs id f + s0 s2 XCHG // elect credits cur_hash grams past_elect f id fs + 64 LDU // elect credits cur_hash grams past_elect f id _50 fs + 256 LDU // elect credits cur_hash grams past_elect f id tm hash fs + s1 s7 PUSH2 // elect credits cur_hash grams past_elect f id tm hash fs hash cur_hash + EQUAL // elect credits cur_hash grams past_elect f id tm hash fs _56 + IF:<{ // elect credits cur_hash grams past_elect f id tm hash fs + s4 POP // elect credits cur_hash grams past_elect fs id tm hash + s0 s3 XCHG // elect credits cur_hash grams past_elect hash id tm fs + LDDICT // elect credits cur_hash grams past_elect hash id tm _60 fs + LDGRAMS // elect credits cur_hash grams past_elect hash id tm _60 _62 fs + LDGRAMS // elect credits cur_hash grams past_elect hash id tm dict total_stake bonuses fs + s8 PUSH // elect credits cur_hash grams past_elect hash id tm dict total_stake bonuses fs grams + 3 RSHIFT# // elect credits cur_hash grams past_elect hash id tm dict total_stake bonuses fs amount + s9 s9 XCPU // elect credits cur_hash amount past_elect hash id tm dict total_stake bonuses fs grams amount + SUB // elect credits cur_hash amount past_elect hash id tm dict total_stake bonuses fs grams + s2 s9 XCHG2 // elect credits cur_hash grams past_elect hash id tm dict total_stake fs bonuses amount + ADD // elect credits cur_hash grams past_elect hash id tm dict total_stake fs bonuses + NEWC // elect credits cur_hash grams past_elect hash id tm dict total_stake fs bonuses _72 + s1 s5 XCHG // elect credits cur_hash grams past_elect hash id bonuses dict total_stake fs tm _72 + 64 STU // elect credits cur_hash grams past_elect hash id bonuses dict total_stake fs _74 + s1 s6 XCHG // elect credits cur_hash grams past_elect fs id bonuses dict total_stake hash _74 + 256 STU // elect credits cur_hash grams past_elect fs id bonuses dict total_stake _76 + s1 s2 XCHG // elect credits cur_hash grams past_elect fs id bonuses total_stake dict _76 + STDICT // elect credits cur_hash grams past_elect fs id bonuses total_stake _77 + SWAP // elect credits cur_hash grams past_elect fs id bonuses _77 total_stake + STGRAMS // elect credits cur_hash grams past_elect fs id bonuses _78 + SWAP // elect credits cur_hash grams past_elect fs id _78 bonuses + STGRAMS // elect credits cur_hash grams past_elect fs id _79 + ROT // elect credits cur_hash grams past_elect id _79 fs + STSLICER // elect credits cur_hash grams past_elect id _80 + SWAP + 32 PUSHINT + s1 s3 s3 PUXC2 // elect credits cur_hash grams id _80 id past_elect _81=32 + DICTUSETB // elect credits cur_hash grams id past_elect + FALSE // elect credits cur_hash grams id past_elect f + }>ELSE<{ + s3 s5 XCHG + s3 s4 XCHG + 3 BLKDROP // elect credits cur_hash grams id past_elect f + }> + }>ELSE<{ + s3 s0 s3 XCHG3 + DROP // elect credits cur_hash grams id past_elect f + }> + NOT // elect credits cur_hash grams id past_elect _84 + s1 s2 XCHG // elect credits cur_hash grams past_elect id _84 + }> // elect credits cur_hash grams past_elect id + DUP // elect credits cur_hash grams past_elect id id + ISNULL // elect credits cur_hash grams past_elect id _85 + IF:<{ // elect credits cur_hash grams past_elect id + DROP // elect credits cur_hash grams past_elect + 0 PUSHINT // elect credits cur_hash grams past_elect _86=0 + }>ELSE<{ // elect credits cur_hash grams past_elect _86 + }> // elect credits active_hash grams past_elect active_id + s0 s3 XCHG2 // elect credits past_elect grams active_id active_hash + store_data CALLDICT + TRUE // _89 + }> + cell_hash_eq? PROC:<{ + // vset expected_vset_hash + OVER // vset expected_vset_hash vset + ISNULL // vset expected_vset_hash _2 + IF:<{ // vset expected_vset_hash + 2DROP // + FALSE // _3 + }>ELSE<{ // vset expected_vset_hash + SWAP // expected_vset_hash vset + HASHCU // expected_vset_hash _5 + SWAP // _5 expected_vset_hash + EQUAL // _3 + }> + }> + validator_set_installed PROC:<{ + // ds elect credits + SWAP // ds credits elect + unpack_elect CALLDICT // ds credits _44 _45 _46 _47 _48 _49 _50 + s0 s5 XCHG + 5 BLKDROP // ds credits elect_at finished + IFNOTJMP:<{ // ds credits elect_at + 3 BLKDROP // + FALSE // _11 + }> // ds credits elect_at + s0 s2 XCHG // elect_at credits ds + LDDICT // elect_at credits past_elections ds + s3 s1 XCPU + 32 PUSHINT // ds credits past_elections elect_at past_elections _17=32 + DICTUGET + NULLSWAPIFNOT // ds credits past_elections fs f + IFNOTJMP:<{ // ds credits past_elections fs + 4 BLKDROP // + FALSE // _19 + }> // ds credits past_elections fs + 64 PUSHINT // ds credits past_elections fs _21=64 + SDSKIPFIRST // ds credits past_elections _22 + 256 PLDU // ds credits past_elections vset_hash + 34 PUSHINT // ds credits past_elections vset_hash _25=34 + CONFIGOPTPARAM // ds credits past_elections vset_hash _26 + OVER // ds credits past_elections vset_hash _26 vset_hash + cell_hash_eq? CALLDICT // ds credits past_elections vset_hash _27 + 36 PUSHINT // ds credits past_elections vset_hash _27 _28=36 + CONFIGOPTPARAM // ds credits past_elections vset_hash _27 _29 + ROT // ds credits past_elections _27 _29 vset_hash + cell_hash_eq? CALLDICT // ds credits past_elections _27 _30 + OR // ds credits past_elections _31 + IFJMP:<{ // ds credits past_elections + FALSE // ds credits past_elections _32 + NEWC // ds credits past_elections _32 _33 + 1 STI // ds credits past_elections _35 + s1 s2 XCHG // ds past_elections credits _35 + STDICT // ds past_elections _36 + STDICT // ds _37 + SWAP // _37 ds + STSLICER // _38 + ENDC // _39 + c4 POP + update_active_vset_id CALLDICT // _41 + DROP // + TRUE // _42 + }> // ds credits past_elections + 3 BLKDROP // + FALSE // _43 + }> + check_unfreeze PROC:<{ + // + load_data CALLDICT // elect credits past_elect grams active_id active_hash + -1 PUSHINT // elect credits past_elect grams active_id active_hash id=-1 + UNTIL:<{ + s4 PUSH + 32 PUSHINT // elect credits past_elect grams active_id active_hash id past_elect _11=32 + DICTUGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT // elect credits past_elect grams active_id active_hash fs id f + DUP // elect credits past_elect grams active_id active_hash fs id f f + IF:<{ // elect credits past_elect grams active_id active_hash fs id f + s0 s2 XCHG // elect credits past_elect grams active_id active_hash f id fs + 32 LDU // elect credits past_elect grams active_id active_hash f id _39 _38 + DROP // elect credits past_elect grams active_id active_hash f id unfreeze_at + NOW // elect credits past_elect grams active_id active_hash f id unfreeze_at _17 + LEQ // elect credits past_elect grams active_id active_hash f id _18 + s1 s4 PUSH2 // elect credits past_elect grams active_id active_hash f id _18 id active_id + NEQ // elect credits past_elect grams active_id active_hash f id _18 _19 + AND // elect credits past_elect grams active_id active_hash f id _20 + IF:<{ // elect credits past_elect grams active_id active_hash f id + NIP // elect credits past_elect grams active_id active_hash id + s5 s4 s4 XC2PU // elect active_hash id grams active_id credits past_elect id + unfreeze_all CALLDICT // elect active_hash id grams active_id credits past_elect unused_prizes + s1 s4 XCHG // elect active_hash id past_elect active_id credits grams unused_prizes + ADD // elect active_hash id past_elect active_id credits grams + s6 s1 s3 PUSH3 + s3 s5 s8 PUSH3 // elect active_hash id past_elect active_id credits grams elect credits past_elect grams active_id active_hash + store_data CALLDICT + FALSE // elect active_hash id past_elect active_id credits grams f + }>ELSE<{ + s6 s1 s5 XCHG3 + s4 s4 XCHG2 // elect active_hash id past_elect active_id credits grams f + }> + }>ELSE<{ + s3 s7 XCHG + s1 s6 XCHG + s5 s0 s5 XCHG3 + DROP // elect active_hash id past_elect active_id credits grams f + }> + NOT // elect active_hash id past_elect active_id credits grams _26 + s2 s6 XCHG + s4 s5 XCHG + s1 s4 XCHG // elect credits past_elect grams active_id active_hash id _26 + }> // elect credits past_elect grams active_id active_hash id + s0 s6 XCHG + 6 BLKDROP // id + ISNULL // _27 + NOT // _28 + }> + announce_new_elections PROC:<{ + // ds elect credits + NIP // ds credits + 36 PUSHINT // ds credits _4=36 + CONFIGOPTPARAM // ds credits next_vset + ISNULL // ds credits _6 + IFNOTJMP:<{ // ds credits + 2DROP // + FALSE // _7 + }> // ds credits + 1 PUSHINT // ds credits _9=1 + CONFIGOPTPARAM // ds credits _10 + CTOS // ds credits _11 + 256 PLDU // ds credits elector_addr + MYADDR // ds credits elector_addr _16 + REWRITESTDADDR // ds credits elector_addr my_wc my_addr + SWAP // ds credits elector_addr my_addr my_wc + INC // ds credits elector_addr my_addr _19 + s0 s2 XCHG // ds credits _19 my_addr elector_addr + NEQ // ds credits _19 _20 + OR // ds credits _21 + IFJMP:<{ // ds credits + 2DROP // + FALSE // _22 + }> // ds credits + 34 PUSHINT // ds credits _24=34 + CONFIGOPTPARAM // ds credits cur_vset + DUP // ds credits cur_vset cur_vset + ISNULL // ds credits cur_vset _26 + IFJMP:<{ // ds credits cur_vset + 3 BLKDROP // + FALSE // _27 + }> // ds credits cur_vset + get_validator_conf CALLDICT // ds credits cur_vset _76 _77 _78 _79 + DROP + s2 POP // ds credits cur_vset elect_end_before elect_begin_before + s0 s2 XCHG // ds credits elect_begin_before elect_end_before cur_vset + CTOS // ds credits elect_begin_before elect_end_before _34 + 40 PUSHINT // ds credits elect_begin_before elect_end_before _34 _37 + SDSKIPFIRST // ds credits elect_begin_before elect_end_before _38 + 32 PLDU // ds credits elect_begin_before elect_end_before cur_valid_until + NOW // ds credits elect_begin_before elect_end_before cur_valid_until t + s1 s3 XCPU // ds credits elect_begin_before elect_end_before t cur_valid_until elect_begin_before + SUB // ds credits elect_begin_before elect_end_before t t0 + 2DUP // ds credits elect_begin_before elect_end_before t t0 t t0 + LESS // ds credits elect_begin_before elect_end_before t t0 _45 + IFJMP:<{ // ds credits elect_begin_before elect_end_before t t0 + 6 BLKDROP // + FALSE // _46 + }> // ds credits elect_begin_before elect_end_before t t0 + 2DUP // ds credits elect_begin_before elect_end_before t t0 t t0 + SUB // ds credits elect_begin_before elect_end_before t t0 _47 + 60 LESSINT // ds credits elect_begin_before elect_end_before t t0 _49 + IF:<{ // ds credits elect_begin_before elect_end_before t t0 + NIP // ds credits elect_begin_before elect_end_before t + }>ELSE<{ + DROP // ds credits elect_begin_before elect_end_before t + }> + 17 PUSHINT // ds credits elect_begin_before elect_end_before t _52=17 + CONFIGOPTPARAM // ds credits elect_begin_before elect_end_before t _53 + CTOS // ds credits elect_begin_before elect_end_before t _54 + LDGRAMS // ds credits elect_begin_before elect_end_before t _81 _80 + DROP // ds credits elect_begin_before elect_end_before t min_stake + s0 s3 XCHG // ds credits min_stake elect_end_before t elect_begin_before + ADD // ds credits min_stake elect_end_before elect_at + s0 DUMP // ds credits min_stake elect_end_before elect_at + s0 s1 PUXC // ds credits min_stake elect_at elect_at elect_end_before + SUB // ds credits min_stake elect_at elect_close + 0 PUSHINT // ds credits min_stake elect_at elect_close _62=0 + NEWDICT // ds credits min_stake elect_at elect_close _62=0 _63 + FALSE // ds credits min_stake elect_at elect_close _62=0 _63 _64 + s4 s5 XCHG + s3 s4 XCHG + FALSE // ds credits elect_at elect_close min_stake _62=0 _63 _64 _65 + pack_elect CALLDICT // ds credits elect + NEWC // ds credits elect _67 + STDICT // ds credits _68 + STDICT // ds _69 + SWAP // _69 ds + STSLICER // _70 + ENDC // _71 + c4 POP + TRUE // _73 + }> + run_ticktock PROC:<{ + // is_tock + DROP // + c4 PUSH // _2 + CTOS // ds + LDDICT // _6 ds + LDDICT // elect credits ds + s2 PUSH // elect credits ds elect + ISNULL // elect credits ds _10 + IF:<{ // elect credits ds + -ROT // ds elect credits + announce_new_elections CALLDICT // _18 + 0 THROWIF + }>ELSE<{ // elect credits ds + s0 s2 s1 PUSH3 // elect credits ds ds elect credits + conduct_elections CALLDICT // elect credits ds _12 + 0 THROWIF + -ROT // ds elect credits + validator_set_installed CALLDICT // _15 + 0 THROWIF + }> + update_active_vset_id CALLDICT // _21 + 0 THROWIF + check_unfreeze CALLDICT // _23 + DROP // + }> + active_election_id PROC:<{ + // + c4 PUSH // _1 + CTOS // _2 + PLDDICT // elect + DUP // elect elect + ISNULL // elect _4 + IF:<{ // elect + DROP // + 0 PUSHINT // _5=0 + }>ELSE<{ // elect + CTOS // _7 + 32 PLDU // _5 + }> + }> + participates_in PROC:<{ + // validator_pubkey + c4 PUSH // validator_pubkey _2 + CTOS // validator_pubkey _3 + PLDDICT // validator_pubkey elect + DUP // validator_pubkey elect elect + ISNULL // validator_pubkey elect _5 + IFJMP:<{ // validator_pubkey elect + 2DROP // + 0 PUSHINT // _6=0 + }> // validator_pubkey elect + unpack_elect CALLDICT // validator_pubkey _23 _24 _25 _26 _27 _28 _29 + s2 s6 XCHG + 6 BLKDROP // validator_pubkey members + 8 PUSHPOW2 // validator_pubkey members _17=256 + DICTUGET + NULLSWAPIFNOT // mem found + IF:<{ // mem + LDGRAMS // _33 _32 + DROP // _19 + }>ELSE<{ // mem + DROP // + 0 PUSHINT // _19=0 + }> + }> + participant_list PROC:<{ + // + c4 PUSH // _1 + CTOS // _2 + PLDDICT // elect + DUP // elect elect + ISNULL // elect _4 + IFJMP:<{ // elect + DROP // + PUSHNULL // _5 + }> // elect + unpack_elect CALLDICT // _35 _36 _37 _38 _39 _40 _41 + s2 s6 XCHG + 6 BLKDROP // members + PUSHNULL // members l + 256 PUSHPOW2DEC // members l id + UNTIL:<{ + s2 PUSH + 8 PUSHPOW2 // members l id members _28=256 + DICTUGETPREV + NULLSWAPIFNOT + NULLSWAPIFNOT // members l fs id f + DUP // members l fs id f f + IF:<{ // members l fs id f + s0 s2 XCHG // members l f id fs + LDGRAMS // members l f id _46 _45 + DROP // members l f id _30 + s1 s(-1) PUXC // members l f id id _30 + PAIR // members l f id _32 + s0 s3 XCHG2 // members id f _32 l + CONS // members id f l + }>ELSE<{ + 2SWAP + DROP // members id f l + }> + SWAP // members id l f + NOT // members id l _34 + s1 s2 XCHG // members l id _34 + }> // members l id + DROP + NIP // l + }> + compute_returned_stake PROC:<{ + // wallet_addr + c4 PUSH // wallet_addr _2 + CTOS // wallet_addr cs + LDDICT // wallet_addr _19 _18 + NIP // wallet_addr cs + LDDICT // wallet_addr _21 _20 + DROP // wallet_addr credits + 8 PUSHPOW2 // wallet_addr credits _12=256 + DICTUGET + NULLSWAPIFNOT // val f + IF:<{ // val + LDGRAMS // _25 _24 + DROP // _14 + }>ELSE<{ // val + DROP // + 0 PUSHINT // _14=0 + }> + }> +}END>c diff --git a/crypto/smartcont/auto/highload-wallet-code.cpp b/crypto/smartcont/auto/highload-wallet-code.cpp new file mode 100644 index 0000000000..ecc0b71d8e --- /dev/null +++ b/crypto/smartcont/auto/highload-wallet-code.cpp @@ -0,0 +1 @@ +with_tvm_code("highload-wallet", "te6ccgEBBgEAhgABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhghgBD0eG+hb6EgmALTB9QwAfsAkTLiAbPmWwGkyMsfyx/L/8ntVAAE0DAAEaCZL9qJoa4WPw=="); diff --git a/crypto/smartcont/auto/highload-wallet-code.fif b/crypto/smartcont/auto/highload-wallet-code.fif new file mode 100644 index 0000000000..d2122a96fb --- /dev/null +++ b/crypto/smartcont/auto/highload-wallet-code.fif @@ -0,0 +1,79 @@ +// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-code.fc` +PROGRAM{ + DECLPROC recv_internal + DECLPROC recv_external + 85143 DECLMETHOD seqno + recv_internal PROC:<{ + // in_msg + DROP // + }> + recv_external PROC:<{ + // in_msg + 9 PUSHPOW2 // in_msg _3=512 + LDSLICEX // signature in_msg + DUP // signature in_msg cs + 32 LDU // signature in_msg _9 cs + 32 LDU // signature in_msg _9 _12 cs + 32 LDU // signature in_msg subwallet_id valid_until msg_seqno cs + NOW // signature in_msg subwallet_id valid_until msg_seqno cs _19 + s1 s3 XCHG // signature in_msg subwallet_id cs msg_seqno valid_until _19 + LEQ // signature in_msg subwallet_id cs msg_seqno _20 + 35 THROWIF + c4 PUSH // signature in_msg subwallet_id cs msg_seqno _23 + CTOS // signature in_msg subwallet_id cs msg_seqno ds + 32 LDU // signature in_msg subwallet_id cs msg_seqno _28 ds + 32 LDU // signature in_msg subwallet_id cs msg_seqno _28 _31 ds + 256 LDU // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key ds + ENDS + s3 s2 XCPU // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet msg_seqno stored_seqno + EQUAL // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet _39 + 33 THROWIFNOT + s4 s4 XCPU // signature in_msg stored_subwallet cs public_key stored_seqno subwallet_id stored_subwallet + EQUAL // signature in_msg stored_subwallet cs public_key stored_seqno _42 + 34 THROWIFNOT + s0 s4 XCHG // signature stored_seqno stored_subwallet cs public_key in_msg + HASHSU // signature stored_seqno stored_subwallet cs public_key _45 + s0 s5 s5 XC2PU // public_key stored_seqno stored_subwallet cs _45 signature public_key + CHKSIGNU // public_key stored_seqno stored_subwallet cs _46 + 35 THROWIFNOT + LDDICT // public_key stored_seqno stored_subwallet dict cs + ENDS + ACCEPT + -1 PUSHINT // public_key stored_seqno stored_subwallet dict i=-1 + UNTIL:<{ + OVER + 16 PUSHINT // public_key stored_seqno stored_subwallet dict i dict _57=16 + DICTIGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT // public_key stored_seqno stored_subwallet dict cs i f + DUP // public_key stored_seqno stored_subwallet dict cs i f f + IF:<{ // public_key stored_seqno stored_subwallet dict cs i f + s0 s2 XCHG // public_key stored_seqno stored_subwallet dict f i cs + 8 LDU // public_key stored_seqno stored_subwallet dict f i mode cs + LDREF // public_key stored_seqno stored_subwallet dict f i mode _100 _99 + DROP // public_key stored_seqno stored_subwallet dict f i mode _63 + SWAP // public_key stored_seqno stored_subwallet dict f i _63 mode + SENDRAWMSG + }>ELSE<{ + s2 POP // public_key stored_seqno stored_subwallet dict f i + }> + SWAP // public_key stored_seqno stored_subwallet dict i f + NOT // public_key stored_seqno stored_subwallet dict i _66 + }> // public_key stored_seqno stored_subwallet dict i + 2DROP // public_key stored_seqno stored_subwallet + SWAP // public_key stored_subwallet stored_seqno + INC // public_key stored_subwallet _68 + NEWC // public_key stored_subwallet _68 _69 + 32 STU // public_key stored_subwallet _71 + 32 STU // public_key _73 + 256 STU // _75 + ENDC // _76 + c4 POP + }> + seqno PROC:<{ + // + c4 PUSH // _0 + CTOS // _1 + 32 PLDU // _3 + }> +}END>c diff --git a/crypto/smartcont/auto/highload-wallet-v2-code.cpp b/crypto/smartcont/auto/highload-wallet-v2-code.cpp new file mode 100644 index 0000000000..ca5d2c70f7 --- /dev/null +++ b/crypto/smartcont/auto/highload-wallet-v2-code.cpp @@ -0,0 +1 @@ +with_tvm_code("highoad-wallet-v2", "te6ccgEBBwEA1wABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQHu8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44YIYAQ9HhvoW+hIJgC0wfUMAH7AJEy4gGz5luDJaHIQDSAQPRDiuYxyBLLHxPLP8v/9ADJ7VQGAATQMABBoZfl2omhpj5jpn+n/mPoCaKkQQCB6BzfQmMktv8ld0fFADoggED0lm+hb6EyURCUMFMDud4gkjM2k0MTW+IBsw=="); diff --git a/crypto/smartcont/auto/highload-wallet-v2-code.fif b/crypto/smartcont/auto/highload-wallet-v2-code.fif new file mode 100644 index 0000000000..9d5e467de5 --- /dev/null +++ b/crypto/smartcont/auto/highload-wallet-v2-code.fif @@ -0,0 +1,134 @@ +// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-v2-code.fc` +PROGRAM{ + DECLPROC recv_internal + DECLPROC recv_external + 117746 DECLMETHOD processed? + recv_internal PROC:<{ + // in_msg + DROP // + }> + recv_external PROC:<{ + // in_msg + 9 PUSHPOW2 // in_msg _3=512 + LDSLICEX // signature in_msg + DUP // signature in_msg cs + 32 LDU // signature in_msg _8 cs + 64 LDU // signature in_msg subwallet_id query_id cs + NOW // signature in_msg subwallet_id query_id cs _15 + 32 LSHIFT# // signature in_msg subwallet_id query_id cs bound + s2 s0 PUSH2 // signature in_msg subwallet_id query_id cs bound query_id bound + LESS // signature in_msg subwallet_id query_id cs bound _19 + 35 THROWIF + c4 PUSH // signature in_msg subwallet_id query_id cs bound _22 + CTOS // signature in_msg subwallet_id query_id cs bound ds + 32 LDU // signature in_msg subwallet_id query_id cs bound _28 ds + 64 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 ds + 256 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 _34 ds + LDDICT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries ds + ENDS + s6 s0 PUSH2 + 64 PUSHINT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries query_id old_queries _42=64 + DICTUGET + NULLSWAPIFNOT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries _115 _116 + NIP // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries found? + 32 THROWIF + s7 s3 XCPU // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key subwallet_id stored_subwallet + EQUAL // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key _47 + 34 THROWIFNOT + s0 s7 XCHG // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned in_msg + HASHSU // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned _50 + s0 s8 s7 XC2PU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _50 signature public_key + CHKSIGNU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _51 + 35 THROWIFNOT + s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound cs + LDDICT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs + ENDS + ACCEPT + -1 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i=-1 + UNTIL:<{ + OVER + 16 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i dict _62=16 + DICTIGETNEXT + NULLSWAPIFNOT + NULLSWAPIFNOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f + DUP // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f f + IF:<{ // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f + s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i cs + 8 LDU // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode cs + LDREF // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _125 _124 + DROP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _68 + SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i _68 mode + SENDRAWMSG + }>ELSE<{ + s2 POP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i + }> + SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict i f + NOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i _71 + }> // last_cleaned public_key old_queries query_id stored_subwallet bound dict i + 2DROP // last_cleaned public_key old_queries query_id stored_subwallet bound + 38 PUSHPOW2 // last_cleaned public_key old_queries query_id stored_subwallet bound _74 + SUB // last_cleaned public_key old_queries query_id stored_subwallet bound + NEWC // last_cleaned public_key old_queries query_id stored_subwallet bound _77 + s0 s3 s4 XCHG3 + 64 PUSHINT // last_cleaned public_key stored_subwallet bound _77 query_id old_queries _78=64 + DICTUSETB // last_cleaned public_key stored_subwallet bound old_queries + UNTIL:<{ + DUP + 64 PUSHINT // last_cleaned public_key stored_subwallet bound old_queries old_queries _85=64 + DICTUREMMIN + NULLSWAPIFNOT + NULLSWAPIFNOT // last_cleaned public_key stored_subwallet bound old_queries _126 _128 _127 _129 + s2 POP // last_cleaned public_key stored_subwallet bound old_queries old_queries' f i + s1 s0 XCPU // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f + IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f + DROP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i + s0 s3 PUSH2 // last_cleaned public_key stored_subwallet bound old_queries old_queries' i i bound + LESS // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f + }> // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f + DUP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f + IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f + s3 POP + s6 POP // last_cleaned public_key stored_subwallet bound f old_queries + }>ELSE<{ + s3 s1 s3 XCHG3 + 2DROP // last_cleaned public_key stored_subwallet bound f old_queries + }> + SWAP // last_cleaned public_key stored_subwallet bound old_queries f + NOT // last_cleaned public_key stored_subwallet bound old_queries _90 + }> // last_cleaned public_key stored_subwallet bound old_queries + NIP // last_cleaned public_key stored_subwallet old_queries + NEWC // last_cleaned public_key stored_subwallet old_queries _91 + s1 s2 XCHG // last_cleaned public_key old_queries stored_subwallet _91 + 32 STU // last_cleaned public_key old_queries _93 + s1 s3 XCHG // old_queries public_key last_cleaned _93 + 64 STU // old_queries public_key _95 + 256 STU // old_queries _97 + STDICT // _98 + ENDC // _99 + c4 POP + }> + processed? PROC:<{ + // query_id + c4 PUSH // query_id _2 + CTOS // query_id ds + 32 LDU // query_id _29 _28 + NIP // query_id ds + 64 LDU // query_id _11 ds + 256 LDU // query_id _11 _33 _32 + NIP // query_id _11 ds + LDDICT // query_id last_cleaned old_queries ds + ENDS + s2 s(-1) PUXC + 64 PUSHINT // query_id last_cleaned query_id old_queries _22=64 + DICTUGET + NULLSWAPIFNOT // query_id last_cleaned _36 _37 + NIP // query_id last_cleaned found + IF:<{ // query_id last_cleaned + 2DROP // + TRUE // _24 + }>ELSE<{ // query_id last_cleaned + LEQ // _26 + NEGATE // _24 + }> + }> +}END>c diff --git a/crypto/smartcont/auto/simple-wallet-code.cpp b/crypto/smartcont/auto/simple-wallet-code.cpp new file mode 100644 index 0000000000..ea4603f0ae --- /dev/null +++ b/crypto/smartcont/auto/simple-wallet-code.cpp @@ -0,0 +1 @@ +with_tvm_code("simple-wallet", "te6ccgEBBAEATwABFP8A9KQT9KDyyAsBAgEgAgMABNIwAG7ygwjXGCDTH+1E0NMf0//RUTG68qED+QFUEEL5EPKi+ABRINdKltMH1AL7AN7RpMjLH8v/ye1U"); diff --git a/crypto/smartcont/auto/simple-wallet-code.fif b/crypto/smartcont/auto/simple-wallet-code.fif new file mode 100644 index 0000000000..c052d4b656 --- /dev/null +++ b/crypto/smartcont/auto/simple-wallet-code.fif @@ -0,0 +1,45 @@ +// automatically generated from `smartcont/stdlib.fc` `smartcont/simple-wallet-code.fc` +PROGRAM{ + DECLPROC recv_internal + DECLPROC recv_external + recv_internal PROC:<{ + // in_msg + DROP // + }> + recv_external PROC:<{ + // in_msg + 9 PUSHPOW2 // in_msg _3=512 + LDSLICEX // signature in_msg + DUP // signature in_msg cs + 32 LDU // signature in_msg msg_seqno cs + c4 PUSH // signature in_msg msg_seqno cs _11 + CTOS // signature in_msg msg_seqno cs cs2 + 32 LDU // signature in_msg msg_seqno cs stored_seqno cs2 + 256 LDU // signature in_msg msg_seqno cs stored_seqno public_key cs2 + ENDS + s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno + EQUAL // signature in_msg public_key cs stored_seqno _23 + 33 THROWIFNOT + s0 s3 XCHG // signature stored_seqno public_key cs in_msg + HASHSU // signature stored_seqno public_key cs _26 + s0 s4 s2 XC2PU // cs stored_seqno public_key _26 signature public_key + CHKSIGNU // cs stored_seqno public_key _27 + 34 THROWIFNOT + ACCEPT + s2 s0 XCPU // public_key stored_seqno cs cs + SREFS // public_key stored_seqno cs _32 + IF:<{ // public_key stored_seqno cs + 8 LDU // public_key stored_seqno mode cs + LDREF // public_key stored_seqno mode _37 cs + s0 s2 XCHG // public_key stored_seqno cs _37 mode + SENDRAWMSG + }> // public_key stored_seqno cs + ENDS + INC // public_key _42 + NEWC // public_key _42 _43 + 32 STU // public_key _45 + 256 STU // _47 + ENDC // _48 + c4 POP + }> +}END>c diff --git a/crypto/smartcont/auto/wallet-code.cpp b/crypto/smartcont/auto/wallet-code.cpp new file mode 100644 index 0000000000..52fefeeb07 --- /dev/null +++ b/crypto/smartcont/auto/wallet-code.cpp @@ -0,0 +1 @@ +with_tvm_code("wallet", "te6ccgEBBgEAaAABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQCA8oMI1xgg0x/TH/gjErvyY+1E0NMf0//RUTG68qED+QFUEEL5EPKi+AACkyDXSpbTB9QC+wDo0aTIyx/L/8ntVAAE0DAAEaCZL9qJoa4WPw=="); diff --git a/crypto/smartcont/auto/wallet-code.fif b/crypto/smartcont/auto/wallet-code.fif new file mode 100644 index 0000000000..cd9fa5d0f8 --- /dev/null +++ b/crypto/smartcont/auto/wallet-code.fif @@ -0,0 +1,59 @@ +// automatically generated from `smartcont/stdlib.fc` `smartcont/wallet-code.fc` +PROGRAM{ + DECLPROC recv_internal + DECLPROC recv_external + 85143 DECLMETHOD seqno + recv_internal PROC:<{ + // in_msg + DROP // + }> + recv_external PROC:<{ + // in_msg + 9 PUSHPOW2 // in_msg _3=512 + LDSLICEX // signature in_msg + DUP // signature in_msg cs + 32 LDU // signature in_msg _8 cs + 32 LDU // signature in_msg msg_seqno valid_until cs + NOW // signature in_msg msg_seqno valid_until cs _15 + s1 s2 XCHG // signature in_msg msg_seqno cs valid_until _15 + LEQ // signature in_msg msg_seqno cs _16 + 35 THROWIF + c4 PUSH // signature in_msg msg_seqno cs _19 + CTOS // signature in_msg msg_seqno cs ds + 32 LDU // signature in_msg msg_seqno cs _23 ds + 256 LDU // signature in_msg msg_seqno cs stored_seqno public_key ds + ENDS + s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno + EQUAL // signature in_msg public_key cs stored_seqno _31 + 33 THROWIFNOT + s0 s3 XCHG // signature stored_seqno public_key cs in_msg + HASHSU // signature stored_seqno public_key cs _34 + s0 s4 s2 XC2PU // cs stored_seqno public_key _34 signature public_key + CHKSIGNU // cs stored_seqno public_key _35 + 34 THROWIFNOT + ACCEPT + s0 s2 XCHG // public_key stored_seqno cs + WHILE:<{ + DUP // public_key stored_seqno cs cs + SREFS // public_key stored_seqno cs _40 + }>DO<{ // public_key stored_seqno cs + 8 LDU // public_key stored_seqno mode cs + LDREF // public_key stored_seqno mode _45 cs + s0 s2 XCHG // public_key stored_seqno cs _45 mode + SENDRAWMSG + }> // public_key stored_seqno cs + ENDS + INC // public_key _50 + NEWC // public_key _50 _51 + 32 STU // public_key _53 + 256 STU // _55 + ENDC // _56 + c4 POP + }> + seqno PROC:<{ + // + c4 PUSH // _0 + CTOS // _1 + 32 PLDU // _3 + }> +}END>c diff --git a/crypto/smartcont/gen-zerostate-test.fif b/crypto/smartcont/gen-zerostate-test.fif index 2e6d87a75e..6fe3fbed03 100644 --- a/crypto/smartcont/gen-zerostate-test.fif +++ b/crypto/smartcont/gen-zerostate-test.fif @@ -121,7 +121,7 @@ dup make_special dup constant smc3_addr ."address = " x. cr // SmartContract #4 (elector) -"elector-code.fif" include // code in separate source file +"auto/elector-code.fif" include // code in separate source file // data GR$10 // balance: 10 grams 0 // split_depth diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 0248722068..70abe412e0 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -133,7 +133,7 @@ dup make_special dup constant smc3_addr * SmartContract #4 (elector) * */ -"elector-code.fif" include // code in separate source file +"auto/elector-code.fif" include // code in separate source file // data: dict dict dict grams uint32 uint256 empty_cell // libraries GR$10 // balance: 10 grams @@ -202,8 +202,7 @@ smc1_addr config.minter_smc! // 17 add-validator // newkeypair nip dup ."Validator #2 public key = " Bx. cr // 239 add-validator -// 100000 =: orig_vset_valid_for -100 =: orig_vset_valid_for // original validator set valid 100 seconds only (DEBUG) +100000 =: orig_vset_valid_for // original validator set valid 100000 seconds now dup orig_vset_valid_for + 0 config.validators! /* @@ -211,7 +210,7 @@ now dup orig_vset_valid_for + 0 config.validators! * SmartContract #5 (Configuration smart contract) * */ -"config-code.fif" include // code in separate source file +"auto/config-code.fif" include // code in separate source file []" cr + ."Creates a restricted wallet in the masterchain controlled by the private key corresponding to the specified public key" cr + ."and saves its address into .addr ('rwallet.addr' by default)" cr 1 halt +} : usage +$# 1- -2 and ' usage if + +-1 =: wc +$1 parse-pubkey =: PubKey +def? $2 { @' $2 } { "rwallet" } cond constant file-base + +."Creating new restricted wallet in workchain " wc . ."controlled by public key " PubKey .pubkey cr + +// Create new restricted wallet; code taken from `auto/restricted-wallet-code.fif` +"auto/restricted-wallet-code.fif" include // code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."External message for initialization is " B dup Bx. cr +file-base +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/crypto/smartcont/new-restricted-wallet2.fif b/crypto/smartcont/new-restricted-wallet2.fif new file mode 100644 index 0000000000..8f95e4c1cc --- /dev/null +++ b/crypto/smartcont/new-restricted-wallet2.fif @@ -0,0 +1,47 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a restricted lockup wallet in the masterchain controlled by the private key corresponding to the specified public key" cr + ."and saves its address into .addr ('rwallet.addr' by default)" cr 1 halt +} : usage +$# 2- -2 and ' usage if + +-1 =: wc +$1 parse-pubkey =: PubKey +$2 $>GR =: amount +def? $3 { @' $3 } { "rwallet" } cond constant file-base + +."Creating new restricted lockup wallet in workchain " wc . ."controlled by public key " PubKey .pubkey +." with nominal amount " amount .GR cr + +// D x t -- D' +{ idict! not abort"cannot add value" +} : rdict-entry +// balance -- dict +{ dictnew + over -32768 rdict-entry + over 3/4 */ 92 rdict-entry + over 1/2 */ 183 rdict-entry + swap 1/4 */ 366 rdict-entry + 0 548 rdict-entry +} : make-rdict + +// Create new restricted wallet; code taken from `auto/restricted-wallet2-code.fif` +"auto/restricted-wallet-code.fif" include // code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."External message for initialization is " B dup Bx. cr +file-base +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/crypto/smartcont/new-wallet-v2.fif b/crypto/smartcont/new-wallet-v2.fif index f85dc12b47..d59c6c5201 100755 --- a/crypto/smartcont/new-wallet-v2.fif +++ b/crypto/smartcont/new-wallet-v2.fif @@ -13,7 +13,7 @@ def? $2 { @' $2 } { "new-wallet" } cond constant file-base ."Creating new advanced wallet in workchain " wc . cr -// Create new advanced wallet; code adapted from `wallet-code.fif` +// Create new advanced wallet; code adapted from `auto/wallet-code.fif` <{ SETCP0 DUP IFNOTRET // return if recv_internal DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method DROP c4 PUSHCTR CTOS 32 PLDU // cnt diff --git a/crypto/smartcont/restricted-wallet-code.fc b/crypto/smartcont/restricted-wallet-code.fc new file mode 100644 index 0000000000..83639dfdff --- /dev/null +++ b/crypto/smartcont/restricted-wallet-code.fc @@ -0,0 +1,67 @@ +;; Restricted wallet (a variant of wallet-code.fc) +;; until configuration parameter -13 is set, accepts messages only to elector smc + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +_ restricted?() inline { + var p = config_param(-13); + return null?(p) ? true : begin_parse(p).preload_uint(32) > now(); +} + +_ check_destination(msg, dest) inline_ref { + var cs = msg.begin_parse(); + var flags = cs~load_uint(4); + if (flags & 8) { + ;; external messages are always valid + return true; + } + var (s_addr, d_addr) = (cs~load_msg_addr(), cs~load_msg_addr()); + var (dest_wc, dest_addr) = parse_std_addr(d_addr); + return (dest_wc == -1) & (dest_addr == dest); +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + var (msg_seqno, valid_until) = (cs~load_uint(32), cs~load_uint(32)); + throw_if(35, valid_until <= now()); + var ds = get_data().begin_parse(); + var (stored_seqno, public_key) = (ds~load_uint(32), ds~load_uint(256)); + ds.end_parse(); + throw_unless(33, msg_seqno == stored_seqno); + ifnot (msg_seqno) { + accept_message(); + set_data(begin_cell().store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell()); + return (); + } + throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); + accept_message(); + var restrict = restricted?(); + var elector = config_param(1).begin_parse().preload_uint(256); + cs~touch(); + while (cs.slice_refs()) { + var mode = cs~load_uint(8); + var msg = cs~load_ref(); + var ok = true; + if (restrict) { + ok = check_destination(msg, elector); + } + if (ok) { + send_raw_message(msg, mode); + } + } + cs.end_parse(); + set_data(begin_cell().store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell()); +} + +;; Get methods + +int seqno() method_id { + return get_data().begin_parse().preload_uint(32); +} + +int balance() method_id { + return restricted?() ? 0 : get_balance().first(); +} diff --git a/crypto/smartcont/restricted-wallet2-code.fc b/crypto/smartcont/restricted-wallet2-code.fc new file mode 100644 index 0000000000..fa57e77d24 --- /dev/null +++ b/crypto/smartcont/restricted-wallet2-code.fc @@ -0,0 +1,69 @@ +;; Restricted wallet (a variant of wallet-code.fc) +;; until configuration parameter -13 is set, accepts messages only to elector smc + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +_ days_passed() inline { + var p = config_param(-13); + return null?(p) ? -1 : (now() - begin_parse(p).preload_uint(32)) / 86400; +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + var (msg_seqno, valid_until) = (cs~load_uint(32), cs~load_uint(32)); + throw_if(35, valid_until <= now()); + var ds = get_data().begin_parse(); + var (stored_seqno, public_key, rdict) = (ds~load_uint(32), ds~load_uint(256), ds~load_dict()); + ds.end_parse(); + throw_unless(33, msg_seqno == stored_seqno); + ifnot (msg_seqno) { + accept_message(); + set_data(begin_cell() + .store_uint(stored_seqno + 1, 32) + .store_uint(public_key, 256) + .store_dict(rdict) + .end_cell()); + return (); + } + throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); + accept_message(); + var ts = days_passed(); + var (_, value, found) = rdict.idict_get_preveq?(16, ts); + if (found) { + raw_reserve(value~load_grams(), 2); + } + cs~touch(); + while (cs.slice_refs()) { + var mode = cs~load_uint(8); + var msg = cs~load_ref(); + send_raw_message(msg, mode); + } + cs.end_parse(); + set_data(begin_cell() + .store_uint(stored_seqno + 1, 32) + .store_uint(public_key, 256) + .store_dict(rdict) + .end_cell()); +} + +;; Get methods + +int seqno() method_id { + return get_data().begin_parse().preload_uint(32); +} + +int balance() method_id { + var ds = get_data().begin_parse().skip_bits(32 + 256); + var rdict = ds~load_dict(); + ds.end_parse(); + var ts = days_passed(); + var balance = get_balance().first(); + var (_, value, found) = rdict.idict_get_preveq?(16, ts); + if (found) { + balance = max(balance - value~load_grams(), 0); + } + return balance; +} diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index c6e296ff57..ff3c6b9a19 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -3,6 +3,7 @@ int now() asm "NOW"; slice my_address() asm "MYADDR"; +tuple get_balance() asm "BALANCE"; int cell_hash(cell c) asm "HASHCU"; int slice_hash(slice s) asm "HASHSU"; @@ -100,9 +101,13 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va (cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; cell new_dict() asm "NEWDICT"; int dict_empty?(cell c) asm "DICTEMPTY"; diff --git a/crypto/smartcont/update-config-smc.fif b/crypto/smartcont/update-config-smc.fif index 0a6d41fa0a..33e5fbcebc 100755 --- a/crypto/smartcont/update-config-smc.fif +++ b/crypto/smartcont/update-config-smc.fif @@ -2,7 +2,7 @@ "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr - ."Creates a request to simple configuration smart contract requesting to change configuration smart contract code to the one currently stored in config-code.fif, " + ."Creates a request to simple configuration smart contract requesting to change configuration smart contract code to the one currently stored in auto/config-code.fif, " ."with private key loaded from file .pk, " ."and saves it into .boc ('config-query.boc' by default)" cr 1 halt } : usage @@ -12,7 +12,7 @@ $# dup 2 < swap 3 > or ' usage if 0 constant seqno -1 constant idx true constant bounce -"config-code.fif" constant config-source +"auto/config-code.fif" constant config-source 100 constant interval // valid for 100 seconds $1 =: file-base diff --git a/crypto/smartcont/update-elector-smc.fif b/crypto/smartcont/update-elector-smc.fif index 318163ccde..b9ce501f3f 100755 --- a/crypto/smartcont/update-elector-smc.fif +++ b/crypto/smartcont/update-elector-smc.fif @@ -2,7 +2,7 @@ "TonUtil.fif" include { ."usage: " @' $0 type ." []" cr - ."Creates a request to simple configuration smart contract requesting to change elector smart contract code to the one currently stored in elector-code.fif, " + ."Creates a request to simple configuration smart contract requesting to change elector smart contract code to the one currently stored in auto/elector-code.fif, " ."with private key loaded from file .pk, " ."and saves it into .boc ('config-query.boc' by default)" cr 1 halt } : usage @@ -12,7 +12,7 @@ $# dup 2 < swap 3 > or ' usage if 0 constant seqno -1 constant idx true constant bounce -"elector-code.fif" constant elector-source +"auto/elector-code.fif" constant elector-source 100 constant interval // valid for 100 seconds $1 =: file-base diff --git a/doc/tvm.tex b/doc/tvm.tex index 28e8c87d7e..f103b343c4 100644 --- a/doc/tvm.tex +++ b/doc/tvm.tex @@ -2173,11 +2173,13 @@ \section*{Introduction} The following primitives read configuration data provided in the {\em Tuple\/} stored in the first component of the {\em Tuple\/} at {\tt c7}. Whenever TVM is invoked for executing TON Blockchain smart contracts, this {\em Tuple\/} is initialized by a {\em SmartContractInfo\/} structure; configuration primitives assume that it has remained intact. \begin{itemize} \item {\tt F82$i$} --- {\tt GETPARAM $i$} ( -- $x$), returns the $i$-th parameter from the {\em Tuple\/} provided at {\tt c7} for $0\leq i<16$. Equivalent to {\tt PUSH c7}; {\tt FIRST}; {\tt INDEX $i$}. If one of these internal operations fails, throws an appropriate type checking or range checking exception. -\item {\tt F823} --- {\tt NOW} ( -- $x$), returns the current Unix time as an {\em Integer}. If it is impossible to recover the requested value starting from {\tt c7}, throws a type checking or range checking exception as appropriate. Equivalent to {\tt PUSH c7}; {\tt FIRST}; {\tt INDEX 3}. -\item {\tt F824} --- {\tt BLOCKLT} ( -- $x$), returns the starting logical time of the current block. Equivalent to {\tt PUSH c7}; {\tt FIRST}; {\tt INDEX 4}. -\item {\tt F825} --- {\tt LTIME} ( -- $x$), returns the logical time of the current transaction. Equivalent to {\tt PUSH c7}; {\tt FIRST}; {\tt INDEX 5}. -\item {\tt F828} --- {\tt MYADDR} ( -- $s$), returns the internal address of the current smart contract as a {\em Slice\/} with a {\tt MsgAddressInt}. If necessary, it can be parsed further using primitives such as {\tt PARSESTDADDR} or {\tt REWRITESTDADDR}. Equivalent to {\tt PUSH c7}; {\tt FIRST}; {\tt INDEX 8}. -\item {\tt F829} --- {\tt CONFIGROOT} ( -- $D$), returns the {\em Maybe Cell\/}~$D$ with the current global configuration dictionary. Equivalent to {\tt PUSH c7}; {\tt FIRST}; {\tt INDEX 9}. +\item {\tt F823} --- {\tt NOW} ( -- $x$), returns the current Unix time as an {\em Integer}. If it is impossible to recover the requested value starting from {\tt c7}, throws a type checking or range checking exception as appropriate. Equivalent to {\tt GETPARAM 3}. +\item {\tt F824} --- {\tt BLOCKLT} ( -- $x$), returns the starting logical time of the current block. Equivalent to {\tt GETPARAM 4}. +\item {\tt F825} --- {\tt LTIME} ( -- $x$), returns the logical time of the current transaction. Equivalent to {\tt GETPARAM 5}. +\item {\tt F826} --- {\tt BALANCE} ( -- $t$), returns the remaining balance of the smart contract as a {\em Tuple\/} consisting of an {\em Integer} (the remaining Gram balance in nanograms) and a {\em Maybe Cell} (a dictionary with 32-bit keys representing the balance of ``extra currencies''). Equivalent to {\tt GETPARAM 6}. Note that {\tt RAW} primitives such as {\tt SENDRAWMSG} do not update this field. +\item {\tt F827} --- {\tt RANDSEED} ( -- $x$), returns the current random seed as an unsigned 256-bit {\em Integer}. Equivalent to {\tt GETPARAM 7}. +\item {\tt F828} --- {\tt MYADDR} ( -- $s$), returns the internal address of the current smart contract as a {\em Slice\/} with a {\tt MsgAddressInt}. If necessary, it can be parsed further using primitives such as {\tt PARSESTDADDR} or {\tt REWRITESTDADDR}. Equivalent to {\tt GETPARAM 8}. +\item {\tt F829} --- {\tt CONFIGROOT} ( -- $D$), returns the {\em Maybe Cell\/}~$D$ with the current global configuration dictionary. Equivalent to {\tt GETPARAM 9}. \item {\tt F830} --- {\tt CONFIGDICT} ( -- $D$ $32$), returns the global configuration dictionary along with its key length (32). Equivalent to {\tt CONFIGROOT}; {\tt PUSHINT 32}. \item {\tt F832} --- {\tt CONFIGPARAM} ($i$ -- $c$ $-1$ or $0$), returns the value of the global configuration parameter with integer index $i$ as a {\em Cell\/}~$c$, and a flag to indicate success. Equivalent to {\tt CONFIGDICT}; {\tt DICTIGETREF}. \item {\tt F833} --- {\tt CONFIGOPTPARAM} ($i$ -- $c^?$), returns the value of the global configuration parameter with integer index $i$ as a {\em Maybe Cell\/}~$c^?$. Equivalent to {\tt CONFIGDICT}; {\tt DICTIGETOPTREF}. diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 3817e987f8..1db283f064 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -1680,7 +1680,7 @@ bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string fi true); LOG(INFO) << "requesting " << params.size() << " configuration parameters with respect to masterchain block " << blkid.to_str(); - return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, filename, + return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, filename, blkid, params = std::move(params) ](td::Result R) mutable { if (R.is_error()) { return; @@ -1690,20 +1690,25 @@ bool TestNode::get_config_params(ton::BlockIdExt blkid, int mode, std::string fi LOG(ERROR) << "cannot parse answer to liteServer.getConfigParams"; } else { auto f = F.move_as_ok(); - td::actor::send_closure_later(Self, &TestNode::got_config_params, ton::create_block_id(f->id_), + td::actor::send_closure_later(Self, &TestNode::got_config_params, blkid, ton::create_block_id(f->id_), std::move(f->state_proof_), std::move(f->config_proof_), mode, filename, std::move(params)); } }); } -void TestNode::got_config_params(ton::BlockIdExt blkid, td::BufferSlice state_proof, td::BufferSlice cfg_proof, - int mode, std::string filename, std::vector params) { +void TestNode::got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof, + td::BufferSlice cfg_proof, int mode, std::string filename, std::vector params) { LOG(INFO) << "got configuration parameters"; if (!blkid.is_masterchain_ext()) { LOG(ERROR) << "reference block " << blkid.to_str() << " for the configuration is not a valid masterchain block"; return; } + if (blkid != req_blkid) { + LOG(ERROR) << "got configuration parameters with respect to block " << blkid.to_str() << " instead of " + << req_blkid.to_str(); + return; + } auto R = block::check_extract_state_proof(blkid, state_proof.as_slice(), cfg_proof.as_slice()); if (R.is_error()) { LOG(ERROR) << "masterchain state proof for " << blkid.to_str() << " is invalid : " << R.move_as_error().to_string(); diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 19fe481e13..36830e8d5b 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -122,8 +122,8 @@ class TestNode : public td::actor::Actor { bool get_all_shards(bool use_last = true, ton::BlockIdExt blkid = {}); void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data); bool get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = ""); - void got_config_params(ton::BlockIdExt blkid, td::BufferSlice state_proof, td::BufferSlice cfg_proof, int mode, - std::string filename, std::vector params); + void got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof, + td::BufferSlice cfg_proof, int mode, std::string filename, std::vector params); bool get_block(ton::BlockIdExt blk, bool dump = false); void got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump); bool get_state(ton::BlockIdExt blk, bool dump = false); diff --git a/tdactor/td/actor/core/ActorExecutor.cpp b/tdactor/td/actor/core/ActorExecutor.cpp index 7554a05d10..8894266204 100644 --- a/tdactor/td/actor/core/ActorExecutor.cpp +++ b/tdactor/td/actor/core/ActorExecutor.cpp @@ -76,7 +76,7 @@ void ActorExecutor::send(ActorSignals signals) { } void ActorExecutor::start() noexcept { - //LOG(ERROR) << "START " << actor_info_.get_name() << " " << tag("from_queue", options.from_queue); + //LOG(ERROR) << "START " << actor_info_.get_name() << " " << tag("from_queue", options_.from_queue); if (is_closed()) { return; } diff --git a/tdactor/td/actor/core/Scheduler.cpp b/tdactor/td/actor/core/Scheduler.cpp index 7e343c4dea..9569fac360 100644 --- a/tdactor/td/actor/core/Scheduler.cpp +++ b/tdactor/td/actor/core/Scheduler.cpp @@ -133,6 +133,7 @@ void Scheduler::ContextImpl::add_to_queue(ActorInfoPtr actor_info_ptr, Scheduler if (!scheduler_id.is_valid()) { scheduler_id = get_scheduler_id(); } + //LOG(ERROR) << "Add to queue: " << actor_info_ptr->get_name() << " " << scheduler_id.value(); auto &info = scheduler_group()->schedulers.at(scheduler_id.value()); if (need_poll || !info.cpu_queue) { info.io_queue->writer_put(std::move(actor_info_ptr)); diff --git a/tdutils/td/utils/CancellationToken.h b/tdutils/td/utils/CancellationToken.h index f3bca2fcde..08f413c4b4 100644 --- a/tdutils/td/utils/CancellationToken.h +++ b/tdutils/td/utils/CancellationToken.h @@ -32,8 +32,13 @@ struct RawCancellationToken { class CancellationToken { public: explicit operator bool() const { + // Empty CancellationToken is never cancelled + if (!token_) { + return false; + } return token_->is_cancelled_.load(std::memory_order_acquire); } + CancellationToken() = default; explicit CancellationToken(std::shared_ptr token) : token_(std::move(token)) { } diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index cf0ed2b837..3fad6c36a6 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -65,7 +65,11 @@ generic.accountStateUninited account_state:uninited.accountState = generic.Accou sendGramsResult sent_until:int53 body_hash:bytes = SendGramsResult; +syncStateDone = SyncState; +syncStateInProgress from_seqno:int32 to_seqno:int32 current_seqno:int32 = SyncState; + updateSendLiteServerQuery id:int64 data:bytes = Update; +updateSyncState sync_state:SyncState = Update; //@class LogStream @description Describes a stream to which TDLib internal log is written @@ -134,6 +138,8 @@ testGiver.getAccountState = testGiver.AccountState; testGiver.getAccountAddress = AccountAddress; testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult; +sync = Ok; + //generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress; generic.getAccountState account_address:accountAddress = generic.AccountState; generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = SendGramsResult; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 1af5d79a941e63ba7697439dba37f678c9d5f5db..c48574b2d510507701d3edaa452b167321fe7267 100644 GIT binary patch delta 369 zcmeCkT9C{8Xtur;0~F|PE=lG+nm{)mY;)pi(y(^^7B#|7(m9{JSOA=GP}r`O^3s_?I1`D zX4zFau9)LTve|^;`aJUjit^KoQj3dWdYOTWrJvo`=#QAlrUV`Gh-v zDcDDF?}F5TT+Ilx8RWY7Vo*qcwW9k1<};AK%_Tw}Jd6gL_sUrdaG&m+JD&xlnSo() MJ-_PaA9@08037mv1^@s6 delta 88 zcmZ3G+mXflXtur;0~F|N*^$bOr1 jvV+hY7LfF017RO9WAhzh4<1JS&8iC40-Glo2(SSF@FpE@ diff --git a/tonlib/tonlib/Client.cpp b/tonlib/tonlib/Client.cpp index c42b19655a..d22e7ac792 100644 --- a/tonlib/tonlib/Client.cpp +++ b/tonlib/tonlib/Client.cpp @@ -56,8 +56,10 @@ class Client::Impl final { std::shared_ptr output_queue_; }; - scheduler_.run_in_context( - [&] { tonlib_ = td::actor::create_actor("Tonlib", td::make_unique(output_queue_)); }); + scheduler_.run_in_context([&] { + tonlib_ = td::actor::create_actor(td::actor::ActorOptions().with_name("Tonlib").with_poll(), + td::make_unique(output_queue_)); + }); scheduler_thread_ = td::thread([&] { scheduler_.run(); }); } @@ -107,7 +109,7 @@ class Client::Impl final { std::atomic receive_lock_{false}; bool is_closed_{false}; - td::actor::Scheduler scheduler_{{0}}; + td::actor::Scheduler scheduler_{{1}}; td::thread scheduler_thread_; td::actor::ActorOwn tonlib_; diff --git a/tonlib/tonlib/ExtClient.cpp b/tonlib/tonlib/ExtClient.cpp index 442d65033a..1ce77337c6 100644 --- a/tonlib/tonlib/ExtClient.cpp +++ b/tonlib/tonlib/ExtClient.cpp @@ -21,6 +21,10 @@ #include "tonlib/LastBlock.h" namespace tonlib { +ExtClient::~ExtClient() { + last_block_queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); }); + queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); }); +} void ExtClient::with_last_block(td::Promise promise) { auto query_id = last_block_queries_.create(std::move(promise)); td::Promise P = [query_id, self = this, diff --git a/tonlib/tonlib/ExtClient.h b/tonlib/tonlib/ExtClient.h index 1547337948..beb0671fbe 100644 --- a/tonlib/tonlib/ExtClient.h +++ b/tonlib/tonlib/ExtClient.h @@ -53,6 +53,7 @@ class ExtClient { ExtClientRef get_client() { return client_; } + ~ExtClient(); void with_last_block(td::Promise promise); diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index d498aa7a25..9740a24abe 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -36,11 +36,22 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state << td::tag("init_block", state.init_block_id.to_str()); } -LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr callback) - : state_(std::move(state)), config_(std::move(config)), callback_(std::move(callback)) { +LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::CancellationToken cancellation_token, + td::unique_ptr callback) + : callback_(std::move(callback)) + , state_(std::move(state)) + , config_(std::move(config)) + , cancellation_token_(std::move(cancellation_token)) { client_.set_client(client); state_.last_block_id = state_.last_key_block_id; + if (state_.last_key_block_id.is_valid()) { + min_seqno_ = state_.last_key_block_id.id.seqno; + } + if (config_.init_block_id.is_valid() && config_.init_block_id != state_.init_block_id) { + min_seqno_ = td::min(min_seqno_, config_.init_block_id.id.seqno); + } + current_seqno_ = min_seqno_; VLOG(last_block) << "State: " << state_; } @@ -61,6 +72,9 @@ void LastBlock::get_last_block(td::Promise promise) { } void LastBlock::sync_loop() { + SCOPE_EXIT { + update_sync_state(); + }; if (promises_.empty()) { return; } @@ -147,7 +161,7 @@ td::Result> LastBlock::process_block_pro return td::Status::Error(PSLICE() << "block proof chain starts from block " << chain->from.to_str() << ", not from requested block " << from.to_str()); } - TRY_STATUS(chain->validate()); + TRY_STATUS(chain->validate(cancellation_token_)); return std::move(chain); } @@ -155,6 +169,8 @@ void LastBlock::update_state(block::BlockProofChain& chain) { // Update state_ bool is_changed = false; is_changed |= update_mc_last_block(chain.to); + current_seqno_ = td::max(current_seqno_, chain.to.id.seqno); + max_seqno_ = td::max(max_seqno_, current_seqno_); if (chain.has_key_block) { is_changed |= update_mc_last_key_block(chain.key_blkid); } @@ -193,10 +209,10 @@ void LastBlock::on_block_proof( if (chain->complete) { VLOG(last_block) << "get_last_block: done\n" << get_last_block_stats_; get_last_block_state_ = QueryState::Done; - sync_loop(); } else { do_get_last_block(); } + sync_loop(); } void LastBlock::on_init_block_proof( @@ -209,6 +225,7 @@ void LastBlock::on_init_block_proof( check_init_block_state_ = QueryState::Empty; VLOG(last_block) << "check_init_block: error " << r_chain.error(); on_sync_error(r_chain.move_as_error_suffix("(during check init block)")); + sync_loop(); return; } auto chain = r_chain.move_as_ok(); @@ -220,10 +237,10 @@ void LastBlock::on_init_block_proof( if (update_init_block(config_.init_block_id)) { save_state(); } - sync_loop(); } else { do_check_init_block(chain->to, to); } + sync_loop(); } void LastBlock::on_masterchain_info( @@ -233,6 +250,7 @@ void LastBlock::on_masterchain_info( update_zero_state(create_zero_state_id(info->init_), "masterchain info"); // last block is not validated! Do not update it get_mc_info_state_ = QueryState::Done; + max_seqno_ = td::max(max_seqno_, (unsigned)info->last_->seqno_); VLOG(last_block) << "get_masterchain_info: done"; } else { get_mc_info_state_ = QueryState::Empty; @@ -334,6 +352,9 @@ void LastBlock::on_sync_ok() { } void LastBlock::on_sync_error(td::Status status) { VLOG(last_block) << "sync: error " << status; + if (cancellation_token_) { + status = TonlibError::Cancelled(); + } for (auto& promise : promises_) { promise.set_error(status.clone()); } @@ -348,4 +369,32 @@ void LastBlock::on_fatal_error(td::Status status) { bool LastBlock::has_fatal_error() const { return fatal_error_.is_error(); } + +LastBlockSyncState LastBlock::get_sync_state() { + LastBlockSyncState state; + if (promises_.empty()) { + state.type = LastBlockSyncState::Done; + return state; + } + state.type = LastBlockSyncState::InProgress; + state.from_seqno = min_seqno_; + state.to_seqno = max_seqno_; + state.current_seqno = current_seqno_; + return state; +} + +void LastBlock::update_sync_state() { + auto new_state = get_sync_state(); + if (new_state == sync_state_) { + return; + } + sync_state_ = new_state; + VLOG(last_block) << "Sync state: " << current_seqno_ - min_seqno_ << " / " << max_seqno_ - min_seqno_; + callback_->on_sync_state_changed(sync_state_); +} + +void LastBlock::tear_down() { + on_sync_error(TonlibError::Cancelled()); +} + } // namespace tonlib diff --git a/tonlib/tonlib/LastBlock.h b/tonlib/tonlib/LastBlock.h index edd8c6f387..e01a7fde71 100644 --- a/tonlib/tonlib/LastBlock.h +++ b/tonlib/tonlib/LastBlock.h @@ -22,6 +22,7 @@ #include "tonlib/Config.h" #include "tonlib/ExtClient.h" +#include "td/utils/CancellationToken.h" #include "td/utils/tl_helpers.h" namespace block { @@ -132,6 +133,20 @@ struct LastBlockState { } }; +struct LastBlockSyncState { + enum Type { Invalid, InProgress, Done } type = Invalid; + td::int32 from_seqno{0}; + td::int32 to_seqno{0}; + td::int32 current_seqno{0}; + + auto as_key() const { + return std::tie(type, from_seqno, to_seqno, current_seqno); + } + bool operator==(const LastBlockSyncState &other) const { + return as_key() == other.as_key(); + } +}; + class LastBlock : public td::actor::Actor { public: class Callback { @@ -139,16 +154,19 @@ class LastBlock : public td::actor::Actor { virtual ~Callback() { } virtual void on_state_changed(LastBlockState state) = 0; + virtual void on_sync_state_changed(LastBlockSyncState state) = 0; }; - explicit LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr callback); + explicit LastBlock(ExtClientRef client, LastBlockState state, Config config, td::CancellationToken cancellation_token, + td::unique_ptr callback); void get_last_block(td::Promise promise); private: + td::unique_ptr callback_; ExtClient client_; LastBlockState state_; Config config_; - td::unique_ptr callback_; + td::CancellationToken cancellation_token_; td::Status fatal_error_; @@ -157,6 +175,11 @@ class LastBlock : public td::actor::Actor { QueryState check_init_block_state_{QueryState::Empty}; // init_block <---> last_key_block (from older to newer) QueryState get_last_block_state_{QueryState::Empty}; // last_key_block_id --> ? + unsigned min_seqno_ = 0; + unsigned current_seqno_ = 0; + unsigned max_seqno_ = 0; + LastBlockSyncState sync_state_; + // stats struct Stats { td::Timer total_sync_; @@ -209,6 +232,10 @@ class LastBlock : public td::actor::Actor { void on_fatal_error(td::Status status); bool has_fatal_error() const; + LastBlockSyncState get_sync_state(); + void update_sync_state(); void sync_loop(); + + void tear_down() override; }; } // namespace tonlib diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index ccf44df640..070630e737 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -166,6 +166,9 @@ class GetTransactionHistory : public td::actor::Actor { hash_), [self = this](auto r_transactions) { self->with_transactions(std::move(r_transactions)); }); } + void hangup() override { + check(TonlibError::Cancelled()); + } }; class GetRawAccountState : public td::actor::Actor { @@ -277,6 +280,9 @@ class GetRawAccountState : public td::actor::Actor { stop(); } } + void hangup() override { + check(TonlibError::Cancelled()); + } }; TonlibClient::TonlibClient(td::unique_ptr callback) : callback_(std::move(callback)) { @@ -284,6 +290,7 @@ TonlibClient::TonlibClient(td::unique_ptr callback) : callback_( TonlibClient::~TonlibClient() = default; void TonlibClient::hangup() { + source_.cancel(); is_closing_ = true; ref_cnt_--; raw_client_ = {}; @@ -300,7 +307,7 @@ ExtClientRef TonlibClient::get_client_ref() { } void TonlibClient::proxy_request(td::int64 query_id, std::string data) { - callback_->on_result(0, tonlib_api::make_object(query_id, data)); + on_update(tonlib_api::make_object(query_id, data)); } void TonlibClient::init_ext_client() { @@ -342,8 +349,28 @@ void TonlibClient::init_ext_client() { } void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 config_generation) { - if (config_generation == config_generation_) { - last_block_storage_.save_state(blockchain_name_, state); + if (config_generation != config_generation_) { + return; + } + last_block_storage_.save_state(blockchain_name_, state); +} + +void TonlibClient::update_sync_state(LastBlockSyncState state, td::uint32 config_generation) { + if (config_generation != config_generation_) { + return; + } + switch (state.type) { + case LastBlockSyncState::Done: + on_update( + tonlib_api::make_object(tonlib_api::make_object())); + break; + case LastBlockSyncState::InProgress: + on_update( + tonlib_api::make_object(tonlib_api::make_object( + state.from_seqno, state.to_seqno, state.current_seqno))); + break; + default: + LOG(ERROR) << "Unknown LastBlockSyncState type " << state.type; } } @@ -357,6 +384,9 @@ void TonlibClient::init_last_block() { void on_state_changed(LastBlockState state) override { send_closure(client_, &TonlibClient::update_last_block_state, std::move(state), config_generation_); } + void on_sync_state_changed(LastBlockSyncState sync_state) override { + send_closure(client_, &TonlibClient::update_sync_state, std::move(sync_state), config_generation_); + } private: td::actor::ActorShared client_; @@ -379,19 +409,23 @@ void TonlibClient::init_last_block() { state = r_state.move_as_ok(); } - raw_last_block_ = - td::actor::create_actor("LastBlock", get_client_ref(), std::move(state), config_, - td::make_unique(td::actor::actor_shared(this), config_generation_)); + raw_last_block_ = td::actor::create_actor( + td::actor::ActorOptions().with_name("LastBlock").with_poll(false), get_client_ref(), std::move(state), config_, + source_.get_cancellation_token(), td::make_unique(td::actor::actor_shared(this), config_generation_)); } void TonlibClient::on_result(td::uint64 id, tonlib_api::object_ptr response) { - VLOG(tonlib_query) << "Tonlib answer query " << td::tag("id", id) << " " << to_string(response); + VLOG_IF(tonlib_query, id != 0) << "Tonlib answer query " << td::tag("id", id) << " " << to_string(response); + VLOG_IF(tonlib_query, id == 0) << "Tonlib update " << to_string(response); if (response->get_id() == tonlib_api::error::ID) { callback_->on_error(id, tonlib_api::move_object_as(response)); return; } callback_->on_result(id, std::move(response)); } +void TonlibClient::on_update(object_ptr response) { + on_result(0, std::move(response)); +} void TonlibClient::request(td::uint64 id, tonlib_api::object_ptr function) { VLOG(tonlib_query) << "Tonlib got query " << td::tag("id", id) << " " << to_string(function); @@ -415,7 +449,9 @@ void TonlibClient::request(td::uint64 id, tonlib_api::object_ptr::ReturnType; - td::Promise promise = [actor_id = actor_id(self), id](td::Result r_result) { + ref_cnt_++; + td::Promise promise = [actor_id = actor_id(self), id, + tmp = actor_shared(self)](td::Result r_result) { tonlib_api::object_ptr result; if (r_result.is_error()) { result = status_to_tonlib_api(r_result.error()); @@ -647,6 +683,7 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request, td::Promise>&& promise) { CHECK(state_ != State::Closed); state_ = State::Closed; + source_.cancel(); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } @@ -1257,6 +1294,9 @@ class GenericSendGrams : public TonlibQueryActor { void start_up() override { check(do_start_up()); } + void hangup() override { + check(TonlibError::Cancelled()); + } td::Status do_start_up() { if (!send_grams_.destination_) { @@ -1426,6 +1466,15 @@ td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, return td::Status::OK(); } +td::Status TonlibClient::do_request(tonlib_api::sync& request, td::Promise>&& promise) { + client_.with_last_block([promise = std::move(promise)](td::Result r_last_block) mutable { + TRY_RESULT_PROMISE(promise, last_block, std::move(r_last_block)); + (void)last_block; + promise.set_value(tonlib_api::make_object()); + }); + return td::Status::OK(); +} + td::Result public_key_from_bytes(td::Slice bytes) { TRY_RESULT_PREFIX(key_bytes, block::PublicKey::from_bytes(bytes), TonlibError::Internal()); return key_bytes; diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index e2ea77d1c6..148f8bae7e 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -29,6 +29,8 @@ #include "td/actor/actor.h" +#include "td/utils/CancellationToken.h" + #include namespace tonlib { @@ -66,6 +68,8 @@ class TonlibClient : public td::actor::Actor { td::actor::ActorOwn raw_last_block_; ExtClient client_; + td::CancellationTokenSource source_; + std::map> actors_; td::int64 actor_id_{1}; @@ -92,7 +96,9 @@ class TonlibClient : public td::actor::Actor { } void update_last_block_state(LastBlockState state, td::uint32 config_generation_); + void update_sync_state(LastBlockSyncState state, td::uint32 config_generation); void on_result(td::uint64 id, object_ptr response); + void on_update(object_ptr response); static bool is_static_request(td::int32 id); static bool is_uninited_request(td::int32 id); template @@ -159,6 +165,8 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(tonlib_api::generic_sendGrams& request, td::Promise>&& promise); + td::Status do_request(tonlib_api::sync& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::createNewKey& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::exportKey& request, td::Promise>&& promise); diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index f681474e76..21f68b4b6a 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -94,7 +94,7 @@ class TonlibCli : public td::actor::Actor { td::actor::ActorShared id_; }; ref_cnt_++; - client_ = td::actor::create_actor("Tonlib", td::make_unique(actor_shared(this))); + client_ = td::actor::create_actor("Tonlib", td::make_unique(actor_shared(this, 1))); td::mkdir(options_.key_dir).ignore(); @@ -144,6 +144,9 @@ class TonlibCli : public td::actor::Actor { void hangup_shared() override { CHECK(ref_cnt_ > 0); ref_cnt_--; + if (get_link_token() == 1) { + io_.reset(); + } try_stop(); } void try_stop() { @@ -221,7 +224,6 @@ class TonlibCli : public td::actor::Actor { generate_key(); } else if (cmd == "exit") { is_closing_ = true; - io_.reset(); client_.reset(); ref_cnt_--; try_stop(); @@ -263,9 +265,20 @@ class TonlibCli : public td::actor::Actor { set_bounceable(addr, to_bool(bounceable, true)); } else if (cmd == "netstats") { dump_netstats(); + } else if (cmd == "sync") { + sync(); } } + void sync() { + using tonlib_api::make_object; + send_query(make_object(), [](auto r_ok) { + LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); + if (r_ok.is_ok()) { + td::TerminalIO::out() << "synchronized\n"; + } + }); + } void dump_netstats() { td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n"; td::TerminalIO::out() << td::tag("rcv", td::format::as_size(rcv_bytes_)) << "\n"; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index 24f7c45d18..d1d816ebc8 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -215,12 +215,21 @@ void ValidatorManagerMasterchainReiniter::choose_masterchain_state() { << " is_persistent=" << (!p || ValidatorManager::is_persistent_state(h->unix_time(), p->unix_time())) << " ttl=" << ValidatorManager::persistent_state_ttl(h->unix_time()) << " syncbefore=" << opts_->sync_blocks_before(); + if (h->unix_time() + opts_->sync_blocks_before() > td::Clocks::system()) { + LOG(INFO) << "ignoring: too new block"; + continue; + } if (!p || ValidatorManager::is_persistent_state(h->unix_time(), p->unix_time())) { auto ttl = ValidatorManager::persistent_state_ttl(h->unix_time()); - if (ttl > td::Clocks::system() + opts_->sync_blocks_before()) { + td::Clocks::Duration time_to_download = 3600; + if (ttl > td::Clocks::system() + time_to_download) { handle = h; break; + } else { + LOG(INFO) << "ignoring: state is expiring shortly: expire_at=" << ttl; } + } else { + LOG(INFO) << "ignoring: state is not persistent"; } } From c50074fd55b64c7e5f300bd3700b03f4879d75f6 Mon Sep 17 00:00:00 2001 From: ton Date: Mon, 14 Oct 2019 11:45:47 +0400 Subject: [PATCH 012/667] fixed bug in initial synchronization --- validator/manager-init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index d1d816ebc8..d96eef778e 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -222,7 +222,7 @@ void ValidatorManagerMasterchainReiniter::choose_masterchain_state() { if (!p || ValidatorManager::is_persistent_state(h->unix_time(), p->unix_time())) { auto ttl = ValidatorManager::persistent_state_ttl(h->unix_time()); td::Clocks::Duration time_to_download = 3600; - if (ttl > td::Clocks::system() + time_to_download) { + if (ttl < td::Clocks::system() + time_to_download) { handle = h; break; } else { From 9c9248a9ae1791152d88c9c36574b4becdf5e8b6 Mon Sep 17 00:00:00 2001 From: ton Date: Wed, 16 Oct 2019 13:00:43 +0400 Subject: [PATCH 013/667] updated func - updated func - deleted autogenerated files from git - updated liteclient - increase state sync timeout --- .gitignore | 1 + crypto/block/check-proof.cpp | 11 +- crypto/block/check-proof.h | 6 +- crypto/block/transaction.cpp | 7 +- crypto/func/builtins.cpp | 100 +- crypto/func/func.h | 25 +- crypto/func/keywords.cpp | 25 +- crypto/func/parse-func.cpp | 118 +- crypto/func/test/a6.fc | 18 +- crypto/func/test/a6_3.fc | 22 + crypto/func/unify-types.cpp | 14 +- crypto/smartcont/auto/config-code.cpp | 1 - crypto/smartcont/auto/config-code.fif | 323 ---- crypto/smartcont/auto/elector-code.cpp | 1 - crypto/smartcont/auto/elector-code.fif | 1712 ----------------- .../smartcont/auto/highload-wallet-code.cpp | 1 - .../smartcont/auto/highload-wallet-code.fif | 79 - .../auto/highload-wallet-v2-code.cpp | 1 - .../auto/highload-wallet-v2-code.fif | 134 -- crypto/smartcont/auto/simple-wallet-code.cpp | 1 - crypto/smartcont/auto/simple-wallet-code.fif | 45 - crypto/smartcont/auto/wallet-code.cpp | 1 - crypto/smartcont/auto/wallet-code.fif | 59 - crypto/smartcont/stdlib.fc | 16 + lite-client/lite-client.cpp | 39 +- lite-client/lite-client.h | 2 + validator/manager.cpp | 3 +- validator/net/download-state.cpp | 2 +- 28 files changed, 302 insertions(+), 2465 deletions(-) create mode 100644 crypto/func/test/a6_3.fc delete mode 100644 crypto/smartcont/auto/config-code.cpp delete mode 100644 crypto/smartcont/auto/config-code.fif delete mode 100644 crypto/smartcont/auto/elector-code.cpp delete mode 100644 crypto/smartcont/auto/elector-code.fif delete mode 100644 crypto/smartcont/auto/highload-wallet-code.cpp delete mode 100644 crypto/smartcont/auto/highload-wallet-code.fif delete mode 100644 crypto/smartcont/auto/highload-wallet-v2-code.cpp delete mode 100644 crypto/smartcont/auto/highload-wallet-v2-code.fif delete mode 100644 crypto/smartcont/auto/simple-wallet-code.cpp delete mode 100644 crypto/smartcont/auto/simple-wallet-code.fif delete mode 100644 crypto/smartcont/auto/wallet-code.cpp delete mode 100644 crypto/smartcont/auto/wallet-code.fif diff --git a/.gitignore b/.gitignore index cc30727975..44ed769e4e 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ compile_commands.json crypto/block/block-auto.cpp crypto/block/block-auto.h crypto/smartcont/*-code.fif +crypto/smartcont/auto/ test/regression-tests.cache/ *.swp **/*build*/ diff --git a/crypto/block/check-proof.cpp b/crypto/block/check-proof.cpp index 98d2c1cf23..7bc21eb3e6 100644 --- a/crypto/block/check-proof.cpp +++ b/crypto/block/check-proof.cpp @@ -32,7 +32,7 @@ namespace block { using namespace std::literals::string_literals; td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to, - bool check_state_hash, td::uint32* save_utime) { + bool check_state_hash, td::uint32* save_utime, ton::LogicalTime* save_lt) { ton::RootHash vhash{root->get_hash().bits()}; if (vhash != blkid.root_hash) { return td::Status::Error(PSTRING() << " block header for block " << blkid.to_str() << " has incorrect root hash " @@ -50,6 +50,9 @@ td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blki if (save_utime) { *save_utime = info.gen_utime; } + if (save_lt) { + *save_lt = info.end_lt; + } if (store_shard_hash_to) { vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update}; if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update @@ -157,7 +160,7 @@ td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td: td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr, td::Ref root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash, - td::uint32* save_utime) { + td::uint32* save_utime, ton::LogicalTime* save_lt) { TRY_RESULT_PREFIX(Q_roots, vm::std_boc_deserialize_multi(std::move(proof)), "cannot deserialize account proof"); if (Q_roots.size() != 2) { return td::Status::Error(PSLICE() << "account state proof must have exactly two roots"); @@ -174,7 +177,7 @@ td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const } ton::Bits256 state_hash = state_root->get_hash().bits(); TRY_STATUS_PREFIX(check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, - &state_hash, true, save_utime), + &state_hash, true, save_utime, save_lt), "error in account shard block header proof : "); block::gen::ShardStateUnsplit::Record sstate; if (!(tlb::unpack_cell(std::move(state_root), sstate))) { @@ -238,7 +241,7 @@ td::Result AccountState::validate(ton::BlockIdExt ref_blk, b Info res; TRY_STATUS(block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, - &res.last_trans_hash, &res.gen_utime)); + &res.last_trans_hash, &res.gen_utime, &res.gen_lt)); res.root = std::move(root); return res; diff --git a/crypto/block/check-proof.h b/crypto/block/check-proof.h index 87d6b442b7..173ae9e475 100644 --- a/crypto/block/check-proof.h +++ b/crypto/block/check-proof.h @@ -26,11 +26,12 @@ using td::Ref; td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false, - td::uint32* save_utime = nullptr); + td::uint32* save_utime = nullptr, ton::LogicalTime* save_lt = nullptr); td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof); td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr, td::Ref root, ton::LogicalTime* last_trans_lt = nullptr, - ton::Bits256* last_trans_hash = nullptr, td::uint32* save_utime = nullptr); + ton::Bits256* last_trans_hash = nullptr, td::uint32* save_utime = nullptr, + ton::LogicalTime* save_lt = nullptr); td::Result check_state_proof(ton::BlockIdExt blkid, td::Slice proof); td::Result> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data); @@ -48,6 +49,7 @@ struct AccountState { td::Ref root; ton::LogicalTime last_trans_lt = 0; ton::Bits256 last_trans_hash; + ton::LogicalTime gen_lt{0}; td::uint32 gen_utime{0}; }; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 1e0084e943..bc6d8e220f 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -806,7 +806,6 @@ bool Transaction::prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputeP } Ref Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const { - // TODO: fix initialization of c7 td::BitArray<256> rand_seed; td::RefInt256 rand_seed_int{true}; if (!(prepare_rand_seed(rand_seed, cfg) && rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false))) { @@ -1305,9 +1304,9 @@ bool Transaction::check_rewrite_dest_addr(Ref& dest_addr, const A int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing) { block::gen::OutAction::Record_action_send_msg act_rec; - // mode: +128 = attach all remaining balance, +64 = attach all remaining balance of the inbound message, +1 = pay message fees, +2 = skip if message cannot be sent + // mode: +128 = attach all remaining balance, +64 = attach all remaining balance of the inbound message, +32 = delete smart contract if balance becomes zero, +1 = pay message fees, +2 = skip if message cannot be sent vm::CellSlice cs{cs0}; - if (!tlb::unpack_exact(cs, act_rec) || (act_rec.mode & ~0xc3) || (act_rec.mode & 0xc0) == 0xc0) { + if (!tlb::unpack_exact(cs, act_rec) || (act_rec.mode & ~0xe3) || (act_rec.mode & 0xc0) == 0xc0) { return -1; } bool skip_invalid = (act_rec.mode & 2); @@ -1574,7 +1573,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap, ap.total_action_fees += fees_collected; ap.total_fwd_fees += fees_total; - if (act_rec.mode & 0x80) { + if ((act_rec.mode & 0xa0) == 0xa0) { CHECK(ap.remaining_balance.is_zero()); ap.acc_delete_req = ap.reserved_balance.is_zero(); } diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index f9434b5b80..dd156f24c6 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -50,15 +50,15 @@ void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, b } template -void define_builtin_func_x(std::string name, TypeExpr* func_type, const T& func, std::initializer_list arg_order, - std::initializer_list ret_order = {}, bool impure = false) { +void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list arg_order, + std::initializer_list ret_order = {}, bool impure = false) { SymDef* def = predefine_builtin_func(name, func_type); def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, impure}; } -void define_builtin_func_x(std::string name, TypeExpr* func_type, const AsmOp& macro, - std::initializer_list arg_order, std::initializer_list ret_order = {}, - bool impure = false) { +void define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro, + std::initializer_list arg_order, std::initializer_list ret_order = {}, + bool impure = false) { SymDef* def = predefine_builtin_func(name, func_type); def->value = new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, impure}; } @@ -429,10 +429,14 @@ AsmOp compile_mul(std::vector& res, std::vector& args) { } return exec_arg_op("MULCONST", y.int_const, 1); } - if (k >= 0) { + if (k > 0) { y.unused(); return exec_arg_op("LSHIFT#", k, 1); } + if (k == 0) { + y.unused(); + return AsmOp::Nop(); + } } if (x.is_int_const()) { int k = is_pos_pow2(x.int_const); @@ -451,10 +455,14 @@ AsmOp compile_mul(std::vector& res, std::vector& args) { } return exec_arg_op("MULCONST", x.int_const, 1); } - if (k >= 0) { + if (k > 0) { x.unused(); return exec_arg_op("LSHIFT#", k, 1); } + if (k == 0) { + x.unused(); + return AsmOp::Nop(); + } } return exec_op("MUL", 2); } @@ -848,10 +856,7 @@ void define_builtins() { auto X = TypeExpr::new_var(); auto Y = TypeExpr::new_var(); auto Z = TypeExpr::new_var(); - auto T = TypeExpr::new_var(); auto XY = TypeExpr::new_tensor({X, Y}); - auto XYZ = TypeExpr::new_tensor({X, Y, Z}); - auto XYZT = TypeExpr::new_tensor({X, Y, Z, T}); auto arith_bin_op = TypeExpr::new_map(Int2, Int); auto arith_un_op = TypeExpr::new_map(Int, Int); auto impure_bin_op = TypeExpr::new_map(Int2, Unit); @@ -869,16 +874,20 @@ void define_builtins() { define_builtin_func("-_", arith_un_op, compile_negate); define_builtin_func("_*_", arith_bin_op, compile_mul); define_builtin_func("_/_", arith_bin_op, std::bind(compile_div, _1, _2, -1)); - define_builtin_func("_/~_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); - define_builtin_func("_/^_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); + define_builtin_func("_~/_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); + define_builtin_func("_^/_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); define_builtin_func("_%_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); - define_builtin_func("_%~_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); - define_builtin_func("_%^_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); + define_builtin_func("_~%_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); + define_builtin_func("_^%_", arith_bin_op, std::bind(compile_mod, _1, _2, 1)); define_builtin_func("_/%_", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2)); + define_builtin_func("divmod", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2)); + define_builtin_func("~divmod", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2)); + define_builtin_func("moddiv", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2), {}, {1, 0}); + define_builtin_func("~moddiv", TypeExpr::new_map(Int2, Int2), AsmOp::Custom("DIVMOD", 2, 2), {}, {1, 0}); define_builtin_func("_<<_", arith_bin_op, compile_lshift); define_builtin_func("_>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1)); - define_builtin_func("_>>~_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); - define_builtin_func("_>>^_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); + define_builtin_func("_~>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); + define_builtin_func("_^>>_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); define_builtin_func("_&_", arith_bin_op, AsmOp::Custom("AND", 2)); define_builtin_func("_|_", arith_bin_op, AsmOp::Custom("OR", 2)); define_builtin_func("_^_", arith_bin_op, AsmOp::Custom("XOR", 2)); @@ -887,15 +896,15 @@ void define_builtins() { define_builtin_func("^_-=_", arith_bin_op, compile_sub); define_builtin_func("^_*=_", arith_bin_op, compile_mul); define_builtin_func("^_/=_", arith_bin_op, std::bind(compile_div, _1, _2, -1)); - define_builtin_func("^_/~=_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); - define_builtin_func("^_/^=_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); + define_builtin_func("^_~/=_", arith_bin_op, std::bind(compile_div, _1, _2, 0)); + define_builtin_func("^_^/=_", arith_bin_op, std::bind(compile_div, _1, _2, 1)); define_builtin_func("^_%=_", arith_bin_op, std::bind(compile_mod, _1, _2, -1)); - define_builtin_func("^_%~=_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); - define_builtin_func("^_%^=_", arith_bin_op, std::bind(compile_mod, _1, _2, 1)); + define_builtin_func("^_~%=_", arith_bin_op, std::bind(compile_mod, _1, _2, 0)); + define_builtin_func("^_^%=_", arith_bin_op, std::bind(compile_mod, _1, _2, 1)); define_builtin_func("^_<<=_", arith_bin_op, compile_lshift); define_builtin_func("^_>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, -1)); - define_builtin_func("^_>>~=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); - define_builtin_func("^_>>^=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); + define_builtin_func("^_~>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 0)); + define_builtin_func("^_^>>=_", arith_bin_op, std::bind(compile_rshift, _1, _2, 1)); define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2)); define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2)); define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2)); @@ -915,42 +924,18 @@ void define_builtins() { define_builtin_func("nil", Tuple, AsmOp::Const("PUSHNULL")); define_builtin_func("Nil", Tuple, AsmOp::Const("NIL")); define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null); - define_builtin_func("cons", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor(X, Tuple), Tuple)), - AsmOp::Custom("CONS", 2, 1)); - define_builtin_func("uncons", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, TypeExpr::new_tensor(X, Tuple))), - AsmOp::Custom("UNCONS", 1, 2)); - define_builtin_func_x("list_next", - TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, TypeExpr::new_tensor(Tuple, X))), - AsmOp::Custom("UNCONS", 1, 2), {}, {1, 0}); - define_builtin_func("car", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("CAR", 1, 1)); - define_builtin_func("cdr", TypeExpr::new_map(Tuple, Tuple), AsmOp::Custom("CDR", 1, 1)); - define_builtin_func("pair", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(XY, Tuple)), AsmOp::Custom("PAIR", 2, 1)); - define_builtin_func("unpair", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(Tuple, XY)), - AsmOp::Custom("UNPAIR", 1, 2)); - define_builtin_func("triple", TypeExpr::new_forall({X, Y, Z}, TypeExpr::new_map(XYZ, Tuple)), - AsmOp::Custom("TRIPLE", 3, 1)); - define_builtin_func("untriple", TypeExpr::new_forall({X, Y, Z}, TypeExpr::new_map(Tuple, XYZ)), - AsmOp::Custom("UNTRIPLE", 1, 3)); - define_builtin_func("tuple4", TypeExpr::new_forall({X, Y, Z, T}, TypeExpr::new_map(XYZT, Tuple)), - AsmOp::Custom("4 TUPLE", 4, 1)); - define_builtin_func("untuple4", TypeExpr::new_forall({X, Y, Z, T}, TypeExpr::new_map(Tuple, XYZT)), - AsmOp::Custom("4 UNTUPLE", 1, 4)); - define_builtin_func("first", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("FIRST", 1, 1)); - define_builtin_func("second", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("SECOND", 1, 1)); - define_builtin_func("third", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("THIRD", 1, 1)); - define_builtin_func("fourth", TypeExpr::new_forall({X}, TypeExpr::new_map(Tuple, X)), AsmOp::Custom("3 INDEX", 1, 1)); define_builtin_func("throw", impure_un_op, compile_throw, true); define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); define_builtin_func("throw_unless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true); - define_builtin_func_x("load_int", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0}); - define_builtin_func_x("load_uint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0}); + define_builtin_func("load_int", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0}); + define_builtin_func("load_uint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0}); define_builtin_func("preload_int", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true)); define_builtin_func("preload_uint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false)); - define_builtin_func_x("store_int", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); - define_builtin_func_x("store_uint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); - define_builtin_func_x("~store_int", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); - define_builtin_func_x("~store_uint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); - define_builtin_func_x("load_bits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0}); + define_builtin_func("store_int", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); + define_builtin_func("store_uint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); + define_builtin_func("~store_int", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); + define_builtin_func("~store_uint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); + define_builtin_func("load_bits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0}); define_builtin_func("preload_bits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false)); define_builtin_func("int_at", TypeExpr::new_map(TupleInt, Int), compile_tuple_at); define_builtin_func("cell_at", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); @@ -967,13 +952,12 @@ void define_builtins() { AsmOp::Custom("s0 DUMP", 1, 1)); 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_x("run_method1", - TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)), - [](auto a, auto b, auto c) { return compile_run_method(a, b, c, 1, false); }, {1, 0}, {}, true); - define_builtin_func_x( + define_builtin_func("run_method1", TypeExpr::new_forall({X}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X}), Unit)), + [](auto a, auto b, auto c) { return compile_run_method(a, b, c, 1, false); }, {1, 0}, {}, true); + define_builtin_func( "run_method2", TypeExpr::new_forall({X, Y}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X, Y}), Unit)), [](auto a, auto b, auto c) { return compile_run_method(a, b, c, 2, false); }, {1, 2, 0}, {}, true); - define_builtin_func_x( + define_builtin_func( "run_method3", TypeExpr::new_forall({X, Y, Z}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X, Y, Z}), Unit)), [](auto a, auto b, auto c) { return compile_run_method(a, b, c, 3, false); }, {1, 2, 3, 0}, {}, true); } diff --git a/crypto/func/func.h b/crypto/func/func.h index 77d1efd7b3..d1481299b8 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -67,6 +67,8 @@ enum Keyword { _RshiftC, _DivR, _DivC, + _ModR, + _ModC, _DivMod, _PlusLet, _MinusLet, @@ -75,6 +77,8 @@ enum Keyword { _DivRLet, _DivCLet, _ModLet, + _ModRLet, + _ModCLet, _LshiftLet, _RshiftLet, _RshiftRLet, @@ -90,6 +94,7 @@ enum Keyword { _Tuple, _Type, _Mapsto, + _Forall, _Asm, _Impure, _Extern, @@ -128,7 +133,7 @@ class IdSc { */ struct TypeExpr { - enum te_type { te_Unknown, te_Indirect, te_Atomic, te_Tensor, te_Map, te_Type, te_ForAll } constr; + enum te_type { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Map, te_Type, te_ForAll } constr; enum { _Int = Keyword::_Int, _Cell = Keyword::_Cell, @@ -173,6 +178,9 @@ struct TypeExpr { bool is_int() const { return is_atomic(_Int); } + bool is_var() const { + return constr == te_Var; + } bool has_fixed_width() const { return minw == maxw; } @@ -215,7 +223,10 @@ struct TypeExpr { return new_tensor({te1, te2, te3}); } static TypeExpr* new_var() { - return new TypeExpr{te_Unknown, --type_vars, 1}; + return new TypeExpr{te_Var, --type_vars, 1}; + } + static TypeExpr* new_var(int idx) { + return new TypeExpr{te_Var, idx, 1}; } static TypeExpr* new_forall(std::vector list, TypeExpr* body) { return new TypeExpr{te_ForAll, body, std::move(list)}; @@ -724,6 +735,16 @@ struct SymValCodeFunc : SymValFunc { } }; +struct SymValType : sym::SymValBase { + TypeExpr* sym_type; + SymValType(int _type, int _idx, TypeExpr* _stype = nullptr) : sym::SymValBase(_type, _idx), sym_type(_stype) { + } + ~SymValType() override = default; + TypeExpr* get_type() const { + return sym_type; + } +}; + extern int glob_func_cnt, undef_func_cnt; extern std::vector glob_func; diff --git a/crypto/func/keywords.cpp b/crypto/func/keywords.cpp index b2b6120ad3..c640f4fe71 100644 --- a/crypto/func/keywords.cpp +++ b/crypto/func/keywords.cpp @@ -54,6 +54,7 @@ void define_keywords() { .add_kw_char('>') .add_kw_char('&') .add_kw_char('|') + .add_kw_char('^') .add_kw_char('~'); using Kw = funC::Keyword; @@ -64,22 +65,26 @@ void define_keywords() { .add_keyword("<=>", Kw::_Spaceship) .add_keyword("<<", Kw::_Lshift) .add_keyword(">>", Kw::_Rshift) - .add_keyword(">>~", Kw::_RshiftR) - .add_keyword(">>^", Kw::_RshiftC) - .add_keyword("/~", Kw::_DivR) - .add_keyword("/^", Kw::_DivC) + .add_keyword("~>>", Kw::_RshiftR) + .add_keyword("^>>", Kw::_RshiftC) + .add_keyword("~/", Kw::_DivR) + .add_keyword("^/", Kw::_DivC) + .add_keyword("~%", Kw::_ModR) + .add_keyword("^%", Kw::_ModC) .add_keyword("/%", Kw::_DivMod) .add_keyword("+=", Kw::_PlusLet) .add_keyword("-=", Kw::_MinusLet) .add_keyword("*=", Kw::_TimesLet) .add_keyword("/=", Kw::_DivLet) + .add_keyword("~/=", Kw::_DivRLet) + .add_keyword("^/=", Kw::_DivCLet) .add_keyword("%=", Kw::_ModLet) - .add_keyword("/~=", Kw::_DivRLet) - .add_keyword("/^=", Kw::_DivCLet) + .add_keyword("~%=", Kw::_ModRLet) + .add_keyword("^%=", Kw::_ModCLet) .add_keyword("<<=", Kw::_LshiftLet) .add_keyword(">>=", Kw::_RshiftLet) - .add_keyword(">>~=", Kw::_RshiftRLet) - .add_keyword(">>^=", Kw::_RshiftCLet) + .add_keyword("~>>=", Kw::_RshiftRLet) + .add_keyword("^>>=", Kw::_RshiftCLet) .add_keyword("&=", Kw::_AndLet) .add_keyword("|=", Kw::_OrLet) .add_keyword("^=", Kw::_XorLet); @@ -103,7 +108,9 @@ void define_keywords() { .add_keyword("builder", Kw::_Builder) .add_keyword("cont", Kw::_Cont) .add_keyword("tuple", Kw::_Tuple) - .add_keyword("->", Kw::_Mapsto); + .add_keyword("type", Kw::_Type) + .add_keyword("->", Kw::_Mapsto) + .add_keyword("forall", Kw::_Forall); sym::symbols.add_keyword("extern", Kw::_Extern) .add_keyword("asm", Kw::_Asm) diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index 29c79283bf..e04077918c 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -89,6 +89,15 @@ TypeExpr* parse_type1(Lexer& lex) { case '_': lex.next(); return TypeExpr::new_hole(); + case _Ident: { + auto sym = sym::lookup_symbol(lex.cur().val); + if (sym && dynamic_cast(sym->value)) { + auto val = dynamic_cast(sym->value); + lex.next(); + return val->get_type(); + } + lex.cur().error_at("`", "` is not a type identifier"); + } } lex.expect('('); if (lex.tp() == ')') { @@ -133,7 +142,14 @@ FormalArg parse_formal_arg(Lexer& lex, int fa_idx) { } else if (lex.tp() != _Ident) { arg_type = parse_type(lex); } else { - arg_type = TypeExpr::new_hole(); + auto sym = sym::lookup_symbol(lex.cur().val); + if (sym && dynamic_cast(sym->value)) { + auto val = dynamic_cast(sym->value); + lex.next(); + arg_type = val->get_type(); + } else { + arg_type = TypeExpr::new_hole(); + } } if (lex.tp() == '_' || lex.tp() == ',' || lex.tp() == ')') { if (lex.tp() == '_') { @@ -147,6 +163,9 @@ FormalArg parse_formal_arg(Lexer& lex, int fa_idx) { } loc = lex.cur().loc; SymDef* new_sym_def = sym::define_symbol(lex.cur().val, true, loc); + if (!new_sym_def) { + lex.cur().error_at("cannot define symbol `", "`"); + } if (new_sym_def->value) { lex.cur().error_at("redefined formal parameter `", "`"); } @@ -295,6 +314,15 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { return res; } if (t == _Ident) { + auto sym = sym::lookup_symbol(lex.cur().val); + if (sym && dynamic_cast(sym->value)) { + auto val = dynamic_cast(sym->value); + Expr* res = new Expr{Expr::_Type, lex.cur().loc}; + res->flags = Expr::_IsType; + res->e_type = val->get_type(); + lex.next(); + return res; + } Expr* res = new Expr{Expr::_Var, lex.cur().loc}; if (nv) { res->val = ~lex.cur().val; @@ -302,14 +330,14 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { res->flags = Expr::_IsLvalue | Expr::_IsNewVar; // std::cerr << "defined new variable " << lex.cur().str << " : " << res->e_type << std::endl; } else { - res->sym = sym::lookup_symbol(lex.cur().val); - if (!res->sym) { + if (!sym) { check_global_func(lex.cur()); - res->sym = sym::lookup_symbol(lex.cur().val); + sym = sym::lookup_symbol(lex.cur().val); } + res->sym = sym; SymVal* val = nullptr; - if (res->sym) { - val = dynamic_cast(res->sym->value); + if (sym) { + val = dynamic_cast(sym->value); } if (!val) { lex.cur().error_at("undefined identifier `", "`"); @@ -463,7 +491,7 @@ Expr* parse_expr75(Lexer& lex, CodeBlob& code, bool nv) { Expr* parse_expr30(Lexer& lex, CodeBlob& code, bool nv) { Expr* res = parse_expr75(lex, code, nv); while (lex.tp() == '*' || lex.tp() == '/' || lex.tp() == '%' || lex.tp() == _DivMod || lex.tp() == _DivC || - lex.tp() == _DivR || lex.tp() == '&') { + lex.tp() == _DivR || lex.tp() == _ModC || lex.tp() == _ModR || lex.tp() == '&') { res->chk_rvalue(lex.cur()); int t = lex.tp(); sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); @@ -481,7 +509,7 @@ Expr* parse_expr30(Lexer& lex, CodeBlob& code, bool nv) { return res; } -// parse [-] E { (+ | - | `|` ) E } +// parse [-] E { (+ | - | `|` | ^) E } Expr* parse_expr20(Lexer& lex, CodeBlob& code, bool nv) { Expr* res; int t = lex.tp(); @@ -500,7 +528,7 @@ Expr* parse_expr20(Lexer& lex, CodeBlob& code, bool nv) { } else { res = parse_expr30(lex, code, nv); } - while (lex.tp() == '-' || lex.tp() == '+' || lex.tp() == '|') { + while (lex.tp() == '-' || lex.tp() == '+' || lex.tp() == '|' || lex.tp() == '^') { res->chk_rvalue(lex.cur()); t = lex.tp(); sym_idx_t name = symbols.lookup_add(std::string{"_"} + lex.cur().str + "_"); @@ -586,8 +614,8 @@ Expr* parse_expr10(Lexer& lex, CodeBlob& code, bool nv) { auto x = parse_expr13(lex, code, nv); int t = lex.tp(); if (t == _PlusLet || t == _MinusLet || t == _TimesLet || t == _DivLet || t == _DivRLet || t == _DivCLet || - t == _ModLet || t == _LshiftLet || t == _RshiftLet || t == _RshiftCLet || t == _RshiftRLet || t == _AndLet || - t == _OrLet || t == _XorLet) { + t == _ModLet || t == _ModCLet || t == _ModRLet || t == _LshiftLet || t == _RshiftLet || t == _RshiftCLet || + t == _RshiftRLet || t == _AndLet || t == _OrLet || t == _XorLet) { x->chk_lvalue(lex.cur()); x->chk_rvalue(lex.cur()); sym_idx_t name = symbols.lookup_add(std::string{"^_"} + lex.cur().str + "_"); @@ -960,9 +988,76 @@ SymValAsmFunc* parse_asm_func_body(Lexer& lex, TypeExpr* func_type, const Formal return res; } +std::vector parse_type_var_list(Lexer& lex) { + std::vector res; + lex.expect(_Forall); + int idx = 0; + while (true) { + if (lex.tp() == _Type) { + lex.next(); + } + if (lex.tp() != _Ident) { + throw src::ParseError{lex.cur().loc, "free type identifier expected"}; + } + auto loc = lex.cur().loc; + SymDef* new_sym_def = sym::define_symbol(lex.cur().val, true, loc); + if (new_sym_def->value) { + lex.cur().error_at("redefined type variable `", "`"); + } + auto var = TypeExpr::new_var(idx); + new_sym_def->value = new SymValType{SymVal::_Typename, idx++, var}; + res.push_back(var); + lex.next(); + if (lex.tp() != ',') { + break; + } + lex.next(); + } + lex.expect(_Mapsto); + return res; +} + +void type_var_usage(TypeExpr* expr, const std::vector& typevars, std::vector& used) { + if (expr->constr != TypeExpr::te_Var) { + for (auto arg : expr->args) { + type_var_usage(arg, typevars, used); + } + return; + } + for (std::size_t i = 0; i < typevars.size(); i++) { + if (typevars[i] == expr) { + used.at(i) = true; + return; + } + } + return; +} + +TypeExpr* compute_type_closure(TypeExpr* expr, const std::vector& typevars) { + if (typevars.empty()) { + return expr; + } + std::vector used(typevars.size(), false); + type_var_usage(expr, typevars, used); + std::vector used_vars; + for (std::size_t i = 0; i < typevars.size(); i++) { + if (used.at(i)) { + used_vars.push_back(typevars[i]); + } + } + if (!used_vars.empty()) { + expr = TypeExpr::new_forall(std::move(used_vars), expr); + } + return expr; +} + void parse_func_def(Lexer& lex) { SrcLocation loc{lex.cur().loc}; sym::open_scope(lex); + std::vector type_vars; + if (lex.tp() == _Forall) { + type_vars = parse_type_var_list(lex); + } auto ret_type = parse_type(lex); if (lex.tp() != _Ident) { throw src::ParseError{lex.cur().loc, "function name identifier expected"}; @@ -1010,6 +1105,7 @@ void parse_func_def(Lexer& lex) { lex.expect('{', "function body block expected"); } TypeExpr* func_type = TypeExpr::new_map(extract_total_arg_type(arg_list), ret_type); + func_type = compute_type_closure(func_type, type_vars); if (verbosity >= 1) { std::cerr << "function " << func_name.str << " : " << func_type << std::endl; } diff --git a/crypto/func/test/a6.fc b/crypto/func/test/a6.fc index 06eeed9fe3..3a387aace1 100644 --- a/crypto/func/test/a6.fc +++ b/crypto/func/test/a6.fc @@ -59,20 +59,20 @@ operator _/%_ infix 20; return (x / y, x % y); } -} -{- -< type A, type B, type C > + +forall A, B, C -> (B, C, A) rot (A x, B y, C z) { return (y, z, x); } --} + int ataninv(int base, int q) { ;; computes base*atan(1/q) - base /~= q; + base ~/= q; q *= - q; int sum = 0; int n = 1; do { - sum += base /~ n; - base /~= q; + sum += base ~/ n; + base ~/= q; n += 2; } until base == 0; return sum; @@ -81,5 +81,9 @@ int ataninv(int base, int q) { ;; computes base*atan(1/q) int calc_pi() { int base = 64; repeat (70) { base *= 10; } - return (ataninv(base << 2, 5) - ataninv(base, 239)) >>~ 4; + return (ataninv(base << 2, 5) - ataninv(base, 239)) ~>> 4; +} + +int main() { + return calc_pi(); } diff --git a/crypto/func/test/a6_3.fc b/crypto/func/test/a6_3.fc new file mode 100644 index 0000000000..7c8ff3af2e --- /dev/null +++ b/crypto/func/test/a6_3.fc @@ -0,0 +1,22 @@ +forall A, B, C -> +(B, C, A) rot(A x, B y, C z) { + return (y, z, x); +} + +forall A, B, C -> +_ rot2(A x, B y, C z) { + return rot(rot(x, y, z)); +} + +_ test() { + return rot2(2, 3, 9); +} + +_ test2(cell x, slice y, tuple z) { + return rot2(x, y, z); +} + +forall A -> +_ test3(cell x, A y, int z) { + return rot2(x, y, z); +} diff --git a/crypto/func/unify-types.cpp b/crypto/func/unify-types.cpp index fd841a0fbe..698d21feba 100644 --- a/crypto/func/unify-types.cpp +++ b/crypto/func/unify-types.cpp @@ -153,7 +153,7 @@ bool TypeExpr::remove_forall(TypeExpr*& te) { bool TypeExpr::remove_forall_in(TypeExpr*& te, TypeExpr* te2, const std::vector& new_vars) { assert(te); assert(te2 && te2->constr == te_ForAll); - if (te->constr == te_Unknown) { + if (te->constr == te_Var) { for (std::size_t i = 0; i < new_vars.size(); i++) { if (te == te2->args[i + 1]) { te = new_vars[i]; @@ -201,12 +201,14 @@ std::ostream& operator<<(std::ostream& os, TypeExpr* type_expr) { std::ostream& TypeExpr::print(std::ostream& os, int lex_level) { switch (constr) { case te_Unknown: - if (value >= 0) { - return os << "??" << value; - } else if (value >= -26) { - return os << (char)(64 - value); + return os << "??" << value; + case te_Var: + if (value >= -26 && value < 0) { + return os << "_" << (char)(91 + value); + } else if (value >= 0 && value < 26) { + return os << (char)(65 + value); } else { - return os << "TVAR" << -value; + return os << "TVAR" << value; } case te_Indirect: return os << args[0]; diff --git a/crypto/smartcont/auto/config-code.cpp b/crypto/smartcont/auto/config-code.cpp deleted file mode 100644 index 4b04b77b54..0000000000 --- a/crypto/smartcont/auto/config-code.cpp +++ /dev/null @@ -1 +0,0 @@ -with_tvm_code("config", "te6ccgECEwEAAhQAART/APSkE/Sg8sgLAQIBIAIDAgLPBAUCAvEPEAIBIAYHAgEgDA0CASAICQIBIAoLAOkAdDTA/pAMCD6RAGkA3GwE7EjxwCxkl8D4ALTH9M/IoIQTlZTVLqOPTIB1NFx+DMgbpIwf5TQ1wv/4gNwBLqbMiHwAiH4I7wCvLCRAuKdgCRY8AGCEO52T0vwBOAxghDudk9v8AXgECRfBCDAAAGDHrCx8qWAAJTtRNDUUDOAIPQVyMwBzxbJ7VSAAMzQ0wcBwBHyidMf0x/TD9MPMCDCAPKJvvKJgACscIAYyMsFUAXPFhTLbssfyz/JAfsAgAgEgDg4AW0cfgz0NcL//gjghBOQ29kcIIAxP/IyxAUy/+DHfoCE8tqEssfyz8BzxbJcPsAgACSAQPADgAK0MO1E0NSAIIAkUzH0aiBukxAjW444INAg10nCJ44p0wfXCx8BwBH4IxK+sI4VAYAiVBBE9G5UUyH0bjCAJEAT9FowkxAjW+KUEDRfA+LiyMwBzxbJ7VSABASARAf6DCNcYINMf0x/TH/gjErnyY+1E0NTTH9P/0VFBuvKhBfkBVBBj+RDyovgAIoIQQ2ZQIbqcMgHTH9TRQBSAIPQVjjcighBOQ29kupYyAdTR+wSOJCKCEFBiSyG6lTEx0//RjhMighBOQ+8FupQyAfAGlDAB8mDi4uIC4gGkyBLMEgAOyx/L/8ntVA=="); diff --git a/crypto/smartcont/auto/config-code.fif b/crypto/smartcont/auto/config-code.fif deleted file mode 100644 index 1a4084fafe..0000000000 --- a/crypto/smartcont/auto/config-code.fif +++ /dev/null @@ -1,323 +0,0 @@ -// automatically generated from `smartcont/stdlib.fc` `smartcont/config-code.fc` -PROGRAM{ - DECLPROC set_conf_param - DECLPROC check_validator_set - DECLPROC send_answer - DECLPROC send_confirmation - DECLPROC send_error - DECLPROC recv_internal - DECLPROC change_elector_code - DECLPROC recv_external - DECLPROC run_ticktock - set_conf_param PROC:<{ - // index value - c4 PUSH // index value _3 - CTOS // index value cs - LDREF // index value cfg_dict cs - s3 s3 XCHG2 - 32 PUSHINT // cs value index cfg_dict _9=32 - DICTISETREF // cs cfg_dict - NEWC // cs cfg_dict _11 - STREF // cs _12 - SWAP // _12 cs - STSLICER // _13 - ENDC // _14 - c4 POP - }> - check_validator_set PROC:<{ - // vset - CTOS // cs - 8 LDU // _4 cs - SWAP // cs _4 - 17 EQINT // cs _8 - 9 THROWIFNOT - 32 LDU // utime_since cs - 32 LDU // utime_since utime_until cs - 16 LDU // utime_since utime_until total cs - 16 LDU // utime_since utime_until total _42 _41 - DROP // utime_since utime_until total main - DUP // utime_since utime_until total main main - 0 GTINT // utime_since utime_until total main _28 - 9 THROWIFNOT - GEQ // utime_since utime_until _31 - 9 THROWIFNOT - }> - send_answer PROC:<{ - // addr query_id ans_tag mode - 0 PUSHINT // addr query_id ans_tag mode _4=0 - 24 PUSHINT // addr query_id ans_tag mode _4=0 _5=24 - NEWC // addr query_id ans_tag mode _4=0 _5=24 _6 - 6 STU // addr query_id ans_tag mode _4=0 _8 - s0 s5 XCHG2 // _4=0 query_id ans_tag mode _8 addr - STSLICER // _4=0 query_id ans_tag mode _9 - s1 s4 XCHG // mode query_id ans_tag _4=0 _9 - 111 STU // mode query_id ans_tag _23 - 32 STU // mode query_id _25 - 64 STU // mode _27 - ENDC // mode _28 - SWAP // _28 mode - SENDRAWMSG - }> - send_confirmation PROC:<{ - // addr query_id ans_tag - 64 PUSHINT // addr query_id ans_tag _3=64 - send_answer CALLDICT - }> - send_error PROC:<{ - // addr query_id ans_tag - 64 PUSHINT // addr query_id ans_tag _3=64 - send_answer CALLDICT - }> - recv_internal PROC:<{ - // in_msg_cell in_msg - SWAP // in_msg in_msg_cell - CTOS // in_msg cs - 4 LDU // in_msg flags cs - LDMSGADDR // in_msg flags _74 _73 - DROP // in_msg flags s_addr - DUP // in_msg flags s_addr s_addr - REWRITESTDADDR // in_msg flags s_addr src_wc src_addr - SWAP // in_msg flags s_addr src_addr src_wc - INC // in_msg flags s_addr src_addr _15 - s0 s3 XCHG - 1 PUSHINT // in_msg _15 s_addr src_addr flags _16=1 - AND // in_msg _15 s_addr src_addr _17 - s1 s3 XCHG // in_msg src_addr s_addr _15 _17 - OR // in_msg src_addr s_addr _18 - s3 PUSH // in_msg src_addr s_addr _18 in_msg - SEMPTY // in_msg src_addr s_addr _18 _19 - OR // in_msg src_addr s_addr _20 - IFJMP:<{ // in_msg src_addr s_addr - 3 BLKDROP // - }> // in_msg src_addr s_addr - s0 s2 XCHG // s_addr src_addr in_msg - 32 LDU // s_addr src_addr tag in_msg - 64 LDU // s_addr src_addr tag query_id in_msg - s2 PUSH - 1314280276 PUSHINT // s_addr src_addr tag query_id in_msg tag _29=1314280276 - EQUAL // s_addr src_addr tag query_id in_msg _30 - IFJMP:<{ // s_addr src_addr tag query_id in_msg - s2 POP // s_addr src_addr in_msg query_id - SWAP // s_addr src_addr query_id in_msg - LDREF // s_addr src_addr query_id vset in_msg - ENDS - 1 PUSHINT // s_addr src_addr query_id vset _36=1 - CONFIGOPTPARAM // s_addr src_addr query_id vset elector_param - DUP // s_addr src_addr query_id vset elector_param elector_param - ISNULL // s_addr src_addr query_id vset elector_param _39 - IF:<{ // s_addr src_addr query_id vset elector_param - DROP // s_addr src_addr query_id vset - -1 PUSHINT // s_addr src_addr query_id vset _40=-1 - }>ELSE<{ // s_addr src_addr query_id vset elector_param - CTOS // s_addr src_addr query_id vset _42 - 256 PLDU // s_addr src_addr query_id vset _40 - }> // s_addr src_addr query_id vset elector_addr - s0 s3 XCHG - FALSE - s0 s4 XCHG // s_addr ok query_id vset src_addr elector_addr - EQUAL // s_addr ok query_id vset _47 - IF:<{ // s_addr ok query_id vset - s2 POP // s_addr vset query_id - OVER // s_addr vset query_id vset - check_validator_set CALLDICT // s_addr vset query_id t_since t_until - OVER - NOW // s_addr vset query_id t_since t_until t_since t - GREATER // s_addr vset query_id t_since t_until _53 - s0 s2 XCHG // s_addr vset query_id _53 t_until t_since - GREATER // s_addr vset query_id _53 _54 - AND // s_addr vset query_id ok - }>ELSE<{ - s0 s2 XCHG // s_addr vset query_id ok - }> - IFJMP:<{ // s_addr vset query_id - 36 PUSHINT // s_addr vset query_id _56=36 - ROT // s_addr query_id _56=36 vset - set_conf_param CALLDICT - 4000730955 PUSHINT // s_addr query_id _58=4000730955 - send_confirmation CALLDICT - }> // s_addr vset query_id - NIP // s_addr query_id - 4000730991 PUSHINT // s_addr query_id _60=4000730991 - send_error CALLDICT - }> // s_addr src_addr tag query_id in_msg - s2 s4 XCHG - 4 BLKDROP // tag - DUP // tag tag - 0 EQINT // tag _64 - SWAP - 31 PUSHPOW2 // _64 tag _67 - AND // _64 _68 - OR // _69 - 37 THROWIFNOT - }> - change_elector_code PROC:<{ - // cs - 1 PUSHINT // cs _2=1 - CONFIGOPTPARAM // cs _3 - CTOS // cs _4 - 256 PLDU // cs dest_addr - NOW // cs dest_addr query_id - 1313042276 PUSHINT // cs dest_addr query_id _9=1313042276 - 0 PUSHINT // cs dest_addr query_id _9=1313042276 _10=0 - 50431 PUSHINT // cs dest_addr query_id _9=1313042276 _10=0 _11=50431 - NEWC // cs dest_addr query_id _9=1313042276 _10=0 _11=50431 _12 - 17 STU // cs dest_addr query_id _9=1313042276 _10=0 _14 - s1 s4 XCHG // cs _10=0 query_id _9=1313042276 dest_addr _14 - 256 STU // cs _10=0 query_id _9=1313042276 _16 - 30 PUSHPOW2 // cs _10=0 query_id _9=1313042276 _16 _19 - STGRAMS // cs _10=0 query_id _9=1313042276 _20 - s1 s3 XCHG // cs _9=1313042276 query_id _10=0 _20 - 107 STU // cs _9=1313042276 query_id _34 - s1 s2 XCHG // cs query_id _9=1313042276 _34 - 32 STU // cs query_id _36 - 64 STU // cs _38 - SWAP // _38 cs - STSLICER // _39 - ENDC // _40 - 0 PUSHINT // _40 _41=0 - SENDRAWMSG - }> - recv_external PROC:<{ - // in_msg - 9 PUSHPOW2 // in_msg _3=512 - LDSLICEX // signature in_msg - DUP // signature in_msg cs - 32 LDU // signature in_msg action cs - 32 LDU // signature in_msg action msg_seqno cs - 32 LDU // signature in_msg action msg_seqno valid_until cs - NOW // signature in_msg action msg_seqno valid_until cs _19 - s1 s2 XCHG // signature in_msg action msg_seqno cs valid_until _19 - LESS // signature in_msg action msg_seqno cs _20 - 35 THROWIF - c4 PUSH // signature in_msg action msg_seqno cs _23 - CTOS // signature in_msg action msg_seqno cs cs2 - LDREF // signature in_msg action msg_seqno cs cfg_dict cs2 - 32 LDU // signature in_msg action msg_seqno cs cfg_dict stored_seqno cs2 - 256 LDU // signature in_msg action msg_seqno cs cfg_dict stored_seqno public_key cs2 - ENDS - s4 s1 XCPU // signature in_msg action public_key cs cfg_dict stored_seqno msg_seqno stored_seqno - EQUAL // signature in_msg action public_key cs cfg_dict stored_seqno _38 - 33 THROWIFNOT - s0 s5 XCHG // signature stored_seqno action public_key cs cfg_dict in_msg - HASHSU // signature stored_seqno action public_key cs cfg_dict _41 - s0 s6 s3 XC2PU // cfg_dict stored_seqno action public_key cs _41 signature public_key - CHKSIGNU // cfg_dict stored_seqno action public_key cs _42 - 34 THROWIFNOT - ACCEPT - s2 PUSH - 1130778657 PUSHINT // cfg_dict stored_seqno action public_key cs action _45=1130778657 - EQUAL // cfg_dict stored_seqno action public_key cs _46 - IF:<{ // cfg_dict stored_seqno action public_key cs - s2 POP // cfg_dict stored_seqno cs public_key - SWAP // cfg_dict stored_seqno public_key cs - 32 LDU // cfg_dict stored_seqno public_key param_index cs - LDREF // cfg_dict stored_seqno public_key param_index param_value cs - ENDS - s0 s1 s4 XCHG3 - 32 PUSHINT // public_key stored_seqno param_value param_index cfg_dict _56=32 - DICTISETREF // public_key stored_seqno cfg_dict - }>ELSE<{ // cfg_dict stored_seqno action public_key cs - s2 PUSH - 1313042276 PUSHINT // cfg_dict stored_seqno action public_key cs action _58=1313042276 - EQUAL // cfg_dict stored_seqno action public_key cs _59 - IF:<{ // cfg_dict stored_seqno action public_key cs - s2 POP // cfg_dict stored_seqno cs public_key - SWAP // cfg_dict stored_seqno public_key cs - LDREF // cfg_dict stored_seqno public_key new_code cs - ENDS - SETCODE - }>ELSE<{ // cfg_dict stored_seqno action public_key cs - s2 PUSH - 1348619041 PUSHINT // cfg_dict stored_seqno action public_key cs action _65=1348619041 - EQUAL // cfg_dict stored_seqno action public_key cs _66 - IF:<{ // cfg_dict stored_seqno action public_key cs - NIP - NIP // cfg_dict stored_seqno cs - 256 LDU // cfg_dict stored_seqno public_key cs - ENDS - }>ELSE<{ // cfg_dict stored_seqno action public_key cs - s2 PUSH - 1313074949 PUSHINT // cfg_dict stored_seqno action public_key cs action _71=1313074949 - EQUAL // cfg_dict stored_seqno action public_key cs _72 - IF:<{ // cfg_dict stored_seqno action public_key cs - s2 POP // cfg_dict stored_seqno cs public_key - SWAP // cfg_dict stored_seqno public_key cs - change_elector_code CALLDICT - }>ELSE<{ // cfg_dict stored_seqno action public_key cs - DROP // cfg_dict stored_seqno action public_key - SWAP // cfg_dict stored_seqno public_key action - 32 THROWIF - }> - }> - }> - s0 s2 XCHG // public_key stored_seqno cfg_dict - }> - SWAP // public_key cfg_dict stored_seqno - INC // public_key cfg_dict _77 - NEWC // public_key cfg_dict _77 _78 - s1 s2 XCHG // public_key _77 cfg_dict _78 - STREF // public_key _77 _79 - 32 STU // public_key _81 - 256 STU // _83 - ENDC // _84 - c4 POP - }> - run_ticktock PROC:<{ - // is_tock - DROP // - c4 PUSH // _2 - CTOS // cs - LDREF // cfg_dict cs - 32 PUSHINT // cfg_dict cs kl=32 - 36 PUSHINT // cfg_dict cs kl=32 _10=36 - s3 s1 PUSH2 // cfg_dict cs kl=32 _10=36 cfg_dict kl=32 - DICTIGETOPTREF // cfg_dict cs kl=32 next_vset - DUP // cfg_dict cs kl=32 next_vset next_vset - ISNULL // cfg_dict cs kl=32 next_vset _12 - IFNOT:<{ // cfg_dict cs kl=32 next_vset - DUP // cfg_dict cs kl=32 next_vset next_vset - CTOS // cfg_dict cs kl=32 next_vset ds - DUP // cfg_dict cs kl=32 next_vset ds ds - SBITS // cfg_dict cs kl=32 next_vset ds _15 - 39 GTINT // cfg_dict cs kl=32 next_vset ds _17 - IF:<{ // cfg_dict cs kl=32 next_vset ds - 8 LDU // cfg_dict cs kl=32 next_vset tag ds - 32 PLDU // cfg_dict cs kl=32 next_vset tag since - SWAP // cfg_dict cs kl=32 next_vset since tag - 17 EQINT // cfg_dict cs kl=32 next_vset since _26 - NOW // cfg_dict cs kl=32 next_vset since _26 _27 - s1 s2 XCHG // cfg_dict cs kl=32 next_vset _26 since _27 - GEQ // cfg_dict cs kl=32 next_vset _26 _28 - AND // cfg_dict cs kl=32 next_vset _29 - IF:<{ // cfg_dict cs kl=32 next_vset - SWAP - 34 PUSHINT - s0 s4 s4 XC2PU // kl=32 cs next_vset _32=34 cfg_dict kl=32 - DICTISETGETOPTREF // kl=32 cs cfg_dict cur_vset - s3 s1 s0 PUXCPU // kl=32 cs cur_vset _35=32 cfg_dict kl=32 - DICTISETGETOPTREF // kl=32 cs _51 _52 - DROP // kl=32 cs cfg_dict - 36 PUSHINT // kl=32 cs cfg_dict _38=36 - s0 s1 s3 XCHG3 // cs _38=36 cfg_dict kl=32 - DICTIDEL // cs _53 _54 - DROP // cs cfg_dict - }>ELSE<{ - s2 s3 XCHG - 2DROP // cs cfg_dict - }> - }>ELSE<{ - s3 s4 XCHG - 3 BLKDROP // cs cfg_dict - }> - }>ELSE<{ - s2 s3 XCHG - 2DROP // cs cfg_dict - }> - NEWC // cs cfg_dict _40 - STREF // cs _41 - SWAP // _41 cs - STSLICER // _42 - ENDC // _43 - c4 POP - }> -}END>c diff --git a/crypto/smartcont/auto/elector-code.cpp b/crypto/smartcont/auto/elector-code.cpp deleted file mode 100644 index eb520f79ef..0000000000 --- a/crypto/smartcont/auto/elector-code.cpp +++ /dev/null @@ -1 +0,0 @@ -with_tvm_code("elector-code", "te6ccgECVAEACrkAART/APSkE/Sg8sgLAQIBIAIDAgFIBAUAUaX//xh2omh6AnoCETdKrPgN+SBOKjgQ+At5ICz4DPkgcXgL+SB4DRhAAgLFBgcCASAKCwIBywgJACyqgjGCEE5Db2SCEM5Db2RwQzCAQPAGAgEgEBECASAzNAIDeqAMDQIBbg4PACSqB+1E0PQFIG6SMHCU0NcLH+IAQqss7UTQ9AUgbpJbcODwAxAmXwaDB/QOb6GT+gAwkjBw4gBzsKV7UTQ9AUgbpIwbeDwAxAmXwZthP+OHiKDB/R+b6FvoSCcAvoAMFIQbwJQA28Cklow4gGzEuYwMYAAzs+A7UTQ9AQx9AQwgwf0Dm+hk/oAMJIwcOKACASASEwIBICMkAgEgFBUCASAdHgIBIBYXAgEgGxwBASAYACE7UTQ9AT0BPQE+gDTH9P/0YAL8AdDTAwFxsJJfA+D6QDAhxwCUMQHwEOAB0x8hwACUWwHwEODTPyKCEE5zdEu6lTIQI/AL4DQhghBHZXQkupMT8BHgIYIQTkNvZLqOGVRSRPASloIQzkNvZJKEH+JwEDRDMIBA8AbgIYIQ7nZPS7oighDudk9vulIQsYrgMDMgGRoADDIQIwHwDwAkgx6wm4QfcBA0QzCAQPAG4V8DACkyBb0ABT0ABL0AAH6Assfy//J7VSAAITQ0x/TH/oA+gD0BNIA0gDRgAgEgHyACASAhIgArMgXyx8Vyx9QA/oCAfoC9ADKAMoAyYAAdIAP+DPQ0h/SH9If1wofgAEUcIAYyMsFUAfPFlj6AhXLahPLH8s/IcL/kssfkTHiyQH7AIAAbIIQ7m9FTHBDMIBA8AaACASAlJgIBIC0uAgEgJygCASApKgAjIIQ83RITIIQO5rKAEMwcvAGgAEcghBOVlNUcIIAxP/IyxAVy/+DHfoCFMtqE8sfEss/zMlx+wCAAMxTEoMH9A5voZT6ADCgkTDiyAH6AgKDB/RDgAQEgKwH+I/pE7UTQ9AQhbgSkFLGXEDVfBXDwB+AE0//TH9Mf0//UAdCDCNcZAdGCEGVMUHTIyx9SQMsfUjDLH1Jgy/9SIMv/ydBRFfkRlxBoXwhx8AfhIYMPuZcQaF8IdvAH4AfwAzEF/iANghA7msoAoSCqCyO5mBC8XwwxcvAH4FEioCwA/lF9vZcQrF8Mc/AH4ASXEJtfC3DwB+BTY4MH9A5voSCfMPoAWaAB0z8x0/8wUoC9kTHilxCbXwt08AfgUwG5lxCbXwt18AfgIPKs+AD4I8hY+gLLHxTLHxbL/xjL/0A3gwf0Q3AQVxA0ECMQJnDwBMj0AAHPFsntVCCTcPAI4FsCASAvMAIBIDEyAHkcCB/jjAkgwf0fG+hb6EgjhwC0//TPzH6ANIA0ZQxUTOgl1QYiPAKBwPiUEOgk1BCMOIBsxPmMDMBuvK7gAKEcFMAf447JoMH9HxvoW+hII4nAtP/0z8x+gDSANGUMVEzoI4RVHcIqYRRZqBSF6BLsPAKCQPiUFOgk1BSMOIBsxTmMDUDulMhu7DyuxKgAaGAAVwBgCD0Zm+hkjBw4dM/MdP/MfQE+gD6APQEMdEgwgCUEDTwDZUwECPwDOISgAKMMgL6RHD4M9DXC//tRND0BASkWr2xIW6xkl8E4PADBV8FUhW9BLMUsZJfA+D4AAGRW44d9AT0BPoAQzTwDnDIygAT9AD0AFmg+gIBzxbJ7VTigAgEgNTYCAUhOTwIBIDc4AgEgPT4CASA5OgIBIDs8AKU8AEH+kQBpLEhwACxlwWgVRMB8ALgUwKAIPQOb6GOJ9M/0//0BPoA+gALoMgVyz8Ty//0AAH6AgH6AlAGzxYBgCBURkT0Q5QwBaAB4hA1QUPwAoADZDEh+kQBpJ8wghD////+cBA0E4BA8Abg7UTQ9AT0BFAzgwf0Zm+hjhBfBIIQ/////nAQNBOAQPAG4TYF+gDRyBL0ABX0AAHPFsntVIIQ+W9zJHCAGMjLBVAEzxZQBPoCEstqEssfyz/JgED7AIABpHD4MyBuk18EcODQ1wv/I/pEAaQCvbGTXwNw4PgAAdQh+wQgxwCSXwSZAe1TAfEGgvIA4n+AAAxwgAgEgP0ACASBJSgA1HACjhMCbyIhbxACbxEkqKsPErYIEqBY5DAxgAQEgQQP+gBD4M9DTD9MPMdMP0XG2CXBtf45GKYMH9HxvoW+hII4wAvoA0x/TH9P/0//RA6PIFct/FMofUkDL/8nQURq2CMjLHxPL/8v/QBSBAaD0QQOklBA0WjDiAbNDMOYwNFi2CFMBuZZfB21tcCDgbYrmMzSlXJJvEeRwIIrmMjI0NEJDRABqA4EBoPSSb6FvoSCOIAHTf1EZtggB0x8x1wv/A9Mf0/8x1wv/QTAUbwRQBW8Ck0YWW+IEsxQASgJvIgFvEASkU0i+jhBUZQbwFFMCvJMyMiKSEzDik1A0MOJTNr4BXiLAAFJDuRKxll8EbW1wIOAg/iAipZJvEeRvEG8QcCBtbYrmMzMzNVIUuvKxECMBRQL+BW8iAW8kUxyDB/QOb6HyvfoAMdM/MdcL/1OLuY5XUTmoqw9SQLYIUUShJKo7LakEUYWgghCOgSeKI5KAc5KAU+LIywfLH1JAy/9SkMs/I5MTy/+TUAMw4gGAEFRKmfRDcCTIy/8Zyz9QBfoCF8oAQBmDB/RDlgoQRgNfA+IjikZHAAhLc/AKARiK4gOkKW4QShBGREBIAAZLc1sBASBLAa08AGAIvgz+QBTAbqTXwdw4CKOL1MkgCD0Dm+hjh/THzEg0x/T/zBQBLryufgjUAOgyMsfWM8WQASAIPRDlBUTXwPik0QUW+J/iuYgbpIwcJDiUAPwAn+BNAf4B8AM0+CMluZNfCHDgcPgzbpRfCPAT4IAR+DPQ+gD6APoA0x/RU2G5lF8M8BPgBJRfC/AT4AaTXwpw4BA4KFEyUHfwFSDAACCzKgYQWhBJEDhMzPAEIo4TMjIyMjLIEvQA9AABzxbJ7VTwE+HwBTL4IwGgpsQotgmAEPgz0IAQTADI1yHXCw9SYLYIUxOggBHIywdSMMsfyx8Xyw8Wyw8J0BnPFslw+DPQ1wv/UxjwCQn0BHAL+QBQY6ApoMjLHxnLH8v/FfQAUAP6AnD6AhbKAEA0gCD0Q8gT9AD0APQAAc8Wye1UfwCkIYAg9HxvoW+hII4/AtM/0/9TF7qOLTQD9AT6APoAKKsCUZmhUCmgyBXLPxbL/xL0AAH6AgH6AljPFgGAIFRBRPRDcJYQNRA0XwPik0MDMOKzEgIBIFBRAgEgUlMAGQhbpJbcJUB+QABuuKAAlQB8AMFXwWTXwNw4QL0BFExgCD0Dm+hk18EcOGAQNch1wv/gCL4MyHwGIAk+DNY8BixjhNwyMoAEvQA9AABzxbJ7VTwFzB/4F8DcIACTPABf44+JIAg9HxvoW+hII4kAtMfMPgju1MUvbCOETFUFUTwDhSgVHYTVHNY8AJwlEYVUETilhA3FkUFMOKzECYQRRTmBl8GbrOAA4QxgCT4M26SW3Dhcfgz0NcL//go+kQBpAK9sZJbcOCAIvgzIG6TXwNw4PAFMDIC0IAo1yHXCx/4I1EToVy5k18GcOBcocE8kTGRMOKAEfgz0PoAMAOg/iBSAqFwbXAQRRA0cPAEyPQA9AABzxbJ7VR/g"); diff --git a/crypto/smartcont/auto/elector-code.fif b/crypto/smartcont/auto/elector-code.fif deleted file mode 100644 index d964063b2e..0000000000 --- a/crypto/smartcont/auto/elector-code.fif +++ /dev/null @@ -1,1712 +0,0 @@ -// automatically generated from `smartcont/stdlib.fc` `smartcont/elector-code.fc` -PROGRAM{ - DECLPROC load_data - DECLPROC store_data - DECLPROC unpack_elect - DECLPROC pack_elect - DECLPROC get_validator_conf - DECLPROC send_message_back - DECLPROC return_stake - DECLPROC send_confirmation - DECLPROC send_validator_set_to_config - DECLPROC ~credit_to - DECLPROC process_new_stake - DECLPROC unfreeze_without_bonuses - DECLPROC unfreeze_with_bonuses - DECLPROC unfreeze_all - DECLPROC config_set_confirmed - DECLPROC process_simple_transfer - DECLPROC recover_stake - 1666 DECLMETHOD after_code_upgrade - DECLPROC upgrade_code - DECLPROC recv_internal - DECLPROC postpone_elections - DECLPROC compute_total_stake - DECLPROC try_elect - DECLPROC conduct_elections - DECLPROC update_active_vset_id - DECLPROC cell_hash_eq? - DECLPROC validator_set_installed - DECLPROC check_unfreeze - DECLPROC announce_new_elections - DECLPROC run_ticktock - 86535 DECLMETHOD active_election_id - 87852 DECLMETHOD participates_in - 123541 DECLMETHOD participant_list - 130944 DECLMETHOD compute_returned_stake - load_data PROC:<{ - // - c4 PUSH // _1 - CTOS // cs - LDDICT // _4 cs - LDDICT // _4 _6 cs - LDDICT // _4 _6 _8 cs - LDGRAMS // _4 _6 _8 _10 cs - 32 LDU // _4 _6 _8 _10 _12 cs - 256 LDU // res res res res res res cs - ENDS - }> - store_data PROC:<{ - // elect credits past_elect grams active_id active_hash - NEWC // elect credits past_elect grams active_id active_hash _6 - s1 s6 XCHG // active_hash credits past_elect grams active_id elect _6 - STDICT // active_hash credits past_elect grams active_id _7 - s1 s4 XCHG // active_hash active_id past_elect grams credits _7 - STDICT // active_hash active_id past_elect grams _8 - s1 s2 XCHG // active_hash active_id grams past_elect _8 - STDICT // active_hash active_id grams _9 - SWAP // active_hash active_id _9 grams - STGRAMS // active_hash active_id _10 - 32 STU // active_hash _12 - 256 STU // _14 - ENDC // _15 - c4 POP - }> - unpack_elect PROC:<{ - // elect - CTOS // es - 32 LDU // _4 es - 32 LDU // _4 _7 es - LDGRAMS // _4 _7 _10 es - LDGRAMS // _4 _7 _10 _12 es - LDDICT // _4 _7 _10 _12 _14 es - 1 LDI // _4 _7 _10 _12 _14 _16 es - 1 LDI // res res res res res res res es - ENDS - }> - pack_elect PROC:<{ - // elect_at elect_close min_stake total_stake members failed finished - NEWC // elect_at elect_close min_stake total_stake members failed finished _7 - s1 s7 XCHG // finished elect_close min_stake total_stake members failed elect_at _7 - 32 STU // finished elect_close min_stake total_stake members failed _9 - s1 s5 XCHG // finished failed min_stake total_stake members elect_close _9 - 32 STU // finished failed min_stake total_stake members _11 - s0 s3 XCHG2 // finished failed members total_stake _11 min_stake - STGRAMS // finished failed members total_stake _12 - SWAP // finished failed members _12 total_stake - STGRAMS // finished failed members _13 - STDICT // finished failed _14 - 1 STI // finished _16 - 1 STI // _18 - ENDC // _19 - }> - get_validator_conf PROC:<{ - // - 15 PUSHINT // _1=15 - CONFIGOPTPARAM // _2 - CTOS // cs - 32 LDI // _4 cs - 32 LDI // _4 _7 cs - 32 LDI // _4 _7 _10 cs - 32 PLDI // _4 _7 _10 _14 - }> - send_message_back PROC:<{ - // addr ans_tag query_id body grams mode - 0 PUSHINT // addr ans_tag query_id body grams mode _7=0 - 24 PUSHINT // addr ans_tag query_id body grams mode _7=0 _8=24 - NEWC // addr ans_tag query_id body grams mode _7=0 _8=24 _9 - 6 STU // addr ans_tag query_id body grams mode _7=0 _11 - s0 s7 XCHG2 // _7=0 ans_tag query_id body grams mode _11 addr - STSLICER // _7=0 ans_tag query_id body grams mode _12 - ROT // _7=0 ans_tag query_id body mode _12 grams - STGRAMS // _7=0 ans_tag query_id body mode _13 - s1 s5 XCHG // mode ans_tag query_id body _7=0 _13 - 107 STU // mode ans_tag query_id body _27 - s1 s3 XCHG // mode body query_id ans_tag _27 - 32 STU // mode body query_id _29 - 64 STU // mode body msg - OVER // mode body msg body - -1 GTINT // mode body msg _33 - IF:<{ // mode body msg - 32 STU // mode msg - }>ELSE<{ - NIP // mode msg - }> - ENDC // mode _37 - SWAP // _37 mode - SENDRAWMSG - }> - return_stake PROC:<{ - // addr query_id reason - 4000269644 PUSHINT // addr query_id reason _3=4000269644 - 0 PUSHINT // addr query_id reason _3=4000269644 _4=0 - s3 s3 s0 XCHG3 - 64 PUSHINT // addr _3=4000269644 query_id reason _4=0 _5=64 - send_message_back CALLDICT - }> - send_confirmation PROC:<{ - // addr query_id comment - 4084484172 PUSHINT // addr query_id comment _3=4084484172 - 1000000000 PUSHINT // addr query_id comment _3=4084484172 _4=1000000000 - s3 s3 s0 XCHG3 - 2 PUSHINT // addr _3=4084484172 query_id comment _4=1000000000 _5=2 - send_message_back CALLDICT - }> - send_validator_set_to_config PROC:<{ - // config_addr vset query_id - 1314280276 PUSHINT // config_addr vset query_id _4=1314280276 - 0 PUSHINT // config_addr vset query_id _4=1314280276 _5=0 - 50431 PUSHINT // config_addr vset query_id _4=1314280276 _5=0 _6=50431 - NEWC // config_addr vset query_id _4=1314280276 _5=0 _6=50431 _7 - 17 STU // config_addr vset query_id _4=1314280276 _5=0 _9 - s1 s5 XCHG // _5=0 vset query_id _4=1314280276 config_addr _9 - 256 STU // _5=0 vset query_id _4=1314280276 _11 - 30 PUSHPOW2 // _5=0 vset query_id _4=1314280276 _11 _14 - STGRAMS // _5=0 vset query_id _4=1314280276 _15 - s1 s4 XCHG // _4=1314280276 vset query_id _5=0 _15 - 107 STU // _4=1314280276 vset query_id _29 - s1 s3 XCHG // query_id vset _4=1314280276 _29 - 32 STU // query_id vset _31 - s1 s2 XCHG // vset query_id _31 - 64 STU // vset _33 - STREF // msg - ENDC // _35 - 1 PUSHINT // _35 _36=1 - SENDRAWMSG - }> - ~credit_to PROC:<{ - // credits addr amount - s1 s2 PUSH2 - 8 PUSHPOW2 // credits addr amount addr credits _5=256 - DICTUGET - NULLSWAPIFNOT // credits addr amount val f - IF:<{ // credits addr amount val - LDGRAMS // credits addr amount _18 _17 - DROP // credits addr amount _7 - ADD // credits addr amount - }>ELSE<{ - DROP // credits addr amount - }> - NEWC // credits addr amount _11 - SWAP // credits addr _11 amount - STGRAMS // credits addr _12 - s0 s2 XCHG - 8 PUSHPOW2 // _12 addr credits _13=256 - DICTUSETB // credits - }> - process_new_stake PROC:<{ - // s_addr msg_value cs query_id - s3 PUSH // s_addr msg_value cs query_id s_addr - REWRITESTDADDR // s_addr msg_value cs query_id src_wc src_addr - c4 PUSH // s_addr msg_value cs query_id src_wc src_addr _8 - CTOS // s_addr msg_value cs query_id src_wc src_addr ds - LDDICT // s_addr msg_value cs query_id src_wc src_addr elect ds - OVER // s_addr msg_value cs query_id src_wc src_addr elect ds elect - ISNULL // s_addr msg_value cs query_id src_wc src_addr elect ds _13 - s0 s4 XCHG // s_addr msg_value cs query_id _13 src_addr elect ds src_wc - INC // s_addr msg_value cs query_id _13 src_addr elect ds _15 - s1 s4 XCHG // s_addr msg_value cs query_id ds src_addr elect _13 _15 - OR // s_addr msg_value cs query_id ds src_addr elect _16 - IFJMP:<{ // s_addr msg_value cs query_id ds src_addr elect - s3 s5 XCHG - 5 BLKDROP // s_addr query_id - 0 PUSHINT // s_addr query_id _17=0 - return_stake CALLDICT - }> // s_addr msg_value cs query_id ds src_addr elect - s0 s4 XCHG // s_addr msg_value elect query_id ds src_addr cs - 256 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey cs - 32 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at cs - 32 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor cs - 256 LDU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs - LDREF // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _36 cs - SWAP // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs _36 - CTOS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs _38 - 9 PUSHPOW2 // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs _38 _39=512 - PLDSLICEX // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr cs signature - SWAP // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature cs - ENDS - 1699500148 PUSHINT // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _42=1699500148 - NEWC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _42=1699500148 _43 - 32 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _45 - s4 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature stake_at _45 - 32 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _47 - s3 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature max_factor _47 - 32 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _49 - s6 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature src_addr _49 - 256 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _51 - s2 s(-1) PUXC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature adnl_addr _51 - 256 STU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _53 - ENDC // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _54 - CTOS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr signature _55 - s1 s5 XCPU // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _55 signature validator_pubkey - CHKSIGNS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _56 - IFNOTJMP:<{ // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr - s6 s8 XCHG - 8 BLKDROP // s_addr query_id - 1 PUSHINT // s_addr query_id _57=1 - return_stake CALLDICT - }> // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr - OVER - 16 PUSHPOW2 // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr max_factor _59=65536 - LESS // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr _60 - IFJMP:<{ // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr - s6 s8 XCHG - 8 BLKDROP // s_addr query_id - 6 PUSHINT // s_addr query_id _61=6 - return_stake CALLDICT - }> // s_addr msg_value elect query_id ds src_addr validator_pubkey stake_at max_factor adnl_addr - s0 s7 XCHG // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor elect - unpack_elect CALLDICT // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor _146 _147 _148 _149 _150 _151 _152 - NIP // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor elect_at elect_close min_stake total_stake members finished - s0 s5 XCHG // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members elect_at - s0 DUMP // s_addr msg_value adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members elect_at - s0 s13 XCHG - 1000000000 PUSHINT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _73=1000000000 - SUB // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value - DUP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value msg_value - 12 LSHIFT# // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _76 - s3 PUSH // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _76 total_stake - LESS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value _77 - IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value - s11 s12 XCHG - 12 BLKDROP - NIP // s_addr query_id - 2 PUSHINT // s_addr query_id _78=2 - return_stake CALLDICT - }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake total_stake members msg_value - s2 s2 XCPU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake msg_value members total_stake msg_value - ADD // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey stake_at max_factor finished elect_close min_stake msg_value members total_stake - s7 s13 XCPU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members stake_at elect_at - NEQ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members _81 - IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members - s10 s12 XCHG - 12 BLKDROP // s_addr query_id - 3 PUSHINT // s_addr query_id _82=3 - return_stake CALLDICT - }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor finished elect_close min_stake msg_value members - s0 s4 XCHG // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value finished - IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value - s9 s11 XCHG - 11 BLKDROP // s_addr query_id - 0 PUSHINT // s_addr query_id _84=0 - return_stake CALLDICT - }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value - s6 s3 PUSH2 - 8 PUSHPOW2 // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value validator_pubkey members _88=256 - DICTUGET - NULLSWAPIFNOT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem found - DUP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem found found - IF:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem found - DROP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem - LDGRAMS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _90 mem - -ROT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake mem msg_value _90 - ADD // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake mem msg_value - SWAP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem - 64 LDU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _158 _157 - NIP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value mem - 256 LDU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _160 _159 - DROP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _96 - s8 s(-1) PUXC // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value src_addr _96 - NEQ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value found - }>ELSE<{ - NIP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value found - }> - IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value - s9 s11 XCHG - 11 BLKDROP // s_addr query_id - 4 PUSHINT // s_addr query_id _100=4 - return_stake CALLDICT - }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value - s0 s1 PUSH2 // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value msg_value min_stake - LESS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _102 - IFJMP:<{ // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value - s9 s11 XCHG - 11 BLKDROP // s_addr query_id - 5 PUSHINT // s_addr query_id _103=5 - return_stake CALLDICT - }> // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value - DUP // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value msg_value - 44 THROWIFNOT - ACCEPT - NOW // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _109 - NEWC // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake msg_value _109 _110 - ROT // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake _109 _110 msg_value - STGRAMS // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake _109 _111 - 32 STU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake max_factor members elect_close min_stake _113 - s1 s4 XCHG // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake min_stake members elect_close max_factor _113 - 32 STU // s_addr elect_at adnl_addr query_id ds src_addr validator_pubkey total_stake min_stake members elect_close _115 - s1 s6 XCHG // s_addr elect_at adnl_addr query_id ds elect_close validator_pubkey total_stake min_stake members src_addr _115 - 256 STU // s_addr elect_at adnl_addr query_id ds elect_close validator_pubkey total_stake min_stake members _117 - s1 s8 XCHG // s_addr elect_at members query_id ds elect_close validator_pubkey total_stake min_stake adnl_addr _117 - 256 STU // s_addr elect_at members query_id ds elect_close validator_pubkey total_stake min_stake _119 - s0 s3 s7 XCHG3 - 8 PUSHPOW2 // s_addr elect_at total_stake query_id ds elect_close min_stake _119 validator_pubkey members _120=256 - DICTUSETB // s_addr elect_at total_stake query_id ds elect_close min_stake members - FALSE // s_addr elect_at total_stake query_id ds elect_close min_stake members _122 - s5 s7 XCHG - s3 s4 XCHG - s2 s3 XCHG - s2 s6 XCHG - FALSE // s_addr query_id ds elect_at elect_close min_stake total_stake members _122 _123 - pack_elect CALLDICT // s_addr query_id ds elect - NEWC // s_addr query_id ds elect _125 - STDICT // s_addr query_id ds _126 - SWAP // s_addr query_id _126 ds - STSLICER // s_addr query_id _127 - ENDC // s_addr query_id _128 - c4 POP - DUP // s_addr query_id query_id - IFJMP:<{ // s_addr query_id - 0 PUSHINT // s_addr query_id _130=0 - send_confirmation CALLDICT - }> // s_addr query_id - 2DROP // - }> - unfreeze_without_bonuses PROC:<{ - // credits freeze_dict tot_stakes - 0 PUSHINT // credits freeze_dict tot_stakes _5=0 - DUP // credits freeze_dict tot_stakes total=0 recovered=0 - -1 PUSHINT // credits freeze_dict tot_stakes total=0 recovered=0 pubkey=-1 - UNTIL:<{ - s4 PUSH - 8 PUSHPOW2 // credits freeze_dict tot_stakes total recovered pubkey freeze_dict _10=256 - DICTUGETNEXT - NULLSWAPIFNOT - NULLSWAPIFNOT // credits freeze_dict tot_stakes total recovered cs pubkey f - DUP // credits freeze_dict tot_stakes total recovered cs pubkey f f - IF:<{ // credits freeze_dict tot_stakes total recovered cs pubkey f - s0 s2 XCHG // credits freeze_dict tot_stakes total recovered f pubkey cs - 256 LDU // credits freeze_dict tot_stakes total recovered f pubkey _16 cs - 64 LDU // credits freeze_dict tot_stakes total recovered f pubkey _16 _42 _41 - NIP // credits freeze_dict tot_stakes total recovered f pubkey _16 cs - LDGRAMS // credits freeze_dict tot_stakes total recovered f pubkey _16 _22 cs - 1 LDI // credits freeze_dict tot_stakes total recovered f pubkey addr stake banned cs - ENDS - IF:<{ // credits freeze_dict tot_stakes total recovered f pubkey addr stake - NIP // credits freeze_dict tot_stakes total recovered f pubkey stake - s3 s3 XCPU // credits freeze_dict tot_stakes total stake f pubkey recovered stake - ADD // credits freeze_dict tot_stakes total stake f pubkey recovered - }>ELSE<{ // credits freeze_dict tot_stakes total recovered f pubkey addr stake - s8 s8 s8 XC2PU // stake freeze_dict tot_stakes total recovered f pubkey credits addr stake - ~credit_to CALLDICT // stake freeze_dict tot_stakes total recovered f pubkey credits - s0 s7 XCHG - s0 s3 XCHG // credits freeze_dict tot_stakes total stake f pubkey recovered - }> - s4 s3 XCHG2 // credits freeze_dict tot_stakes pubkey recovered f total stake - ADD // credits freeze_dict tot_stakes pubkey recovered f total - }>ELSE<{ - s4 s2 XCHG2 - DROP // credits freeze_dict tot_stakes pubkey recovered f total - }> - SWAP // credits freeze_dict tot_stakes pubkey recovered total f - NOT // credits freeze_dict tot_stakes pubkey recovered total _32 - s1 s3 XCHG // credits freeze_dict tot_stakes total recovered pubkey _32 - }> // credits freeze_dict tot_stakes total recovered pubkey - DROP - s3 POP // credits recovered tot_stakes total - SWAP // credits recovered total tot_stakes - EQUAL // credits recovered _34 - 59 THROWIFNOT - }> - unfreeze_with_bonuses PROC:<{ - // credits freeze_dict tot_stakes tot_bonuses - 0 PUSHINT // credits freeze_dict tot_stakes tot_bonuses _7=0 - s0 s0 PUSH2 // credits freeze_dict tot_stakes tot_bonuses total=0 returned_bonuses=0 recovered=0 - -1 PUSHINT // credits freeze_dict tot_stakes tot_bonuses total=0 returned_bonuses=0 recovered=0 pubkey=-1 - UNTIL:<{ - s6 PUSH - 8 PUSHPOW2 // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered pubkey freeze_dict _12=256 - DICTUGETNEXT - NULLSWAPIFNOT - NULLSWAPIFNOT // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered cs pubkey f - DUP // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered cs pubkey f f - IF:<{ // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered cs pubkey f - s0 s2 XCHG // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey cs - 256 LDU // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 cs - 64 LDU // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 _52 _51 - NIP // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 cs - LDGRAMS // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey _18 _24 cs - 1 LDI // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake banned cs - ENDS - IF:<{ // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake - NIP // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey stake - s3 s3 XCPU // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses stake f pubkey recovered stake - ADD // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses stake f pubkey recovered - }>ELSE<{ // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake - s7 s0 s8 PUSH3 // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake tot_bonuses stake tot_stakes - MULDIV // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake bonus - s6 s6 XCPU // credits freeze_dict tot_stakes tot_bonuses total bonus recovered f pubkey addr stake returned_bonuses bonus - ADD // credits freeze_dict tot_stakes tot_bonuses total bonus recovered f pubkey addr stake returned_bonuses - s1 s6 PUXC // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake stake bonus - ADD // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey addr stake _35 - s11 s11 s0 XCHG3 // stake freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey credits addr _35 - ~credit_to CALLDICT // stake freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered f pubkey credits - s0 s9 XCHG - s0 s3 XCHG // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses stake f pubkey recovered - }> - s5 s3 XCHG2 // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered f total stake - ADD // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered f total - }>ELSE<{ - s5 s2 XCHG2 - DROP // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered f total - }> - SWAP // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered total f - NOT // credits freeze_dict tot_stakes tot_bonuses pubkey returned_bonuses recovered total _38 - s1 s4 XCHG // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered pubkey _38 - }> // credits freeze_dict tot_stakes tot_bonuses total returned_bonuses recovered pubkey - DROP - s5 POP // credits recovered tot_stakes tot_bonuses total returned_bonuses - s0 s3 XCHG // credits recovered returned_bonuses tot_bonuses total tot_stakes - EQUAL // credits recovered returned_bonuses tot_bonuses _40 - s2 s1 PUSH2 // credits recovered returned_bonuses tot_bonuses _40 returned_bonuses tot_bonuses - LEQ // credits recovered returned_bonuses tot_bonuses _40 _41 - AND // credits recovered returned_bonuses tot_bonuses _42 - 59 THROWIFNOT - s1 s2 XCHG // credits returned_bonuses recovered tot_bonuses - ADD // credits returned_bonuses _44 - SWAP // credits _44 returned_bonuses - SUB // credits _45 - }> - unfreeze_all PROC:<{ - // credits past_elections elect_id - SWAP - 32 PUSHINT // credits elect_id past_elections _6=32 - DICTUDELGET - NULLSWAPIFNOT // credits past_elections fs f - IFNOTJMP:<{ // credits past_elections fs - DROP // credits past_elections - 0 PUSHINT // credits past_elections _8=0 - }> // credits past_elections fs - 64 LDU // credits past_elections _44 _43 - NIP // credits past_elections fs - 256 LDU // credits past_elections _46 _45 - NIP // credits past_elections fs - LDDICT // credits past_elections _21 fs - LDGRAMS // credits past_elections _21 _23 fs - LDGRAMS // credits past_elections _21 _23 _25 fs - LDDICT // credits past_elections _21 _23 _25 _54 _53 - NIP // credits past_elections fdict tot_stakes bonuses fs - ENDS - DUP // credits past_elections fdict tot_stakes bonuses bonuses - 0 GTINT // credits past_elections fdict tot_stakes bonuses _32 - IF:<{ // credits past_elections fdict tot_stakes bonuses - s3 s4 XCHG // past_elections credits fdict tot_stakes bonuses - unfreeze_with_bonuses CALLDICT // past_elections credits _33 - }>ELSE<{ // credits past_elections fdict tot_stakes bonuses - DROP // credits past_elections fdict tot_stakes - s2 s3 XCHG // past_elections credits fdict tot_stakes - unfreeze_without_bonuses CALLDICT // past_elections credits _33 - }> // past_elections credits unused_prizes - s1 s2 XCHG // credits past_elections unused_prizes - }> - config_set_confirmed PROC:<{ - // s_addr cs query_id ok - s2 POP // s_addr ok query_id - s0 s2 XCHG // query_id ok s_addr - REWRITESTDADDR // query_id ok src_wc src_addr - 0 PUSHINT // query_id ok src_wc src_addr _8=0 - CONFIGOPTPARAM // query_id ok src_wc src_addr _9 - CTOS // query_id ok src_wc src_addr _10 - 256 PLDU // query_id ok src_wc src_addr config_addr - c4 PUSH // query_id ok src_wc src_addr config_addr _14 - CTOS // query_id ok src_wc src_addr config_addr ds - LDDICT // query_id ok src_wc src_addr config_addr elect ds - s0 s4 XCHG // query_id ok ds src_addr config_addr elect src_wc - INC // query_id ok ds src_addr config_addr elect _20 - 2SWAP // query_id ok ds elect _20 src_addr config_addr - NEQ // query_id ok ds elect _20 _21 - OR // query_id ok ds elect _22 - OVER // query_id ok ds elect _22 elect - ISNULL // query_id ok ds elect _22 _23 - OR // query_id ok ds elect _24 - IFJMP:<{ // query_id ok ds elect - 4 BLKDROP // - }> // query_id ok ds elect - unpack_elect CALLDICT // query_id ok ds _63 _64 _65 _66 _67 _68 _69 - s0 s5 XCHG - 5 BLKDROP // query_id ok ds elect_at finished - s1 s4 PUXC // finished ok ds elect_at elect_at query_id - NEQ // finished ok ds elect_at _33 - s0 s4 XCHG // _33 ok ds elect_at finished - NOT // _33 ok ds elect_at _34 - s1 s4 XCHG // elect_at ok ds _33 _34 - OR // elect_at ok ds _35 - IFJMP:<{ // elect_at ok ds - 3 BLKDROP // - }> // elect_at ok ds - ACCEPT - SWAP // elect_at ds ok - IFNOT:<{ // elect_at ds - LDDICT // elect_at _40 ds - LDDICT // elect_at _40 _42 ds - LDGRAMS // elect_at credits past_elections grams ds - s3 s3 s4 XCHG3 // ds grams credits past_elections elect_at - unfreeze_all CALLDICT // ds grams credits past_elections unused_prizes - FALSE // ds grams credits past_elections unused_prizes _48 - NEWC // ds grams credits past_elections unused_prizes _48 _49 - 1 STI // ds grams credits past_elections unused_prizes _51 - s1 s3 XCHG // ds grams unused_prizes past_elections credits _51 - STDICT // ds grams unused_prizes past_elections _52 - STDICT // ds grams unused_prizes _53 - -ROT // ds _53 grams unused_prizes - ADD // ds _53 _54 - STGRAMS // ds _55 - SWAP // _55 ds - STSLICER // _56 - ENDC // _57 - c4 POP - }>ELSE<{ - 2DROP // - }> - }> - process_simple_transfer PROC:<{ - // s_addr msg_value - load_data CALLDICT // s_addr msg_value elect credits past_elect grams active_id active_hash - s0 s7 XCHG // active_hash msg_value elect credits past_elect grams active_id s_addr - REWRITESTDADDR // active_hash msg_value elect credits past_elect grams active_id src_wc src_addr - SWAP // active_hash msg_value elect credits past_elect grams active_id src_addr src_wc - INC // active_hash msg_value elect credits past_elect grams active_id src_addr _13 - OR // active_hash msg_value elect credits past_elect grams active_id _14 - OVER // active_hash msg_value elect credits past_elect grams active_id _14 active_id - 0 EQINT // active_hash msg_value elect credits past_elect grams active_id _14 _16 - OR // active_hash msg_value elect credits past_elect grams active_id _17 - IFJMP:<{ // active_hash msg_value elect credits past_elect grams active_id - s0 s5 XCHG // active_hash active_id elect credits past_elect grams msg_value - ADD // active_hash active_id elect credits past_elect grams - 2 4 BLKSWAP - SWAP // elect credits past_elect grams active_id active_hash - store_data CALLDICT - }> // active_hash msg_value elect credits past_elect grams active_id - s0 s2 PUSH2 - 32 PUSHINT // active_hash msg_value elect credits past_elect grams active_id active_id past_elect _22=32 - DICTUGET - NULLSWAPIFNOT // active_hash msg_value elect credits past_elect grams active_id fs f - IF:<{ // active_hash msg_value elect credits past_elect grams active_id fs - 64 LDU // active_hash msg_value elect credits past_elect grams active_id _30 fs - 256 LDU // active_hash msg_value elect credits past_elect grams active_id _30 _33 fs - LDDICT // active_hash msg_value elect credits past_elect grams active_id _30 _33 _36 fs - LDGRAMS // active_hash msg_value elect credits past_elect grams active_id _30 _33 _36 _38 fs - LDGRAMS // active_hash msg_value elect credits past_elect grams active_id data hash dict total_stake bonuses fs - s0 s11 XCHG // active_hash fs elect credits past_elect grams active_id data hash dict total_stake bonuses msg_value - ADD // active_hash fs elect credits past_elect grams active_id data hash dict total_stake bonuses - NEWC // active_hash fs elect credits past_elect grams active_id data hash dict total_stake bonuses _44 - s1 s5 XCHG // active_hash fs elect credits past_elect grams active_id bonuses hash dict total_stake data _44 - 64 STU // active_hash fs elect credits past_elect grams active_id bonuses hash dict total_stake _46 - s1 s3 XCHG // active_hash fs elect credits past_elect grams active_id bonuses total_stake dict hash _46 - 256 STU // active_hash fs elect credits past_elect grams active_id bonuses total_stake dict _48 - STDICT // active_hash fs elect credits past_elect grams active_id bonuses total_stake _49 - SWAP // active_hash fs elect credits past_elect grams active_id bonuses _49 total_stake - STGRAMS // active_hash fs elect credits past_elect grams active_id bonuses _50 - SWAP // active_hash fs elect credits past_elect grams active_id _50 bonuses - STGRAMS // active_hash fs elect credits past_elect grams active_id _51 - s0 s6 XCHG2 // active_hash active_id elect credits past_elect grams _51 fs - STSLICER // active_hash active_id elect credits past_elect grams _52 - SWAP - 32 PUSHINT - s6 s3 s3 PUXC2 // active_hash active_id elect credits grams _52 active_id past_elect _53=32 - DICTUSETB // active_hash active_id elect credits grams past_elect - }>ELSE<{ // active_hash msg_value elect credits past_elect grams active_id fs - DROP // active_hash msg_value elect credits past_elect grams active_id - s0 s5 XCHG // active_hash active_id elect credits past_elect grams msg_value - ADD // active_hash active_id elect credits past_elect grams - SWAP // active_hash active_id elect credits grams past_elect - }> - s3 s5 XCHG - s1 s4 s3 XCHG3 // elect credits past_elect grams active_id active_hash - store_data CALLDICT - }> - recover_stake PROC:<{ - // op s_addr cs query_id - NIP // op s_addr query_id - OVER // op s_addr query_id s_addr - REWRITESTDADDR // op s_addr query_id src_wc src_addr - SWAP // op s_addr query_id src_addr src_wc - INC // op s_addr query_id src_addr _8 - IFJMP:<{ // op s_addr query_id src_addr - DROP // op s_addr query_id - 4294967294 PUSHINT // op s_addr query_id _9=4294967294 - 0 PUSHINT // op s_addr query_id _9=4294967294 _10=0 - s3 s4 XCHG - s1 s3 XCHG - 64 PUSHINT // s_addr _9=4294967294 query_id op _10=0 _11=64 - send_message_back CALLDICT - }> // op s_addr query_id src_addr - c4 PUSH // op s_addr query_id src_addr _14 - CTOS // op s_addr query_id src_addr ds - LDDICT // op s_addr query_id src_addr _18 ds - LDDICT // op s_addr query_id src_addr elect credits ds - s3 s3 XCHG2 - 8 PUSHPOW2 // op s_addr query_id ds elect src_addr credits _25=256 - DICTUDELGET - NULLSWAPIFNOT // op s_addr query_id ds elect credits cs f - IFNOTJMP:<{ // op s_addr query_id ds elect credits cs - 4 BLKDROP // op s_addr query_id - 4294967294 PUSHINT // op s_addr query_id _27=4294967294 - 0 PUSHINT // op s_addr query_id _27=4294967294 _28=0 - s3 s4 XCHG - s1 s3 XCHG - 64 PUSHINT // s_addr _27=4294967294 query_id op _28=0 _29=64 - send_message_back CALLDICT - }> // op s_addr query_id ds elect credits cs - s6 POP // cs s_addr query_id ds elect credits - s0 s5 XCHG // credits s_addr query_id ds elect cs - LDGRAMS // credits s_addr query_id ds elect amount cs - ENDS - NEWC // credits s_addr query_id ds elect amount _35 - s1 s2 XCHG // credits s_addr query_id ds amount elect _35 - STDICT // credits s_addr query_id ds amount _36 - s1 s5 XCHG // amount s_addr query_id ds credits _36 - STDICT // amount s_addr query_id ds _37 - SWAP // amount s_addr query_id _37 ds - STSLICER // amount s_addr query_id _38 - ENDC // amount s_addr query_id _39 - c4 POP - 4184830756 PUSHINT // amount s_addr query_id _41=4184830756 - 0 PUSHINT // amount s_addr query_id _41=4184830756 _42=0 - 24 PUSHINT // amount s_addr query_id _41=4184830756 _42=0 _43=24 - NEWC // amount s_addr query_id _41=4184830756 _42=0 _43=24 _44 - 6 STU // amount s_addr query_id _41=4184830756 _42=0 _46 - s0 s4 XCHG2 // amount _42=0 query_id _41=4184830756 _46 s_addr - STSLICER // amount _42=0 query_id _41=4184830756 _47 - s0 s4 XCHG2 // _41=4184830756 _42=0 query_id _47 amount - STGRAMS // _41=4184830756 _42=0 query_id _48 - s1 s2 XCHG // _41=4184830756 query_id _42=0 _48 - 107 STU // _41=4184830756 query_id _62 - s1 s2 XCHG // query_id _41=4184830756 _62 - 32 STU // query_id _64 - 64 STU // _66 - ENDC // _67 - 64 PUSHINT // _67 _68=64 - SENDRAWMSG - }> - after_code_upgrade PROC:<{ - // s_addr cs query_id - NIP // s_addr query_id - 1313042276 PUSHINT // s_addr query_id op=1313042276 - 3460525924 PUSHINT // s_addr query_id op=1313042276 _5=3460525924 - 0 PUSHINT // s_addr query_id op=1313042276 _5=3460525924 _6=0 - s3 s3 s0 XCHG3 - 64 PUSHINT // s_addr _5=3460525924 query_id op=1313042276 _6=0 _7=64 - send_message_back CALLDICT - }> - upgrade_code PROC:<{ - // s_addr cs query_id - 0 PUSHINT // s_addr cs query_id _4=0 - CONFIGOPTPARAM // s_addr cs query_id c_addr - DUP // s_addr cs query_id c_addr c_addr - ISNULL // s_addr cs query_id c_addr _6 - IFJMP:<{ // s_addr cs query_id c_addr - 4 BLKDROP // - FALSE // _7 - }> // s_addr cs query_id c_addr - CTOS // s_addr cs query_id _9 - 256 PLDU // s_addr cs query_id config_addr - s3 PUSH // s_addr cs query_id config_addr s_addr - REWRITESTDADDR // s_addr cs query_id config_addr src_wc src_addr - SWAP // s_addr cs query_id config_addr src_addr src_wc - INC // s_addr cs query_id config_addr src_addr _16 - s0 s2 XCHG // s_addr cs query_id _16 src_addr config_addr - NEQ // s_addr cs query_id _16 _17 - OR // s_addr cs query_id _18 - IFJMP:<{ // s_addr cs query_id - 3 BLKDROP // - FALSE // _19 - }> // s_addr cs query_id - ACCEPT - SWAP // s_addr query_id cs - LDREF // s_addr query_id code cs - OVER // s_addr query_id code cs code - SETCODE - DUP // s_addr query_id code cs cs - SEMPTY // s_addr query_id code cs _25 - IFNOT:<{ // s_addr query_id code cs - SWAP // s_addr query_id cs code - c3 POP - SWAP // s_addr cs query_id - after_code_upgrade CALLDICT - 0 THROW - }>ELSE<{ - 4 BLKDROP // - }> - TRUE // _30 - }> - recv_internal PROC:<{ - // msg_value in_msg_cell in_msg - SWAP // msg_value in_msg in_msg_cell - CTOS // msg_value in_msg cs - 4 LDU // msg_value in_msg flags cs - SWAP - 1 PUSHINT // msg_value in_msg cs flags _9=1 - AND // msg_value in_msg cs _10 - IFJMP:<{ // msg_value in_msg cs - 3 BLKDROP // - }> // msg_value in_msg cs - LDMSGADDR // msg_value in_msg _61 _60 - DROP // msg_value in_msg s_addr - OVER // msg_value in_msg s_addr in_msg - SEMPTY // msg_value in_msg s_addr _14 - IFJMP:<{ // msg_value in_msg s_addr - NIP // msg_value s_addr - SWAP // s_addr msg_value - process_simple_transfer CALLDICT - }> // msg_value in_msg s_addr - SWAP // msg_value s_addr in_msg - 32 LDU // msg_value s_addr op in_msg - OVER // msg_value s_addr op in_msg op - 0 EQINT // msg_value s_addr op in_msg _21 - IFJMP:<{ // msg_value s_addr op in_msg - 2DROP // msg_value s_addr - SWAP // s_addr msg_value - process_simple_transfer CALLDICT - }> // msg_value s_addr op in_msg - 64 LDU // msg_value s_addr op query_id in_msg - s2 PUSH - 1316189259 PUSHINT // msg_value s_addr op query_id in_msg op _27=1316189259 - EQUAL // msg_value s_addr op query_id in_msg _28 - IFJMP:<{ // msg_value s_addr op query_id in_msg - s2 POP // msg_value s_addr in_msg query_id - s2 s3 XCHG // s_addr msg_value in_msg query_id - process_new_stake CALLDICT - }> // msg_value s_addr op query_id in_msg - s4 POP // in_msg s_addr op query_id - OVER - 1197831204 PUSHINT // in_msg s_addr op query_id op _30=1197831204 - EQUAL // in_msg s_addr op query_id _31 - IFJMP:<{ // in_msg s_addr op query_id - s1 s3 XCHG // op s_addr in_msg query_id - recover_stake CALLDICT - }> // in_msg s_addr op query_id - OVER - 1313042276 PUSHINT // in_msg s_addr op query_id op _33=1313042276 - EQUAL // in_msg s_addr op query_id _34 - IFJMP:<{ // in_msg s_addr op query_id - s2 s3 s3 PUXCPU // query_id s_addr op s_addr in_msg query_id - upgrade_code CALLDICT // query_id s_addr op ok - IF:<{ // query_id s_addr op - 3460525924 PUSHINT // query_id s_addr op _37=3460525924 - }>ELSE<{ // query_id s_addr op - 32 PUSHPOW2DEC // query_id s_addr op _37=4294967295 - }> - 0 PUSHINT // query_id s_addr op _37 _40=0 - s3 s4 XCHG - s3 s3 s0 XCHG3 - 64 PUSHINT // s_addr _37 query_id op _40=0 _41=64 - send_message_back CALLDICT - }> // in_msg s_addr op query_id - OVER - 4000730955 PUSHINT // in_msg s_addr op query_id op _44=4000730955 - EQUAL // in_msg s_addr op query_id cfg_ok - s2 PUSH - 4000730991 PUSHINT // in_msg s_addr op query_id cfg_ok op _46=4000730991 - EQUAL // in_msg s_addr op query_id cfg_ok _47 - s1 s(-1) PUXC // in_msg s_addr op query_id cfg_ok cfg_ok _47 - OR // in_msg s_addr op query_id cfg_ok _48 - IFJMP:<{ // in_msg s_addr op query_id cfg_ok - s2 POP // in_msg s_addr cfg_ok query_id - s2 s3 XCHG - SWAP // s_addr in_msg query_id cfg_ok - config_set_confirmed CALLDICT - }> // in_msg s_addr op query_id cfg_ok - DROP - s3 POP // query_id s_addr op - DUP - 31 PUSHPOW2 // query_id s_addr op op _52 - AND // query_id s_addr op _53 - IFNOTJMP:<{ // query_id s_addr op - 32 PUSHPOW2DEC // query_id s_addr op _54=4294967295 - 0 PUSHINT // query_id s_addr op _54=4294967295 _55=0 - s3 s4 XCHG - s3 s3 s0 XCHG3 - 64 PUSHINT // s_addr _54=4294967295 query_id op _55=0 _56=64 - send_message_back CALLDICT - }> // query_id s_addr op - 3 BLKDROP // - }> - postpone_elections PROC:<{ - // - FALSE // _0 - }> - compute_total_stake PROC:<{ - // l n m_stake - 0 PUSHINT // l n m_stake tot_stake=0 - s0 s2 XCHG // l tot_stake=0 m_stake n - REPEAT:<{ // l tot_stake m_stake - s0 s2 XCHG // m_stake tot_stake l - UNCONS // m_stake tot_stake h l - OVER // m_stake tot_stake h l h - 0 INDEX // m_stake tot_stake h l stake - s0 s2 XCHG // m_stake tot_stake stake l h - 1 INDEX // m_stake tot_stake stake l max_f - s4 PUSH // m_stake tot_stake stake l max_f m_stake - MUL // m_stake tot_stake stake l _13 - 16 RSHIFT# // m_stake tot_stake stake l _15 - s1 s2 XCHG // m_stake tot_stake l stake _15 - MIN // m_stake tot_stake l stake - s1 s2 XCHG // m_stake l tot_stake stake - ADD // m_stake l tot_stake - ROT // l tot_stake m_stake - }> - DROP - NIP // tot_stake - }> - try_elect PROC:<{ - // credits members min_stake max_stake min_total_stake max_stake_factor - 16 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor _7=16 - CONFIGOPTPARAM // credits members min_stake max_stake min_total_stake max_stake_factor _8 - CTOS // credits members min_stake max_stake min_total_stake max_stake_factor cs - 16 LDU // credits members min_stake max_stake min_total_stake max_stake_factor _13 cs - 16 LDU // credits members min_stake max_stake min_total_stake max_stake_factor _13 _253 _252 - NIP // credits members min_stake max_stake min_total_stake max_stake_factor _13 cs - 16 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators cs - ENDS - 1 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators _23=1 - MAX // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators - 0 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n=0 - NEWDICT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n=0 sdict - -1 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n=0 sdict pubkey=-1 - UNTIL:<{ - s9 PUSH - 8 PUSHPOW2 // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict pubkey members _33=256 - DICTUGETNEXT - NULLSWAPIFNOT - NULLSWAPIFNOT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict cs pubkey f - DUP // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict cs pubkey f f - IF:<{ // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict cs pubkey f - s0 s2 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey cs - LDGRAMS // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 cs - 32 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 _42 cs - 32 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 _42 _45 cs - 256 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _40 _42 _45 _48 cs - 256 LDU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake time max_factor addr adnl_addr cs - ENDS - s0 s3 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake adnl_addr max_factor addr time - NEGATE // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake adnl_addr max_factor addr _56 - NEWC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey stake adnl_addr max_factor addr _56 _57 - s1 s5 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _56 adnl_addr max_factor addr stake _57 - 128 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey _56 adnl_addr max_factor addr _59 - s1 s4 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _56 _59 - 32 STI // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _61 - s4 s(-1) PUXC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor pubkey _61 - 256 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _63 - ENDC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor _64 - CTOS // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr max_factor key - s1 s10 XCPU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key max_factor max_stake_factor - MIN // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key _67 - NEWC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key _67 _68 - 32 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey addr adnl_addr key _70 - s1 s3 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey key adnl_addr addr _70 - 256 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey key adnl_addr _72 - 256 STU // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict f pubkey key _74 - s0 s1 s4 XCHG3 - 416 PUSHINT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n pubkey f _74 key sdict _79 - DICTSETB // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n pubkey f sdict - s0 s3 XCHG // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey f n - INC // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey f n - }>ELSE<{ - s3 s4 XCHG - 2SWAP - DROP // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey f n - }> - SWAP // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey n f - NOT // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators sdict pubkey n _83 - s3 s3 s0 XCHG3 // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict pubkey _83 - }> // credits members min_stake max_stake min_total_stake max_stake_factor max_validators min_validators n sdict pubkey - DROP - s4 POP // credits members min_stake max_stake min_total_stake sdict max_validators min_validators n - ROT // credits members min_stake max_stake min_total_stake sdict min_validators n max_validators - MIN // credits members min_stake max_stake min_total_stake sdict min_validators n - s0 s1 PUSH2 // credits members min_stake max_stake min_total_stake sdict min_validators n n min_validators - LESS // credits members min_stake max_stake min_total_stake sdict min_validators n _85 - IFJMP:<{ // credits members min_stake max_stake min_total_stake sdict min_validators n - 7 BLKDROP // credits - NEWDICT // credits _86 - NEWDICT // credits _86 _87 - 0 PUSHINT // credits _86 _87 _88=0 - DUP // credits _86 _87 _88=0 _89=0 - }> // credits members min_stake max_stake min_total_stake sdict min_validators n - PUSHNULL // credits members min_stake max_stake min_total_stake sdict min_validators n l - UNTIL:<{ - s0 s3 XCHG - 416 PUSHINT // credits members min_stake max_stake min_total_stake l min_validators n sdict _100 - DICTREMMIN - NULLSWAPIFNOT - NULLSWAPIFNOT // credits members min_stake max_stake min_total_stake l min_validators n sdict cs key f - DUP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs key f f - IF:<{ // credits members min_stake max_stake min_total_stake l min_validators n sdict cs key f - SWAP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f key - 128 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _105 key - s1 s9 XCPU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f key _105 max_stake - MIN // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f key _108 - SWAP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _108 key - 32 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _108 _279 _278 - NIP // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f _108 key - 256 PLDU // credits members min_stake max_stake min_total_stake l min_validators n sdict cs f stake pubkey - s0 s3 XCHG // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake cs - 32 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake _117 cs - 256 LDU // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake _117 _283 _282 - NIP // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake _117 cs - 256 PLDU // credits members min_stake max_stake min_total_stake l min_validators n sdict pubkey f stake max_f adnl_addr - s1 s3 s0 XCHG3 - s1 s4 XCHG // credits members min_stake max_stake min_total_stake l min_validators n sdict f stake max_f pubkey adnl_addr - 4 TUPLE // credits members min_stake max_stake min_total_stake l min_validators n sdict f _125 - s0 s5 XCHG2 // credits members min_stake max_stake min_total_stake f min_validators n sdict _125 l - CONS // credits members min_stake max_stake min_total_stake f min_validators n sdict l - }>ELSE<{ - s6 s1 s6 XCHG3 - 2DROP // credits members min_stake max_stake min_total_stake f min_validators n sdict l - }> - s0 s4 XCHG // credits members min_stake max_stake min_total_stake l min_validators n sdict f - NOT // credits members min_stake max_stake min_total_stake l min_validators n sdict _127 - s1 s4 XCHG // credits members min_stake max_stake min_total_stake sdict min_validators n l _127 - }> // credits members min_stake max_stake min_total_stake sdict min_validators n l - s3 POP - s4 POP // credits members min_stake n min_total_stake l min_validators - DEC // credits members min_stake n min_total_stake l i - 2DUP // credits members min_stake n min_total_stake l i l1 i - REPEAT:<{ // credits members min_stake n min_total_stake l i l1 - CDR // credits members min_stake n min_total_stake l i l1 - }> - 0 PUSHINT // credits members min_stake n min_total_stake l i l1 _135=0 - DUP // credits members min_stake n min_total_stake l i l1 best_stake=0 m=0 - UNTIL:<{ - s0 s2 XCHG // credits members min_stake n min_total_stake l i m best_stake l1 - UNCONS // credits members min_stake n min_total_stake l i m best_stake _138 l1 - SWAP // credits members min_stake n min_total_stake l i m best_stake l1 _138 - 0 INDEX // credits members min_stake n min_total_stake l i m best_stake l1 stake - s0 s4 XCHG // credits members min_stake n min_total_stake l stake m best_stake l1 i - INC // credits members min_stake n min_total_stake l stake m best_stake l1 i - s4 s8 PUSH2 // credits members min_stake n min_total_stake l stake m best_stake l1 i stake min_stake - GEQ // credits members min_stake n min_total_stake l stake m best_stake l1 i _144 - IF:<{ // credits members min_stake n min_total_stake l stake m best_stake l1 i - s5 s(-1) s4 PU2XC // credits members min_stake n min_total_stake l i m best_stake l1 l i stake - compute_total_stake CALLDICT // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake - s0 s2 PUSH2 // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake tot_stake best_stake - GREATER // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake _147 - IF:<{ // credits members min_stake n min_total_stake l i m best_stake l1 tot_stake - s2 POP - s2 POP // credits members min_stake n min_total_stake l i l1 tot_stake - s2 PUSH // credits members min_stake n min_total_stake l i l1 best_stake m - }>ELSE<{ - s1 s3 XCHG - DROP // credits members min_stake n min_total_stake l i l1 best_stake m - }> - }>ELSE<{ - s3 s4 XCHG2 - DROP // credits members min_stake n min_total_stake l i l1 best_stake m - }> - s3 s6 PUSH2 // credits members min_stake n min_total_stake l i l1 best_stake m i n - GEQ // credits members min_stake n min_total_stake l i l1 best_stake m _148 - }> // credits members min_stake n min_total_stake l i l1 best_stake m - s2 POP - s2 POP - s4 POP - s4 POP // credits members best_stake m min_total_stake l - s2 PUSH // credits members best_stake m min_total_stake l m - 0 EQINT // credits members best_stake m min_total_stake l _150 - s4 s2 PUXC // credits members best_stake m _150 l best_stake min_total_stake - LESS // credits members best_stake m _150 l _151 - s1 s2 XCHG // credits members best_stake m l _150 _151 - OR // credits members best_stake m l _152 - IFJMP:<{ // credits members best_stake m l - 4 BLKDROP // credits - NEWDICT // credits _153 - NEWDICT // credits _153 _154 - 0 PUSHINT // credits _153 _154 _155=0 - DUP // credits _153 _154 _155=0 _156=0 - }> // credits members best_stake m l - DUP // credits members best_stake m l l1 - s0 DUMP // credits members best_stake m l l1 - s2 PUSH // credits members best_stake m l l1 m - DEC // credits members best_stake m l l1 _161 - REPEAT:<{ // credits members best_stake m l l1 - CDR // credits members best_stake m l l1 - }> - CAR // credits members best_stake m l _164 - 0 INDEX // credits members best_stake m l m_stake - 0 PUSHINT // credits members best_stake m l m_stake i=0 - DUP // credits members best_stake m l m_stake i=0 tot_stake=0 - NEWDICT // credits members best_stake m l m_stake i=0 tot_stake=0 vset - NEWDICT // credits members best_stake m l m_stake i=0 tot_stake=0 vset frozen - UNTIL:<{ - s0 s5 XCHG // credits members best_stake m frozen m_stake i tot_stake vset l - UNCONS // credits members best_stake m frozen m_stake i tot_stake vset _179 l - SWAP // credits members best_stake m frozen m_stake i tot_stake vset l _179 - 4 UNTUPLE // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr - s1 s12 PUSH2 - 8 PUSHPOW2 // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr pubkey members _184=256 - DICTUGET - NULLSWAPIFNOT // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr val f - 61 THROWIFNOT - LDGRAMS // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr _295 _294 - NIP // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr val - 64 LDU // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr _297 _296 - NIP // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr val - 256 PLDU // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr - s8 s11 PUSH2 // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr i m - LESS // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr _198 - IF:<{ // credits members best_stake m frozen m_stake i tot_stake vset l stake max_f pubkey adnl_addr src_addr - s3 s9 XCPU // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr max_f m_stake - MUL // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr _200 - 16 RSHIFT# // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr _202 - s4 s(-1) PUXC // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr stake _202 - MIN // credits members best_stake m frozen m_stake i tot_stake vset l stake src_addr pubkey adnl_addr true_stake - s4 s4 XCPU // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake true_stake - SUB // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake - s4 PUSH // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake true_stake - 60 LSHIFT# // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake _207 - s13 PUSH // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake _207 best_stake - DIV // credits members best_stake m frozen m_stake i tot_stake vset l true_stake src_addr pubkey adnl_addr stake weight - s8 s5 XCPU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake true_stake - ADD // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake - 2390828938 PUSHINT // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 - s3 PUSH // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 adnl_addr - IF:<{ // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 - 115 PUSHINT // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _212=115 - }>ELSE<{ // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 - 83 PUSHINT // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _212=83 - }> - NEWC // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _212 _215 - 8 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _211=2390828938 _217 - 32 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _219 - s4 s(-1) PUXC // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake pubkey _219 - 256 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake _221 - s9 s(-1) PUXC // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake weight _221 - 64 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake vinfo - s3 PUSH // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake vinfo adnl_addr - IF:<{ // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey adnl_addr stake tot_stake vinfo - s1 s3 XCHG // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey tot_stake stake adnl_addr vinfo - 256 STU // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey tot_stake stake vinfo - }>ELSE<{ - s0 s3 XCHG2 - DROP // credits members best_stake m frozen m_stake i weight vset l true_stake src_addr pubkey tot_stake stake vinfo - }> - SWAP - 16 PUSHINT - s10 s8 s8 PUXC2 // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vinfo i vset _228=16 - DICTUSETB // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset - FALSE // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset _231 - s4 PUSH - NEWC // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset _231 src_addr _232 - 256 STU // credits members best_stake m frozen m_stake i weight stake l true_stake src_addr pubkey tot_stake vset _231 _234 - s1 s9 XCHG // credits members best_stake m frozen m_stake i _231 stake l true_stake src_addr pubkey tot_stake vset weight _234 - 64 STU // credits members best_stake m frozen m_stake i _231 stake l true_stake src_addr pubkey tot_stake vset _236 - s0 s5 XCHG2 // credits members best_stake m frozen m_stake i _231 stake l vset src_addr pubkey tot_stake _236 true_stake - STGRAMS // credits members best_stake m frozen m_stake i _231 stake l vset src_addr pubkey tot_stake _237 - s1 s7 XCHG // credits members best_stake m frozen m_stake i tot_stake stake l vset src_addr pubkey _231 _237 - 1 STI // credits members best_stake m frozen m_stake i tot_stake stake l vset src_addr pubkey _239 - s0 s1 s9 XCHG3 - 8 PUSHPOW2 // credits members best_stake m src_addr m_stake i tot_stake stake l vset _239 pubkey frozen _240=256 - DICTUSETB // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen - }>ELSE<{ - s0 s10 XCHG - s4 s6 XCHG - s0 s3 XCHG - 3 BLKDROP // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen - }> - s3 PUSH // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen stake - IF:<{ // credits members best_stake m src_addr m_stake i tot_stake stake l vset frozen - s11 s7 s3 XCHG3 // l members best_stake m vset m_stake i tot_stake frozen credits src_addr stake - ~credit_to CALLDICT // l members best_stake m vset m_stake i tot_stake frozen credits - }>ELSE<{ - s11 s7 s3 XCHG3 - 2DROP // l members best_stake m vset m_stake i tot_stake frozen credits - }> - s0 s3 XCHG // l members best_stake m vset m_stake credits tot_stake frozen i - INC // l members best_stake m vset m_stake credits tot_stake frozen i - s9 PUSH // l members best_stake m vset m_stake credits tot_stake frozen i l - ISNULL // l members best_stake m vset m_stake credits tot_stake frozen i _246 - s4 s10 XCHG - s4 s6 XCHG - s4 s4 s0 XCHG3 // credits members best_stake m l m_stake i tot_stake vset frozen _246 - }> // credits members best_stake m l m_stake i tot_stake vset frozen - s3 POP - s3 POP - s3 POP - s5 POP // credits frozen best_stake m tot_stake vset - s1 s3 PUXC // credits frozen vset m tot_stake tot_stake best_stake - EQUAL // credits frozen vset m tot_stake _248 - 49 THROWIFNOT - s2 s3 XCHG - SWAP // credits vset frozen tot_stake m - }> - conduct_elections PROC:<{ - // ds elect credits - SWAP // ds credits elect - unpack_elect CALLDICT // ds credits _130 _131 _132 _133 _134 _135 _136 - s4 POP // ds credits elect_at elect_close finished total_stake members failed - NOW // ds credits elect_at elect_close finished total_stake members failed _11 - s5 PUSH // ds credits elect_at elect_close finished total_stake members failed _11 elect_close - LESS // ds credits elect_at elect_close finished total_stake members failed _12 - IFJMP:<{ // ds credits elect_at elect_close finished total_stake members failed - 8 BLKDROP // - FALSE // _13 - }> // ds credits elect_at elect_close finished total_stake members failed - 0 PUSHINT // ds credits elect_at elect_close finished total_stake members failed _14=0 - CONFIGOPTPARAM // ds credits elect_at elect_close finished total_stake members failed _15 - ISNULL // ds credits elect_at elect_close finished total_stake members failed _16 - IFJMP:<{ // ds credits elect_at elect_close finished total_stake members failed - 8 BLKDROP // - postpone_elections CALLDICT // _17 - }> // ds credits elect_at elect_close finished total_stake members failed - 17 PUSHINT // ds credits elect_at elect_close finished total_stake members failed _19=17 - CONFIGOPTPARAM // ds credits elect_at elect_close finished total_stake members failed _20 - CTOS // ds credits elect_at elect_close finished total_stake members failed cs - LDGRAMS // ds credits elect_at elect_close finished total_stake members failed min_stake cs - LDGRAMS // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake cs - LDGRAMS // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake cs - 32 LDU // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor cs - ENDS - s6 s1 PUSH2 // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor total_stake min_total_stake - LESS // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor _35 - IFJMP:<{ // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor - 12 BLKDROP // - postpone_elections CALLDICT // _36 - }> // ds credits elect_at elect_close finished total_stake members failed min_stake max_stake min_total_stake max_stake_factor - s0 s4 XCHG // ds credits elect_at elect_close finished total_stake members max_stake_factor min_stake max_stake min_total_stake failed - IFJMP:<{ // ds credits elect_at elect_close finished total_stake members max_stake_factor min_stake max_stake min_total_stake - 11 BLKDROP // - postpone_elections CALLDICT // _37 - }> // ds credits elect_at elect_close finished total_stake members max_stake_factor min_stake max_stake min_total_stake - s0 s6 XCHG // ds credits elect_at elect_close min_total_stake total_stake members max_stake_factor min_stake max_stake finished - IFJMP:<{ // ds credits elect_at elect_close min_total_stake total_stake members max_stake_factor min_stake max_stake - 10 BLKDROP // - FALSE // _38 - }> // ds credits elect_at elect_close min_total_stake total_stake members max_stake_factor min_stake max_stake - s3 s8 XCHG - s8 PUSH - s3 s2 XCPU - s7 s7 XCHG2 // ds members elect_at elect_close min_stake total_stake credits members min_stake max_stake min_total_stake max_stake_factor - try_elect CALLDICT // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt - DUP // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt cnt - 0 EQINT // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt failed - DUP // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt failed failed - NOT // ds members elect_at elect_close min_stake total_stake credits vdict frozen total_stakes cnt failed finished - s10 PUSH - s0 s6 XCHG - s5 s10 XCHG - s4 s9 XCHG - s3 s8 XCHG - s12 s12 s12 XCHG3 // ds vdict elect_at frozen total_stakes cnt credits elect_at elect_close min_stake total_stake members failed finished - pack_elect CALLDICT // ds vdict elect_at frozen total_stakes cnt credits elect - s2 PUSH // ds vdict elect_at frozen total_stakes cnt credits elect cnt - IFNOTJMP:<{ // ds vdict elect_at frozen total_stakes cnt credits elect - s2 POP - s2 POP - s2 POP - s2 POP - s2 POP // ds elect credits - NEWC // ds elect credits _48 - s1 s2 XCHG // ds credits elect _48 - STDICT // ds credits _49 - STDICT // ds _50 - SWAP // _50 ds - STSLICER // _51 - ENDC // _52 - c4 POP - postpone_elections CALLDICT // _54 - }> // ds vdict elect_at frozen total_stakes cnt credits elect - get_validator_conf CALLDICT // ds vdict elect_at frozen total_stakes cnt credits elect _150 _151 _152 _153 - s2 POP // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held elect_end_before - NOW // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held elect_end_before _61 - SWAP // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _61 elect_end_before - ADD // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _62 - -60 ADDCONST // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _64 - s8 PUSH // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held _64 elect_at - MAX // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start - 16 PUSHINT // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _67=16 - CONFIGOPTPARAM // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _68 - CTOS // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _69 - 16 PUSHINT // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _69 _70=16 - SDSKIPFIRST // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _71 - 16 PLDU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start main_validators - s6 s(-1) PUXC // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start cnt main_validators - MIN // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 - s1 s3 PUSH2 // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 start elect_for - ADD // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 - 17 PUSHINT // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _77=17 - NEWC // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _77=17 _78 - 8 STU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _80 - s3 s(-1) PUXC // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 start _80 - 32 STU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _76 _82 - 32 STU // ds vdict elect_at frozen total_stakes cnt credits elect elect_for stake_held start _75 _84 - s1 s7 XCHG // ds vdict elect_at frozen total_stakes _75 credits elect elect_for stake_held start cnt _84 - 16 STU // ds vdict elect_at frozen total_stakes _75 credits elect elect_for stake_held start _86 - s1 s6 XCHG // ds vdict elect_at frozen total_stakes start credits elect elect_for stake_held _75 _86 - 16 STU // ds vdict elect_at frozen total_stakes start credits elect elect_for stake_held _88 - s0 s9 XCHG // ds _88 elect_at frozen total_stakes start credits elect elect_for stake_held vdict - CTOS // ds _88 elect_at frozen total_stakes start credits elect elect_for stake_held _89 - s1 s9 XCHG // ds stake_held elect_at frozen total_stakes start credits elect elect_for _88 _89 - STSLICER // ds stake_held elect_at frozen total_stakes start credits elect elect_for _90 - ENDC // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset - 0 PUSHINT // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset _93=0 - CONFIGOPTPARAM // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset _94 - CTOS // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset _95 - 256 PLDU // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset config_addr - s1 s8 PUSH2 // ds stake_held elect_at frozen total_stakes start credits elect elect_for vset config_addr vset elect_at - send_validator_set_to_config CALLDICT - s0 s9 XCHG // vset stake_held elect_at frozen total_stakes start credits elect elect_for ds - LDDICT // vset stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds - FALSE // vset stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds _103 - s0 s11 XCHG // _103 stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds vset - HASHCU // _103 stake_held elect_at frozen total_stakes start credits elect elect_for past_elect ds _104 - s6 s3 XCHG2 // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect start elect_for - ADD // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _105 - s9 PUSH // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _105 stake_held - ADD // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _106 - NEWC // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _106 _107 - 32 STU // _103 stake_held elect_at frozen total_stakes ds credits elect _104 past_elect _109 - s1 s9 XCHG // _103 past_elect elect_at frozen total_stakes ds credits elect _104 stake_held _109 - 32 STU // _103 past_elect elect_at frozen total_stakes ds credits elect _104 _111 - 256 STU // _103 past_elect elect_at frozen total_stakes ds credits elect _113 - s1 s5 XCHG // _103 past_elect elect_at elect total_stakes ds credits frozen _113 - STDICT // _103 past_elect elect_at elect total_stakes ds credits _114 - s0 s3 XCHG2 // _103 past_elect elect_at elect credits ds _114 total_stakes - STGRAMS // _103 past_elect elect_at elect credits ds _115 - 0 PUSHINT // _103 past_elect elect_at elect credits ds _115 _116=0 - STGRAMS // _103 past_elect elect_at elect credits ds _117 - s1 s6 XCHG // ds past_elect elect_at elect credits _103 _117 - 1 STI // ds past_elect elect_at elect credits _119 - s0 s3 s4 XCHG3 - 32 PUSHINT // ds elect credits _119 elect_at past_elect _120=32 - DICTUSETB // ds elect credits past_elect - NEWC // ds elect credits past_elect _122 - s1 s3 XCHG // ds past_elect credits elect _122 - STDICT // ds past_elect credits _123 - STDICT // ds past_elect _124 - STDICT // ds _125 - SWAP // _125 ds - STSLICER // _126 - ENDC // _127 - c4 POP - TRUE // _129 - }> - update_active_vset_id PROC:<{ - // - load_data CALLDICT // elect credits past_elect grams active_id active_hash - 34 PUSHINT // elect credits past_elect grams active_id active_hash _8=34 - CONFIGOPTPARAM // elect credits past_elect grams active_id active_hash _9 - HASHCU // elect credits past_elect grams active_id active_hash cur_hash - s0 s1 PUSH2 // elect credits past_elect grams active_id active_hash cur_hash cur_hash active_hash - EQUAL // elect credits past_elect grams active_id active_hash cur_hash _11 - IFJMP:<{ // elect credits past_elect grams active_id active_hash cur_hash - 7 BLKDROP // - FALSE // _12 - }> // elect credits past_elect grams active_id active_hash cur_hash - s2 PUSH // elect credits past_elect grams active_id active_hash cur_hash active_id - IF:<{ // elect credits past_elect grams active_id active_hash cur_hash - s2 s4 PUSH2 - 32 PUSHINT // elect credits past_elect grams active_id active_hash cur_hash active_id past_elect _15=32 - DICTUGET - NULLSWAPIFNOT // elect credits past_elect grams active_id active_hash cur_hash fs f - IF:<{ // elect credits past_elect grams active_id active_hash cur_hash fs - 32 LDU // elect credits past_elect grams active_id active_hash cur_hash _99 _98 - NIP // elect credits past_elect grams active_id active_hash cur_hash fs - DUP // elect credits past_elect grams active_id active_hash cur_hash fs0 fs - 32 LDU // elect credits past_elect grams active_id active_hash cur_hash fs0 _24 fs - 256 LDU // elect credits past_elect grams active_id active_hash cur_hash fs0 _24 _103 _102 - DROP // elect credits past_elect grams active_id active_hash cur_hash fs0 stake_held hash - s0 s4 XCHG2 // elect credits past_elect grams active_id stake_held cur_hash fs0 hash active_hash - EQUAL // elect credits past_elect grams active_id stake_held cur_hash fs0 _31 - 57 THROWIFNOT - NOW // elect credits past_elect grams active_id stake_held cur_hash fs0 _33 - s0 s3 XCHG2 // elect credits past_elect grams active_id fs0 cur_hash _33 stake_held - ADD // elect credits past_elect grams active_id fs0 cur_hash unfreeze_time - NEWC // elect credits past_elect grams active_id fs0 cur_hash unfreeze_time _36 - 32 STU // elect credits past_elect grams active_id fs0 cur_hash _38 - ROT // elect credits past_elect grams active_id cur_hash _38 fs0 - STSLICER // elect credits past_elect grams active_id cur_hash _39 - s0 s0 s4 XCHG3 - 32 PUSHINT // elect credits cur_hash grams _39 active_id past_elect _40=32 - DICTUSETB // elect credits cur_hash grams past_elect - }>ELSE<{ - s1 s5 XCHG - s1 s3 XCHG - 3 BLKDROP // elect credits cur_hash grams past_elect - }> - }>ELSE<{ - s4 s1 s4 XCHG3 - 2DROP // elect credits cur_hash grams past_elect - }> - -1 PUSHINT // elect credits cur_hash grams past_elect id=-1 - UNTIL:<{ - OVER - 32 PUSHINT // elect credits cur_hash grams past_elect id past_elect _46=32 - DICTUGETNEXT - NULLSWAPIFNOT - NULLSWAPIFNOT // elect credits cur_hash grams past_elect fs id f - DUP // elect credits cur_hash grams past_elect fs id f f - IF:<{ // elect credits cur_hash grams past_elect fs id f - s0 s2 XCHG // elect credits cur_hash grams past_elect f id fs - 64 LDU // elect credits cur_hash grams past_elect f id _50 fs - 256 LDU // elect credits cur_hash grams past_elect f id tm hash fs - s1 s7 PUSH2 // elect credits cur_hash grams past_elect f id tm hash fs hash cur_hash - EQUAL // elect credits cur_hash grams past_elect f id tm hash fs _56 - IF:<{ // elect credits cur_hash grams past_elect f id tm hash fs - s4 POP // elect credits cur_hash grams past_elect fs id tm hash - s0 s3 XCHG // elect credits cur_hash grams past_elect hash id tm fs - LDDICT // elect credits cur_hash grams past_elect hash id tm _60 fs - LDGRAMS // elect credits cur_hash grams past_elect hash id tm _60 _62 fs - LDGRAMS // elect credits cur_hash grams past_elect hash id tm dict total_stake bonuses fs - s8 PUSH // elect credits cur_hash grams past_elect hash id tm dict total_stake bonuses fs grams - 3 RSHIFT# // elect credits cur_hash grams past_elect hash id tm dict total_stake bonuses fs amount - s9 s9 XCPU // elect credits cur_hash amount past_elect hash id tm dict total_stake bonuses fs grams amount - SUB // elect credits cur_hash amount past_elect hash id tm dict total_stake bonuses fs grams - s2 s9 XCHG2 // elect credits cur_hash grams past_elect hash id tm dict total_stake fs bonuses amount - ADD // elect credits cur_hash grams past_elect hash id tm dict total_stake fs bonuses - NEWC // elect credits cur_hash grams past_elect hash id tm dict total_stake fs bonuses _72 - s1 s5 XCHG // elect credits cur_hash grams past_elect hash id bonuses dict total_stake fs tm _72 - 64 STU // elect credits cur_hash grams past_elect hash id bonuses dict total_stake fs _74 - s1 s6 XCHG // elect credits cur_hash grams past_elect fs id bonuses dict total_stake hash _74 - 256 STU // elect credits cur_hash grams past_elect fs id bonuses dict total_stake _76 - s1 s2 XCHG // elect credits cur_hash grams past_elect fs id bonuses total_stake dict _76 - STDICT // elect credits cur_hash grams past_elect fs id bonuses total_stake _77 - SWAP // elect credits cur_hash grams past_elect fs id bonuses _77 total_stake - STGRAMS // elect credits cur_hash grams past_elect fs id bonuses _78 - SWAP // elect credits cur_hash grams past_elect fs id _78 bonuses - STGRAMS // elect credits cur_hash grams past_elect fs id _79 - ROT // elect credits cur_hash grams past_elect id _79 fs - STSLICER // elect credits cur_hash grams past_elect id _80 - SWAP - 32 PUSHINT - s1 s3 s3 PUXC2 // elect credits cur_hash grams id _80 id past_elect _81=32 - DICTUSETB // elect credits cur_hash grams id past_elect - FALSE // elect credits cur_hash grams id past_elect f - }>ELSE<{ - s3 s5 XCHG - s3 s4 XCHG - 3 BLKDROP // elect credits cur_hash grams id past_elect f - }> - }>ELSE<{ - s3 s0 s3 XCHG3 - DROP // elect credits cur_hash grams id past_elect f - }> - NOT // elect credits cur_hash grams id past_elect _84 - s1 s2 XCHG // elect credits cur_hash grams past_elect id _84 - }> // elect credits cur_hash grams past_elect id - DUP // elect credits cur_hash grams past_elect id id - ISNULL // elect credits cur_hash grams past_elect id _85 - IF:<{ // elect credits cur_hash grams past_elect id - DROP // elect credits cur_hash grams past_elect - 0 PUSHINT // elect credits cur_hash grams past_elect _86=0 - }>ELSE<{ // elect credits cur_hash grams past_elect _86 - }> // elect credits active_hash grams past_elect active_id - s0 s3 XCHG2 // elect credits past_elect grams active_id active_hash - store_data CALLDICT - TRUE // _89 - }> - cell_hash_eq? PROC:<{ - // vset expected_vset_hash - OVER // vset expected_vset_hash vset - ISNULL // vset expected_vset_hash _2 - IF:<{ // vset expected_vset_hash - 2DROP // - FALSE // _3 - }>ELSE<{ // vset expected_vset_hash - SWAP // expected_vset_hash vset - HASHCU // expected_vset_hash _5 - SWAP // _5 expected_vset_hash - EQUAL // _3 - }> - }> - validator_set_installed PROC:<{ - // ds elect credits - SWAP // ds credits elect - unpack_elect CALLDICT // ds credits _44 _45 _46 _47 _48 _49 _50 - s0 s5 XCHG - 5 BLKDROP // ds credits elect_at finished - IFNOTJMP:<{ // ds credits elect_at - 3 BLKDROP // - FALSE // _11 - }> // ds credits elect_at - s0 s2 XCHG // elect_at credits ds - LDDICT // elect_at credits past_elections ds - s3 s1 XCPU - 32 PUSHINT // ds credits past_elections elect_at past_elections _17=32 - DICTUGET - NULLSWAPIFNOT // ds credits past_elections fs f - IFNOTJMP:<{ // ds credits past_elections fs - 4 BLKDROP // - FALSE // _19 - }> // ds credits past_elections fs - 64 PUSHINT // ds credits past_elections fs _21=64 - SDSKIPFIRST // ds credits past_elections _22 - 256 PLDU // ds credits past_elections vset_hash - 34 PUSHINT // ds credits past_elections vset_hash _25=34 - CONFIGOPTPARAM // ds credits past_elections vset_hash _26 - OVER // ds credits past_elections vset_hash _26 vset_hash - cell_hash_eq? CALLDICT // ds credits past_elections vset_hash _27 - 36 PUSHINT // ds credits past_elections vset_hash _27 _28=36 - CONFIGOPTPARAM // ds credits past_elections vset_hash _27 _29 - ROT // ds credits past_elections _27 _29 vset_hash - cell_hash_eq? CALLDICT // ds credits past_elections _27 _30 - OR // ds credits past_elections _31 - IFJMP:<{ // ds credits past_elections - FALSE // ds credits past_elections _32 - NEWC // ds credits past_elections _32 _33 - 1 STI // ds credits past_elections _35 - s1 s2 XCHG // ds past_elections credits _35 - STDICT // ds past_elections _36 - STDICT // ds _37 - SWAP // _37 ds - STSLICER // _38 - ENDC // _39 - c4 POP - update_active_vset_id CALLDICT // _41 - DROP // - TRUE // _42 - }> // ds credits past_elections - 3 BLKDROP // - FALSE // _43 - }> - check_unfreeze PROC:<{ - // - load_data CALLDICT // elect credits past_elect grams active_id active_hash - -1 PUSHINT // elect credits past_elect grams active_id active_hash id=-1 - UNTIL:<{ - s4 PUSH - 32 PUSHINT // elect credits past_elect grams active_id active_hash id past_elect _11=32 - DICTUGETNEXT - NULLSWAPIFNOT - NULLSWAPIFNOT // elect credits past_elect grams active_id active_hash fs id f - DUP // elect credits past_elect grams active_id active_hash fs id f f - IF:<{ // elect credits past_elect grams active_id active_hash fs id f - s0 s2 XCHG // elect credits past_elect grams active_id active_hash f id fs - 32 LDU // elect credits past_elect grams active_id active_hash f id _39 _38 - DROP // elect credits past_elect grams active_id active_hash f id unfreeze_at - NOW // elect credits past_elect grams active_id active_hash f id unfreeze_at _17 - LEQ // elect credits past_elect grams active_id active_hash f id _18 - s1 s4 PUSH2 // elect credits past_elect grams active_id active_hash f id _18 id active_id - NEQ // elect credits past_elect grams active_id active_hash f id _18 _19 - AND // elect credits past_elect grams active_id active_hash f id _20 - IF:<{ // elect credits past_elect grams active_id active_hash f id - NIP // elect credits past_elect grams active_id active_hash id - s5 s4 s4 XC2PU // elect active_hash id grams active_id credits past_elect id - unfreeze_all CALLDICT // elect active_hash id grams active_id credits past_elect unused_prizes - s1 s4 XCHG // elect active_hash id past_elect active_id credits grams unused_prizes - ADD // elect active_hash id past_elect active_id credits grams - s6 s1 s3 PUSH3 - s3 s5 s8 PUSH3 // elect active_hash id past_elect active_id credits grams elect credits past_elect grams active_id active_hash - store_data CALLDICT - FALSE // elect active_hash id past_elect active_id credits grams f - }>ELSE<{ - s6 s1 s5 XCHG3 - s4 s4 XCHG2 // elect active_hash id past_elect active_id credits grams f - }> - }>ELSE<{ - s3 s7 XCHG - s1 s6 XCHG - s5 s0 s5 XCHG3 - DROP // elect active_hash id past_elect active_id credits grams f - }> - NOT // elect active_hash id past_elect active_id credits grams _26 - s2 s6 XCHG - s4 s5 XCHG - s1 s4 XCHG // elect credits past_elect grams active_id active_hash id _26 - }> // elect credits past_elect grams active_id active_hash id - s0 s6 XCHG - 6 BLKDROP // id - ISNULL // _27 - NOT // _28 - }> - announce_new_elections PROC:<{ - // ds elect credits - NIP // ds credits - 36 PUSHINT // ds credits _4=36 - CONFIGOPTPARAM // ds credits next_vset - ISNULL // ds credits _6 - IFNOTJMP:<{ // ds credits - 2DROP // - FALSE // _7 - }> // ds credits - 1 PUSHINT // ds credits _9=1 - CONFIGOPTPARAM // ds credits _10 - CTOS // ds credits _11 - 256 PLDU // ds credits elector_addr - MYADDR // ds credits elector_addr _16 - REWRITESTDADDR // ds credits elector_addr my_wc my_addr - SWAP // ds credits elector_addr my_addr my_wc - INC // ds credits elector_addr my_addr _19 - s0 s2 XCHG // ds credits _19 my_addr elector_addr - NEQ // ds credits _19 _20 - OR // ds credits _21 - IFJMP:<{ // ds credits - 2DROP // - FALSE // _22 - }> // ds credits - 34 PUSHINT // ds credits _24=34 - CONFIGOPTPARAM // ds credits cur_vset - DUP // ds credits cur_vset cur_vset - ISNULL // ds credits cur_vset _26 - IFJMP:<{ // ds credits cur_vset - 3 BLKDROP // - FALSE // _27 - }> // ds credits cur_vset - get_validator_conf CALLDICT // ds credits cur_vset _76 _77 _78 _79 - DROP - s2 POP // ds credits cur_vset elect_end_before elect_begin_before - s0 s2 XCHG // ds credits elect_begin_before elect_end_before cur_vset - CTOS // ds credits elect_begin_before elect_end_before _34 - 40 PUSHINT // ds credits elect_begin_before elect_end_before _34 _37 - SDSKIPFIRST // ds credits elect_begin_before elect_end_before _38 - 32 PLDU // ds credits elect_begin_before elect_end_before cur_valid_until - NOW // ds credits elect_begin_before elect_end_before cur_valid_until t - s1 s3 XCPU // ds credits elect_begin_before elect_end_before t cur_valid_until elect_begin_before - SUB // ds credits elect_begin_before elect_end_before t t0 - 2DUP // ds credits elect_begin_before elect_end_before t t0 t t0 - LESS // ds credits elect_begin_before elect_end_before t t0 _45 - IFJMP:<{ // ds credits elect_begin_before elect_end_before t t0 - 6 BLKDROP // - FALSE // _46 - }> // ds credits elect_begin_before elect_end_before t t0 - 2DUP // ds credits elect_begin_before elect_end_before t t0 t t0 - SUB // ds credits elect_begin_before elect_end_before t t0 _47 - 60 LESSINT // ds credits elect_begin_before elect_end_before t t0 _49 - IF:<{ // ds credits elect_begin_before elect_end_before t t0 - NIP // ds credits elect_begin_before elect_end_before t - }>ELSE<{ - DROP // ds credits elect_begin_before elect_end_before t - }> - 17 PUSHINT // ds credits elect_begin_before elect_end_before t _52=17 - CONFIGOPTPARAM // ds credits elect_begin_before elect_end_before t _53 - CTOS // ds credits elect_begin_before elect_end_before t _54 - LDGRAMS // ds credits elect_begin_before elect_end_before t _81 _80 - DROP // ds credits elect_begin_before elect_end_before t min_stake - s0 s3 XCHG // ds credits min_stake elect_end_before t elect_begin_before - ADD // ds credits min_stake elect_end_before elect_at - s0 DUMP // ds credits min_stake elect_end_before elect_at - s0 s1 PUXC // ds credits min_stake elect_at elect_at elect_end_before - SUB // ds credits min_stake elect_at elect_close - 0 PUSHINT // ds credits min_stake elect_at elect_close _62=0 - NEWDICT // ds credits min_stake elect_at elect_close _62=0 _63 - FALSE // ds credits min_stake elect_at elect_close _62=0 _63 _64 - s4 s5 XCHG - s3 s4 XCHG - FALSE // ds credits elect_at elect_close min_stake _62=0 _63 _64 _65 - pack_elect CALLDICT // ds credits elect - NEWC // ds credits elect _67 - STDICT // ds credits _68 - STDICT // ds _69 - SWAP // _69 ds - STSLICER // _70 - ENDC // _71 - c4 POP - TRUE // _73 - }> - run_ticktock PROC:<{ - // is_tock - DROP // - c4 PUSH // _2 - CTOS // ds - LDDICT // _6 ds - LDDICT // elect credits ds - s2 PUSH // elect credits ds elect - ISNULL // elect credits ds _10 - IF:<{ // elect credits ds - -ROT // ds elect credits - announce_new_elections CALLDICT // _18 - 0 THROWIF - }>ELSE<{ // elect credits ds - s0 s2 s1 PUSH3 // elect credits ds ds elect credits - conduct_elections CALLDICT // elect credits ds _12 - 0 THROWIF - -ROT // ds elect credits - validator_set_installed CALLDICT // _15 - 0 THROWIF - }> - update_active_vset_id CALLDICT // _21 - 0 THROWIF - check_unfreeze CALLDICT // _23 - DROP // - }> - active_election_id PROC:<{ - // - c4 PUSH // _1 - CTOS // _2 - PLDDICT // elect - DUP // elect elect - ISNULL // elect _4 - IF:<{ // elect - DROP // - 0 PUSHINT // _5=0 - }>ELSE<{ // elect - CTOS // _7 - 32 PLDU // _5 - }> - }> - participates_in PROC:<{ - // validator_pubkey - c4 PUSH // validator_pubkey _2 - CTOS // validator_pubkey _3 - PLDDICT // validator_pubkey elect - DUP // validator_pubkey elect elect - ISNULL // validator_pubkey elect _5 - IFJMP:<{ // validator_pubkey elect - 2DROP // - 0 PUSHINT // _6=0 - }> // validator_pubkey elect - unpack_elect CALLDICT // validator_pubkey _23 _24 _25 _26 _27 _28 _29 - s2 s6 XCHG - 6 BLKDROP // validator_pubkey members - 8 PUSHPOW2 // validator_pubkey members _17=256 - DICTUGET - NULLSWAPIFNOT // mem found - IF:<{ // mem - LDGRAMS // _33 _32 - DROP // _19 - }>ELSE<{ // mem - DROP // - 0 PUSHINT // _19=0 - }> - }> - participant_list PROC:<{ - // - c4 PUSH // _1 - CTOS // _2 - PLDDICT // elect - DUP // elect elect - ISNULL // elect _4 - IFJMP:<{ // elect - DROP // - PUSHNULL // _5 - }> // elect - unpack_elect CALLDICT // _35 _36 _37 _38 _39 _40 _41 - s2 s6 XCHG - 6 BLKDROP // members - PUSHNULL // members l - 256 PUSHPOW2DEC // members l id - UNTIL:<{ - s2 PUSH - 8 PUSHPOW2 // members l id members _28=256 - DICTUGETPREV - NULLSWAPIFNOT - NULLSWAPIFNOT // members l fs id f - DUP // members l fs id f f - IF:<{ // members l fs id f - s0 s2 XCHG // members l f id fs - LDGRAMS // members l f id _46 _45 - DROP // members l f id _30 - s1 s(-1) PUXC // members l f id id _30 - PAIR // members l f id _32 - s0 s3 XCHG2 // members id f _32 l - CONS // members id f l - }>ELSE<{ - 2SWAP - DROP // members id f l - }> - SWAP // members id l f - NOT // members id l _34 - s1 s2 XCHG // members l id _34 - }> // members l id - DROP - NIP // l - }> - compute_returned_stake PROC:<{ - // wallet_addr - c4 PUSH // wallet_addr _2 - CTOS // wallet_addr cs - LDDICT // wallet_addr _19 _18 - NIP // wallet_addr cs - LDDICT // wallet_addr _21 _20 - DROP // wallet_addr credits - 8 PUSHPOW2 // wallet_addr credits _12=256 - DICTUGET - NULLSWAPIFNOT // val f - IF:<{ // val - LDGRAMS // _25 _24 - DROP // _14 - }>ELSE<{ // val - DROP // - 0 PUSHINT // _14=0 - }> - }> -}END>c diff --git a/crypto/smartcont/auto/highload-wallet-code.cpp b/crypto/smartcont/auto/highload-wallet-code.cpp deleted file mode 100644 index ecc0b71d8e..0000000000 --- a/crypto/smartcont/auto/highload-wallet-code.cpp +++ /dev/null @@ -1 +0,0 @@ -with_tvm_code("highload-wallet", "te6ccgEBBgEAhgABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhghgBD0eG+hb6EgmALTB9QwAfsAkTLiAbPmWwGkyMsfyx/L/8ntVAAE0DAAEaCZL9qJoa4WPw=="); diff --git a/crypto/smartcont/auto/highload-wallet-code.fif b/crypto/smartcont/auto/highload-wallet-code.fif deleted file mode 100644 index d2122a96fb..0000000000 --- a/crypto/smartcont/auto/highload-wallet-code.fif +++ /dev/null @@ -1,79 +0,0 @@ -// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-code.fc` -PROGRAM{ - DECLPROC recv_internal - DECLPROC recv_external - 85143 DECLMETHOD seqno - recv_internal PROC:<{ - // in_msg - DROP // - }> - recv_external PROC:<{ - // in_msg - 9 PUSHPOW2 // in_msg _3=512 - LDSLICEX // signature in_msg - DUP // signature in_msg cs - 32 LDU // signature in_msg _9 cs - 32 LDU // signature in_msg _9 _12 cs - 32 LDU // signature in_msg subwallet_id valid_until msg_seqno cs - NOW // signature in_msg subwallet_id valid_until msg_seqno cs _19 - s1 s3 XCHG // signature in_msg subwallet_id cs msg_seqno valid_until _19 - LEQ // signature in_msg subwallet_id cs msg_seqno _20 - 35 THROWIF - c4 PUSH // signature in_msg subwallet_id cs msg_seqno _23 - CTOS // signature in_msg subwallet_id cs msg_seqno ds - 32 LDU // signature in_msg subwallet_id cs msg_seqno _28 ds - 32 LDU // signature in_msg subwallet_id cs msg_seqno _28 _31 ds - 256 LDU // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key ds - ENDS - s3 s2 XCPU // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet msg_seqno stored_seqno - EQUAL // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet _39 - 33 THROWIFNOT - s4 s4 XCPU // signature in_msg stored_subwallet cs public_key stored_seqno subwallet_id stored_subwallet - EQUAL // signature in_msg stored_subwallet cs public_key stored_seqno _42 - 34 THROWIFNOT - s0 s4 XCHG // signature stored_seqno stored_subwallet cs public_key in_msg - HASHSU // signature stored_seqno stored_subwallet cs public_key _45 - s0 s5 s5 XC2PU // public_key stored_seqno stored_subwallet cs _45 signature public_key - CHKSIGNU // public_key stored_seqno stored_subwallet cs _46 - 35 THROWIFNOT - LDDICT // public_key stored_seqno stored_subwallet dict cs - ENDS - ACCEPT - -1 PUSHINT // public_key stored_seqno stored_subwallet dict i=-1 - UNTIL:<{ - OVER - 16 PUSHINT // public_key stored_seqno stored_subwallet dict i dict _57=16 - DICTIGETNEXT - NULLSWAPIFNOT - NULLSWAPIFNOT // public_key stored_seqno stored_subwallet dict cs i f - DUP // public_key stored_seqno stored_subwallet dict cs i f f - IF:<{ // public_key stored_seqno stored_subwallet dict cs i f - s0 s2 XCHG // public_key stored_seqno stored_subwallet dict f i cs - 8 LDU // public_key stored_seqno stored_subwallet dict f i mode cs - LDREF // public_key stored_seqno stored_subwallet dict f i mode _100 _99 - DROP // public_key stored_seqno stored_subwallet dict f i mode _63 - SWAP // public_key stored_seqno stored_subwallet dict f i _63 mode - SENDRAWMSG - }>ELSE<{ - s2 POP // public_key stored_seqno stored_subwallet dict f i - }> - SWAP // public_key stored_seqno stored_subwallet dict i f - NOT // public_key stored_seqno stored_subwallet dict i _66 - }> // public_key stored_seqno stored_subwallet dict i - 2DROP // public_key stored_seqno stored_subwallet - SWAP // public_key stored_subwallet stored_seqno - INC // public_key stored_subwallet _68 - NEWC // public_key stored_subwallet _68 _69 - 32 STU // public_key stored_subwallet _71 - 32 STU // public_key _73 - 256 STU // _75 - ENDC // _76 - c4 POP - }> - seqno PROC:<{ - // - c4 PUSH // _0 - CTOS // _1 - 32 PLDU // _3 - }> -}END>c diff --git a/crypto/smartcont/auto/highload-wallet-v2-code.cpp b/crypto/smartcont/auto/highload-wallet-v2-code.cpp deleted file mode 100644 index ca5d2c70f7..0000000000 --- a/crypto/smartcont/auto/highload-wallet-v2-code.cpp +++ /dev/null @@ -1 +0,0 @@ -with_tvm_code("highoad-wallet-v2", "te6ccgEBBwEA1wABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQHu8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44YIYAQ9HhvoW+hIJgC0wfUMAH7AJEy4gGz5luDJaHIQDSAQPRDiuYxyBLLHxPLP8v/9ADJ7VQGAATQMABBoZfl2omhpj5jpn+n/mPoCaKkQQCB6BzfQmMktv8ld0fFADoggED0lm+hb6EyURCUMFMDud4gkjM2k0MTW+IBsw=="); diff --git a/crypto/smartcont/auto/highload-wallet-v2-code.fif b/crypto/smartcont/auto/highload-wallet-v2-code.fif deleted file mode 100644 index 9d5e467de5..0000000000 --- a/crypto/smartcont/auto/highload-wallet-v2-code.fif +++ /dev/null @@ -1,134 +0,0 @@ -// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-v2-code.fc` -PROGRAM{ - DECLPROC recv_internal - DECLPROC recv_external - 117746 DECLMETHOD processed? - recv_internal PROC:<{ - // in_msg - DROP // - }> - recv_external PROC:<{ - // in_msg - 9 PUSHPOW2 // in_msg _3=512 - LDSLICEX // signature in_msg - DUP // signature in_msg cs - 32 LDU // signature in_msg _8 cs - 64 LDU // signature in_msg subwallet_id query_id cs - NOW // signature in_msg subwallet_id query_id cs _15 - 32 LSHIFT# // signature in_msg subwallet_id query_id cs bound - s2 s0 PUSH2 // signature in_msg subwallet_id query_id cs bound query_id bound - LESS // signature in_msg subwallet_id query_id cs bound _19 - 35 THROWIF - c4 PUSH // signature in_msg subwallet_id query_id cs bound _22 - CTOS // signature in_msg subwallet_id query_id cs bound ds - 32 LDU // signature in_msg subwallet_id query_id cs bound _28 ds - 64 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 ds - 256 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 _34 ds - LDDICT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries ds - ENDS - s6 s0 PUSH2 - 64 PUSHINT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries query_id old_queries _42=64 - DICTUGET - NULLSWAPIFNOT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries _115 _116 - NIP // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries found? - 32 THROWIF - s7 s3 XCPU // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key subwallet_id stored_subwallet - EQUAL // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key _47 - 34 THROWIFNOT - s0 s7 XCHG // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned in_msg - HASHSU // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned _50 - s0 s8 s7 XC2PU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _50 signature public_key - CHKSIGNU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _51 - 35 THROWIFNOT - s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound cs - LDDICT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs - ENDS - ACCEPT - -1 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i=-1 - UNTIL:<{ - OVER - 16 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i dict _62=16 - DICTIGETNEXT - NULLSWAPIFNOT - NULLSWAPIFNOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f - DUP // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f f - IF:<{ // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f - s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i cs - 8 LDU // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode cs - LDREF // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _125 _124 - DROP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _68 - SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i _68 mode - SENDRAWMSG - }>ELSE<{ - s2 POP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i - }> - SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict i f - NOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i _71 - }> // last_cleaned public_key old_queries query_id stored_subwallet bound dict i - 2DROP // last_cleaned public_key old_queries query_id stored_subwallet bound - 38 PUSHPOW2 // last_cleaned public_key old_queries query_id stored_subwallet bound _74 - SUB // last_cleaned public_key old_queries query_id stored_subwallet bound - NEWC // last_cleaned public_key old_queries query_id stored_subwallet bound _77 - s0 s3 s4 XCHG3 - 64 PUSHINT // last_cleaned public_key stored_subwallet bound _77 query_id old_queries _78=64 - DICTUSETB // last_cleaned public_key stored_subwallet bound old_queries - UNTIL:<{ - DUP - 64 PUSHINT // last_cleaned public_key stored_subwallet bound old_queries old_queries _85=64 - DICTUREMMIN - NULLSWAPIFNOT - NULLSWAPIFNOT // last_cleaned public_key stored_subwallet bound old_queries _126 _128 _127 _129 - s2 POP // last_cleaned public_key stored_subwallet bound old_queries old_queries' f i - s1 s0 XCPU // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f - IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f - DROP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i - s0 s3 PUSH2 // last_cleaned public_key stored_subwallet bound old_queries old_queries' i i bound - LESS // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f - }> // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f - DUP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f - IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f - s3 POP - s6 POP // last_cleaned public_key stored_subwallet bound f old_queries - }>ELSE<{ - s3 s1 s3 XCHG3 - 2DROP // last_cleaned public_key stored_subwallet bound f old_queries - }> - SWAP // last_cleaned public_key stored_subwallet bound old_queries f - NOT // last_cleaned public_key stored_subwallet bound old_queries _90 - }> // last_cleaned public_key stored_subwallet bound old_queries - NIP // last_cleaned public_key stored_subwallet old_queries - NEWC // last_cleaned public_key stored_subwallet old_queries _91 - s1 s2 XCHG // last_cleaned public_key old_queries stored_subwallet _91 - 32 STU // last_cleaned public_key old_queries _93 - s1 s3 XCHG // old_queries public_key last_cleaned _93 - 64 STU // old_queries public_key _95 - 256 STU // old_queries _97 - STDICT // _98 - ENDC // _99 - c4 POP - }> - processed? PROC:<{ - // query_id - c4 PUSH // query_id _2 - CTOS // query_id ds - 32 LDU // query_id _29 _28 - NIP // query_id ds - 64 LDU // query_id _11 ds - 256 LDU // query_id _11 _33 _32 - NIP // query_id _11 ds - LDDICT // query_id last_cleaned old_queries ds - ENDS - s2 s(-1) PUXC - 64 PUSHINT // query_id last_cleaned query_id old_queries _22=64 - DICTUGET - NULLSWAPIFNOT // query_id last_cleaned _36 _37 - NIP // query_id last_cleaned found - IF:<{ // query_id last_cleaned - 2DROP // - TRUE // _24 - }>ELSE<{ // query_id last_cleaned - LEQ // _26 - NEGATE // _24 - }> - }> -}END>c diff --git a/crypto/smartcont/auto/simple-wallet-code.cpp b/crypto/smartcont/auto/simple-wallet-code.cpp deleted file mode 100644 index ea4603f0ae..0000000000 --- a/crypto/smartcont/auto/simple-wallet-code.cpp +++ /dev/null @@ -1 +0,0 @@ -with_tvm_code("simple-wallet", "te6ccgEBBAEATwABFP8A9KQT9KDyyAsBAgEgAgMABNIwAG7ygwjXGCDTH+1E0NMf0//RUTG68qED+QFUEEL5EPKi+ABRINdKltMH1AL7AN7RpMjLH8v/ye1U"); diff --git a/crypto/smartcont/auto/simple-wallet-code.fif b/crypto/smartcont/auto/simple-wallet-code.fif deleted file mode 100644 index c052d4b656..0000000000 --- a/crypto/smartcont/auto/simple-wallet-code.fif +++ /dev/null @@ -1,45 +0,0 @@ -// automatically generated from `smartcont/stdlib.fc` `smartcont/simple-wallet-code.fc` -PROGRAM{ - DECLPROC recv_internal - DECLPROC recv_external - recv_internal PROC:<{ - // in_msg - DROP // - }> - recv_external PROC:<{ - // in_msg - 9 PUSHPOW2 // in_msg _3=512 - LDSLICEX // signature in_msg - DUP // signature in_msg cs - 32 LDU // signature in_msg msg_seqno cs - c4 PUSH // signature in_msg msg_seqno cs _11 - CTOS // signature in_msg msg_seqno cs cs2 - 32 LDU // signature in_msg msg_seqno cs stored_seqno cs2 - 256 LDU // signature in_msg msg_seqno cs stored_seqno public_key cs2 - ENDS - s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno - EQUAL // signature in_msg public_key cs stored_seqno _23 - 33 THROWIFNOT - s0 s3 XCHG // signature stored_seqno public_key cs in_msg - HASHSU // signature stored_seqno public_key cs _26 - s0 s4 s2 XC2PU // cs stored_seqno public_key _26 signature public_key - CHKSIGNU // cs stored_seqno public_key _27 - 34 THROWIFNOT - ACCEPT - s2 s0 XCPU // public_key stored_seqno cs cs - SREFS // public_key stored_seqno cs _32 - IF:<{ // public_key stored_seqno cs - 8 LDU // public_key stored_seqno mode cs - LDREF // public_key stored_seqno mode _37 cs - s0 s2 XCHG // public_key stored_seqno cs _37 mode - SENDRAWMSG - }> // public_key stored_seqno cs - ENDS - INC // public_key _42 - NEWC // public_key _42 _43 - 32 STU // public_key _45 - 256 STU // _47 - ENDC // _48 - c4 POP - }> -}END>c diff --git a/crypto/smartcont/auto/wallet-code.cpp b/crypto/smartcont/auto/wallet-code.cpp deleted file mode 100644 index 52fefeeb07..0000000000 --- a/crypto/smartcont/auto/wallet-code.cpp +++ /dev/null @@ -1 +0,0 @@ -with_tvm_code("wallet", "te6ccgEBBgEAaAABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQCA8oMI1xgg0x/TH/gjErvyY+1E0NMf0//RUTG68qED+QFUEEL5EPKi+AACkyDXSpbTB9QC+wDo0aTIyx/L/8ntVAAE0DAAEaCZL9qJoa4WPw=="); diff --git a/crypto/smartcont/auto/wallet-code.fif b/crypto/smartcont/auto/wallet-code.fif deleted file mode 100644 index cd9fa5d0f8..0000000000 --- a/crypto/smartcont/auto/wallet-code.fif +++ /dev/null @@ -1,59 +0,0 @@ -// automatically generated from `smartcont/stdlib.fc` `smartcont/wallet-code.fc` -PROGRAM{ - DECLPROC recv_internal - DECLPROC recv_external - 85143 DECLMETHOD seqno - recv_internal PROC:<{ - // in_msg - DROP // - }> - recv_external PROC:<{ - // in_msg - 9 PUSHPOW2 // in_msg _3=512 - LDSLICEX // signature in_msg - DUP // signature in_msg cs - 32 LDU // signature in_msg _8 cs - 32 LDU // signature in_msg msg_seqno valid_until cs - NOW // signature in_msg msg_seqno valid_until cs _15 - s1 s2 XCHG // signature in_msg msg_seqno cs valid_until _15 - LEQ // signature in_msg msg_seqno cs _16 - 35 THROWIF - c4 PUSH // signature in_msg msg_seqno cs _19 - CTOS // signature in_msg msg_seqno cs ds - 32 LDU // signature in_msg msg_seqno cs _23 ds - 256 LDU // signature in_msg msg_seqno cs stored_seqno public_key ds - ENDS - s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno - EQUAL // signature in_msg public_key cs stored_seqno _31 - 33 THROWIFNOT - s0 s3 XCHG // signature stored_seqno public_key cs in_msg - HASHSU // signature stored_seqno public_key cs _34 - s0 s4 s2 XC2PU // cs stored_seqno public_key _34 signature public_key - CHKSIGNU // cs stored_seqno public_key _35 - 34 THROWIFNOT - ACCEPT - s0 s2 XCHG // public_key stored_seqno cs - WHILE:<{ - DUP // public_key stored_seqno cs cs - SREFS // public_key stored_seqno cs _40 - }>DO<{ // public_key stored_seqno cs - 8 LDU // public_key stored_seqno mode cs - LDREF // public_key stored_seqno mode _45 cs - s0 s2 XCHG // public_key stored_seqno cs _45 mode - SENDRAWMSG - }> // public_key stored_seqno cs - ENDS - INC // public_key _50 - NEWC // public_key _50 _51 - 32 STU // public_key _53 - 256 STU // _55 - ENDC // _56 - c4 POP - }> - seqno PROC:<{ - // - c4 PUSH // _0 - CTOS // _1 - 32 PLDU // _3 - }> -}END>c diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index ff3c6b9a19..c60d96c92e 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -1,6 +1,22 @@ ;; Standard library for funC ;; +forall X -> tuple cons(X head, tuple tail) asm "CONS"; +forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; +forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS"; +forall X -> X car(tuple list) asm "CAR"; +tuple cdr(tuple list) asm "CDR"; +forall X, Y -> tuple pair(X x, Y y) asm "PAIR"; +forall X, Y -> (X, Y) unpair(tuple t) asm "UNPAIR"; +forall X, Y, Z -> tuple triple(X x, Y y, Z z) asm "TRIPLE"; +forall X, Y, Z -> (X, Y, Z) untriple(tuple t) asm "UNTRIPLE"; +forall X, Y, Z, W -> tuple tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; +forall X, Y, Z, W -> (X, Y, Z, W) untuple4(tuple t) asm "4 UNTUPLE"; +forall X -> X first(tuple t) asm "FIRST"; +forall X -> X second(tuple t) asm "SECOND"; +forall X -> X third(tuple t) asm "THIRD"; +forall X -> X fourth(tuple t) asm "3 INDEX"; + int now() asm "NOW"; slice my_address() asm "MYADDR"; tuple get_balance() asm "BALANCE"; diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 1db283f064..552979de81 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -58,6 +58,7 @@ #include "vm/continuation.h" #include "vm/cp0.h" #include "ton/ton-shard.h" +#include "openssl/rand.hpp" #if TD_DARWIN || TD_LINUX #include @@ -1153,16 +1154,24 @@ void TestNode::got_account_state(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, t vm::load_cell_slice(info.root).print_rec(outp); out << outp.str(); out << "last transaction lt = " << info.last_trans_lt << " hash = " << info.last_trans_hash.to_hex() << std::endl; + block::gen::Account::Record_account acc; + block::gen::AccountStorage::Record store; + block::CurrencyCollection balance; + if (tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) && balance.unpack(store.balance)) { + out << "account balance is " << balance.to_str() << std::endl; + } } else { out << "account state is empty" << std::endl; } } else if (info.root.not_null()) { block::gen::Account::Record_account acc; block::gen::AccountStorage::Record store; - if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store))) { + block::CurrencyCollection balance; + if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) && balance.unpack(store.balance))) { LOG(ERROR) << "error unpacking account state"; return; } + out << "account balance is " << balance.to_str() << std::endl; int tag = block::gen::t_AccountState.get_tag(*store.state); switch (tag) { case block::gen::AccountState::account_uninit: @@ -1214,6 +1223,28 @@ void TestNode::got_account_state(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, t } } +Ref TestNode::prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, Ref my_addr, + const block::CurrencyCollection& balance) const { + td::BitArray<256> rand_seed; + td::RefInt256 rand_seed_int{true}; + prng::rand_gen().rand_bytes(rand_seed.data(), 32); + if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) { + return {}; + } + auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea + td::make_refint(0), // actions:Integer + td::make_refint(0), // msgs_sent:Integer + td::make_refint(now), // unixtime:Integer + td::make_refint(lt), // block_lt:Integer + td::make_refint(lt), // trans_lt:Integer + std::move(rand_seed_int), // rand_seed:Integer + balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] + my_addr, // myself:MsgAddressInt + vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo; + LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); + return vm::make_tuple_ref(std::move(tuple)); +} + void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, @@ -1240,7 +1271,9 @@ void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton: } block::gen::Account::Record_account acc; block::gen::AccountStorage::Record store; - if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store))) { + block::CurrencyCollection balance; + if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) && + balance.validate_unpack(store.balance))) { LOG(ERROR) << "error unpacking account state"; return; } @@ -1276,6 +1309,8 @@ void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton: vm::GasLimits gas{gas_limit}; LOG(DEBUG) << "creating VM"; vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()}; + vm.set_c7(prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo + // vm.incr_stack_trace(1); // enable stack dump after each step LOG(INFO) << "starting VM to run method `" << method << "` (" << method_id << ") of smart contract " << workchain << ":" << addr.to_hex(); int exit_code = ~vm.run(); diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 36830e8d5b..17cc295552 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -119,6 +119,8 @@ class TestNode : public td::actor::Actor { td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, std::vector params); + Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, Ref my_addr, + const block::CurrencyCollection& balance) const; bool get_all_shards(bool use_last = true, ton::BlockIdExt blkid = {}); void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data); bool get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = ""); diff --git a/validator/manager.cpp b/validator/manager.cpp index 376b367e9e..eba3f24363 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1290,7 +1290,8 @@ void ValidatorManagerImpl::send_get_zero_state_request(BlockIdExt id, td::uint32 void ValidatorManagerImpl::send_get_persistent_state_request(BlockIdExt id, BlockIdExt masterchain_block_id, td::uint32 priority, td::Promise promise) { - callback_->download_persistent_state(id, masterchain_block_id, priority, td::Timestamp::in(60.0), std::move(promise)); + callback_->download_persistent_state(id, masterchain_block_id, priority, td::Timestamp::in(3600.0), + std::move(promise)); } void ValidatorManagerImpl::send_get_block_proof_request(BlockIdExt block_id, td::uint32 priority, diff --git a/validator/net/download-state.cpp b/validator/net/download-state.cpp index 3fe50247ce..6fc8c366fb 100644 --- a/validator/net/download-state.cpp +++ b/validator/net/download-state.cpp @@ -200,7 +200,7 @@ void DownloadState::got_block_state_part(td::BufferSlice data, td::uint32 reques return; } - td::uint32 part_size = 4 << 20; + td::uint32 part_size = 1 << 18; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), part_size](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &DownloadState::abort_query, R.move_as_error()); From c860ce3d1e6c44886df29070c63d60f30530f60b Mon Sep 17 00:00:00 2001 From: ton Date: Wed, 23 Oct 2019 17:43:50 +0400 Subject: [PATCH 014/667] updated smartcontracts - updated smartcontracts - updated fullnode database layout - fixed memory leak in blockchain-explorer - updated tonlib --- CMakeLists.txt | 10 +- adnl/adnl-ext-server.cpp | 4 +- adnl/adnl-ext-server.hpp | 65 +- blockchain-explorer/blockchain-explorer.cpp | 4 +- common/int-to-string.hpp | 24 + crypto/CMakeLists.txt | 33 + crypto/block/block.tlb | 3 + crypto/block/check-proof.cpp | 2 - crypto/block/mc-config.cpp | 77 +- crypto/block/mc-config.h | 44 +- crypto/block/transaction.cpp | 16 +- crypto/block/transaction.h | 27 +- crypto/common/bitstring.h | 2 + crypto/fift/lib/Asm.fif | 1 + crypto/fift/lib/TonUtil.fif | 2 +- crypto/smartcont/CreateState.fif | 19 +- crypto/smartcont/config-code.fc | 124 +- crypto/smartcont/elector-code.fc | 19 +- crypto/smartcont/gen-zerostate.fif | 16 + crypto/smartcont/multisig-code.fc | 265 +++ crypto/smartcont/simple-wallet-ext-code.fc | 67 + crypto/smartcont/stdlib.fc | 16 + crypto/smc-envelope/GenericAccount.cpp | 99 ++ crypto/smc-envelope/GenericAccount.h | 31 + crypto/smc-envelope/MultisigWallet.cpp | 171 ++ crypto/smc-envelope/MultisigWallet.h | 64 + crypto/smc-envelope/SmartContract.cpp | 188 ++ crypto/smc-envelope/SmartContract.h | 116 ++ crypto/smc-envelope/SmartContractCode.cpp | 72 + crypto/smc-envelope/SmartContractCode.h | 30 + crypto/smc-envelope/TestGiver.cpp | 62 + crypto/smc-envelope/TestGiver.h | 39 + crypto/smc-envelope/TestWallet.cpp | 91 + crypto/smc-envelope/TestWallet.h | 48 + crypto/smc-envelope/Wallet.cpp | 100 ++ crypto/smc-envelope/Wallet.h | 48 + crypto/test/Ed25519.cpp | 75 + crypto/test/test-db.cpp | 32 +- crypto/test/test-smartcont.cpp | 524 ++++++ crypto/test/wycheproof.h | 1160 +++++++++++++ crypto/vm/cells/CellString.cpp | 64 + crypto/vm/cells/CellString.h | 22 + crypto/vm/continuation.cpp | 3 + crypto/vm/continuation.h | 52 +- crypto/vm/excno.hpp | 17 + crypto/vm/tonops.cpp | 11 +- doc/tvm.tex | 3 +- .../android/src/drinkless/org/ton/Client.java | 28 +- .../main/java/drinkless/org/ton/Client.java | 28 +- example/cpp/tonjson_example.cpp | 4 +- lite-client/lite-client-common.cpp | 22 + lite-client/lite-client-common.h | 5 +- lite-client/lite-client.cpp | 24 +- lite-client/lite-client.h | 2 - tdactor/benchmark/benchmark.cpp | 279 +++ tdactor/td/actor/PromiseFuture.h | 23 + tdactor/td/actor/actor.h | 25 + tdnet/td/net/TcpListener.cpp | 52 + tdnet/td/net/TcpListener.h | 19 + tdnet/td/net/UdpServer.cpp | 64 - tdutils/td/utils/Status.h | 14 + tdutils/td/utils/invoke.h | 4 +- tdutils/td/utils/tests.h | 28 + .../JavadocTlDocumentationGenerator.php | 6 +- tl/generate/scheme/ton_api.tl | 11 +- tl/generate/scheme/ton_api.tlo | Bin 54392 -> 55652 bytes tl/generate/scheme/tonlib_api.tl | 73 +- tl/generate/scheme/tonlib_api.tlo | Bin 13984 -> 16460 bytes tonlib/CMakeLists.txt | 16 +- tonlib/test/offline.cpp | 206 +-- tonlib/test/online.cpp | 565 ++++-- tonlib/tonlib/Client.cpp | 2 +- tonlib/tonlib/ExtClient.cpp | 15 + tonlib/tonlib/ExtClient.h | 5 + tonlib/tonlib/ExtClientLazy.cpp | 7 + tonlib/tonlib/KeyStorage.cpp | 35 +- tonlib/tonlib/KeyStorage.h | 4 + tonlib/tonlib/LastBlock.cpp | 9 +- tonlib/tonlib/LastConfig.cpp | 152 ++ tonlib/tonlib/LastConfig.h | 70 + tonlib/tonlib/Logging.cpp | 44 +- tonlib/tonlib/TonlibClient.cpp | 1507 ++++++++++++----- tonlib/tonlib/TonlibClient.h | 108 +- tonlib/tonlib/TonlibError.h | 11 + tonlib/tonlib/tonlib-cli.cpp | 316 +++- tonlib/tonlib/utils.cpp | 19 +- tonlib/tonlib/utils.h | 13 +- validator/CMakeLists.txt | 5 + validator/block-handle.hpp | 13 + validator/db/archive-db.cpp | 332 ++++ validator/db/archive-db.hpp | 97 ++ validator/db/archiver.cpp | 51 +- validator/db/archiver.hpp | 5 +- validator/db/filedb.cpp | 3 + validator/db/filedb.hpp | 14 +- validator/db/files-async.hpp | 2 +- validator/db/package.cpp | 143 ++ validator/db/package.hpp | 40 + validator/db/rootdb.cpp | 81 +- validator/db/rootdb.hpp | 4 +- validator/interfaces/block-handle.h | 2 + validator/manager.cpp | 21 + validator/manager.hpp | 22 +- validator/net/download-state.cpp | 2 +- 104 files changed, 7294 insertions(+), 1320 deletions(-) create mode 100644 common/int-to-string.hpp create mode 100644 crypto/smartcont/multisig-code.fc create mode 100644 crypto/smartcont/simple-wallet-ext-code.fc create mode 100644 crypto/smc-envelope/GenericAccount.cpp create mode 100644 crypto/smc-envelope/GenericAccount.h create mode 100644 crypto/smc-envelope/MultisigWallet.cpp create mode 100644 crypto/smc-envelope/MultisigWallet.h create mode 100644 crypto/smc-envelope/SmartContract.cpp create mode 100644 crypto/smc-envelope/SmartContract.h create mode 100644 crypto/smc-envelope/SmartContractCode.cpp create mode 100644 crypto/smc-envelope/SmartContractCode.h create mode 100644 crypto/smc-envelope/TestGiver.cpp create mode 100644 crypto/smc-envelope/TestGiver.h create mode 100644 crypto/smc-envelope/TestWallet.cpp create mode 100644 crypto/smc-envelope/TestWallet.h create mode 100644 crypto/smc-envelope/Wallet.cpp create mode 100644 crypto/smc-envelope/Wallet.h create mode 100644 crypto/test/test-smartcont.cpp create mode 100644 crypto/test/wycheproof.h create mode 100644 crypto/vm/cells/CellString.cpp create mode 100644 crypto/vm/cells/CellString.h create mode 100644 tonlib/tonlib/LastConfig.cpp create mode 100644 tonlib/tonlib/LastConfig.h create mode 100644 validator/db/archive-db.cpp create mode 100644 validator/db/archive-db.hpp create mode 100644 validator/db/package.cpp create mode 100644 validator/db/package.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d71959b55e..583578c6b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,7 +9,7 @@ get_filename_component(TON_REAL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" REALPAT get_filename_component(TON_REAL_BINARY_DIR "${CMAKE_CURRENT_BINARY_DIR}" REALPATH) if (TON_REAL_BINARY_DIR STREQUAL TON_REAL_SOURCE_DIR) - message(" Out-of-source build should be used to build TDLib.") + message(" Out-of-source build should be used to build TON.") message(" You need to remove the files already created by CMake and") message(" rerun CMake from a new directory:") message(" rm -rf CMakeFiles CMakeCache.txt") @@ -190,6 +190,7 @@ endif() set(CMAKE_THREAD_PREFER_PTHREAD ON) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) +find_package(ZLIB REQUIRED) if (TON_ARCH AND NOT MSVC) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=${TON_ARCH}") @@ -380,7 +381,7 @@ if (NOT CMAKE_CROSSCOMPILING) if (TDUTILS_MIME_TYPE) set(TDMIME_AUTO tdmime_auto) endif() - add_custom_target(prepare_cross_compiling DEPENDS tl_generate_common tlb_generate_block ${TDMIME_AUTO}) + add_custom_target(prepare_cross_compiling DEPENDS tl_generate_common tlb_generate_block gen_fif ${TDMIME_AUTO}) endif() #TESTS @@ -390,6 +391,9 @@ target_link_libraries(test-ed25519 PRIVATE ton_crypto) add_executable(test-vm test/test-td-main.cpp ${TONVM_TEST_SOURCE}) target_link_libraries(test-vm PRIVATE ton_crypto fift-lib) +add_executable(test-smartcont test/test-td-main.cpp ${SMARTCONT_TEST_SOURCE}) +target_link_libraries(test-smartcont PRIVATE smc-envelope fift-lib ton_db) + add_executable(test-cells test/test-td-main.cpp ${CELLS_TEST_SOURCE}) target_link_libraries(test-cells PRIVATE ton_crypto) @@ -490,10 +494,12 @@ endif() enable_testing() set(TEST_OPTIONS "--regression ${CMAKE_CURRENT_SOURCE_DIR}/test/regression-tests.ans --filter -Bench") separate_arguments(TEST_OPTIONS) +add_test(test-ed25519-crypto crypto/test-ed25519-crypto) add_test(test-ed25519 test-ed25519) add_test(test-vm test-vm ${TEST_OPTIONS}) add_test(test-fift test-fift ${TEST_OPTIONS}) add_test(test-cells test-cells ${TEST_OPTIONS}) +add_test(test-smartcont test-smartcont) add_test(test-net test-net) add_test(test-actors test-tdactor) diff --git a/adnl/adnl-ext-server.cpp b/adnl/adnl-ext-server.cpp index 168e9839cd..a23d8610d6 100644 --- a/adnl/adnl-ext-server.cpp +++ b/adnl/adnl-ext-server.cpp @@ -146,8 +146,8 @@ void AdnlExtServerImpl::add_tcp_port(td::uint16 port) { } }; - auto act = td::actor::create_actor(td::actor::ActorOptions().with_name("listener").with_poll(), - port, std::make_unique(actor_id(this))); + auto act = td::actor::create_actor( + td::actor::ActorOptions().with_name("listener").with_poll(), port, std::make_unique(actor_id(this))); listeners_.emplace(port, std::move(act)); } diff --git a/adnl/adnl-ext-server.hpp b/adnl/adnl-ext-server.hpp index f7912c8511..7ffa9e69dc 100644 --- a/adnl/adnl-ext-server.hpp +++ b/adnl/adnl-ext-server.hpp @@ -32,69 +32,6 @@ namespace ton { namespace adnl { -class TcpInfiniteListener : public td::actor::Actor { - public: - TcpInfiniteListener(td::int32 port, std::unique_ptr callback) - : port_(port), callback_(std::move(callback)) { - } - - private: - td::int32 port_; - std::unique_ptr callback_; - td::actor::ActorOwn tcp_listener_; - td::int32 refcnt_{0}; - bool close_flag_{false}; - - void start_up() override { - loop(); - } - - void hangup() override { - close_flag_ = true; - tcp_listener_.reset(); - if (refcnt_ == 0) { - stop(); - } - } - - void loop() override { - if (!tcp_listener_.empty()) { - return; - } - class Callback : public td::TcpListener::Callback { - public: - Callback(td::actor::ActorShared parent) : parent_(std::move(parent)) { - } - void accept(td::SocketFd fd) override { - td::actor::send_closure(parent_, &TcpInfiniteListener::accept, std::move(fd)); - } - - private: - td::actor::ActorShared parent_; - }; - refcnt_++; - tcp_listener_ = td::actor::create_actor( - td::actor::ActorOptions().with_name(PSLICE() << "TcpListener" << td::tag("port", port_)).with_poll(), port_, - std::make_unique(actor_shared(this))); - } - - void accept(td::SocketFd fd) { - callback_->accept(std::move(fd)); - } - - void hangup_shared() override { - refcnt_--; - tcp_listener_.reset(); - if (close_flag_) { - if (refcnt_ == 0) { - stop(); - } - } else { - alarm_timestamp() = td::Timestamp::in(5 /*5 seconds*/); - } - } -}; - class AdnlExtServerImpl; class AdnlInboundConnection : public AdnlExtConnection { @@ -150,7 +87,7 @@ class AdnlExtServerImpl : public AdnlExtServer { td::actor::ActorId peer_table_; std::set local_ids_; std::set ports_; - std::map> listeners_; + std::map> listeners_; }; } // namespace adnl diff --git a/blockchain-explorer/blockchain-explorer.cpp b/blockchain-explorer/blockchain-explorer.cpp index 01e4f95461..2fa1bf75c3 100644 --- a/blockchain-explorer/blockchain-explorer.cpp +++ b/blockchain-explorer/blockchain-explorer.cpp @@ -393,8 +393,8 @@ class CoreActor : public CoreActorInterface { clients_.emplace_back(ton::adnl::AdnlExtClient::create(ton::adnl::AdnlNodeIdFull{remote_public_key_}, remote_addr_, make_callback(0))); } - daemon_ = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, static_cast(http_port_), nullptr, nullptr, - &process_http_request, nullptr, MHD_OPTION_END); + daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast(http_port_), nullptr, nullptr, + &process_http_request, nullptr, MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END); CHECK(daemon_ != nullptr); } }; diff --git a/common/int-to-string.hpp b/common/int-to-string.hpp new file mode 100644 index 0000000000..414342cd0f --- /dev/null +++ b/common/int-to-string.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include "td/utils/int_types.h" +#include "td/utils/Slice.h" + +namespace ton { + +template +typename std::enable_if_t::value, td::MutableSlice> store_int_to_slice(td::MutableSlice S, + const T &v) { + CHECK(S.size() >= sizeof(T)); + S.copy_from(td::Slice(reinterpret_cast(&v), sizeof(T))); + return S.remove_prefix(sizeof(T)); +} + +template +typename std::enable_if_t::value, T> fetch_int_from_slice(td::Slice S) { + CHECK(S.size() >= sizeof(T)); + T v; + td::MutableSlice(reinterpret_cast(&v), sizeof(T)).copy_from(S.truncate(sizeof(T))); + return v; +} + +} // namespace ton diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index dc7d7cf14c..8b8f147ab4 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -88,6 +88,7 @@ set(TON_CRYPTO_SOURCE vm/cells/CellBuilder.cpp vm/cells/CellHash.cpp vm/cells/CellSlice.cpp + vm/cells/CellString.cpp vm/cells/CellTraits.cpp vm/cells/CellUsageTree.cpp vm/cells/DataCell.cpp @@ -99,6 +100,7 @@ set(TON_CRYPTO_SOURCE vm/cells/CellBuilder.h vm/cells/CellHash.h vm/cells/CellSlice.h + vm/cells/CellString.h vm/cells/CellTraits.h vm/cells/CellUsageTree.h vm/cells/CellWithStorage.h @@ -197,6 +199,24 @@ set(BLOCK_SOURCE block/transaction.h ) +set(SMC_ENVELOPE_SOURCE + smc-envelope/GenericAccount.cpp + smc-envelope/MultisigWallet.cpp + smc-envelope/SmartContract.cpp + smc-envelope/SmartContractCode.cpp + smc-envelope/TestGiver.cpp + smc-envelope/TestWallet.cpp + smc-envelope/Wallet.cpp + + smc-envelope/GenericAccount.h + smc-envelope/MultisigWallet.h + smc-envelope/SmartContract.h + smc-envelope/SmartContractCode.h + smc-envelope/TestGiver.h + smc-envelope/TestWallet.h + smc-envelope/Wallet.h +) + set(ED25519_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/Ed25519.cpp PARENT_SCOPE @@ -217,6 +237,11 @@ set(TONVM_TEST_SOURCE PARENT_SCOPE ) +set(SMARTCONT_TEST_SOURCE + ${CMAKE_CURRENT_SOURCE_DIR}/test/test-smartcont.cpp + PARENT_SCOPE +) + set(FIFT_TEST_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/test/fift.cpp PARENT_SCOPE @@ -329,10 +354,18 @@ if (NOT CMAKE_CROSSCOMPILING) GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet) GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2) GenFif(DEST smartcont/auto/elector-code SOURCE smartcont/elector-code.fc NAME elector-code) + GenFif(DEST smartcont/auto/multisig-code SOURCE smartcont/multisig-code.fc NAME multisig) GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet) GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2) + + GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext) endif() +add_library(smc-envelope ${SMC_ENVELOPE_SOURCE}) +target_include_directories(smc-envelope PUBLIC $) +target_link_libraries(smc-envelope PUBLIC ton_crypto PRIVATE tdutils ton_block) +add_dependencies(smc-envelope gen_fif) + add_executable(create-state block/create-state.cpp) target_include_directories(create-state PUBLIC $ $) diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index d4bba1a182..ec91ef5755 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -551,6 +551,9 @@ validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = Validat validators#11 utime_since:uint32 utime_until:uint32 total:(## 16) main:(## 16) { main <= total } { main >= 1 } list:(Hashmap 16 ValidatorDescr) = ValidatorSet; +validators_ext#12 utime_since:uint32 utime_until:uint32 + total:(## 16) main:(## 16) { main <= total } { main >= 1 } + total_weight:uint64 list:(HashmapE 16 ValidatorDescr) = ValidatorSet; _ config_addr:bits256 = ConfigParam 0; _ elector_addr:bits256 = ConfigParam 1; diff --git a/crypto/block/check-proof.cpp b/crypto/block/check-proof.cpp index 7bc21eb3e6..7db7a97c6c 100644 --- a/crypto/block/check-proof.cpp +++ b/crypto/block/check-proof.cpp @@ -221,8 +221,6 @@ td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const td::Result AccountState::validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const { TRY_RESULT_PREFIX(root, vm::std_boc_deserialize(state.as_slice(), true), "cannot deserialize account state"); - LOG(INFO) << "got account state for " << addr << " with respect to blocks " << blk.to_str() - << (shard_blk == blk ? "" : std::string{" and "} + shard_blk.to_str()); if (blk != ref_blk && ref_blk.id.seqno != ~0U) { return td::Status::Error(PSLICE() << "obtained getAccountState() for a different reference block " << blk.to_str() << " instead of requested " << ref_blk.to_str()); diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index ceefca79e5..80e399eed1 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -387,11 +387,25 @@ td::Result> Config::unpack_validator_set(Ref dict_root; + if (!tlb::unpack_cell(vset_root, rec)) { + gen::ValidatorSet::Record_validators rec0; + if (!tlb::unpack_cell(std::move(vset_root), rec0)) { + return td::Status::Error("validator set is invalid"); + } + rec.utime_since = rec0.utime_since; + rec.utime_until = rec0.utime_until; + rec.total = rec0.total; + rec.main = rec0.main; + dict_root = vm::Dictionary::construct_root_from(*rec0.list); + rec.total_weight = 0; + } else if (rec.total_weight) { + dict_root = rec.list->prefetch_ref(); + } else { + return td::Status::Error("validator set cannot have zero total weight"); } - vm::Dictionary dict{vm::Dictionary::construct_root_from(*rec.list), 16}; + vm::Dictionary dict{std::move(dict_root), 16}; td::BitArray<16> key_buffer; auto last = dict.get_minmax_key(key_buffer.bits(), 16, true); if (last.is_null() || (int)key_buffer.to_ulong() != rec.total - 1) { @@ -428,6 +442,9 @@ td::Result> Config::unpack_validator_set(Reflist.emplace_back(sig_pubkey.pubkey, descr.weight, ptr->total_weight, descr.adnl_addr); ptr->total_weight += descr.weight; } + if (rec.total_weight && rec.total_weight != ptr->total_weight) { + return td::Status::Error("validator set declares incorrect total weight"); + } return std::move(ptr); } @@ -517,6 +534,58 @@ td::Result> Config::get_storage_prices() const { return std::move(res); } +td::Result Config::get_gas_limits_prices(bool is_masterchain) const { + GasLimitsPrices res; + auto id = is_masterchain ? 20 : 21; + auto cell = get_config_param(id); + if (cell.is_null()) { + return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent"); + } + auto cs = vm::load_cell_slice(std::move(cell)); + block::gen::GasLimitsPrices::Record_gas_flat_pfx flat; + if (tlb::unpack(cs, flat)) { + cs = *flat.other; + res.flat_gas_limit = flat.flat_gas_limit; + res.flat_gas_price = flat.flat_gas_price; + } + auto f = [&](const auto& r, td::uint64 spec_limit) { + res.gas_limit = r.gas_limit; + res.special_gas_limit = spec_limit; + res.gas_credit = r.gas_credit; + res.gas_price = r.gas_price; + res.freeze_due_limit = r.freeze_due_limit; + res.delete_due_limit = r.delete_due_limit; + }; + block::gen::GasLimitsPrices::Record_gas_prices_ext rec; + if (tlb::unpack(cs, rec)) { + f(rec, rec.special_gas_limit); + } else { + block::gen::GasLimitsPrices::Record_gas_prices rec0; + if (tlb::unpack(cs, rec0)) { + f(rec0, rec0.gas_limit); + } else { + return td::Status::Error(PSLICE() << "configuration parameter " << id + << " with gas prices is invalid - can't parse"); + } + } + return res; +} + +td::Result Config::get_msg_prices(bool is_masterchain) const { + auto id = is_masterchain ? 24 : 25; + auto cell = get_config_param(id); + if (cell.is_null()) { + return td::Status::Error(PSLICE() << "configuration parameter " << id << " with msg prices is absent"); + } + auto cs = vm::load_cell_slice(std::move(cell)); + block::gen::MsgForwardPrices::Record rec; + if (!tlb::unpack(cs, rec)) { + return td::Status::Error(PSLICE() << "configuration parameter " << id + << " with msg prices is invalid - can't parse"); + } + return MsgPrices(rec.lump_price, rec.bit_price, rec.cell_price, rec.ihr_price_factor, rec.first_frac, rec.next_frac); +} + CatchainValidatorsConfig Config::unpack_catchain_validators_config(Ref cell) { block::gen::CatchainConfig::Record cfg; if (cell.is_null() || !tlb::unpack_cell(std::move(cell), cfg)) { diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 24351c0c71..018a47adbe 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -50,7 +50,7 @@ struct ValidatorDescr { : pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) { adnl_addr.set_zero(); } - bool operator<(td::uint64 wt_pos) const & { + bool operator<(td::uint64 wt_pos) const& { return cum_weight < wt_pos; } }; @@ -327,6 +327,46 @@ struct StoragePrices { , mc_bit_price(_mc_bprice) , mc_cell_price(_mc_cprice) { } + static td::RefInt256 compute_storage_fees(ton::UnixTime now, const std::vector& pricing, + const vm::CellStorageStat& storage_stat, ton::UnixTime last_paid, + bool is_special, bool is_masterchain); +}; + +struct GasLimitsPrices { + td::uint64 flat_gas_limit{0}; + td::uint64 flat_gas_price{0}; + td::uint64 gas_price{0}; + td::uint64 special_gas_limit{0}; + td::uint64 gas_limit{0}; + td::uint64 gas_credit{0}; + td::uint64 block_gas_limit{0}; + td::uint64 freeze_due_limit{0}; + td::uint64 delete_due_limit{0}; + + td::RefInt256 compute_gas_price(td::uint64 gas_used) const; +}; + +// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms +// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms +// bits in the root cell of a message are not included in msg.bits (lump_price pays for them) + +struct MsgPrices { + td::uint64 lump_price; + td::uint64 bit_price; + td::uint64 cell_price; + td::uint32 ihr_factor; + td::uint32 first_frac; + td::uint32 next_frac; + td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const; + std::pair compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits, + bool ihr_disabled = false) const; + MsgPrices() = default; + MsgPrices(td::uint64 lump, td::uint64 bitp, td::uint64 cellp, td::uint32 ihrf, td::uint32 firstf, td::uint32 nextf) + : lump_price(lump), bit_price(bitp), cell_price(cellp), ihr_factor(ihrf), first_frac(firstf), next_frac(nextf) { + } + td::RefInt256 get_first_part(td::RefInt256 total) const; + td::uint64 get_first_part(td::uint64 total) const; + td::RefInt256 get_next_part(td::RefInt256 total) const; }; struct CatchainValidatorsConfig { @@ -499,6 +539,8 @@ class Config { bool is_special_smartcontract(const ton::StdSmcAddress& addr) const; static td::Result> unpack_validator_set(Ref valset_root); td::Result> get_storage_prices() const; + td::Result get_gas_limits_prices(bool is_masterchain = false) const; + td::Result get_msg_prices(bool is_masterchain = false) const; static CatchainValidatorsConfig unpack_catchain_validators_config(Ref cell); CatchainValidatorsConfig get_catchain_validators_config() const; td::Status visit_validator_params() const; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index bc6d8e220f..6667cf8774 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -421,7 +421,9 @@ void add_partial_storage_payment(td::BigInt256& payment, ton::UnixTime delta, co payment += b; } -td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector& pricing) const { +td::RefInt256 StoragePrices::compute_storage_fees(ton::UnixTime now, const std::vector& pricing, + const vm::CellStorageStat& storage_stat, ton::UnixTime last_paid, + bool is_special, bool is_masterchain) { if (now <= last_paid || !last_paid || is_special || pricing.empty() || now <= pricing[0].valid_since) { return {}; } @@ -438,7 +440,7 @@ td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector ton::UnixTime valid_until = (i < n - 1 ? std::min(now, pricing[i + 1].valid_since) : now); if (upto < valid_until) { assert(upto >= pricing[i].valid_since); - add_partial_storage_payment(total.unique_write(), valid_until - upto, pricing[i], storage_stat, is_masterchain()); + add_partial_storage_payment(total.unique_write(), valid_until - upto, pricing[i], storage_stat, is_masterchain); } upto = valid_until; } @@ -446,6 +448,10 @@ td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector return total; } +td::RefInt256 Account::compute_storage_fees(ton::UnixTime now, const std::vector& pricing) const { + return StoragePrices::compute_storage_fees(now, pricing, storage_stat, last_paid, is_special, is_masterchain()); +} + Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now, Ref _inmsg) : trans_type(ttype) @@ -969,7 +975,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { gas = vm.get_gas_limits(); cp.gas_used = std::min(gas.gas_consumed(), gas.gas_limit); cp.accepted = (gas.gas_credit == 0); - cp.success = (cp.accepted && (unsigned)cp.exit_code <= 1); + cp.success = (cp.accepted && vm.committed()); if (cp.accepted & use_msg_state) { was_activated = true; acc_status = Account::acc_active; @@ -978,8 +984,8 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { << ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit; LOG(INFO) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success; if (cp.success) { - cp.new_data = vm.get_c4(); // c4 -> persistent data - cp.actions = vm.get_d(5); // c5 -> action list + cp.new_data = vm.get_committed_state().c4; // c4 -> persistent data + cp.actions = vm.get_committed_state().c5; // c5 -> action list int out_act_num = output_actions_count(cp.actions); if (verbosity > 2) { std::cerr << "new smart contract data: "; diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 015dc3f39e..25f8059560 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -65,10 +65,10 @@ struct NewOutMsg { NewOutMsg(ton::LogicalTime _lt, Ref _msg, Ref _trans) : lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) { } - bool operator<(const NewOutMsg& other) const & { + bool operator<(const NewOutMsg& other) const& { return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash()); } - bool operator>(const NewOutMsg& other) const & { + bool operator>(const NewOutMsg& other) const& { return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash()); } }; @@ -132,29 +132,6 @@ struct ComputePhaseConfig { bool parse_GasLimitsPrices(Ref cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit); }; -// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms -// ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms -// bits in the root cell of a message are not included in msg.bits (lump_price pays for them) - -struct MsgPrices { - td::uint64 lump_price; - td::uint64 bit_price; - td::uint64 cell_price; - td::uint32 ihr_factor; - td::uint32 first_frac; - td::uint32 next_frac; - td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const; - std::pair compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits, - bool ihr_disabled = false) const; - MsgPrices() = default; - MsgPrices(td::uint64 lump, td::uint64 bitp, td::uint64 cellp, td::uint32 ihrf, td::uint32 firstf, td::uint32 nextf) - : lump_price(lump), bit_price(bitp), cell_price(cellp), ihr_factor(ihrf), first_frac(firstf), next_frac(nextf) { - } - td::RefInt256 get_first_part(td::RefInt256 total) const; - td::uint64 get_first_part(td::uint64 total) const; - td::RefInt256 get_next_part(td::RefInt256 total) const; -}; - struct ActionPhaseConfig { int max_actions{255}; MsgPrices fwd_std; diff --git a/crypto/common/bitstring.h b/crypto/common/bitstring.h index b2a37c72f0..901ff709b7 100644 --- a/crypto/common/bitstring.h +++ b/crypto/common/bitstring.h @@ -224,6 +224,8 @@ class BitSliceGen { BitSliceGen(BitSliceGen&& bs, unsigned _offs, unsigned _len); BitSliceGen(Pt* _ptr, unsigned _len) : ref(), ptr(_ptr), offs(0), len(_len) { } + explicit BitSliceGen(Slice slice) : BitSliceGen(slice.data(), slice.size() * 8) { + } ~BitSliceGen() { } Pt* get_ptr() const { diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 6854717378..eb9034ec1e 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -970,6 +970,7 @@ x{F4B7} @Defop SUBDICTURPGET x{F800} @Defop ACCEPT x{F801} @Defop SETGASLIMIT +x{F80F} @Defop COMMIT x{F82} @Defop(4u) GETPARAM x{F823} @Defop NOW diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif index 44f1ff1cfd..7f1a376bb5 100644 --- a/crypto/fift/lib/TonUtil.fif +++ b/crypto/fift/lib/TonUtil.fif @@ -139,7 +139,7 @@ recursive append-long-bytes { // ( S -- x ) parse public key { dup $len 48 <> abort"public key must be 48 characters long" - base64>B dup Blen 36 <> abort"public key must be 48 characters long" + base64url>B dup Blen 36 <> abort"public key must be 48 characters long" 34 B| 16 B>u@ over crc16 <> abort"crc16 mismatch in public key" 16 B>u@+ 0x3ee6 <> abort"invalid tag in public key" 256 B>u@ diff --git a/crypto/smartcont/CreateState.fif b/crypto/smartcont/CreateState.fif index d3c05c00c9..ed82499b5f 100644 --- a/crypto/smartcont/CreateState.fif +++ b/crypto/smartcont/CreateState.fif @@ -1,4 +1,5 @@ "Asm.fif" include +"TonUtil.fif" include 31 -1<< constant wc_undef 0 constant wc_base @@ -187,6 +188,7 @@ dictnew constant special-dict // restricted wallet creation +"auto/wallet-code.fif" include =: WCode0 "auto/restricted-wallet-code.fif" include =: RWCode1 "auto/restricted-wallet2-code.fif" include =: RWCode2 @@ -200,7 +202,7 @@ dictnew constant special-dict 0 // ticktock 2 // mode: create register_smc - Masterchain 6 .Addr cr + Masterchain swap 6 .Addr cr } : create-wallet1 // D x t -- D' @@ -225,5 +227,18 @@ dictnew constant special-dict 0 // ticktock 2 // mode: create register_smc - Masterchain 6 .Addr cr + Masterchain swap 6 .Addr cr } : create-wallet2 + +// pubkey amount +{ over ."Key " pubkey>$ type ." -> " + WCode0 // code + // data + empty_cell // libs + 3 roll // balance + 0 // split_depth + 0 // ticktock + 2 // mode: create + register_smc + Masterchain swap 6 .Addr cr +} : create-wallet0 diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index 5e0508f537..a49ad1378a 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -1,15 +1,32 @@ ;; Simple configuration smart contract () set_conf_param(int index, cell value) impure { - var cs = begin_parse(get_data()); + var cs = get_data().begin_parse(); var cfg_dict = cs~load_ref(); cfg_dict~idict_set_ref(32, index, value); set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell()); } +(cell, int, int, cell) load_data() inline { + var cs = get_data().begin_parse(); + var (cfg_dict, stored_seqno, public_key) = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256)); + var vote_dict = cs.slice_empty?() ? new_dict() : cs~load_dict(); + cs.end_parse(); + return (cfg_dict, stored_seqno, public_key, vote_dict); +} + +() store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline { + set_data(begin_cell() + .store_ref(cfg_dict) + .store_uint(stored_seqno, 32) + .store_uint(public_key, 256) + .store_dict(vote_dict) + .end_cell()); +} + (int, int) check_validator_set(cell vset) { var cs = vset.begin_parse(); - throw_unless(9, cs~load_uint(8) == 0x11); ;; validators#11 + throw_if(9, (cs~load_uint(8) - 0x11) & -2); ;; validators#11 or validators_ext#12 int utime_since = cs~load_uint(32); int utime_until = cs~load_uint(32); int total = cs~load_uint(16); @@ -86,43 +103,112 @@ .end_cell(), 0); } -() recv_external(slice in_msg) impure { - var signature = in_msg~load_bits(512); - var cs = in_msg; - int action = cs~load_uint(32); - int msg_seqno = cs~load_uint(32); - var valid_until = cs~load_uint(32); - throw_if(35, valid_until < now()); - var cs2 = begin_parse(get_data()); - var cfg_dict = cs2~load_ref(); - var stored_seqno = cs2~load_uint(32); - var public_key = cs2~load_uint(256); - cs2.end_parse(); - throw_unless(33, msg_seqno == stored_seqno); - throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); - accept_message(); +() after_code_upgrade(slice param, cell old_code) impure method_id(1666) { +} + +_ perform_action(cfg_dict, public_key, action, cs) { if (action == 0x43665021) { ;; change one configuration parameter var param_index = cs~load_uint(32); var param_value = cs~load_ref(); cs.end_parse(); cfg_dict~idict_set_ref(32, param_index, param_value); + return (cfg_dict, public_key); } elseif (action == 0x4e436f64) { ;; change configuration smart contract code var new_code = cs~load_ref(); - cs.end_parse(); set_code(new_code); + var old_code = get_c3(); + set_c3(new_code); + after_code_upgrade(cs, old_code); + throw(0); + return (cfg_dict, public_key); } elseif (action == 0x50624b21) { ;; change configuration master public key public_key = cs~load_uint(256); cs.end_parse(); + return (cfg_dict, public_key); } elseif (action == 0x4e43ef05) { ;; change election smart contract code change_elector_code(cs); + return (cfg_dict, public_key); } else { throw_if(32, action); + return (cfg_dict, public_key); + } +} + +slice get_validator_descr(int idx) inline_ref { + var vset = config_param(34); + if (vset.null?()) { + return null(); + } + var cs = begin_parse(vset); + cs~skip_bits(8 + 32 + 32 + 16 + 16); + var dict = begin_cell().store_slice(cs).end_cell(); + var (value, _) = dict.udict_get?(16, idx); + return value; +} + +(int, int) unpack_validator_descr(slice cs) inline { + ;; ed25519_pubkey#8e81278a pubkey:bits256 = SigPubKey; + ;; validator#53 public_key:SigPubKey weight:uint64 = ValidatorDescr; + ;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr; + throw_unless(41, (cs~load_uint(8) & ~ 0x20) == 0x53); + throw_unless(41, cs~load_uint(32) == 0x8e81278a); + return (cs~load_uint(256), cs~load_uint(64)); +} + +slice create_new_entry(cs) inline { + return begin_cell().store_int(false, 1).store_uint(0, 64).store_uint(0, 256).store_slice(cs).end_cell().begin_parse(); +} + +cell register_vote(vote_dict, action, cs, idx, weight) { + int hash = 0; + var entry = null(); + if (action & 1) { + hash = slice_hash(cs); + (entry, var found?) = vote_dict.udict_get?(256, hash); + ifnot (found?) { + entry = create_new_entry(cs); + } + } else { + hash = cs.preload_uint(256); + (entry, var found?) = vote_dict.udict_get?(256, hash); + throw_unless(42, found?); + } + return vote_dict; +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + int action = cs~load_uint(32); + int msg_seqno = cs~load_uint(32); + var valid_until = cs~load_uint(32); + throw_if(35, valid_until < now()); + var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data(); + throw_unless(33, msg_seqno == stored_seqno); + ifnot ((action - 0x566f7465) & -2) { + var idx = cs~load_uint(16); + var vdescr = get_validator_descr(idx); + var (val_pubkey, weight) = unpack_validator_descr(vdescr); + throw_unless(34, check_signature(slice_hash(in_msg), signature, val_pubkey)); + accept_message(); + stored_seqno += 1; + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + commit(); + vote_dict = register_vote(vote_dict, action, cs, idx, weight); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + return (); } - set_data(begin_cell().store_ref(cfg_dict).store_uint(stored_seqno + 1, 32).store_uint(public_key, 256).end_cell()); + throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); + accept_message(); + stored_seqno += 1; + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + commit(); + (cfg_dict, public_key) = perform_action(cfg_dict, public_key, action, cs); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); } () run_ticktock(int is_tock) impure { diff --git a/crypto/smartcont/elector-code.fc b/crypto/smartcont/elector-code.fc index b97b0b3ce4..fe000c14d4 100644 --- a/crypto/smartcont/elector-code.fc +++ b/crypto/smartcont/elector-code.fc @@ -408,7 +408,7 @@ _ compute_total_stake(l, n, m_stake) { return tot_stake; } -(cell, cell, cell, int, int) try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor) { +(cell, cell, int, cell, int, int) try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor) { var cs = 16.config_param().begin_parse(); var (max_validators, _, min_validators) = (cs~load_uint(16), cs~load_uint(16), cs~load_uint(16)); cs.end_parse(); @@ -435,7 +435,7 @@ _ compute_total_stake(l, n, m_stake) { } until (~ f); n = min(n, max_validators); if (n < min_validators) { - return (credits, new_dict(), new_dict(), 0, 0); + return (credits, new_dict(), 0, new_dict(), 0, 0); } var l = nil; do { @@ -464,7 +464,7 @@ _ compute_total_stake(l, n, m_stake) { } } until (i >= n); if ((m == 0) | (best_stake < min_total_stake)) { - return (credits, new_dict(), new_dict(), 0, 0); + return (credits, new_dict(), 0, new_dict(), 0, 0); } ;; we have to select first m validators from list l l1 = touch(l); @@ -476,6 +476,7 @@ _ compute_total_stake(l, n, m_stake) { ;; create both the new validator set and the refund set int i = 0; var tot_stake = 0; + var tot_weight = 0; var vset = new_dict(); var frozen = new_dict(); do { @@ -492,6 +493,7 @@ _ compute_total_stake(l, n, m_stake) { ;; validator_addr#73 public_key:SigPubKey weight:uint64 adnl_addr:bits256 = ValidatorDescr; var weight = (true_stake << 60) / best_stake; tot_stake += true_stake; + tot_weight += weight; var vinfo = begin_cell() .store_uint(adnl_addr ? 0x73 : 0x53, 8) ;; validator_addr#73 or validator#53 .store_uint(0x8e81278a, 32) ;; ed25519_pubkey#8e81278a @@ -514,7 +516,7 @@ _ compute_total_stake(l, n, m_stake) { i += 1; } until (l.null?()); throw_unless(49, tot_stake == best_stake); - return (credits, vset, frozen, tot_stake, m); + return (credits, vset, tot_weight, frozen, tot_stake, m); } int conduct_elections(ds, elect, credits) impure { @@ -545,7 +547,7 @@ int conduct_elections(ds, elect, credits) impure { ;; elections finished return false; } - (credits, var vdict, var frozen, var total_stakes, var cnt) = try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor); + (credits, var vdict, var total_weight, var frozen, var total_stakes, var cnt) = try_elect(credits, members, min_stake, max_stake, min_total_stake, max_stake_factor); ;; pack elections; if cnt==0, set failed=true, finished=false. failed = (cnt == 0); finished = ~ failed; @@ -561,12 +563,13 @@ int conduct_elections(ds, elect, credits) impure { var start = max(now() + elect_end_before - 60, elect_at); var main_validators = config_param(16).begin_parse().skip_bits(16).preload_uint(16); var vset = begin_cell() - .store_uint(0x11, 8) ;; validators#11 + .store_uint(0x12, 8) ;; validators_ext#12 .store_uint(start, 32) ;; utime_since:uint32 .store_uint(start + elect_for, 32) ;; utime_until:uint32 .store_uint(cnt, 16) ;; total:(## 16) - .store_uint(min(cnt, main_validators), 16) ;; main:(## 16) - .store_slice(vdict.begin_parse()) ;; list:(Hashmap 16 ValidatorDescr) + .store_uint(min(cnt, main_validators), 16) ;; main:(## 16) + .store_uint(total_weight, 64) ;; total_weight:uint64 + .store_dict(vdict) ;; list:(HashmapE 16 ValidatorDescr) .end_cell(); var config_addr = config_param(0).begin_parse().preload_uint(256); send_validator_set_to_config(config_addr, vset, elect_at); diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 70abe412e0..66d686241e 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -225,6 +225,22 @@ Masterchain swap "config-master" +suffix +".addr" save-address-verbose // Other data +/* + * + * Initial wallets (test) + * + */ + +// pubkey amount `create-wallet1` or pubkey amount `create-wallet2` +PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100000000 create-wallet1 +PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0 + +/* + * + * Create state + * + */ + create_state cr cr ."new state is:" cr dup B dup Bx. cr diff --git a/crypto/smartcont/multisig-code.fc b/crypto/smartcont/multisig-code.fc new file mode 100644 index 0000000000..cbe2d30feb --- /dev/null +++ b/crypto/smartcont/multisig-code.fc @@ -0,0 +1,265 @@ +;; Simple wallet smart contract + +_ unpack_state() inline_ref { + var ds = begin_parse(get_data()); + var res = (ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict()); + ds.end_parse(); + return res; +} + +_ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, int n) inline_ref { + return begin_cell() + .store_uint(n, 8) + .store_uint(k, 8) + .store_uint(last_cleaned, 64) + .store_dict(public_keys) + .store_dict(pending_queries) + .end_cell(); +} + +(int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref { + int cnt = 0; + + do { + slice cs = signatures.begin_parse(); + slice signature = cs~load_bits(512); + + int i = cs~load_uint(8); + signatures = cs~load_dict(); + + (slice public_key, var found?) = public_keys.udict_get?(8, i); + throw_unless(37, found?); + throw_unless(38, check_signature(hash, signature, public_key.preload_uint(256))); + + int mask = (1 << i); + int old_cnt_bits = cnt_bits; + cnt_bits |= mask; + int should_check = cnt_bits != old_cnt_bits; + cnt -= should_check; + } until (cell_null?(signatures)); + + return (cnt, cnt_bits); +} + + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +(int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?) inline_ref { + if (found?) { + throw_unless(35, query~load_int(1)); + (int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(n), query); + throw_unless(36, slice_hash(msg) == slice_hash(in_msg)); + return (cnt, cnt_bits, msg); + } + return (0, 0, in_msg); +} + +() try_init() impure inline_ref { + ;; first query without signatures is always accepted + (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state(); + throw_if(37, last_cleaned); + accept_message(); + set_data(pack_state(pending_queries, public_keys, 1, k, n)); +} + +cell update_pending_queries(cell pending_queries, slice msg, int query_id, int cnt, int cnt_bits, int n, int k) impure inline_ref { + if (cnt >= k) { + while (msg.slice_refs()) { + var mode = msg~load_uint(8); + send_raw_message(msg~load_ref(), mode); + } + pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1)); + } else { + pending_queries~udict_set_builder(64, query_id, begin_cell() + .store_uint(1, 1) + .store_uint(cnt, 8) + .store_uint(cnt_bits, n) + .store_slice(msg)); + } + return pending_queries; +} + +() recv_external(slice in_msg) impure { + ;; empty message triggers init + if (slice_empty?(in_msg)) { + return try_init(); + } + + ;; Check root signature + slice root_signature = in_msg~load_bits(512); + int root_hash = slice_hash(in_msg); + int root_i = in_msg~load_uint(8); + + (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state(); + last_cleaned -= last_cleaned == 0; + + (slice public_key, var found?) = public_keys.udict_get?(8, root_i); + throw_unless(31, found?); + throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256))); + + cell signatures = in_msg~load_dict(); + + var hash = slice_hash(in_msg); + int query_id = in_msg~load_uint(64); + + var bound = (now() << 32); + throw_if(33, query_id < bound); + + (slice query, var found?) = pending_queries.udict_get?(64, query_id); + (int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?); + int mask = 1 << root_i; + throw_if(34, cnt_bits & mask); + cnt_bits |= mask; + cnt += 1; + + ;; TODO: reserve some gas or FAIL + accept_message(); + + pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k); + set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n)); + + commit(); + + int need_save = 0; + ifnot (cell_null?(signatures) | (cnt >= k)) { + (int new_cnt, cnt_bits) = check_signatures(public_keys, signatures, hash, cnt_bits); + cnt += new_cnt; + pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k); + need_save = -1; + } + + bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago + int old_last_cleaned = last_cleaned; + do { + var (pending_queries', i, _, f) = pending_queries.udict_delete_get_min(64); + f~touch(); + if (f) { + f = (i < bound); + } + if (f) { + pending_queries = pending_queries'; + last_cleaned = i; + need_save = -1; + } + } until (~ f); + + if (need_save) { + set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n)); + } +} + +;; Get methods +;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten) +(int, int) get_query_state(int query_id) method_id { + (int n, _, int last_cleaned, _, cell pending_queries) = unpack_state(); + (slice cs, var found) = pending_queries.udict_get?(64, query_id); + if (found) { + if (cs~load_int(1)) { + cs~load_uint(8); + return (0, cs~load_uint(n)); + } else { + return (-1, 0); + } + } else { + return (-(query_id <= last_cleaned), 0); + } +} + +int processed?(int query_id) method_id { + (int x, _) = get_query_state(query_id); + return x; +} + +cell create_init_state(int n, int k, cell public_keys) method_id { + return pack_state(new_dict(), public_keys, 0, k, n); +} + +cell merge_list(cell a, cell b) { + if (cell_null?(a)) { + return b; + } + if (cell_null?(b)) { + return a; + } + slice as = a.begin_parse(); + if (as.slice_refs() != 0) { + cell tail = merge_list(as~load_ref(), b); + return begin_cell().store_slice(as).store_ref(tail).end_cell(); + } + + as~skip_last_bits(1); + ;; as~skip_bits(1); + return begin_cell().store_slice(as).store_dict(b).end_cell(); + +} + +cell get_public_keys() method_id { + (_, _, _, cell public_keys, _) = unpack_state(); + return public_keys; +} + +(int, int) check_query_signatures(cell query) method_id { + slice cs = query.begin_parse(); + slice root_signature = cs~load_bits(512); + int root_hash = slice_hash(cs); + int root_i = cs~load_uint(8); + + cell public_keys = get_public_keys(); + (slice public_key, var found?) = public_keys.udict_get?(8, root_i); + throw_unless(31, found?); + throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256))); + + int mask = 1 << root_i; + + cell signatures = cs~load_dict(); + if (cell_null?(signatures)) { + return (1, mask); + } + (int cnt, mask) = check_signatures(public_keys, signatures, slice_hash(cs), mask); + return (cnt + 1, mask); +} + +cell messages_by_mask(int mask) method_id { + (int n, _, _, _, cell pending_queries) = unpack_state(); + int i = -1; + cell a = new_dict(); + do { + (i, var cs, var f) = pending_queries.udict_get_next?(64, i); + if (f) { + if (cs~load_int(1)) { + int cnt_bits = cs.skip_bits(8).preload_uint(n); + if (cnt_bits & mask) { + a~udict_set_builder(64, i, begin_cell().store_slice(cs)); + } + } + } + } until (~ f); + return a; +} + +cell get_messages_unsigned_by_id(int id) method_id { + return messages_by_mask(1 << id); +} + +cell get_messages_unsigned() method_id { + return messages_by_mask(~ 0); +} + +(int, int) get_n_k() method_id { + (int n, int k, _, _, _) = unpack_state(); + return (n, k); +} + +cell merge_inner_queries(cell a, cell b) method_id { + slice ca = a.begin_parse(); + slice cb = b.begin_parse(); + cell list_a = ca~load_dict(); + cell list_b = cb~load_dict(); + throw_unless(31, slice_hash(ca) == slice_hash(cb)); + return begin_cell() + .store_dict(merge_list(list_a, list_b)) + .store_slice(ca) + .end_cell(); +} diff --git a/crypto/smartcont/simple-wallet-ext-code.fc b/crypto/smartcont/simple-wallet-ext-code.fc new file mode 100644 index 0000000000..a20b96a861 --- /dev/null +++ b/crypto/smartcont/simple-wallet-ext-code.fc @@ -0,0 +1,67 @@ +;; Simple wallet smart contract + +cell create_state(int seqno, int public_key) { + return begin_cell().store_uint(seqno, 32).store_uint(public_key, 256).end_cell(); +} + +(int, int) load_state() { + var cs2 = begin_parse(get_data()); + return (cs2~load_uint(32), cs2~load_uint(256)); +} + +() save_state(int seqno, int public_key) impure { + set_data(create_state(seqno, public_key)); +} + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +slice do_verify_message(slice in_msg, int seqno, int public_key) { + var signature = in_msg~load_bits(512); + var cs = in_msg; + int msg_seqno = cs~load_uint(32); + throw_unless(33, msg_seqno == seqno); + throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); + return cs; +} + +() recv_external(slice in_msg) impure { + (int stored_seqno, int public_key) = load_state(); + var cs = do_verify_message(in_msg, stored_seqno, public_key); + accept_message(); + cs~touch_slice(); + if (cs.slice_refs()) { + var mode = cs~load_uint(8); + send_raw_message(cs~load_ref(), mode); + } + cs.end_parse(); + save_state(stored_seqno + 1, public_key); +} + +;; Get methods + +int seqno() method_id { + return get_data().begin_parse().preload_uint(32); +} + +cell create_init_state(int public_key) method_id { + return create_state(0, public_key); +} + +cell prepare_send_message_with_seqno(int mode, cell msg, int seqno) method_id { + return begin_cell().store_uint(seqno, 32).store_uint(mode, 8).store_ref(msg).end_cell(); +} + +cell prepare_send_message(int mode, cell msg) method_id { + return prepare_send_message_with_seqno(mode, msg, seqno()); +} + + +slice verify_message(slice msg) method_id { + var (stored_seqno, public_key) = load_state(); + return do_verify_message(msg, stored_seqno, public_key); +} + + + diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index c60d96c92e..0798a0a1b5 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -16,6 +16,7 @@ forall X -> X first(tuple t) asm "FIRST"; forall X -> X second(tuple t) asm "SECOND"; forall X -> X third(tuple t) asm "THIRD"; forall X -> X fourth(tuple t) asm "3 INDEX"; +forall X -> X null() asm "PUSHNULL"; int now() asm "NOW"; slice my_address() asm "MYADDR"; @@ -34,8 +35,10 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI cell get_data() asm "c4 PUSH"; () set_data(cell c) impure asm "c4 POP"; +cell get_c3() impure asm "c3 PUSH"; () set_c3(cell c) impure asm "c3 POP"; () accept_message() impure asm "ACCEPT"; +() commit() impure asm "COMMIT"; int min(int x, int y) asm "MIN"; int max(int x, int y) asm "MAX"; @@ -52,7 +55,10 @@ cell preload_ref(slice s) asm "PLDREF"; ;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; (slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS"; slice skip_bits(slice s, int len) asm "SDSKIPFIRST"; +(slice, ()) ~skip_bits(slice s, int len) asm "SDSKIPFIRST"; slice first_bits(slice s, int len) asm "SDCUTFIRST"; +slice skip_last_bits(slice s, int len) asm "SDSKIPLAST"; +(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST"; (slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT"; cell preload_dict(slice s) asm "PLDDICT"; slice skip_dict(slice s) asm "SKIPDICT"; @@ -94,6 +100,16 @@ cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "D (cell, slice, int) udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; (cell, (slice, int)) ~idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT"; (cell, (slice, int)) ~udict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGET" "NULLSWAPIFNOT"; +cell udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; +(cell, ()) ~udict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSET"; +cell idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; +(cell, ()) ~idict_set(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISET"; +cell dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +(cell, ()) ~dict_set(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSET"; +(cell, int) udict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADD"; +(cell, int) udict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACE"; +(cell, int) idict_add?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADD"; +(cell, int) idict_replace?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACE"; cell udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; (cell, ()) ~udict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETB"; cell idict_set_builder(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETB"; diff --git a/crypto/smc-envelope/GenericAccount.cpp b/crypto/smc-envelope/GenericAccount.cpp new file mode 100644 index 0000000000..20750fcbad --- /dev/null +++ b/crypto/smc-envelope/GenericAccount.cpp @@ -0,0 +1,99 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "GenericAccount.h" + +#include "block/block-auto.h" +#include "block/block-parse.h" +namespace ton { +td::Ref GenericAccount::get_init_state(td::Ref code, td::Ref data) noexcept { + return vm::CellBuilder() + .store_zeroes(2) + .store_ones(2) + .store_zeroes(1) + .store_ref(std::move(code)) + .store_ref(std::move(data)) + .finalize(); +} +block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, + const td::Ref& init_state) noexcept { + return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/); +} + +void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms) { + td::BigInt256 dest_addr; + dest_addr.import_bits(dest_address.addr.as_bitslice()); + cb.store_zeroes(1) + .store_ones(1) + .store_long(dest_address.bounceable, 1) + .store_zeroes(3) + .store_ones(1) + .store_zeroes(2) + .store_long(dest_address.workchain, 8) + .store_int256(dest_addr, 256); + block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); + cb.store_zeroes(9 + 64 + 32 + 1 + 1); +} + +td::Ref GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref new_state, + td::Ref body) noexcept { + block::gen::Message::Record message; + /*info*/ { + block::gen::CommonMsgInfo::Record_ext_in_msg_info info; + /* src */ + tlb::csr_pack(info.src, block::gen::MsgAddressExt::Record_addr_none{}); + /* dest */ { + block::gen::MsgAddressInt::Record_addr_std dest; + dest.anycast = vm::CellBuilder().store_zeroes(1).as_cellslice_ref(); + dest.workchain_id = address.workchain; + dest.address = address.addr; + + tlb::csr_pack(info.dest, dest); + } + /* import_fee */ { + vm::CellBuilder cb; + block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(0)); + info.import_fee = cb.as_cellslice_ref(); + } + + tlb::csr_pack(message.info, info); + } + /* init */ { + if (new_state.not_null()) { + // Just(Left(new_state)) + message.init = vm::CellBuilder() + .store_ones(1) + .store_zeroes(1) + .append_cellslice(vm::load_cell_slice(new_state)) + .as_cellslice_ref(); + } else { + message.init = vm::CellBuilder().store_zeroes(1).as_cellslice_ref(); + CHECK(message.init.not_null()); + } + } + /* body */ { + message.body = vm::CellBuilder().store_zeroes(1).append_cellslice(vm::load_cell_slice_ref(body)).as_cellslice_ref(); + } + + td::Ref res; + tlb::type_pack_cell(res, block::gen::t_Message_Any, message); + CHECK(res.not_null()); + + return res; +} +} // namespace ton diff --git a/crypto/smc-envelope/GenericAccount.h b/crypto/smc-envelope/GenericAccount.h new file mode 100644 index 0000000000..003b8e1b76 --- /dev/null +++ b/crypto/smc-envelope/GenericAccount.h @@ -0,0 +1,31 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once +#include "vm/cells.h" +#include "block/block.h" +namespace ton { +class GenericAccount { + public: + static td::Ref get_init_state(td::Ref code, td::Ref data) noexcept; + static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) noexcept; + static td::Ref create_ext_message(const block::StdAddress& address, td::Ref new_state, + td::Ref body) noexcept; + static void store_int_message(vm::CellBuilder& cb, const block::StdAddress& dest_address, td::int64 gramms); +}; +} // namespace ton diff --git a/crypto/smc-envelope/MultisigWallet.cpp b/crypto/smc-envelope/MultisigWallet.cpp new file mode 100644 index 0000000000..36830179c5 --- /dev/null +++ b/crypto/smc-envelope/MultisigWallet.cpp @@ -0,0 +1,171 @@ +#include "MultisigWallet.h" + +#include "SmartContractCode.h" + +#include "vm/dict.h" + +#include "td/utils/misc.h" + +namespace ton { + +MultisigWallet::QueryBuilder::QueryBuilder(td::int64 query_id, td::Ref msg, int mode) { + msg_ = vm::CellBuilder().store_long(query_id, 64).store_long(mode, 8).store_ref(std::move(msg)).finalize(); +} +void MultisigWallet::QueryBuilder::sign(td::int32 id, td::Ed25519::PrivateKey& pk) { + CHECK(id < td::narrow_cast(mask_.size())); + auto signature = pk.sign(msg_->get_hash().as_slice()).move_as_ok(); + mask_.set(id); + vm::CellBuilder cb; + cb.store_bytes(signature.as_slice()); + cb.store_long(id, 8); + cb.ensure_throw(cb.store_maybe_ref(std::move(dict_))); + dict_ = cb.finalize(); +} + +td::Ref MultisigWallet::QueryBuilder::create_inner() const { + vm::CellBuilder cb; + cb.ensure_throw(cb.store_maybe_ref(dict_)); + return cb.append_cellslice(vm::load_cell_slice(msg_)).finalize(); +} + +td::Ref MultisigWallet::QueryBuilder::create(td::int32 id, td::Ed25519::PrivateKey& pk) const { + auto cell = create_inner(); + vm::CellBuilder cb; + cb.store_long(id, 8); + cb.append_cellslice(vm::load_cell_slice(cell)); + cell = cb.finalize(); + + auto signature = pk.sign(cell->get_hash().as_slice()).move_as_ok(); + vm::CellBuilder cb2; + cb2.store_bytes(signature.as_slice()); + cb2.append_cellslice(vm::load_cell_slice(cell)); + return cb2.finalize(); +} + +td::Ref MultisigWallet::create(td::Ref data) { + return td::Ref(true, State{ton::SmartContractCode::multisig(), std::move(data)}); +} + +int MultisigWallet::processed(td::uint64 query_id) const { + auto res = run_get_method("processed?", {td::make_refint(query_id)}); + return res.stack.write().pop_smallint_range(1, -1); +} + +MultisigWallet::QueryState MultisigWallet::get_query_state(td::uint64 query_id) const { + auto ans = run_get_method("get_query_state", {td::make_refint(query_id)}); + + auto mask = ans.stack.write().pop_int(); + auto state = ans.stack.write().pop_smallint_range(1, -1); + + QueryState res; + if (state == 1) { + res.state = QueryState::Unknown; + } else if (state == 0) { + res.state = QueryState::NotReady; + for (size_t i = 0; i < res.mask.size(); i++) { + if (mask->get_bit(static_cast(i))) { + res.mask.set(i); + } + } + } else { + res.state = QueryState::Sent; + } + return res; +} + +std::vector MultisigWallet::get_public_keys() const { + auto ans = run_get_method("get_public_keys"); + auto dict_root = ans.stack.write().pop_cell(); + vm::Dictionary dict(std::move(dict_root), 8); + std::vector res; + dict.check_for_each([&](auto cs, auto x, auto y) { + td::SecureString key(32); + cs->prefetch_bytes(key.as_mutable_slice().ubegin(), td::narrow_cast(key.size())); + res.push_back(std::move(key)); + return true; + }); + return res; +} + +td::Ref MultisigWallet::create_init_data(std::vector public_keys, int k) const { + vm::Dictionary pk(8); + for (size_t i = 0; i < public_keys.size(); i++) { + auto key = pk.integer_key(td::make_refint(i), 8, false); + pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice())); + } + auto res = run_get_method("create_init_state", + {td::make_refint(public_keys.size()), td::make_refint(k), pk.get_root_cell()}); + CHECK(res.code == 0); + return res.stack.write().pop_cell(); +} + +td::Ref MultisigWallet::create_init_data_fast(std::vector public_keys, int k) { + vm::Dictionary pk(8); + for (size_t i = 0; i < public_keys.size(); i++) { + auto key = pk.integer_key(td::make_refint(i), 8, false); + pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice())); + } + + vm::CellBuilder cb; + cb.store_long(public_keys.size(), 8).store_long(k, 8).store_long(0, 64); + cb.ensure_throw(cb.store_maybe_ref(pk.get_root_cell())); + cb.ensure_throw(cb.store_maybe_ref({})); + return cb.finalize(); +} + +td::Ref MultisigWallet::merge_queries(td::Ref a, td::Ref b) const { + auto res = run_get_method("merge_queries", {a, b}); + return res.stack.write().pop_cell(); +} + +MultisigWallet::Mask MultisigWallet::to_mask(td::RefInt256 mask) const { + Mask res_mask; + for (size_t i = 0; i < res_mask.size(); i++) { + if (mask->get_bit(static_cast(i))) { + res_mask.set(i); + } + } + return res_mask; +} + +std::pair MultisigWallet::check_query_signatures(td::Ref a) const { + auto ans = run_get_method("check_query_signatures", {a}); + + auto mask = ans.stack.write().pop_int(); + auto cnt = ans.stack.write().pop_smallint_range(128); + return std::make_pair(cnt, to_mask(mask)); +} + +std::pair MultisigWallet::get_n_k() const { + auto ans = run_get_method("get_n_k"); + auto k = ans.stack.write().pop_smallint_range(128); + auto n = ans.stack.write().pop_smallint_range(128); + return std::make_pair(n, k); +} + +std::vector MultisigWallet::get_unsigned_messaged(int id) const { + SmartContract::Answer ans; + if (id == -1) { + ans = run_get_method("get_messages_unsigned"); + } else { + ans = run_get_method("get_messages_unsigned_by_id", {td::make_refint(id)}); + } + auto n_k = get_n_k(); + + auto cell = ans.stack.write().pop_maybe_cell(); + vm::Dictionary dict(std::move(cell), 64); + std::vector res; + dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) { + cs.write().skip_first(8); + Message message; + td::BigInt256 query_id; + query_id.import_bits(ptr, ptr_bits, false); + message.query_id = static_cast(query_id.to_long()); + message.signed_by = to_mask(cs.write().fetch_int256(n_k.first, false)); + message.message = cs.write().fetch_ref(); + res.push_back(std::move(message)); + return true; + }); + return res; +} +} // namespace ton diff --git a/crypto/smc-envelope/MultisigWallet.h b/crypto/smc-envelope/MultisigWallet.h new file mode 100644 index 0000000000..10c6d07604 --- /dev/null +++ b/crypto/smc-envelope/MultisigWallet.h @@ -0,0 +1,64 @@ +#pragma once +#include "vm/cells.h" + +#include "SmartContract.h" +#include "Ed25519.h" + +#include + +namespace ton { +class MultisigWallet : public ton::SmartContract { + public: + MultisigWallet(State state) : SmartContract(std::move(state)) { + } + + using Mask = std::bitset<128>; + struct QueryState { + enum State { Unknown, NotReady, Sent } state = Unknown; + Mask mask; + }; + + class QueryBuilder { + public: + QueryBuilder(td::int64 query_id, td::Ref msg, int mode = 3); + void sign(td::int32 id, td::Ed25519::PrivateKey& pk); + + td::Ref create_inner() const; + td::Ref create(td::int32 id, td::Ed25519::PrivateKey& pk) const; + Mask get_mask() const { + return mask_; + } + + private: + vm::Ref dict_; + td::Ref msg_; + Mask mask_; + }; + + MultisigWallet* make_copy() const override { + return new MultisigWallet{state_}; + } + + // creation + static td::Ref create(td::Ref data = {}); + + td::Ref create_init_data(std::vector public_keys, int k) const; + static td::Ref create_init_data_fast(std::vector public_keys, int k); + + // get methods + int processed(td::uint64 query_id) const; + QueryState get_query_state(td::uint64 query_id) const; + std::vector get_public_keys() const; + td::Ref merge_queries(td::Ref a, td::Ref b) const; + std::pair check_query_signatures(td::Ref a) const; + std::pair get_n_k() const; + Mask to_mask(td::RefInt256 mask) const; + + struct Message { + td::uint64 query_id; + Mask signed_by; + td::Ref message; + }; + std::vector get_unsigned_messaged(int id = -1) const; +}; +} // namespace ton diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp new file mode 100644 index 0000000000..ea76423763 --- /dev/null +++ b/crypto/smc-envelope/SmartContract.cpp @@ -0,0 +1,188 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "SmartContract.h" + +#include "GenericAccount.h" + +#include "block/block.h" +#include "block/block-auto.h" +#include "vm/cellslice.h" +#include "vm/cp0.h" +#include "vm/continuation.h" + +#include "td/utils/crypto.h" + +namespace ton { +namespace { +td::int32 get_method_id(td::Slice method_name) { + unsigned crc = td::crc16(method_name); + return (crc & 0xffff) | 0x10000; +} +td::Ref prepare_vm_stack(td::Ref body) { + td::Ref stack_ref{true}; + td::RefInt256 acc_addr{true}; + //CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256)); + vm::Stack& stack = stack_ref.write(); + stack.push_int(td::RefInt256{true, 10000000000}); + stack.push_int(td::RefInt256{true, 10000000000}); + stack.push_cell(vm::CellBuilder().finalize()); + stack.push_cellslice(std::move(body)); + return stack_ref; +} + +td::Ref prepare_vm_c7() { + // TODO: fix initialization of c7 + td::BitArray<256> rand_seed; + rand_seed.as_slice().fill(0); + td::RefInt256 rand_seed_int{true}; + rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false); + auto tuple = vm::make_tuple_ref( + td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea + td::make_refint(0), // actions:Integer + td::make_refint(0), // msgs_sent:Integer + td::make_refint(0), // unixtime:Integer + td::make_refint(0), // block_lt:Integer + td::make_refint(0), // trans_lt:Integer + std::move(rand_seed_int), // rand_seed:Integer + block::CurrencyCollection(1000000000).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] + vm::load_cell_slice_ref(vm::CellBuilder().finalize()) // myself:MsgAddressInt + //vm::StackEntry::maybe(td::Ref()) + ); // global_config:(Maybe Cell) ] = SmartContractInfo; + //LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); + return vm::make_tuple_ref(std::move(tuple)); +} + +SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref stack, td::Ref c7, + vm::GasLimits gas, bool ignore_chksig) { + auto gas_credit = gas.gas_credit; + vm::init_op_cp0(); + vm::DictionaryBase::get_empty_dictionary(); + + class Logger : public td::LogInterface { + public: + void append(td::CSlice slice) override { + res.append(slice.data(), slice.size()); + } + std::string res; + }; + Logger logger; + vm::VmLog log{&logger, td::LogOptions::plain()}; + + if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) { + log.log_options.level = 4; + log.log_options.fix_newlines = true; + log.log_mask |= vm::VmLog::DumpStack; + } else { + log.log_options.level = 0; + log.log_mask = 0; + } + + SmartContract::Answer res; + vm::VmState vm{state.code, std::move(stack), gas, 1, state.data, log}; + vm.set_c7(std::move(c7)); + vm.set_chksig_always_succeed(ignore_chksig); + try { + res.code = ~vm.run(); + } catch (...) { + LOG(FATAL) << "catch unhandled exception"; + } + res.new_state = std::move(state); + res.stack = vm.get_stack_ref(); + gas = vm.get_gas_limits(); + res.gas_used = gas.gas_consumed(); + res.accepted = gas.gas_credit == 0; + res.success = (res.accepted && (unsigned)res.code <= 1); + if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) { + LOG(DEBUG) << "VM log\n" << logger.res; + std::ostringstream os; + res.stack->dump(os); + LOG(DEBUG) << "VM stack:\n" << os.str(); + LOG(DEBUG) << "VM exit code: " << res.code; + LOG(DEBUG) << "VM accepted: " << res.accepted; + LOG(DEBUG) << "VM success: " << res.success; + } + if (res.success) { + res.new_state.data = vm.get_c4(); + res.actions = vm.get_d(5); + } + LOG_IF(ERROR, gas_credit != 0 && (res.accepted && !res.success)) + << "Accepted but failed with code " << res.code << "\n" + << res.gas_used << "\n"; + return res; +} +} // namespace + +td::Ref SmartContract::empty_slice() { + return vm::load_cell_slice_ref(vm::CellBuilder().finalize()); +} + +size_t SmartContract::code_size() const { + return vm::std_boc_serialize(state_.code).ok().size(); +} +size_t SmartContract::data_size() const { + return vm::std_boc_serialize(state_.data).ok().size(); +} + +block::StdAddress SmartContract::get_address(WorkchainId workchain_id) const { + return GenericAccount::get_address(workchain_id, get_init_state()); +} + +td::Ref SmartContract::get_init_state() const { + return GenericAccount::get_init_state(get_state().code, get_state().data); +} + +SmartContract::Answer SmartContract::run_method(Args args) { + if (!args.c7) { + args.c7 = prepare_vm_c7(); + } + if (!args.limits) { + args.limits = vm::GasLimits{(long long)0, (long long)1000000, (long long)10000}; + } + CHECK(args.stack); + CHECK(args.method_id); + args.stack.value().write().push_smallint(args.method_id.unwrap()); + auto res = + run_smartcont(get_state(), args.stack.unwrap(), args.c7.unwrap(), args.limits.unwrap(), args.ignore_chksig); + state_ = res.new_state; + return res; +} + +SmartContract::Answer SmartContract::run_get_method(Args args) const { + if (!args.c7) { + args.c7 = prepare_vm_c7(); + } + if (!args.limits) { + args.limits = vm::GasLimits{1000000}; + } + if (!args.stack) { + args.stack = td::Ref(true); + } + CHECK(args.method_id); + args.stack.value().write().push_smallint(args.method_id.unwrap()); + return run_smartcont(get_state(), args.stack.unwrap(), args.c7.unwrap(), args.limits.unwrap(), args.ignore_chksig); +} + +SmartContract::Answer SmartContract::run_get_method(td::Slice method, Args args) const { + return run_get_method(args.set_method_id(method)); +} + +SmartContract::Answer SmartContract::send_external_message(td::Ref cell, Args args) { + return run_method(args.set_stack(prepare_vm_stack(vm::load_cell_slice_ref(cell))).set_method_id(-1)); +} +} // namespace ton diff --git a/crypto/smc-envelope/SmartContract.h b/crypto/smc-envelope/SmartContract.h new file mode 100644 index 0000000000..d586436195 --- /dev/null +++ b/crypto/smc-envelope/SmartContract.h @@ -0,0 +1,116 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once + +#include "vm/cells.h" +#include "vm/stack.hpp" +#include "vm/continuation.h" + +#include "td/utils/optional.h" +#include "td/utils/crypto.h" + +#include "block/block.h" + +namespace ton { +class SmartContract : public td::CntObject { + static td::Ref empty_slice(); + + public: + struct State { + td::Ref code; + td::Ref data; + }; + + SmartContract(State state) : state_(std::move(state)) { + } + + struct Answer { + SmartContract::State new_state; + bool accepted; + bool success; + td::Ref stack; + td::Ref actions; + td::int32 code; + td::int64 gas_used; + }; + + struct Args { + td::optional method_id; + td::optional limits; + td::optional> c7; + td::optional> stack; + bool ignore_chksig{false}; + + Args() { + } + Args(std::initializer_list stack) + : stack(td::Ref(true, std::vector(std::move(stack)))) { + } + Args&& set_method_id(td::Slice method_name) { + unsigned crc = td::crc16(method_name); + return set_method_id((crc & 0xffff) | 0x10000); + } + Args&& set_method_id(td::int32 method_id) { + this->method_id = method_id; + return std::move(*this); + } + Args&& set_limits(vm::GasLimits limits) { + this->limits = std::move(limits); + return std::move(*this); + } + Args&& set_c7(td::Ref c7) { + this->c7 = std::move(c7); + return std::move(*this); + } + Args&& set_stack(std::vector stack) { + this->stack = td::Ref(true, std::move(stack)); + return std::move(*this); + } + Args&& set_stack(td::Ref stack) { + this->stack = std::move(stack); + return std::move(*this); + } + Args&& set_ignore_chksig(bool ignore_chksig) { + this->ignore_chksig = ignore_chksig; + return std::move(*this); + } + }; + + Answer run_method(Args args = {}); + Answer run_get_method(Args args = {}) const; + Answer run_get_method(td::Slice method, Args args = {}) const; + Answer send_external_message(td::Ref cell, Args args = {}); + + size_t code_size() const; + size_t data_size() const; + static td::Ref create(State state) { + return td::Ref{true, std::move(state)}; + } + + block::StdAddress get_address(WorkchainId workchain_id = basechainId) const; + td::Ref get_init_state() const; + + const State& get_state() const { + return state_; + } + + protected: + State state_; +}; +} // namespace ton diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp new file mode 100644 index 0000000000..c187131d71 --- /dev/null +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -0,0 +1,72 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "SmartContractCode.h" + +#include "vm/boc.h" +#include + +#include "td/utils/base64.h" + +namespace ton { +namespace { +const auto& get_map() { + static auto map = [] { + class Cmp : public std::less<> { + public: + using is_transparent = void; + }; + std::map, Cmp> map; + auto with_tvm_code = [&](auto name, td::Slice code_str) { + map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok(); + }; +#include "smartcont/auto/multisig-code.cpp" +#include "smartcont/auto/simple-wallet-ext-code.cpp" +#include "smartcont/auto/simple-wallet-code.cpp" +#include "smartcont/auto/wallet-code.cpp" + return map; + }(); + return map; +} +} // namespace + +td::Result> SmartContractCode::load(td::Slice name) { + auto& map = get_map(); + auto it = map.find(name); + if (it == map.end()) { + return td::Status::Error(PSLICE() << "Can't load td::refsecond; +} +td::Ref SmartContractCode::multisig() { + auto res = load("multisig").move_as_ok(); + return res; +} +td::Ref SmartContractCode::wallet() { + auto res = load("wallet").move_as_ok(); + return res; +} +td::Ref SmartContractCode::simple_wallet() { + auto res = load("simple-wallet").move_as_ok(); + return res; +} +td::Ref SmartContractCode::simple_wallet_ext() { + static auto res = load("simple-wallet-ext").move_as_ok(); + return res; +} +} // namespace ton diff --git a/crypto/smc-envelope/SmartContractCode.h b/crypto/smc-envelope/SmartContractCode.h new file mode 100644 index 0000000000..059215e445 --- /dev/null +++ b/crypto/smc-envelope/SmartContractCode.h @@ -0,0 +1,30 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "vm/cells.h" + +namespace ton { +class SmartContractCode { + public: + static td::Result> load(td::Slice name); + static td::Ref multisig(); + static td::Ref wallet(); + static td::Ref simple_wallet(); + static td::Ref simple_wallet_ext(); +}; +} // namespace ton diff --git a/crypto/smc-envelope/TestGiver.cpp b/crypto/smc-envelope/TestGiver.cpp new file mode 100644 index 0000000000..2d44d73073 --- /dev/null +++ b/crypto/smc-envelope/TestGiver.cpp @@ -0,0 +1,62 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "TestGiver.h" +#include "GenericAccount.h" + +#include "td/utils/base64.h" + +namespace ton { +const block::StdAddress& TestGiver::address() noexcept { + static block::StdAddress res = + block::StdAddress::parse("kf_8uRo6OBbQ97jCx2EIuKm8Wmt6Vb15-KsQHFLbKSMiYIny").move_as_ok(); + //static block::StdAddress res = + //block::StdAddress::parse("kf9tswzQaryeJ4aAYLy_phLhx4afF1aEvpUVak-2BuA0CmZi").move_as_ok(); + return res; +} + +vm::CellHash TestGiver::get_init_code_hash() noexcept { + return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok()); + //return vm::CellHash::from_slice(td::base64_decode("YV/IANhoI22HVeatFh6S5LbCHp+5OilARfzW+VQPZgQ=").move_as_ok()); +} + +td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, + const block::StdAddress& dest_address) noexcept { + 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(); + return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); +} + +td::Result TestGiver::get_seqno() const { + return TRY_VM(get_seqno_or_throw()); +} + +td::Result TestGiver::get_seqno_or_throw() const { + if (state_.data.is_null()) { + return 0; + } + auto seqno = vm::load_cell_slice(state_.data).fetch_ulong(32); + if (seqno == vm::CellSlice::fetch_ulong_eof) { + return td::Status::Error("Failed to parse seq_no"); + } + return static_cast(seqno); +} +} // namespace ton diff --git a/crypto/smc-envelope/TestGiver.h b/crypto/smc-envelope/TestGiver.h new file mode 100644 index 0000000000..210b691240 --- /dev/null +++ b/crypto/smc-envelope/TestGiver.h @@ -0,0 +1,39 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once +#include "SmartContract.h" +#include "block/block.h" +#include "vm/cells/CellString.h" +namespace ton { +class TestGiver : public SmartContract { + public: + explicit TestGiver(State state) : ton::SmartContract(std::move(state)) { + } + static constexpr unsigned max_message_size = vm::CellString::max_bytes; + static const block::StdAddress& address() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, + const block::StdAddress& dest_address) noexcept; + + td::Result get_seqno() const; + + private: + td::Result get_seqno_or_throw() const; +}; +} // namespace ton diff --git a/crypto/smc-envelope/TestWallet.cpp b/crypto/smc-envelope/TestWallet.cpp new file mode 100644 index 0000000000..1edf59f911 --- /dev/null +++ b/crypto/smc-envelope/TestWallet.cpp @@ -0,0 +1,91 @@ +/* + 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 . +Copyright 2017-2019 Telegram Systems LLP +*/ +#include "TestWallet.h" +#include "GenericAccount.h" + +#include "vm/boc.h" +#include "td/utils/base64.h" + +namespace ton { +td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { + auto code = get_init_code(); + auto data = get_init_data(public_key); + return GenericAccount::get_init_state(std::move(code), std::move(data)); +} + +td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { + std::string seq_no(4, 0); + auto signature = + private_key.sign(vm::CellBuilder().store_bytes(seq_no).finalize()->get_hash().as_slice()).move_as_ok(); + return vm::CellBuilder().store_bytes(signature).store_bytes(seq_no).finalize(); +} + +td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, + 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(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 TestWallet::get_init_code() noexcept { + static auto res = [] { + auto serialized_code = td::base64_decode( + "te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/" + "0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==") + .move_as_ok(); + return vm::std_boc_deserialize(serialized_code).move_as_ok(); + }(); + return res; +} + +vm::CellHash TestWallet::get_init_code_hash() noexcept { + return get_init_code()->get_hash(); +} + +td::Ref TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { + return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); +} + +td::Result TestWallet::get_seqno() const { + return TRY_VM(get_seqno_or_throw()); +} + +td::Result TestWallet::get_seqno_or_throw() const { + if (state_.data.is_null()) { + return 0; + } + auto seqno = vm::load_cell_slice(state_.data).fetch_ulong(32); + if (seqno == vm::CellSlice::fetch_ulong_eof) { + return td::Status::Error("Failed to parse seq_no"); + } + return static_cast(seqno); +} + +} // namespace ton diff --git a/crypto/smc-envelope/TestWallet.h b/crypto/smc-envelope/TestWallet.h new file mode 100644 index 0000000000..161aef58b5 --- /dev/null +++ b/crypto/smc-envelope/TestWallet.h @@ -0,0 +1,48 @@ +/* + 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 . + + 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 TestWallet : public ton::SmartContract { + public: + explicit TestWallet(State state) : ton::SmartContract(std::move(state)) { + } + static constexpr unsigned max_message_size = vm::CellString::max_bytes; + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; + static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, + td::int64 gramms, td::Slice message, + const block::StdAddress& dest_address) noexcept; + + static td::Ref get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; + + td::Result get_seqno() const; + + private: + td::Result get_seqno_or_throw() const; +}; +} // namespace ton diff --git a/crypto/smc-envelope/Wallet.cpp b/crypto/smc-envelope/Wallet.cpp new file mode 100644 index 0000000000..61958ed959 --- /dev/null +++ b/crypto/smc-envelope/Wallet.cpp @@ -0,0 +1,100 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "Wallet.h" +#include "GenericAccount.h" + +#include "vm/boc.h" +#include "vm/cells/CellString.h" +#include "td/utils/base64.h" + +#include + +namespace ton { +td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { + auto code = get_init_code(); + auto data = get_init_data(public_key); + return GenericAccount::get_init_state(std::move(code), std::move(data)); +} + +td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { + td::uint32 seqno = 0; + td::uint32 valid_until = std::numeric_limits::max(); + auto signature = + private_key + .sign(vm::CellBuilder().store_long(seqno, 32).store_long(valid_until, 32).finalize()->get_hash().as_slice()) + .move_as_ok(); + return vm::CellBuilder().store_bytes(signature).store_long(seqno, 32).store_long(valid_until, 32).finalize(); +} + +td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, 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(seqno, 32) + .store_long(valid_until, 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 Wallet::get_init_code() noexcept { + static auto res = [] { + auto serialized_code = td::base64_decode( + "te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" + "0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=") + .move_as_ok(); + return vm::std_boc_deserialize(serialized_code).move_as_ok(); + }(); + return res; +} + +vm::CellHash Wallet::get_init_code_hash() noexcept { + return get_init_code()->get_hash(); +} + +td::Ref Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { + return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); +} + +td::Result Wallet::get_seqno() const { + return TRY_VM(get_seqno_or_throw()); +} + +td::Result Wallet::get_seqno_or_throw() const { + if (state_.data.is_null()) { + return 0; + } + //FIXME use get method + return static_cast(vm::load_cell_slice(state_.data).fetch_ulong(32)); +} + +} // namespace ton diff --git a/crypto/smc-envelope/Wallet.h b/crypto/smc-envelope/Wallet.h new file mode 100644 index 0000000000..7cd33c81d1 --- /dev/null +++ b/crypto/smc-envelope/Wallet.h @@ -0,0 +1,48 @@ +/* + 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 . + + 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 Wallet : ton::SmartContract { + public: + explicit Wallet(State state) : ton::SmartContract(std::move(state)) { + } + static constexpr unsigned max_message_size = vm::CellString::max_bytes; + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; + static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, + td::uint32 valid_until, td::int64 gramms, td::Slice message, + const block::StdAddress& dest_address) noexcept; + + static td::Ref get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; + + td::Result get_seqno() const; + + private: + td::Result get_seqno_or_throw() const; +}; +} // namespace ton diff --git a/crypto/test/Ed25519.cpp b/crypto/test/Ed25519.cpp index 4d943abbfd..5c263acf67 100644 --- a/crypto/test/Ed25519.cpp +++ b/crypto/test/Ed25519.cpp @@ -21,8 +21,12 @@ #include "td/utils/misc.h" #include "td/utils/Slice.h" #include "td/utils/tests.h" +#include "td/utils/JsonBuilder.h" + +#include "wycheproof.h" #include +#include unsigned char fixed_privkey[32] = "abacabadabacabaeabacabadabacaba"; unsigned char fixed_pubkey[32] = {0x6f, 0x9e, 0x5b, 0xde, 0xce, 0x87, 0x21, 0xeb, 0x57, 0x37, 0xfb, @@ -142,3 +146,74 @@ TEST(Crypto, ed25519) { assert(!std::memcmp(secret12, secret21, 32)); */ } + +TEST(Crypto, wycheproof) { + std::vector> bad_tests; + auto json_str = wycheproof_ed25519(); + auto value = td::json_decode(json_str).move_as_ok(); + auto &root = value.get_object(); + auto test_groups_o = get_json_object_field(root, "testGroups", td::JsonValue::Type::Array, false).move_as_ok(); + auto &test_groups = test_groups_o.get_array(); + auto from_hexc = [](char c) { + if (c >= '0' && c <= '9') { + return c - '0'; + } + return c - 'a' + 10; + }; + auto from_hex = [&](td::Slice s) { + CHECK(s.size() % 2 == 0); + std::string res(s.size() / 2, 0); + for (size_t i = 0; i < s.size(); i += 2) { + res[i / 2] = char(from_hexc(s[i]) * 16 + from_hexc(s[i + 1])); + } + return res; + }; + for (auto &test_o : test_groups) { + auto &test = test_o.get_object(); + auto key_o = get_json_object_field(test, "key", td::JsonValue::Type::Object, false).move_as_ok(); + auto sk_str = td::get_json_object_string_field(key_o.get_object(), "sk", false).move_as_ok(); + auto pk_str = td::get_json_object_string_field(key_o.get_object(), "pk", false).move_as_ok(); + auto pk = td::Ed25519::PublicKey(td::SecureString(from_hex(pk_str))); + auto sk = td::Ed25519::PrivateKey(td::SecureString(from_hex(sk_str))); + CHECK(sk.get_public_key().move_as_ok().as_octet_string().as_slice() == pk.as_octet_string().as_slice()); + + //auto key = + //td::Ed25519::PrivateKey::from_pem( + //td::SecureString(td::get_json_object_string_field(test, "keyPem", false).move_as_ok()), td::SecureString()) + //.move_as_ok(); + + auto tests_o = get_json_object_field(test, "tests", td::JsonValue::Type::Array, false).move_as_ok(); + auto &tests = tests_o.get_array(); + for (auto &test_o : tests) { + auto &test = test_o.get_object(); + auto id = td::get_json_object_string_field(test, "tcId", false).move_as_ok(); + auto comment = td::get_json_object_string_field(test, "comment", false).move_as_ok(); + auto sig = from_hex(td::get_json_object_string_field(test, "sig", false).move_as_ok()); + auto msg = from_hex(td::get_json_object_string_field(test, "msg", false).move_as_ok()); + auto result = td::get_json_object_string_field(test, "result", false).move_as_ok(); + auto has_result = pk.verify_signature(msg, sig).is_ok() ? "valid" : "invalid"; + if (result != has_result) { + bad_tests.push_back({id, comment}); + } + } + } + if (bad_tests.empty()) { + return; + } + LOG(ERROR) << "FAILED: " << td::format::as_array(bad_tests); +} + +TEST(Crypto, almost_zero) { + td::SecureString pub(32); + td::SecureString sig(64); + td::SecureString msg(1); + + pub.as_mutable_slice().ubegin()[31] = static_cast(128); + for (td::int32 j = 0; j < 256; j++) { + msg.as_mutable_slice()[0] = (char)j; + if (td::Ed25519::PublicKey(pub.copy()).verify_signature(msg, sig).is_ok()) { + LOG(ERROR) << "FAILED: " << j; + break; + } + } +} diff --git a/crypto/test/test-db.cpp b/crypto/test/test-db.cpp index 28f05ff8e5..148a185494 100644 --- a/crypto/test/test-db.cpp +++ b/crypto/test/test-db.cpp @@ -51,34 +51,6 @@ #include -struct Step { - std::function func; - td::uint32 weight; -}; -class RandomSteps { - public: - RandomSteps(std::vector steps) : steps_(std::move(steps)) { - for (auto &step : steps_) { - steps_sum_ += step.weight; - } - } - template - void step(Random &rnd) { - auto w = rnd() % steps_sum_; - for (auto &step : steps_) { - if (w < step.weight) { - step.func(); - break; - } - w -= step.weight; - } - } - - private: - std::vector steps_; - td::int32 steps_sum_ = 0; -}; - namespace vm { std::vector do_get_serialization_modes() { @@ -879,7 +851,7 @@ TEST(TonDb, DynamicBoc2) { VLOG(boc) << " OK"; }; - RandomSteps steps({{new_root, 10}, {delete_root, 9}, {commit, 2}, {reset, 1}}); + td::RandomSteps steps({{new_root, 10}, {delete_root, 9}, {commit, 2}, {reset, 1}}); while (first_root_id != total_roots) { VLOG(boc) << first_root_id << " " << last_root_id << " " << kv->count("").ok(); steps.step(rnd); @@ -1732,7 +1704,7 @@ TEST(TonDb, CompactArray) { fast_array = vm::FastCompactArray(size); }; - RandomSteps steps({{reset_array, 1}, {set_value, 1000}, {validate, 10}, {validate_full, 2}, {flush_to_db, 1}}); + td::RandomSteps steps({{reset_array, 1}, {set_value, 1000}, {validate, 10}, {validate_full, 2}, {flush_to_db, 1}}); for (size_t t = 0; t < 100000; t++) { if (t % 10000 == 0) { LOG(ERROR) << t; diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp new file mode 100644 index 0000000000..f9dd86522f --- /dev/null +++ b/crypto/test/test-smartcont.cpp @@ -0,0 +1,524 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "vm/dict.h" +#include "common/bigint.hpp" + +#include "Ed25519.h" + +#include "block/block.h" + +#include "fift/Fift.h" +#include "fift/words.h" +#include "fift/utils.h" + +#include "smc-envelope/GenericAccount.h" +#include "smc-envelope/MultisigWallet.h" +#include "smc-envelope/SmartContract.h" +#include "smc-envelope/SmartContractCode.h" +#include "smc-envelope/TestGiver.h" +#include "smc-envelope/TestWallet.h" +#include "smc-envelope/Wallet.h" + +#include "td/utils/base64.h" +#include "td/utils/crypto.h" +#include "td/utils/Random.h" +#include "td/utils/tests.h" +#include "td/utils/ScopeGuard.h" +#include "td/utils/StringBuilder.h" +#include "td/utils/Timer.h" +#include "td/utils/PathView.h" +#include "td/utils/filesystem.h" +#include "td/utils/port/path.h" + +#include +#include +#include + +std::string current_dir() { + return td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir().str(); +} + +std::string load_source(std::string name) { + return td::read_file_str(current_dir() + "../../crypto/" + name).move_as_ok(); +} + +td::Ref get_test_wallet_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 +512 INT LDSLICEX DUP 32 PLDU // sign cs cnt +c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk +s1 s2 XCPU // sign cs cnt pubk cnt' cnt +EQUAL 33 THROWIFNOT // ( seqno mismatch? ) +s2 PUSH HASHSU // sign cs cnt pubk hash +s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk +CHKSIGNU // pubk cs cnt ? +34 THROWIFNOT // signature mismatch +ACCEPT +SWAP 32 LDU NIP +DUP SREFS IF:<{ + // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance + 8 LDU LDREF // pubk cnt mode msg cs + s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent ) +}> +ENDS +INC NEWC 32 STU 256 STU ENDC c4 POPCTR +)ABCD"; + return fift::compile_asm(code).move_as_ok(); +} + +td::Ref get_wallet_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 // signature in_msg msg_seqno valid_until cs + SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs + c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key + s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno + EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno + s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash + s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key + ACCEPT + s0 s2 XCHG // public_key stored_seqno cs + WHILE:<{ + DUP SREFS // public_key stored_seqno cs _40 + }>DO<{ // public_key stored_seqno cs + // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance + 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode + SENDRAWMSG // public_key stored_seqno cs + }> + ENDS INC // public_key seqno' + NEWC 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()); + CHECK(get_test_wallet_source()->get_hash() == ton::TestWallet::get_init_code()->get_hash()); + auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet.fif"), {"aba", "0"}).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::TestWallet::get_init_state(pub_key); + auto init_message = ton::TestWallet::get_init_message(priv_key); + auto address = ton::GenericAccount::get_address(0, init_state); + + CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); + + td::Ref 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.fif")).ensure(); + 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", "123", + "321", "-C", "TEST"}) + .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::TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", 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()); +} + +td::Ref get_wallet_source_fc() { + return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok(); +} + +TEST(Tonlib, Wallet) { + LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok()); + CHECK(get_wallet_source()->get_hash() == ton::Wallet::get_init_code()->get_hash()); + + auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.fif"), {"aba", "0"}).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::Wallet::get_init_state(pub_key); + auto init_message = ton::Wallet::get_init_message(priv_key); + auto address = ton::GenericAccount::get_address(0, init_state); + + CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); + + td::Ref 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-v2.fif")).ensure(); + class ZeroOsTime : public fift::OsTime { + public: + td::uint32 now() override { + return 0; + } + }; + fift_output.source_lookup.set_os_time(std::make_unique()); + 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", "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::Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", 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(); + LOG(ERROR) << address.bounceable; + auto fift_output = fift::mem_run_fift(load_source("smartcont/testgiver.fif"), + {"aba", address.rserialize(), "0", "6.666", "wallet-query"}) + .move_as_ok(); + LOG(ERROR) << fift_output.output; + + auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; + + auto res = ton::GenericAccount::create_ext_message( + ton::TestGiver::address(), {}, + ton::TestGiver::make_a_gift_message(0, 1000000000ll * 6666 / 1000, "GIFT", address)); + vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr); + CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash()); +} + +class SimpleWallet : public ton::SmartContract { + public: + SimpleWallet(State state) : SmartContract(std::move(state)) { + } + + const State& get_state() const { + return state_; + } + SimpleWallet* make_copy() const override { + return new SimpleWallet{state_}; + } + + static td::Ref create_empty() { + return td::Ref(true, State{ton::SmartContractCode::simple_wallet_ext(), {}}); + } + static td::Ref create(td::Ref data) { + return td::Ref(true, State{ton::SmartContractCode::simple_wallet_ext(), std::move(data)}); + } + static td::Ref create_fast(td::Ref data) { + return td::Ref(true, State{ton::SmartContractCode::simple_wallet(), std::move(data)}); + } + + td::int32 seqno() const { + auto res = run_get_method("seqno"); + return res.stack.write().pop_smallint_range(1000000000); + } + + td::Ref create_init_state(td::Slice public_key) const { + td::RefInt256 pk{true}; + pk.write().import_bytes(public_key.ubegin(), public_key.size(), false); + auto res = run_get_method("create_init_state", {pk}); + return res.stack.write().pop_cell(); + } + + td::Ref prepare_send_message(td::Ref msg, td::int8 mode = 3) const { + auto res = run_get_method("prepare_send_message", {td::make_refint(mode), msg}); + return res.stack.write().pop_cell(); + } + + static td::Ref sign_message(vm::Ref body, const td::Ed25519::PrivateKey& pk) { + auto signature = pk.sign(body->get_hash().as_slice()).move_as_ok(); + return vm::CellBuilder().store_bytes(signature.as_slice()).append_cellslice(vm::load_cell_slice(body)).finalize(); + } +}; + +TEST(Smartcon, Simple) { + auto private_key = td::Ed25519::generate_private_key().move_as_ok(); + auto public_key = private_key.get_public_key().move_as_ok().as_octet_string(); + + auto w_lib = SimpleWallet::create_empty(); + auto init_data = w_lib->create_init_state(public_key); + + auto w = SimpleWallet::create(init_data); + LOG(ERROR) << w->code_size(); + auto fw = SimpleWallet::create_fast(init_data); + LOG(ERROR) << fw->code_size(); + LOG(ERROR) << w->seqno(); + + for (int i = 0; i < 20; i++) { + auto msg = w->sign_message(w->prepare_send_message(vm::CellBuilder().finalize()), private_key); + w.write().send_external_message(msg); + fw.write().send_external_message(msg); + } + ASSERT_EQ(20, w->seqno()); + CHECK(w->get_state().data->get_hash() == fw->get_state().data->get_hash()); +} + +namespace std { // ouch +bool operator<(const ton::MultisigWallet::Mask& a, const ton::MultisigWallet::Mask& b) { + for (size_t i = 0; i < a.size(); i++) { + if (a[i] != b[i]) { + return a[i] < b[i]; + } + } + return false; +} +} // namespace std + +TEST(Smartcon, Multisig) { + auto ms_lib = ton::MultisigWallet::create(); + + int n = 100; + int k = 99; + std::vector keys; + for (int i = 0; i < n; i++) { + keys.push_back(td::Ed25519::generate_private_key().move_as_ok()); + } + auto init_state = ms_lib->create_init_data( + td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k); + auto ms = ton::MultisigWallet::create(init_state); + + td::uint64 query_id = 123; + ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize()); + // first empty query (init) + CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0); + // first empty query + CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code > 0); + + for (int i = 0; i < 10; i++) { + auto query = qb.create(i, keys[i]); + auto ans = ms.write().send_external_message(query); + LOG(INFO) << "CODE: " << ans.code; + LOG(INFO) << "GAS: " << ans.gas_used; + } + for (int i = 0; i + 1 < 50; i++) { + qb.sign(i, keys[i]); + } + auto query = qb.create(49, keys[49]); + + CHECK(ms->get_n_k() == std::make_pair(n, k)); + auto ans = ms.write().send_external_message(query); + LOG(INFO) << "CODE: " << ans.code; + LOG(INFO) << "GAS: " << ans.gas_used; + CHECK(ans.success); + ASSERT_EQ(0, ms->processed(query_id)); + CHECK(ms.write().send_external_message(query).code > 0); + ASSERT_EQ(0, ms->processed(query_id)); + + { + ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize()); + for (int i = 50; i + 1 < 100; i++) { + qb.sign(i, keys[i]); + } + query = qb.create(99, keys[99]); + } + + ans = ms.write().send_external_message(query); + LOG(INFO) << "CODE: " << ans.code; + LOG(INFO) << "GAS: " << ans.gas_used; + ASSERT_EQ(-1, ms->processed(query_id)); +} + +TEST(Smartcont, MultisigStress) { + int n = 10; + int k = 5; + + std::vector keys; + for (int i = 0; i < n; i++) { + keys.push_back(td::Ed25519::generate_private_key().move_as_ok()); + } + auto public_keys = td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }); + auto ms_lib = ton::MultisigWallet::create(); + auto init_state_old = + ms_lib->create_init_data_fast(td::transform(public_keys, [](auto& key) { return key.copy(); }), k); + auto init_state = ms_lib->create_init_data(td::transform(public_keys, [](auto& key) { return key.copy(); }), k); + CHECK(init_state_old->get_hash() == init_state->get_hash()); + auto ms = ton::MultisigWallet::create(init_state); + CHECK(ms->get_public_keys() == public_keys); + + td::int32 now = 0; + td::int32 qid = 1; + using Mask = std::bitset<128>; + struct Query { + td::int64 id; + td::Ref message; + Mask signed_mask; + }; + + std::vector queries; + int max_queries = 300; + + td::Random::Xorshift128plus rnd(123); + + auto new_query = [&] { + if (qid > max_queries) { + return; + } + Query query; + query.id = (static_cast(now) << 32) | qid++; + query.message = vm::CellBuilder().store_bytes(td::rand_string('a', 'z', rnd.fast(0, 100))).finalize(); + queries.push_back(std::move(query)); + }; + + auto verify = [&] { + auto messages = ms->get_unsigned_messaged(); + std::set> s; + std::set> t; + + for (auto& m : messages) { + auto x = std::make_tuple(m.query_id, m.signed_by, m.message->get_hash().as_slice().str()); + s.insert(std::move(x)); + } + + for (auto& q : queries) { + if (q.signed_mask.none()) { + continue; + } + t.insert(std::make_tuple(q.id, q.signed_mask, q.message->get_hash().as_slice().str())); + } + ASSERT_EQ(t.size(), s.size()); + CHECK(s == t); + }; + + auto sign_query = [&](Query& query, Mask mask) { + auto qb = ton::MultisigWallet::QueryBuilder(query.id, query.message); + int first_i = -1; + for (int i = 0; i < (int)mask.size(); i++) { + if (mask.test(i)) { + if (first_i == -1) { + first_i = i; + } else { + qb.sign(i, keys[i]); + } + } + } + return qb.create(first_i, keys[first_i]); + }; + + auto send_signature = [&](td::Ref query) { + auto ans = ms.write().send_external_message(query); + LOG(ERROR) << "GAS: " << ans.gas_used; + return ans.code == 0; + }; + + auto is_ready = [&](Query& query) { return ms->processed(query.id) == -1; }; + + auto gen_query = [&](Query& query) { + auto x = rnd.fast(1, n); + Mask mask; + for (int t = 0; t < x; t++) { + mask.set(rnd() % n); + } + + auto signature = sign_query(query, mask); + return std::make_pair(signature, mask); + }; + + auto rand_sign = [&] { + if (queries.empty()) { + return; + } + + size_t query_i = rnd() % queries.size(); + auto& query = queries[query_i]; + + Mask mask; + td::Ref signature; + std::tie(signature, mask) = gen_query(query); + if (false && rnd() % 6 == 0) { + Mask mask2; + td::Ref signature2; + std::tie(signature2, mask2) = gen_query(query); + for (int i = 0; i < (int)keys.size(); i++) { + if (mask[i]) { + signature = ms->merge_queries(std::move(signature), std::move(signature2)); + break; + } + if (mask2[i]) { + signature = ms->merge_queries(std::move(signature2), std::move(signature)); + break; + } + } + //signature = ms->merge_queries(std::move(signature), std::move(signature2)); + mask |= mask2; + } + + int got_cnt; + Mask got_cnt_bits; + std::tie(got_cnt, got_cnt_bits) = ms->check_query_signatures(signature); + CHECK(mask == got_cnt_bits); + + bool expect_ok = true; + { + auto new_mask = mask & ~query.signed_mask; + expect_ok &= new_mask.any(); + for (size_t i = 0; i < mask.size(); i++) { + if (mask[i]) { + expect_ok &= new_mask[i]; + break; + } + } + } + + ASSERT_EQ(expect_ok, send_signature(std::move(signature))); + if (expect_ok) { + query.signed_mask |= mask; + } + auto expect_is_ready = query.signed_mask.count() >= (size_t)k; + auto state = ms->get_query_state(query.id); + ASSERT_EQ(expect_is_ready, (state.state == ton::MultisigWallet::QueryState::Sent)); + CHECK(expect_is_ready || state.mask == query.signed_mask); + ASSERT_EQ(expect_is_ready, is_ready(query)); + if (expect_is_ready) { + queries.erase(queries.begin() + query_i); + } + verify(); + }; + td::RandomSteps steps({{rand_sign, 2}, {new_query, 1}}); + while (!queries.empty() || qid <= max_queries) { + steps.step(rnd); + //LOG(ERROR) << ms->data_size(); + } + LOG(INFO) << "Final code size: " << ms->code_size(); + LOG(INFO) << "Final data size: " << ms->data_size(); +} diff --git a/crypto/test/wycheproof.h b/crypto/test/wycheproof.h new file mode 100644 index 0000000000..182760acb6 --- /dev/null +++ b/crypto/test/wycheproof.h @@ -0,0 +1,1160 @@ +#pragma once + +#include + +std::string wycheproof_ed25519() { + return + R"abcd({ + "algorithm" : "EDDSA", + "generatorVersion" : "0.4.12", + "notes" : { + "SignatureMalleability" : "EdDSA signatures are non-malleable, if implemented accordingly. Failing to check the range of S allows to modify signatures. See RFC 8032, Section 5.2.7 and Section 8.4." + }, + "numberOfTests" : 111, + "header" : [], + "testGroups" : [ + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "7d4d0e7f6153a69b6242b522abbee685fda4420f8834b108c3bdae369ef549fa", + "sk" : "add4bb8103785baf9ac534258e8aaf65f5f1adb5ef5f3df19bb80ab989c4d64b", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b65700321007d4d0e7f6153a69b6242b522abbee685fda4420f8834b108c3bdae369ef549fa", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAfU0Of2FTpptiQrUiq77mhf2kQg+INLEIw72uNp71Sfo=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 1, + "comment" : "", + "msg" : "", + "sig" : "d4fbdb52bfa726b44d1786a8c0d171c3e62ca83c9e5bbe63de0bb2483f8fd6cc1429ab72cafc41ab56af02ff8fcc43b99bfe4c7ae940f60f38ebaa9d311c4007", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 2, + "comment" : "", + "msg" : "78", + "sig" : "d80737358ede548acb173ef7e0399f83392fe8125b2ce877de7975d8b726ef5b1e76632280ee38afad12125ea44b961bf92f1178c9fa819d020869975bcbe109", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 3, + "comment" : "", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 4, + "comment" : "", + "msg" : "48656c6c6f", + "sig" : "1c1ad976cbaae3b31dee07971cf92c928ce2091a85f5899f5e11ecec90fc9f8e93df18c5037ec9b29c07195ad284e63d548cd0a6fe358cc775bd6c1608d2c905", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 5, + "comment" : "", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2bf0cf5b3a289976458a1be6277a5055545253b45b07dcc1abd96c8b989c00f301", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 6, + "comment" : "", + "msg" : "000000000000000000000000", + "sig" : "d46543bfb892f84ec124dcdfc847034c19363bf3fc2fa89b1267833a14856e52e60736918783f950b6f1dd8d40dc343247cd43ce054c2d68ef974f7ed0f3c60f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 7, + "comment" : "", + "msg" : "6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", + "sig" : "879350045543bc14ed2c08939b68c30d22251d83e018cacbaf0c9d7a48db577e80bdf76ce99e5926762bc13b7b3483260a5ef63d07e34b58eb9c14621ac92f00", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 8, + "comment" : "", + "msg" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60", + "sig" : "7bdc3f9919a05f1d5db4a3ada896094f6871c1f37afc75db82ec3147d84d6f237b7e5ecc26b59cfea0c7eaf1052dc427b0f724615be9c3d3e01356c65b9b5109", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 9, + "comment" : "", + "msg" : "ffffffffffffffffffffffffffffffff", + "sig" : "5dbd7360e55aa38e855d6ad48c34bd35b7871628508906861a7c4776765ed7d1e13d910faabd689ec8618b78295c8ab8f0e19c8b4b43eb8685778499e943ae04", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 10, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 11, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "00000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 12, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "0000000000000000000000000000000000000000000000000000000000000000ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 13, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "0000000000000000000000000000000000000000000000000000000000000000edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 14, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "0000000000000000000000000000000000000000000000000000000000000000edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 15, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 16, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "01000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 17, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "0100000000000000000000000000000000000000000000000000000000000000ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 18, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "0100000000000000000000000000000000000000000000000000000000000000edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 19, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "0100000000000000000000000000000000000000000000000000000000000000edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 20, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edd3f55c1a631258d69cf7a2def9de14000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 21, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edd3f55c1a631258d69cf7a2def9de14000000000000000000000000000000100100000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 22, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 23, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 24, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 25, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 26, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f0100000000000000000000000000000000000000000000000000000000000000", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 27, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 28, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fedd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 29, + "comment" : "special values for r and s", + "msg" : "3f", + "sig" : "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fedffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 30, + "comment" : "empty signature", + "msg" : "54657374", + "sig" : "", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 31, + "comment" : "s missing", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab0", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 32, + "comment" : "signature too short", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 33, + "comment" : "signature too long", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d2020", + "result" : "invalid", + "flags" : [] + },)abcd" R"abcd( + { + "tcId" : 34, + "comment" : "include pk in signature", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d7d4d0e7f6153a69b6242b522abbee685fda4420f8834b108c3bdae369ef549fa", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 35, + "comment" : "prepending 0 byte to signature", + "msg" : "54657374", + "sig" : "007c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 36, + "comment" : "prepending 0 byte to s", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab0007a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 37, + "comment" : "appending 0 byte to signature", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d00", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 38, + "comment" : "removing 0 byte from signature", + "msg" : "546573743137", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b3", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 39, + "comment" : "removing 0 byte from signature", + "msg" : "54657374313236", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab09155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 40, + "comment" : "removing leading 0 byte from signature", + "msg" : "546573743530", + "sig" : "38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 41, + "comment" : "dropping byte from signature", + "msg" : "54657374333437", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab09155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b30d", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 42, + "comment" : "modified bit 0 in R", + "msg" : "313233343030", + "sig" : "647c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2b1d125e5538f38afbcc1c84e489521083041d24bc6240767029da063271a1ff0c", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 43, + "comment" : "modified bit 1 in R", + "msg" : "313233343030", + "sig" : "677c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2bc108ca4b87a49c9ed2cf383aecad8f54a962b2899da891e12004d7993a627e01", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 44, + "comment" : "modified bit 2 in R", + "msg" : "313233343030", + "sig" : "617c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2b9ce23fc6213ed5b87912e9bbf92f5e2c780eae26d15c50a112d1e97d2ea33c06", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 45, + "comment" : "modified bit 7 in R", + "msg" : "313233343030", + "sig" : "e57c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2bbb3eb51cd98dddb235a5f46f2bded6af184a58d09cce928bda43f41d69118a03", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 46, + "comment" : "modified bit 8 in R", + "msg" : "313233343030", + "sig" : "657d1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2bcd237dda9a116501f67a5705a854b9adc304f34720803a91b324f2c13e0f5a09", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 47, + "comment" : "modified bit 16 in R", + "msg" : "313233343030", + "sig" : "657c1592402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2b6b167bbdc0d881cc04d28905552c1876f3709851abc5007376940cc8a435c300", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 48, + "comment" : "modified bit 31 in R", + "msg" : "313233343030", + "sig" : "657c1412402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2b7fd2ac7da14afffcceeb13f2a0d6b887941cb1a5eb57a52f3cb131a16cce7b0e", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 49, + "comment" : "modified bit 32 in R", + "msg" : "313233343030", + "sig" : "657c1492412ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2b7373ba13ebbef99cd2a8ead55ce735c987d85a35320925a8e871702dc7c5c40d", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 50, + "comment" : "modified bit 63 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab54e03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2bd35bd331c03f0855504ca1cab87b83c36a028425a3cf007ede4f4254c261cb00", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 51, + "comment" : "modified bit 64 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce02e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2bcb35101f73cf467deac8c1a03b6c3dc35af544132734b7e57ab20c89b2e4750d", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 52, + "comment" : "modified bit 97 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f2384d051b9cf3570f1207fc78c1bcc98c281c2bb58d2e8878290bff8d3355fdd4ea381924ee578752354eb6dee678ab4011c301", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 53, + "comment" : "modified bit 127 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d851b9cf3570f1207fc78c1bcc98c281c2bb978c866187ffb1cc7b29a0b4045aefc08768df65717194ff0c6e63f4dea0d02", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 54, + "comment" : "modified bit 240 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281d2b0576ecf8eaf675f00f3dfbe19f75b83b7607a6c96414f6821af920a2498d0305", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 55, + "comment" : "modified bit 247 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c289c2be5241a345c7b5428054c74b7c382fa10d4a5f1e8f8b79a71d3fdea2254f1ff0e", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 56, + "comment" : "modified bit 248 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c2a63950c85cd6dc96364e768de50ff7732b538f8a0b1615d799190ab600849230e", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 57, + "comment" : "modified bit 253 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c0b543bd3da0a56a8c9c152f59c9fec12f31fa66434d48b817b30d90cb4efa8b501", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 58, + "comment" : "modified bit 254 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281c6b8da07efd07a6dafb015ed6a32fe136319a972ffbc341f3a0beae97ccf8136505", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 59, + "comment" : "modified bit 255 in R", + "msg" : "313233343030", + "sig" : "657c1492402ab5ce03e2c3a7f0384d051b9cf3570f1207fc78c1bcc98c281cab227aedf259f910f0f3a759a335062665217925d019173b88917eae294f75d40f", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 60, + "comment" : "R==0", + "msg" : "313233343030", + "sig" : "0000000000000000000000000000000000000000000000000000000000000000e0b8e7770d51c7a36375d006c5bffd6af43ff54aaf47e4330dc118c71d61ec02", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 61, + "comment" : "invalid R", + "msg" : "313233343030", + "sig" : "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff463a1908382e7eb7693acef9884f7cf931a215e0791876be22c631a59881fd0e", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 62, + "comment" : "all bits flipped in R", + "msg" : "313233343030", + "sig" : "9a83eb6dbfd54a31fc1d3c580fc7b2fae4630ca8f0edf803873e433673d7e3d40e94254586cb6188c5386c3febed477cb9a6cb29e3979adc4cb27cf5278fb70a", + "result" : "invalid", + "flags" : [] + }, + { + "tcId" : 63, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab067654bce3832c2d76f8f6f5dafc08d9339d4eef676573336a5c51eb6f946b31d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + }, + { + "tcId" : 64, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab05439412b5395d42f462c67008eba6ca839d4eef676573336a5c51eb6f946b32d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + }, + { + "tcId" : 65, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab02ee12ce5875bf9dff26556464bae2ad239d4eef676573336a5c51eb6f946b34d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + }, + { + "tcId" : 66, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab0e2300459f1e742404cd934d2c595a6253ad4eef676573336a5c51eb6f946b38d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + }, + { + "tcId" : 67, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b32d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + },)abcd" R"abcd( + { + "tcId" : 68, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b34d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + }, + { + "tcId" : 69, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab07a9155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b38d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + }, + { + "tcId" : 70, + "comment" : "checking malleability ", + "msg" : "54657374", + "sig" : "7c38e026f29e14aabd059a0f2db8b0cd783040609a8be684db12f82a27774ab0679155711ecfaf7f99f277bad0c6ae7e39d4eef676573336a5c51eb6f946b38d", + "result" : "invalid", + "flags" : [ + "SignatureMalleability" + ] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "a12c2beb77265f2aac953b5009349d94155a03ada416aad451319480e983ca4c", + "sk" : "0a23a20072891237aa0864b5765139514908787878cd77135a0059881d313f00", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100a12c2beb77265f2aac953b5009349d94155a03ada416aad451319480e983ca4c", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAoSwr63cmXyqslTtQCTSdlBVaA62kFqrUUTGUgOmDykw=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 71, + "comment" : "", + "msg" : "", + "sig" : "5056325d2ab440bf30bbf0f7173199aa8b4e6fbc091cf3eb6bc6cf87cd73d992ffc216c85e4ab5b8a0bbc7e9a6e9f8d33b7f6e5ac0ffdc22d9fcaf784af84302", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 72, + "comment" : "", + "msg" : "78", + "sig" : "481fafbf4364d7b682475282f517a3ac0538c9a6b6a562e99a3d8e5afb4f90a559b056b9f07af023905753b02d95eb329a35c77f154b79abbcd291615ce42f02", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 73, + "comment" : "", + "msg" : "54657374", + "sig" : "8a9bb4c465a3863abc9fd0dd35d80bb28f7d33d37d74679802d63f82b20da114b8d765a1206b3e9ad7cf2b2d8d778bb8651f1fa992db293c0039eacb6161480f", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 74, + "comment" : "", + "msg" : "48656c6c6f", + "sig" : "d839c20abfda1fd429531831c64f813f84b913e9928540310cf060b44c3dbf9457d44a7721fdc0d67724ff81cb450dd39b10cfb65db15dda4b8bf09d26bd3801", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 75, + "comment" : "", + "msg" : "313233343030", + "sig" : "9bbb1052dcfa8ad2715c2eb716ae4f1902dea353d42ee09fd4c0b4fcb8b52b5219e2200016e1199d0061891c263e31b0bc3b55673c19610c4e0fa5408004160b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 76, + "comment" : "", + "msg" : "000000000000000000000000", + "sig" : "f63b5c0667c7897fc283296416f7f60e84bbde9cbd832e56be463ed9f568069702b17a2f7c341ebf590706a6388ac76ac613c1675ec0f2c7118f2573422a500b", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 77, + "comment" : "", + "msg" : "6161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", + "sig" : "1bc44d7001e6b5b9090fef34b2ca480f9786bbefa7d279353e5881e8dfb91b803ccd46500e270ef0109bfd741037558832120bc2a4f20fbe7b5fb3c3aaf23e08", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 78, + "comment" : "", + "msg" : "202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f60", + "sig" : "ea8e22143b02372e76e99aece3ed36aec529768a27e2bb49bdc135d44378061e1f62d1ac518f33ebf37b2ee8cc6dde68a4bd7d4a2f4d6cb77f015f71ca9fc30d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 79, + "comment" : "", + "msg" : "ffffffffffffffffffffffffffffffff", + "sig" : "8acd679e1a914fc45d5fa83d3021f0509c805c8d271df54e52f43cfbd00cb6222bf81d58fe1de2de378df67ee9f453786626961fe50a9b05f12b6f0899ebdd0a", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", + "sk" : "9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 80, + "comment" : "draft-josefsson-eddsa-ed25519-02: Test 1", + "msg" : "", + "sig" : "e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", + "sk" : "4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b65700321003d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAPUAXw+hDiVqStwqnTRt+vJyYLM8uxJaMwM1V8Sr0Zgw=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 81, + "comment" : "draft-josefsson-eddsa-ed25519-02: Test 2", + "msg" : "72", + "sig" : "92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025", + "sk" : "c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEA/FHNjmIYoaONpH7QAjDwWAgW7RO6MwOsXeuRFUiQgCU=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 82, + "comment" : "draft-josefsson-eddsa-ed25519-02: Test 3", + "msg" : "af82", + "sig" : "6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e", + "sk" : "f5e5767cf153319517630f226876b86c8160cc583bc013744c6bf255f5cc0ee5", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100278117fc144c72340f67d0f2316e8386ceffbf2b2428c9c51fef7c597f1d426e", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAJ4EX/BRMcjQPZ9DyMW6Dhs7/vyskKMnFH+98WX8dQm4=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 83, + "comment" : "draft-josefsson-eddsa-ed25519-02: Test 1024", + "msg" : "08b8b2b733424243760fe426a4b54908632110a66c2f6591eabd3345e3e4eb98fa6e264bf09efe12ee50f8f54e9f77b1e355f6c50544e23fb1433ddf73be84d879de7c0046dc4996d9e773f4bc9efe5738829adb26c81b37c93a1b270b20329d658675fc6ea534e0810a4432826bf58c941efb65d57a338bbd2e26640f89ffbc1a858efcb8550ee3a5e1998bd177e93a7363c344fe6b199ee5d02e82d522c4feba15452f80288a821a579116ec6dad2b3b310da903401aa62100ab5d1a36553e06203b33890cc9b832f79ef80560ccb9a39ce767967ed628c6ad573cb116dbefefd75499da96bd68a8a97b928a8bbc103b6621fcde2beca1231d206be6cd9ec7aff6f6c94fcd7204ed3455c68c83f4a41da4af2b74ef5c53f1d8ac70bdcb7ed185ce81bd84359d44254d95629e9855a94a7c1958d1f8ada5d0532ed8a5aa3fb2d17ba70eb6248e594e1a2297acbbb39d502f1a8c6eb6f1ce22b3de1a1f40cc24554119a831a9aad6079cad88425de6bde1a9187ebb6092cf67bf2b13fd65f27088d78b7e883c8759d2c4f5c65adb7553878ad575f9fad878e80a0c9ba63bcbcc2732e69485bbc9c90bfbd62481d9089beccf80cfe2df16a2cf65bd92dd597b0707e0917af48bbb75fed413d238f5555a7a569d80c3414a8d0859dc65a46128bab27af87a71314f318c782b23ebfe808b82b0ce26401d2e22f04d83d1255dc51addd3b75a2b1ae0784504df543af8969be3ea7082ff7fc9888c144da2af58429ec96031dbcad3dad9af0dcbaaaf268cb8fcffead94f3c7ca495e056a9b47acdb751fb73e666c6c655ade8297297d07ad1ba5e43f1bca32301651339e22904cc8c42f58c30c04aafdb038dda0847dd988dcda6f3bfd15c4b4c4525004aa06eeff8ca61783aacec57fb3d1f92b0fe2fd1a85f6724517b65e614ad6808d6f6ee34dff7310fdc82aebfd904b01e1dc54b2927094b2db68d6f903b68401adebf5a7e08d78ff4ef5d63653a65040cf9bfd4aca7984a74d37145986780fc0b16ac451649de6188a7dbdf191f64b5fc5e2ab47b57f7f7276cd419c17a3ca8e1b939ae49e488acba6b965610b5480109c8b17b80e1b7b750dfc7598d5d5011fd2dcc5600a32ef5b52a1ecc820e308aa342721aac0943bf6686b64b2579376504ccc493d97e6aed3fb0f9cd71a43dd497f01f17c0e2cb3797aa2a2f256656168e6c496afc5fb93246f6b1116398a346f1a641f3b041e989f7914f90cc2c7fff357876e506b50d334ba77c225bc307ba537152f3f1610e4eafe595f6d9d90d11faa933a15ef1369546868a7f3a45a96768d40fd9d03412c091c6315cf4fde7cb68606937380db2eaaa707b4c4185c32eddcdd306705e4dc1ffc872eeee475a64dfac86aba41c0618983f8741c5ef68d3a101e8a3b8cac60c905c15fc910840b94c00a0b9d0", + "sig" : "0aab4c900501b3e24d7cdf4663326a3a87df5e4843b2cbdb67cbf6e460fec350aa5371b1508f9f4528ecea23c436d94b5e8fcd4f681e30a6ac00a9704a188a03", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "8fd659b77b558ed93882c1157438450ac86ec62d421d568e98ee236f3810295a", + "sk" : "d7ad3f1f6bbe0477c3c357a806a19eb41ae3f94025035bc87f281f8ee9fc0e34", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b65700321008fd659b77b558ed93882c1157438450ac86ec62d421d568e98ee236f3810295a", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAj9ZZt3tVjtk4gsEVdDhFCshuxi1CHVaOmO4jbzgQKVo=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 84, + "comment" : "Random test failure 1", + "msg" : "b0729a713593a92e46b56eaa66b9e435f7a09a8e7de03b078f6f282285276635f301e7aaafe42187c45d6f5b13f9f16b11195cc125c05b90d24dfe4c", + "sig" : "7db17557ac470c0eda4eedaabce99197ab62565653cf911f632ee8be0e5ffcfc88fb94276b42e0798fd3aa2f0318be7fc6a29fae75f70c3dcdc414a0ad866601", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "2a606bf67ac770c607038b004101b325edb569efd3413d2d1f2c3e6b4e6e3082", + "sk" : "ad9b22793336fcdac10e136c4deea599be187a38eef91c1cf7c7a4ec884dda08", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b65700321002a606bf67ac770c607038b004101b325edb569efd3413d2d1f2c3e6b4e6e3082", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAKmBr9nrHcMYHA4sAQQGzJe21ae/TQT0tHyw+a05uMII=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 85, + "comment" : "Random test failure 2", + "msg" : "a8546e50ba31cae3234310d32672447be213fad91a227a19669c53d309b959782b0e6b71f8791fdb470043b58122003157d2d96a43a6cbd7d3a8d86bf4c97391883e268d50af80e1e6e12939c2bd50ca746cdadfad4edf1bda875299740724148efb1ebe73fb60088cda890317658627a5f7ab5a0c075d9d8f3f97b6492b35519e50ff6b38377432a7081f9176bb1c29a862deac1336ca20b097a47829cec10a6a7cec178eda2d12f6dc6c87f910454af0123555ba184e68804d9cced60fd5c8c90943e56599c8f0ba59a38491ba5e5a53460682474c07e40ca142983314fd762856bb1093f359da6eb0a756bd93a3160c10dd8feea6b97e7c6a17cb54bd5d7649c05c66d7bdee056671dfdaf689fa3945bb8e29a429f4bd5d355dce9687b06f01d5e33e3999f0e8", + "sig" : "67d84d4c3945aaf06e06d524be63acbfb5dbb1988c4aea96a5ee9f7a9b9eecc29df4f66b8aa1d9e8607a58fb1ef0c2ad69aac005b4f58e34103344a9c8871a09", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 86, + "comment" : "Random test failure 24", + "msg" : "b477b0480bb84642608b908d29a51cf2fce63f24ee95", + "sig" : "28fafbb62b4d688fa79e1ac92851f46e319b161f801d4dc09acc21fdd6780a2c4292b8c1003c61c2bcebe7f3f88ccc4bb26d407387c5f27cb8c94cf6ce810405", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "c9c946cbc5544ac74eef491f07c5881c16faf7ec31ce4aa91bb60ae7b4539051", + "sk" : "04a6553d68a9baef78a2175af375458eaa01cdb77350c61e282ef5f0c7116599", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100c9c946cbc5544ac74eef491f07c5881c16faf7ec31ce4aa91bb60ae7b4539051", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAyclGy8VUSsdO70kfB8WIHBb69+wxzkqpG7YK57RTkFE=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 87, + "comment" : "Random test failure 3", + "msg" : "cd2212eddb0706f62c995cef958634f0cb7793444cbf4d30e81c27c41ebea6cb02607510131f9c015692dfd521b148841e9a2d3564d20ac401f6cb8e40f520fe0cafbeaa88840b83013369d879f013463fe52a13267aa0c8c59c45cde9399cd1e6be8cc64cf48315ac2eb31a1c567a4fb7d601746d1f63b5ac020712adbbe07519bded6f", + "sig" : "24087d47f3e20af51b9668ae0a88ce76586802d0ec75d8c0f28fc30962b5e1d1a1d509571a1624ed125a8df92a6e963728d6b5de99200b8e285f70feb6f05207", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 88, + "comment" : "Random test failure 20", + "msg" : "27d465bc632743522aefa23c", + "sig" : "c2656951e2a0285585a51ff0eda7e9a23c2dfd2ffa273aee7808f4604e8f9a8c8ea49e9fce4eb2d8d75d36b7238fe6fc13b6c5d9427dd58f8c6615d033c0bd0f", + "result" : "valid", + "flags" : [] + } + ] + },)abcd" R"abcd( + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "32ad026f693d0d2afe7f4388d91c4c964426fcb9e3665c3ebd8650009b815c8e", + "sk" : "c367c8d2ebeeecd70c1e8985b70c3808b75657f243b21ba4f322792540e92257", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b657003210032ad026f693d0d2afe7f4388d91c4c964426fcb9e3665c3ebd8650009b815c8e", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAMq0Cb2k9DSr+f0OI2RxMlkQm/LnjZlw+vYZQAJuBXI4=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 89, + "comment" : "Random test failure 4", + "msg" : "ec5c7cb078", + "sig" : "d920d421a5956b69bfe1ba834c025e2babb6c7a6d78c97de1d9bb1116dfdd1185147b2887e34e15578172e150774275ea2aad9e02106f7e8ca1caa669a066f0c", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 90, + "comment" : "Random test failure 5", + "msg" : "4668c6a76f0e482190a7175b9f3806a5fe4314a004fa69f988373f7a", + "sig" : "4f62daf7f7c162038552ad7d306e195baa37ecf6ca7604142679d7d1128e1f8af52e4cb3545748c44ef1ff1c64e877e4f4d248259b7f6eb56e3ef72097dc8e0c", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 91, + "comment" : "Random test failure 8", + "msg" : "5dc9bb87eb11621a93f92abe53515697d2611b2eef73", + "sig" : "deecafb6f2ede73fec91a6f10e45b9c1c61c4b9bfbe6b6147e2de0b1df6938971f7896c3ab83851fb5d9e537037bff0fca0ccb4a3cc38f056f91f7d7a0557e08", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 92, + "comment" : "Random test failure 10", + "msg" : "7dcfe60f881e1285676f35b68a1b2dbcdd7be6f719a288ababc28d36e3a42ac3010a1ca54b32760e74", + "sig" : "7f8663cf98cbd39d5ff553f00bcf3d0d520605794f8866ce75714d77cc51e66c91818b657d7b0dae430a68353506edc4a714c345f5ddb5c8b958ba3d035f7a01", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 93, + "comment" : "Random test failure 12", + "msg" : "58e456064dff471109def4ca27fa8310a1df32739655b624f27e6418d34b7f007173f3faa5", + "sig" : "6aab49e5c0bc309b783378ee03ffda282f0185cdf94c847701ff307a6ee8d0865411c44e0a8206f6a5f606107451940c2593af790ce1860f4c14ab25b2deae08", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 94, + "comment" : "Random test failure 15", + "msg" : "a1", + "sig" : "1a74ed2cbdc7d8f3827014e8e6ecf8fd2698ac8f86833acccdd400df710fe0d6b0543c9cfa00d52bf024ab7ce0d91981944097233ec134d5c7abbd44bfd32d0d", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 95, + "comment" : "Random test failure 19", + "msg" : "11cb1eafa4c42a8402c4193c4696f7b2e6d4585e4b42dcf1a8b67a80b2da80bc9d4b649fb2f35eaf1f56c426fd0b", + "sig" : "14ceb2eaf4688d995d482f44852d71ad878cd7c77b41e60b0065fd01a59b054ee74759224187dbde9e59a763a70277c960892ef89fba997aba2576b2c54ba608", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 96, + "comment" : "Random test failure 25", + "msg" : "aa365b442d12b7f3c925", + "sig" : "83c40ce13d483cc58ff65844875862d93df4bd367af77efa469ec06a8ed9e6d7905a04879535708ddf225567a815c9b941d405c98e918fd0c151165cea7fb101", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 97, + "comment" : "Random test failure 28", + "msg" : "475f", + "sig" : "71a4a06a34075f2fd47bc3abf4714d46db7e97b08cb6180d3f1539ac50b18ce51f8af8ae95ed21d4fa0daab7235925631ecea1fd9d0d8a2ba7a7583fd04b900c", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "c29ec1894e06d27b4e40486b4fa5063d66a746c7f9c323b12203c03b72b8b78a", + "sk" : "56c1e22d616cbb6dea869288b4b1c02bb98696583c2f6e650013a03e17049c62", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100c29ec1894e06d27b4e40486b4fa5063d66a746c7f9c323b12203c03b72b8b78a", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAwp7BiU4G0ntOQEhrT6UGPWanRsf5wyOxIgPAO3K4t4o=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 98, + "comment" : "Random test failure 6", + "msg" : "0f325ffd87e58131ffa23c05ea4579513b287fdba87b44", + "sig" : "6669acf94667c5b541afe5307bde9476b13ae7e0e6058a772101ac8eb0a94331428eb4db0a2c68a9b6c1763b8624dab259b0876cdcfaeacc17b21a18e3fc010a", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 99, + "comment" : "Random test failure 21", + "msg" : "5ffa", + "sig" : "931e5152fcef078c22cc5d6a3a65f06e396289f6f5f2d1efa6340254a53526ef5dc6874eeddf35c3f50991c53cd02bf06313e37d93ee1f7022128ffa3b8f300b", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "cfda5b899e35764c5229e59295fe1222b7ddce176643697c29e46ecbba10cf10", + "sk" : "b7d2f64276df417fed27d8e15b4e90f6fd93dace707294c338bd32bc4bbd8fdb", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100cfda5b899e35764c5229e59295fe1222b7ddce176643697c29e46ecbba10cf10", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAz9pbiZ41dkxSKeWSlf4SIrfdzhdmQ2l8KeRuy7oQzxA=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 100, + "comment" : "Random test failure 7", + "msg" : "ec5c7cb078", + "sig" : "30490c28f806298225df62103521dcee047153912c33ab8ab8bbdd1ffabd70fd4fdb360f05be535b067d1cf4e78c2cb432206bf280aab3bd21aaa1cb894c5b06", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 101, + "comment" : "Random test failure 9", + "msg" : "67484059b2490b1a0a4f8dee77979e26", + "sig" : "4cd4f77ed473a6647387f3163541c67a1708a3c3bd1673247cb87f0cb68b3c56f04bfa72970c8a483efe659c87009ab4020b590b6641316b3deddb5450544e02", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 102, + "comment" : "Random test failure 11", + "msg" : "a020a4381dc9141f47ee508871ab7a8b5a3648727c4281ae9932376f23a8e1bcda0626b7129197d864178631ec89c4332dbb18", + "sig" : "1e41a24fe732bd7cab14c2a2f5134ee8c87fcbd2e987e60957ed9239e5c32404d56977e1b4282871896cb10625a1937468e4dc266e16a9c1b8e9891177eca802", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 103, + "comment" : "Random test failure 14", + "msg" : "a25176b3afea318b2ec11ddacb10caf7179c0b3f8eabbfa2895581138d3c1e0e", + "sig" : "2a833aadecd9f28235cb5896bf3781521dc71f28af2e91dbe1735a61dce3e31ac15ca24b3fc47817a59d386bbbb2ce60a6adc0a2703bb2bdea8f70f91051f706", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 104, + "comment" : "Random test failure 18", + "msg" : "a9e6d94870a67a9fe1cf13b1e6f9150cdd407bf6480ec841ea586ae3935e9787163cf419c1", + "sig" : "c97e3190f83bae7729ba473ad46b420b8aad735f0808ea42c0f898ccfe6addd4fd9d9fa3355d5e67ee21ab7e1f805cd07f1fce980e307f4d7ad36cc924eef00c", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "529919c9c780985a841c42ba6c180ff2d67a276ccfbe281080e47ab71a758f56", + "sk" : "7d597c3b7283929d07ed8f01f31d2596823e5e46ab226c7be4234d1a9dcaef37", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100529919c9c780985a841c42ba6c180ff2d67a276ccfbe281080e47ab71a758f56", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAUpkZyceAmFqEHEK6bBgP8tZ6J2zPvigQgOR6txp1j1Y=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 105, + "comment" : "Random test failure 13", + "msg" : "e1cbf2d86827825613fb7a85811d", + "sig" : "01abfa4d6bbc726b196928ec84fd03f0c953a4fa2b228249562ff1442a4f63a7150b064f3712b51c2af768d2c2711a71aabf8d186833e941a0301b82f0502905", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 106, + "comment" : "Random test failure 22", + "msg" : "25", + "sig" : "e4ae21f7a8f4b3b325c161a8c6e53e2edd7005b9c2f8a2e3b0ac4ba94aa80be6f2ee22ac8d4a96b9a3eb73a825e7bb5aff4a3393bf5b4a38119e9c9b1b041106", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "2252b3d57c74cbf8bc460dc2e082847926bc022f09ab6ae95756362bfd1167c1", + "sk" : "f401cee4bfb1732f0e9b8d8ba79469565c3115296141dbdf7e9c311a0ac1823b", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b65700321002252b3d57c74cbf8bc460dc2e082847926bc022f09ab6ae95756362bfd1167c1", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAIlKz1Xx0y/i8Rg3C4IKEeSa8Ai8Jq2rpV1Y2K/0RZ8E=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 107, + "comment" : "Random test failure 16", + "msg" : "975ef941710071a9e1e6325a0c860becd7c695b5117c3107b686e330e5", + "sig" : "af0fd9dda7e03e12313410d8d8844ebb6fe6b7f65141f22d7bcba5695a25414a9e54326fb44d59fb14707899a8aae70857b23d4080d7ab2c396ef3a36d45ce02", + "result" : "valid", + "flags" : [] + }, + { + "tcId" : 108, + "comment" : "Random test failure 23", + "msg" : "80fdd6218f29c8c8f6bd820945f9b0854e3a8824", + "sig" : "e097e0bd0370bff5bde359175a11b728ee9639095d5df8eda496395565616edfe079977f7d4dc8c75d6113a83d6a55e6e1676408c0967a2906339b43337dcb01", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "c0a773110f975de3732355bb7ec7f0c41c091c0252966070205516693b992a4a", + "sk" : "3d658956410377d0644676d2599542412a4f3b0e4eadfb7f3f836615f42b18bc", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b6570032100c0a773110f975de3732355bb7ec7f0c41c091c0252966070205516693b992a4a", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAwKdzEQ+XXeNzI1W7fsfwxBwJHAJSlmBwIFUWaTuZKko=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 109, + "comment" : "Random test failure 17", + "msg" : "", + "sig" : "0280427e713378f49d478df6373c6cac847b622b567daa2376c839e7ac10e22c380ab0fa8617c9dcfe76c4d9db5459b21dc1413726e46cc8f387d359e344f407", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "54cda623245759ad6d43e620a606908befc633d60792bc7798447a0ef38e7311", + "sk" : "bccb61323840c2a96fc36f7e54ea6c8e55f9d221f7f05791ed60025e06064439", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b657003210054cda623245759ad6d43e620a606908befc633d60792bc7798447a0ef38e7311", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAVM2mIyRXWa1tQ+YgpgaQi+/GM9YHkrx3mER6DvOOcxE=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 110, + "comment" : "Random test failure 26", + "msg" : "27e792b28b2f1702", + "sig" : "14d9b497c19b91d43481c55bb6f5056de252d9ecb637575c807e58e9b4c5eac8b284089d97e2192dc242014363208e2c9a3435edf8928fb1d893553e9be4c703", + "result" : "valid", + "flags" : [] + } + ] + }, + { + "key" : { + "curve" : "edwards25519", + "keySize" : 255, + "pk" : "2362bac514d5fad33802642e979a1e82de6eb6f1bcbf6a5b304f2bb02b9e57fe", + "sk" : "f2d3023b9c19e241748bc4039a7a43c595701f23675505015213a8a2a0274c1b", + "type" : "EDDSAKeyPair" + }, + "keyDer" : "302a300506032b65700321002362bac514d5fad33802642e979a1e82de6eb6f1bcbf6a5b304f2bb02b9e57fe", + "keyPem" : "-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAI2K6xRTV+tM4AmQul5oegt5utvG8v2pbME8rsCueV/4=\n-----END PUBLIC KEY-----\n", + "type" : "EDDSAVer", + "tests" : [ + { + "tcId" : 111, + "comment" : "Random test failure 27", + "msg" : "eef3bb0f617c17d0420c115c21c28e3762edc7b7fb048529b84a9c2bc6", + "sig" : "242ddb3a5d938d07af690b1b0ef0fa75842c5f9549bf39c8750f75614c712e7cbaf2e37cc0799db38b858d41aec5b9dd2fca6a3c8e082c10408e2cf3932b9d08", + "result" : "valid", + "flags" : [] + } + ] + } + ] +})abcd"; +} diff --git a/crypto/vm/cells/CellString.cpp b/crypto/vm/cells/CellString.cpp new file mode 100644 index 0000000000..ad2cbf5f8f --- /dev/null +++ b/crypto/vm/cells/CellString.cpp @@ -0,0 +1,64 @@ +#include "CellString.h" +#include "td/utils/misc.h" + +#include "vm/cells/CellSlice.h" + +namespace vm { +td::Status CellString::store(CellBuilder &cb, td::Slice slice, unsigned int top_bits) { + td::uint32 size = td::narrow_cast(slice.size() * 8); + return store(cb, td::BitSlice(slice.ubegin(), size), top_bits); +} + +td::Status CellString::store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits) { + if (slice.size() > max_bytes * 8) { + return td::Status::Error("String is too long (1)"); + } + unsigned int head = td::min(slice.size(), td::min(cb.remaining_bits(), top_bits)) / 8 * 8; + auto max_bits = vm::Cell::max_bits / 8 * 8; + auto depth = 1 + (slice.size() - head + max_bits - 1) / max_bits; + if (depth > max_chain_length) { + return td::Status::Error("String is too long (2)"); + } + cb.append_bitslice(slice.subslice(0, head)); + slice.advance(head); + if (slice.size() == 0) { + return td::Status::OK(); + } + CellBuilder child_cb; + store(child_cb, std::move(slice)); + cb.store_ref(child_cb.finalize()); + return td::Status::OK(); +} + +template +void CellString::for_each(F &&f, CellSlice &cs, unsigned int top_bits) { + unsigned int head = td::min(cs.size(), top_bits); + f(cs.prefetch_bits(head)); + if (!cs.have_refs()) { + return; + } + auto ref = cs.prefetch_ref(); + while (true) { + auto cs = vm::load_cell_slice(ref); + f(cs.prefetch_bits(cs.size())); + if (!cs.have_refs()) { + return; + } + ref = cs.prefetch_ref(); + } +} + +td::Result CellString::load(CellSlice &cs, unsigned int top_bits) { + unsigned int size = 0; + for_each([&](auto slice) { size += slice.size(); }, cs, top_bits); + if (size % 8 != 0) { + return td::Status::Error("Size is not divisible by 8"); + } + std::string res(size / 8, 0); + + td::BitPtr to(td::MutableSlice(res).ubegin()); + for_each([&](auto slice) { to.concat(slice); }, cs, top_bits); + CHECK(to.offs == (int)size); + return res; +} +} // namespace vm diff --git a/crypto/vm/cells/CellString.h b/crypto/vm/cells/CellString.h new file mode 100644 index 0000000000..89c933d876 --- /dev/null +++ b/crypto/vm/cells/CellString.h @@ -0,0 +1,22 @@ +#pragma once + +#include "td/utils/Status.h" + +#include "vm/cells/CellBuilder.h" + +namespace vm { +class CellString { + public: + static constexpr unsigned int max_bytes = 1024; + static constexpr unsigned int max_chain_length = 16; + + static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits); + static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits); + static td::Result load(CellSlice &cs, unsigned int top_bits = Cell::max_bits); + + private: + template + static void for_each(F &&f, CellSlice &cs, unsigned int top_bits = Cell::max_bits); +}; + +} // namespace vm diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 7b059dcb59..d4952769a5 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -744,6 +744,9 @@ int VmState::run() { } } while (!res); // LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells(); + if ((res | 1) == -1) { + commit(); + } return res; } diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index 1c5f8cd562..c6c9d320ed 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -154,7 +154,7 @@ struct ControlData { class Continuation : public td::CntObject { public: - virtual int jump(VmState* st) const & = 0; + virtual int jump(VmState* st) const& = 0; virtual int jump_w(VmState* st) &; virtual ControlData* get_cdata() { return 0; @@ -184,7 +184,7 @@ class QuitCont : public Continuation { QuitCont(int _code = 0) : exit_code(_code) { } ~QuitCont() override = default; - int jump(VmState* st) const & override { + int jump(VmState* st) const& override { return ~exit_code; } }; @@ -193,7 +193,7 @@ class ExcQuitCont : public Continuation { public: ExcQuitCont() = default; ~ExcQuitCont() override = default; - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; }; class PushIntCont : public Continuation { @@ -204,7 +204,7 @@ class PushIntCont : public Continuation { PushIntCont(int val, Ref _next) : push_val(val), next(_next) { } ~PushIntCont() override = default; - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; int jump_w(VmState* st) & override; }; @@ -217,7 +217,7 @@ class RepeatCont : public Continuation { : body(std::move(_body)), after(std::move(_after)), count(_count) { } ~RepeatCont() override = default; - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; int jump_w(VmState* st) & override; }; @@ -228,7 +228,7 @@ class AgainCont : public Continuation { AgainCont(Ref _body) : body(std::move(_body)) { } ~AgainCont() override = default; - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; int jump_w(VmState* st) & override; }; @@ -239,7 +239,7 @@ class UntilCont : public Continuation { UntilCont(Ref _body, Ref _after) : body(std::move(_body)), after(std::move(_after)) { } ~UntilCont() override = default; - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; int jump_w(VmState* st) & override; }; @@ -252,7 +252,7 @@ class WhileCont : public Continuation { : cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), chkcond(_chk) { } ~WhileCont() override = default; - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; int jump_w(VmState* st) & override; }; @@ -268,7 +268,7 @@ class ArgContExt : public Continuation { ArgContExt(const ArgContExt&) = default; ArgContExt(ArgContExt&&) = default; ~ArgContExt() override = default; - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; int jump_w(VmState* st) & override; ControlData* get_cdata() override { return &data; @@ -303,7 +303,7 @@ class OrdCont : public Continuation { td::CntObject* make_copy() const override { return new OrdCont{*this}; } - int jump(VmState* st) const & override; + int jump(VmState* st) const& override; int jump_w(VmState* st) & override; ControlData* get_cdata() override { @@ -321,7 +321,7 @@ class OrdCont : public Continuation { Ref get_stack_ref() const { return data.stack; } - Ref copy_ord() const & { + Ref copy_ord() const& { return Ref{true, *this}; } Ref copy_ord() && { @@ -375,10 +375,16 @@ struct GasLimits { } }; +struct CommittedState { + Ref c4, c5; + bool committed{false}; +}; + class VmState final : public VmStateInterface { Ref code; Ref stack; ControlRegs cr; + CommittedState cstate; int cp; long long steps{0}; const DispatchTable* dispatch; @@ -388,6 +394,8 @@ class VmState final : public VmStateInterface { std::vector> libraries; int stack_trace{0}, debug_off{0}; + bool chksig_always_succeed{false}; + public: static constexpr unsigned cell_load_gas_price = 100, cell_create_gas_price = 500, exception_gas_price = 50, tuple_entry_gas_price = 1; @@ -401,6 +409,10 @@ class VmState final : public VmStateInterface { VmState(Ref code_cell, Args&&... args) : VmState(convert_code_cell(std::move(code_cell)), std::forward(args)...) { } + VmState(const VmState&) = delete; + VmState(VmState&&) = delete; + VmState& operator=(const VmState&) = delete; + VmState& operator=(VmState&&) = delete; bool set_gas_limits(long long _max, long long _limit, long long _credit = 0); bool final_gas_ok() const { return gas.final_ok(); @@ -408,6 +420,12 @@ class VmState final : public VmStateInterface { long long gas_consumed() const { return gas.gas_consumed(); } + bool committed() const { + return cstate.committed; + } + const CommittedState& get_committed_state() const { + return cstate; + } void consume_gas(long long amount) { gas.consume(amount); } @@ -567,6 +585,18 @@ class VmState final : public VmStateInterface { return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this); } static Ref convert_code_cell(Ref code_cell); + void commit() { + cstate.c4 = cr.d[0]; + cstate.c5 = cr.d[1]; + cstate.committed = true; + } + + void set_chksig_always_succeed(bool flag) { + chksig_always_succeed = flag; + } + bool get_chksig_always_succeed() const { + return chksig_always_succeed; + } private: void init_cregs(bool same_c3 = false, bool push_0 = true); diff --git a/crypto/vm/excno.hpp b/crypto/vm/excno.hpp index f67d8a4e92..a18f890a36 100644 --- a/crypto/vm/excno.hpp +++ b/crypto/vm/excno.hpp @@ -18,6 +18,8 @@ */ #pragma once +#include "td/utils/Status.h" + namespace vm { enum class Excno : int { @@ -95,4 +97,19 @@ struct VmVirtError { struct VmFatal {}; +template +auto try_f(F&& f) noexcept -> decltype(f()) { + try { + return f(); + } catch (vm::VmError error) { + return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg()); + } catch (vm::VmVirtError error) { + return td::Status::Error(PSLICE() << "Got a vm virtualization exception: " << error.get_msg()); + } catch (vm::VmNoGas error) { + return td::Status::Error(PSLICE() << "Got a vm no gas exception: " << error.get_msg()); + } +} + +#define TRY_VM(f) ::vm::try_f([&] { return f; }) + } // namespace vm diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index 30d3d14b88..e2142f072f 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -75,10 +75,17 @@ int exec_set_gas_limit(VmState* st) { return exec_set_gas_generic(st, gas); } +int exec_commit(VmState* st) { + VM_LOG(st) << "execute COMMIT"; + st->commit(); + return 0; +} + void register_basic_gas_ops(OpcodeTable& cp0) { using namespace std::placeholders; cp0.insert(OpcodeInstr::mksimple(0xf800, 16, "ACCEPT", exec_accept)) - .insert(OpcodeInstr::mksimple(0xf801, 16, "SETGASLIMIT", exec_set_gas_limit)); + .insert(OpcodeInstr::mksimple(0xf801, 16, "SETGASLIMIT", exec_set_gas_limit)) + .insert(OpcodeInstr::mksimple(0xf80f, 16, "COMMIT", exec_commit)); } void register_ton_gas_ops(OpcodeTable& cp0) { @@ -268,7 +275,7 @@ int exec_ed25519_check_signature(VmState* st, bool from_slice) { } td::Ed25519::PublicKey pub_key{td::SecureString(td::Slice{key, 32})}; auto res = pub_key.verify_signature(td::Slice{data, data_len}, td::Slice{signature, 64}); - stack.push_bool(res.is_ok()); + stack.push_bool(res.is_ok() || st->get_chksig_always_succeed()); return 0; } diff --git a/doc/tvm.tex b/doc/tvm.tex index f103b343c4..26f7df2474 100644 --- a/doc/tvm.tex +++ b/doc/tvm.tex @@ -2160,7 +2160,8 @@ \section*{Introduction} \item {\tt F802} --- {\tt BUYGAS} ($x$ -- ), computes the amount of gas that can be bought for $x$ nanograms, and sets $g_l$ accordingly in the same way as {\tt SETGASLIMIT}. \item {\tt F804} --- {\tt GRAMTOGAS} ($x$ -- $g$), computes the amount of gas that can be bought for $x$ nanograms. If $x$ is negative, returns 0. If $g$ exceeds $2^{63}-1$, it is replaced with this value. \item {\tt F805} --- {\tt GASTOGRAM} ($g$ -- $x$), computes the price of $g$ gas in nanograms. -\item {\tt F806}--{\tt F80F} --- Reserved for gas-related primitives. +\item {\tt F806}--{\tt F80E} --- Reserved for gas-related primitives. +\item {\tt F80F} --- {\tt COMMIT} ( -- ), commits the current state of registers {\tt c4} (``persistent data'') and {\tt c5} (``actions'') so that the current execution is considered ``successful'' with the saved values even if an exception is thrown later. \end{itemize} \nxsubpoint\emb{Pseudo-random number generator primitives} diff --git a/example/android/src/drinkless/org/ton/Client.java b/example/android/src/drinkless/org/ton/Client.java index 79534c5b5f..fca82119a0 100644 --- a/example/android/src/drinkless/org/ton/Client.java +++ b/example/android/src/drinkless/org/ton/Client.java @@ -13,18 +13,18 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; /** - * Main class for interaction with the TDLib. + * Main class for interaction with the tonlib. */ public final class Client implements Runnable { static { System.loadLibrary("native-lib"); } /** - * Interface for handler for results of queries to TDLib and incoming updates from TDLib. + * Interface for handler for results of queries to tonlib and incoming updates from tonlib. */ public interface ResultHandler { /** - * Callback called on result of query to TDLib or incoming update from TDLib. + * Callback called on result of query to tonlib or incoming update from tonlib. * * @param object Result of query or update of type TonApi.Update about new events. */ @@ -46,9 +46,9 @@ public interface ExceptionHandler { } /** - * Sends a request to the TDLib. + * Sends a request to the tonlib. * - * @param query Object representing a query to the TDLib. + * @param query Object representing a query to the tonlib. * @param resultHandler Result handler with onResult method which will be called with result * of the query or with TonApi.Error as parameter. If it is null, nothing * will be called. @@ -80,9 +80,9 @@ public void send(TonApi.Function query, ResultHandler resultHandler, ExceptionHa } /** - * Sends a request to the TDLib with an empty ExceptionHandler. + * Sends a request to the tonlib with an empty ExceptionHandler. * - * @param query Object representing a query to the TDLib. + * @param query Object representing a query to the tonlib. * @param resultHandler Result handler with onResult method which will be called with result * of the query or with TonApi.Error as parameter. If it is null, then * defaultExceptionHandler will be called. @@ -93,9 +93,9 @@ public void send(TonApi.Function query, ResultHandler resultHandler) { } /** - * Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously. + * Synchronously executes a tonlib request. Only a few marked accordingly requests can be executed synchronously. * - * @param query Object representing a query to the TDLib. + * @param query Object representing a query to the tonlib. * @return request result. * @throws NullPointerException if query is null. */ @@ -107,10 +107,10 @@ public static TonApi.Object execute(TonApi.Function query) { } /** - * Replaces handler for incoming updates from the TDLib. + * Replaces handler for incoming updates from the tonlib. * * @param updatesHandler Handler with onResult method which will be called for every incoming - * update from the TDLib. + * update from the tonlib. * @param exceptionHandler Exception handler with onException method which will be called on * exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked. */ @@ -119,10 +119,10 @@ public void setUpdatesHandler(ResultHandler updatesHandler, ExceptionHandler exc } /** - * Replaces handler for incoming updates from the TDLib. Sets empty ExceptionHandler. + * Replaces handler for incoming updates from the tonlib. Sets empty ExceptionHandler. * * @param updatesHandler Handler with onResult method which will be called for every incoming - * update from the TDLib. + * update from the tonlib. */ public void setUpdatesHandler(ResultHandler updatesHandler) { setUpdatesHandler(updatesHandler, null); @@ -157,7 +157,7 @@ public void run() { */ public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) { Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler); - new Thread(client, "TDLib thread").start(); + new Thread(client, "tonlib thread").start(); return client; } diff --git a/example/android/test/ton/src/main/java/drinkless/org/ton/Client.java b/example/android/test/ton/src/main/java/drinkless/org/ton/Client.java index 79534c5b5f..fca82119a0 100644 --- a/example/android/test/ton/src/main/java/drinkless/org/ton/Client.java +++ b/example/android/test/ton/src/main/java/drinkless/org/ton/Client.java @@ -13,18 +13,18 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; /** - * Main class for interaction with the TDLib. + * Main class for interaction with the tonlib. */ public final class Client implements Runnable { static { System.loadLibrary("native-lib"); } /** - * Interface for handler for results of queries to TDLib and incoming updates from TDLib. + * Interface for handler for results of queries to tonlib and incoming updates from tonlib. */ public interface ResultHandler { /** - * Callback called on result of query to TDLib or incoming update from TDLib. + * Callback called on result of query to tonlib or incoming update from tonlib. * * @param object Result of query or update of type TonApi.Update about new events. */ @@ -46,9 +46,9 @@ public interface ExceptionHandler { } /** - * Sends a request to the TDLib. + * Sends a request to the tonlib. * - * @param query Object representing a query to the TDLib. + * @param query Object representing a query to the tonlib. * @param resultHandler Result handler with onResult method which will be called with result * of the query or with TonApi.Error as parameter. If it is null, nothing * will be called. @@ -80,9 +80,9 @@ public void send(TonApi.Function query, ResultHandler resultHandler, ExceptionHa } /** - * Sends a request to the TDLib with an empty ExceptionHandler. + * Sends a request to the tonlib with an empty ExceptionHandler. * - * @param query Object representing a query to the TDLib. + * @param query Object representing a query to the tonlib. * @param resultHandler Result handler with onResult method which will be called with result * of the query or with TonApi.Error as parameter. If it is null, then * defaultExceptionHandler will be called. @@ -93,9 +93,9 @@ public void send(TonApi.Function query, ResultHandler resultHandler) { } /** - * Synchronously executes a TDLib request. Only a few marked accordingly requests can be executed synchronously. + * Synchronously executes a tonlib request. Only a few marked accordingly requests can be executed synchronously. * - * @param query Object representing a query to the TDLib. + * @param query Object representing a query to the tonlib. * @return request result. * @throws NullPointerException if query is null. */ @@ -107,10 +107,10 @@ public static TonApi.Object execute(TonApi.Function query) { } /** - * Replaces handler for incoming updates from the TDLib. + * Replaces handler for incoming updates from the tonlib. * * @param updatesHandler Handler with onResult method which will be called for every incoming - * update from the TDLib. + * update from the tonlib. * @param exceptionHandler Exception handler with onException method which will be called on * exception thrown from updatesHandler, if it is null, defaultExceptionHandler will be invoked. */ @@ -119,10 +119,10 @@ public void setUpdatesHandler(ResultHandler updatesHandler, ExceptionHandler exc } /** - * Replaces handler for incoming updates from the TDLib. Sets empty ExceptionHandler. + * Replaces handler for incoming updates from the tonlib. Sets empty ExceptionHandler. * * @param updatesHandler Handler with onResult method which will be called for every incoming - * update from the TDLib. + * update from the tonlib. */ public void setUpdatesHandler(ResultHandler updatesHandler) { setUpdatesHandler(updatesHandler, null); @@ -157,7 +157,7 @@ public void run() { */ public static Client create(ResultHandler updatesHandler, ExceptionHandler updatesExceptionHandler, ExceptionHandler defaultExceptionHandler) { Client client = new Client(updatesHandler, updatesExceptionHandler, defaultExceptionHandler); - new Thread(client, "TDLib thread").start(); + new Thread(client, "tonlib thread").start(); return client; } diff --git a/example/cpp/tonjson_example.cpp b/example/cpp/tonjson_example.cpp index d00dec9fdd..c60d9fd082 100644 --- a/example/cpp/tonjson_example.cpp +++ b/example/cpp/tonjson_example.cpp @@ -28,12 +28,12 @@ #include #include -// Basic example of TONLib JSON interface usage. +// Basic example of tonlib JSON interface usage. // Native interface should be preferred instead in C++, so here is only an example of // the main event cycle, which should be essentially the same for all languages. int main() { - // disable TDLib logging + // disable tonlib logging void *client = tonlib_client_json_create(); // somehow share the client with other threads, which will be able to send requests via tonlib_client_json_send diff --git a/lite-client/lite-client-common.cpp b/lite-client/lite-client-common.cpp index cc6949deac..82503faf35 100644 --- a/lite-client/lite-client-common.cpp +++ b/lite-client/lite-client-common.cpp @@ -4,6 +4,7 @@ #include "tl-utils/lite-utils.hpp" #include "ton/lite-tl.hpp" #include "td/utils/overloaded.h" +#include "td/utils/Random.h" using namespace std::literals::string_literals; @@ -74,4 +75,25 @@ td::Result> deserialize_proof_chain( LOG(DEBUG) << "deserialized a BlkProofChain of " << chain->link_count() << " links"; return std::move(chain); } +td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref my_addr, + const block::CurrencyCollection& balance) { + td::BitArray<256> rand_seed; + td::RefInt256 rand_seed_int{true}; + td::Random::secure_bytes(rand_seed.as_slice()); + if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) { + return {}; + } + auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea + td::make_refint(0), // actions:Integer + td::make_refint(0), // msgs_sent:Integer + td::make_refint(now), // unixtime:Integer + td::make_refint(lt), // block_lt:Integer + td::make_refint(lt), // trans_lt:Integer + std::move(rand_seed_int), // rand_seed:Integer + balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] + my_addr, // myself:MsgAddressInt + vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo; + LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); + return vm::make_tuple_ref(std::move(tuple)); +} } // namespace liteclient diff --git a/lite-client/lite-client-common.h b/lite-client/lite-client-common.h index cbadd2b429..bca2c3079d 100644 --- a/lite-client/lite-client-common.h +++ b/lite-client/lite-client-common.h @@ -7,4 +7,7 @@ namespace liteclient { td::Result> deserialize_proof_chain( ton::lite_api::object_ptr f); -} + +td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref my_addr, + const block::CurrencyCollection& balance); +} // namespace liteclient diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 552979de81..54eb61b6ac 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -1223,28 +1223,6 @@ void TestNode::got_account_state(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, t } } -Ref TestNode::prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, Ref my_addr, - const block::CurrencyCollection& balance) const { - td::BitArray<256> rand_seed; - td::RefInt256 rand_seed_int{true}; - prng::rand_gen().rand_bytes(rand_seed.data(), 32); - if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) { - return {}; - } - auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea - td::make_refint(0), // actions:Integer - td::make_refint(0), // msgs_sent:Integer - td::make_refint(now), // unixtime:Integer - td::make_refint(lt), // block_lt:Integer - td::make_refint(lt), // trans_lt:Integer - std::move(rand_seed_int), // rand_seed:Integer - balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] - my_addr, // myself:MsgAddressInt - vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo; - LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); - return vm::make_tuple_ref(std::move(tuple)); -} - void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, @@ -1309,7 +1287,7 @@ void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton: vm::GasLimits gas{gas_limit}; LOG(DEBUG) << "creating VM"; vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()}; - vm.set_c7(prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo + vm.set_c7(liteclient::prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo // vm.incr_stack_trace(1); // enable stack dump after each step LOG(INFO) << "starting VM to run method `" << method << "` (" << method_id << ") of smart contract " << workchain << ":" << addr.to_hex(); diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 17cc295552..36830e8d5b 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -119,8 +119,6 @@ class TestNode : public td::actor::Actor { td::BufferSlice shard_proof, td::BufferSlice proof, td::BufferSlice state, ton::WorkchainId workchain, ton::StdSmcAddress addr, std::string method, std::vector params); - Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, Ref my_addr, - const block::CurrencyCollection& balance) const; bool get_all_shards(bool use_last = true, ton::BlockIdExt blkid = {}); void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data); bool get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = ""); diff --git a/tdactor/benchmark/benchmark.cpp b/tdactor/benchmark/benchmark.cpp index 09986294b5..442bb84c61 100644 --- a/tdactor/benchmark/benchmark.cpp +++ b/tdactor/benchmark/benchmark.cpp @@ -1145,6 +1145,273 @@ void run_queue_bench(int n, int m) { #endif } +struct Sem { + public: + void post() { + if (++cnt_ == 0) { + { + std::unique_lock lk(mutex_); + } + cnd_.notify_one(); + } + } + void wait(int cnt = 1) { + auto was = cnt_.fetch_sub(cnt); + if (was >= cnt) { + return; + } + std::unique_lock lk(mutex_); + cnd_.wait(lk, [&] { return cnt_ >= 0; }); + } + + private: + std::mutex mutex_; + std::condition_variable cnd_; + std::atomic cnt_{0}; +}; + +class ChainedSpawn : public td::Benchmark { + public: + ChainedSpawn(bool use_io) : use_io_(use_io) { + } + std::string get_description() const { + return PSTRING() << "Chained create_actor use_io(" << use_io_ << ")"; + } + + void run(int n) { + class Task : public td::actor::Actor { + public: + Task(int n, Sem *sem) : n_(n), sem_(sem) { + } + void start_up() override { + if (n_ == 0) { + sem_->post(); + } else { + td::actor::create_actor("Task", n_ - 1, sem_).release(); + } + stop(); + }; + + private: + int n_; + Sem *sem_{nullptr}; + }; + td::actor::Scheduler scheduler{{8}}; + auto sch = td::thread([&] { scheduler.run(); }); + + Sem sem; + scheduler.run_in_context_external([&] { + for (int i = 0; i < n; i++) { + td::actor::create_actor(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), 1000, &sem) + .release(); + sem.wait(); + } + td::actor::SchedulerContext::get()->stop(); + }); + + sch.join(); + } + + private: + bool use_io_{false}; +}; + +class ChainedSpawnInplace : public td::Benchmark { + public: + ChainedSpawnInplace(bool use_io) : use_io_(use_io) { + } + std::string get_description() const { + return PSTRING() << "Chained send_signal(self) use_io(" << use_io_ << ")"; + } + + void run(int n) { + class Task : public td::actor::Actor { + public: + Task(int n, Sem *sem) : n_(n), sem_(sem) { + } + void loop() override { + if (n_ == 0) { + sem_->post(); + stop(); + } else { + n_--; + send_signals(actor_id(this), td::actor::ActorSignals::wakeup()); + } + }; + + private: + int n_; + Sem *sem_; + }; + td::actor::Scheduler scheduler{{8}}; + auto sch = td::thread([&] { scheduler.run(); }); + + Sem sem; + scheduler.run_in_context_external([&] { + for (int i = 0; i < n; i++) { + td::actor::create_actor(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), 1000, &sem) + .release(); + sem.wait(); + } + td::actor::SchedulerContext::get()->stop(); + }); + + sch.join(); + } + + private: + bool use_io_{false}; +}; + +class PingPong : public td::Benchmark { + public: + PingPong(bool use_io) : use_io_(use_io) { + } + std::string get_description() const { + return PSTRING() << "PingPong use_io(" << use_io_ << ")"; + } + + void run(int n) { + if (n < 3) { + n = 3; + } + class Task : public td::actor::Actor { + public: + explicit Task(Sem *sem) : sem_(sem) { + } + void set_peer(td::actor::ActorId peer) { + peer_ = peer; + } + void ping(int n) { + if (n < 0) { + sem_->post(); + stop(); + } + send_closure(peer_, &Task::ping, n - 1); + } + + private: + td::actor::ActorId peer_; + Sem *sem_; + }; + td::actor::Scheduler scheduler{{8}}; + auto sch = td::thread([&] { scheduler.run(); }); + + Sem sem; + scheduler.run_in_context_external([&] { + for (int i = 0; i < n; i++) { + auto a = td::actor::create_actor(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), &sem) + .release(); + auto b = td::actor::create_actor(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), &sem) + .release(); + send_closure(a, &Task::set_peer, b); + send_closure(b, &Task::set_peer, a); + send_closure(a, &Task::ping, 1000); + sem.wait(2); + } + td::actor::SchedulerContext::get()->stop(); + }); + + sch.join(); + } + + private: + bool use_io_{false}; +}; + +class SpawnMany : public td::Benchmark { + public: + SpawnMany(bool use_io) : use_io_(use_io) { + } + std::string get_description() const { + return PSTRING() << "Spawn many use_io(" << use_io_ << ")"; + } + + void run(int n) { + class Task : public td::actor::Actor { + public: + Task(Sem *sem) : sem_(sem) { + } + void start_up() override { + sem_->post(); + stop(); + }; + + private: + Sem *sem_; + }; + td::actor::Scheduler scheduler{{8}}; + Sem sem; + auto sch = td::thread([&] { scheduler.run(); }); + scheduler.run_in_context_external([&] { + for (int i = 0; i < n; i++) { + int spawn_cnt = 10000; + for (int j = 0; j < spawn_cnt; j++) { + td::actor::create_actor(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), &sem).release(); + } + sem.wait(spawn_cnt); + } + td::actor::SchedulerContext::get()->stop(); + }); + sch.join(); + } + + private: + bool use_io_{false}; +}; + +class YieldMany : public td::Benchmark { + public: + YieldMany(bool use_io) : use_io_(use_io) { + } + std::string get_description() const { + return PSTRING() << "Yield many use_io(" << use_io_ << ")"; + } + + void run(int n) { + int num_yield = 1000; + unsigned tasks_per_cpu = 50; + unsigned cpu_n = td::thread::hardware_concurrency(); + class Task : public td::actor::Actor { + public: + explicit Task(int n, Sem *sem) : n_(n), sem_(sem) { + } + void loop() override { + if (n_ == 0) { + sem_->post(); + stop(); + } else { + n_--; + yield(); + } + }; + + private: + int n_; + Sem *sem_; + }; + td::actor::Scheduler scheduler{{cpu_n}}; + auto sch = td::thread([&] { scheduler.run(); }); + unsigned tasks = tasks_per_cpu * cpu_n; + Sem sem; + scheduler.run_in_context_external([&] { + for (int i = 0; i < n; i++) { + for (unsigned j = 0; j < tasks; j++) { + td::actor::create_actor(td::actor::ActorOptions().with_name("Task").with_poll(use_io_), num_yield, &sem) + .release(); + } + sem.wait(tasks); + } + }); + + scheduler.run_in_context_external([&] { td::actor::SchedulerContext::get()->stop(); }); + sch.join(); + } + + private: + bool use_io_{false}; +}; + int main(int argc, char **argv) { if (argc > 1) { if (argv[1][0] == 'a') { @@ -1159,6 +1426,18 @@ int main(int argc, char **argv) { return 0; } + bench(YieldMany(false)); + bench(YieldMany(true)); + bench(SpawnMany(false)); + bench(SpawnMany(true)); + bench(PingPong(false)); + bench(PingPong(true)); + bench(ChainedSpawnInplace(false)); + bench(ChainedSpawnInplace(true)); + bench(ChainedSpawn(false)); + bench(ChainedSpawn(true)); + return 0; + bench(ActorDummyQuery()); bench(ActorExecutorBenchmark()); bench(ActorSignalQuery()); diff --git a/tdactor/td/actor/PromiseFuture.h b/tdactor/td/actor/PromiseFuture.h index 1070e482df..9805a76cbe 100644 --- a/tdactor/td/actor/PromiseFuture.h +++ b/tdactor/td/actor/PromiseFuture.h @@ -225,6 +225,7 @@ class Promise { promise_->set_error(std::move(error)); promise_.reset(); } + void set_result(Result &&result) { if (!promise_) { return; @@ -260,6 +261,28 @@ class Promise { explicit operator bool() { return static_cast(promise_); } + template + auto do_wrap(V &&value, F &&func) { + if (value.is_ok()) { + set_result(func(value.move_as_ok())); + } else { + set_error(value.move_as_error()); + } + } + + template + auto do_wrap(td::Status status, F &&func) { + set_error(std::move(status)); + } + + template + auto wrap(F &&func) { + return [promise = std::move(*this), func = std::move(func)](auto &&res) mutable { + promise.do_wrap(std::move(res), std::move(func)); + }; + } + template + auto send_closure(ArgsT &&... args); private: std::unique_ptr> promise_; diff --git a/tdactor/td/actor/actor.h b/tdactor/td/actor/actor.h index 85685de721..b4eb6f6d68 100644 --- a/tdactor/td/actor/actor.h +++ b/tdactor/td/actor/actor.h @@ -162,4 +162,29 @@ void send_signals_later(ActorIdT &&actor_id, ActorSignals signals) { detail::send_signals_later(id.as_actor_ref(), signals); } } // namespace actor + +class SendClosure { + public: + template + void operator()(ArgsT &&... args) const { + td::actor::send_closure(std::forward(args)...); + } +}; + +template +template +auto Promise::send_closure(ArgsT &&... args) { + return [promise = std::move(*this), t = std::make_tuple(std::forward(args)...)](auto &&r_res) mutable { + TRY_RESULT_PROMISE(promise, res, std::move(r_res)); + td::call_tuple(SendClosure(), std::tuple_cat(std::move(t), std::make_tuple(std::move(res), std::move(promise)))); + }; +} + +template +auto promise_send_closure(ArgsT &&... args) { + return [t = std::make_tuple(std::forward(args)...)](auto &&res) mutable { + td::call_tuple(SendClosure(), std::tuple_cat(std::move(t), std::make_tuple(std::move(res)))); + }; +} + } // namespace td diff --git a/tdnet/td/net/TcpListener.cpp b/tdnet/td/net/TcpListener.cpp index dfc97e608d..5e187342be 100644 --- a/tdnet/td/net/TcpListener.cpp +++ b/tdnet/td/net/TcpListener.cpp @@ -73,5 +73,57 @@ void TcpListener::loop() { return stop(); } } +TcpInfiniteListener::TcpInfiniteListener(int32 port, std::unique_ptr callback) + : port_(port), callback_(std::move(callback)) { +} + +void TcpInfiniteListener::start_up() { + loop(); +} + +void TcpInfiniteListener::hangup() { + close_flag_ = true; + tcp_listener_.reset(); + if (refcnt_ == 0) { + stop(); + } +} + +void TcpInfiniteListener::loop() { + if (!tcp_listener_.empty()) { + return; + } + class Callback : public TcpListener::Callback { + public: + Callback(actor::ActorShared parent) : parent_(std::move(parent)) { + } + void accept(SocketFd fd) override { + actor::send_closure(parent_, &TcpInfiniteListener::accept, std::move(fd)); + } + + private: + actor::ActorShared parent_; + }; + refcnt_++; + tcp_listener_ = actor::create_actor( + actor::ActorOptions().with_name(PSLICE() << "TcpListener" << tag("port", port_)).with_poll(), port_, + std::make_unique(actor_shared(this))); +} + +void TcpInfiniteListener::accept(SocketFd fd) { + callback_->accept(std::move(fd)); +} + +void TcpInfiniteListener::hangup_shared() { + refcnt_--; + tcp_listener_.reset(); + if (close_flag_) { + if (refcnt_ == 0) { + stop(); + } + } else { + alarm_timestamp() = Timestamp::in(5 /*5 seconds*/); + } +} } // namespace td diff --git a/tdnet/td/net/TcpListener.h b/tdnet/td/net/TcpListener.h index bccf9e52f7..d83030eb0f 100644 --- a/tdnet/td/net/TcpListener.h +++ b/tdnet/td/net/TcpListener.h @@ -49,4 +49,23 @@ class TcpListener : public td::actor::Actor, private td::ObserverBase { void loop() override; }; + +class TcpInfiniteListener : public actor::Actor { + public: + TcpInfiniteListener(int32 port, std::unique_ptr callback); + + private: + int32 port_; + std::unique_ptr callback_; + actor::ActorOwn tcp_listener_; + int32 refcnt_{0}; + bool close_flag_{false}; + + void start_up() override; + + void hangup() override; + void loop() override; + void accept(SocketFd fd); + void hangup_shared() override; +}; } // namespace td diff --git a/tdnet/td/net/UdpServer.cpp b/tdnet/td/net/UdpServer.cpp index 1a88d17f14..7dff5e761a 100644 --- a/tdnet/td/net/UdpServer.cpp +++ b/tdnet/td/net/UdpServer.cpp @@ -130,70 +130,6 @@ void UdpServerImpl::hangup_shared() { stop(); } -class TcpInfiniteListener : public actor::Actor { - public: - TcpInfiniteListener(int32 port, std::unique_ptr callback) - : port_(port), callback_(std::move(callback)) { - } - - private: - int32 port_; - std::unique_ptr callback_; - actor::ActorOwn tcp_listener_; - int32 refcnt_{0}; - bool close_flag_{false}; - - void start_up() override { - loop(); - } - - void hangup() override { - close_flag_ = true; - tcp_listener_.reset(); - if (refcnt_ == 0) { - stop(); - } - } - - void loop() override { - if (!tcp_listener_.empty()) { - return; - } - class Callback : public TcpListener::Callback { - public: - Callback(actor::ActorShared parent) : parent_(std::move(parent)) { - } - void accept(SocketFd fd) override { - actor::send_closure(parent_, &TcpInfiniteListener::accept, std::move(fd)); - } - - private: - actor::ActorShared parent_; - }; - VLOG(udp_server) << "Create listener"; - refcnt_++; - tcp_listener_ = actor::create_actor( - actor::ActorOptions().with_name(PSLICE() << "TcpListener" << tag("port", port_)).with_poll(), port_, - std::make_unique(actor_shared(this))); - } - - void accept(SocketFd fd) { - callback_->accept(std::move(fd)); - } - - void hangup_shared() override { - refcnt_--; - tcp_listener_.reset(); - if (close_flag_) { - if (refcnt_ == 0) { - stop(); - } - } else { - alarm_timestamp() = Timestamp::in(5 /*5 seconds*/); - } - } -}; - class TcpClient : public td::actor::Actor, td::ObserverBase { public: class Callback { diff --git a/tdutils/td/utils/Status.h b/tdutils/td/utils/Status.h index 1759c0abef..6ac9210059 100644 --- a/tdutils/td/utils/Status.h +++ b/tdutils/td/utils/Status.h @@ -48,6 +48,15 @@ } \ } +#define TRY_STATUS_PROMISE(promise_name, status) \ + { \ + auto try_status = (status); \ + if (try_status.is_error()) { \ + promise_name.set_error(std::move(try_status)); \ + return; \ + } \ + } + #define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result) #define TRY_RESULT_PROMISE(promise_name, name, result) \ @@ -309,6 +318,11 @@ class Status { return std::move(*this); } + Auto move_as_ok() { + UNREACHABLE(); + return {}; + } + Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT { return status.move_as_error_suffix(message()); } diff --git a/tdutils/td/utils/invoke.h b/tdutils/td/utils/invoke.h index dad0c238d8..63b30ba25a 100644 --- a/tdutils/td/utils/invoke.h +++ b/tdutils/td/utils/invoke.h @@ -128,7 +128,7 @@ auto invoke(F &&f, } template -auto call_tuple_impl(F &func, std::tuple &&tuple, IntSeq) { +auto call_tuple_impl(F &&func, std::tuple &&tuple, IntSeq) { return func(std::forward(std::get(tuple))...); } @@ -163,7 +163,7 @@ class LogicAnd { }; template -auto call_tuple(F &func, std::tuple &&tuple) { +auto call_tuple(F &&func, std::tuple &&tuple) { return detail::call_tuple_impl(func, std::move(tuple), detail::IntRange()); } diff --git a/tdutils/td/utils/tests.h b/tdutils/td/utils/tests.h index eb1fc3a82c..464fa0ea87 100644 --- a/tdutils/td/utils/tests.h +++ b/tdutils/td/utils/tests.h @@ -38,6 +38,34 @@ namespace td { +class RandomSteps { + public: + struct Step { + std::function func; + td::uint32 weight; + }; + RandomSteps(std::vector steps) : steps_(std::move(steps)) { + for (auto &step : steps_) { + steps_sum_ += step.weight; + } + } + template + void step(Random &rnd) { + auto w = rnd() % steps_sum_; + for (auto &step : steps_) { + if (w < step.weight) { + step.func(); + break; + } + w -= step.weight; + } + } + + private: + std::vector steps_; + td::int32 steps_sum_ = 0; +}; + class RegressionTester { public: virtual ~RegressionTester() = default; diff --git a/tl/generate/JavadocTlDocumentationGenerator.php b/tl/generate/JavadocTlDocumentationGenerator.php index f33c9e99ad..155d972460 100644 --- a/tl/generate/JavadocTlDocumentationGenerator.php +++ b/tl/generate/JavadocTlDocumentationGenerator.php @@ -128,7 +128,7 @@ protected function addGlobalDocumentation() $this->addDocumentation('public class TdApi {', << * It has no inner classes, functions or public members. @@ -138,7 +138,7 @@ protected function addGlobalDocumentation() $this->addDocumentation(' public abstract static class Object {', <<addDocumentation(' public abstract static class Function extends Object {', <<85o!+I*M;L;SgXHRnJ&@6eOFLnUe~nfG%QS-~`DQ zaF&QMN^E{AAFWfru2QRB1j!Px1NE{&Y8gO&x_L~<2jtNrXEq%U+qQ!sF{sBGuF7%6 zM9u8og6u(n& zHy>*N>Xm_lfB_^2av$7$sO7~B43qz7>hkdb9T5)~o4n7-$QGmp+4;C)Aq^A@PM~0A zU;wEBS;I2<;pFVeJ0c_|e^BJ`1c@S>ge$fnPGw*K$%9Qn4~)qVm879TAi&EFwiq0r z#SD`ft)wMD_Ao*%0tF&_N@`AONovYu12qv)h_QeSo^0ZyzzIqXAR9O*E6!Az>}#pK zd5Mn-qYg+I*`bpr?sgM~CmK-jq^5w94oKbP2@x8c@MxNBSRga`=ox8{e_6oNXM8mv z44}Hn3Vs1#&gK%o70eQ#)CRI0WECv2fW#)71Zl8<83{oq5XOuk9aND?!6uwA{Y;Y$ zL)0ff@Mf8u5e!woAXo)WeF(adDIsV^PCf%uGa+4KvOuVa0muU6aQJg&<`Hp3_5h|8 WNIn9E1*&^BLT$kIZ_Wr6s0RQ5$6#Fm delta 313 zcmaE|iTTG0X5L4$^{p77V9iF}-|V7gb7yAprX=Yl<>V)4>v`s-vQ E05Umo{r~^~ diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 3fad6c36a6..2a3f0f52c3 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -24,7 +24,8 @@ config config:string blockchain_name:string use_callbacks_for_network:Bool ignor options config:config keystore_type:KeyStoreType = Options; key public_key:string secret:secureBytes = Key; -inputKey key:key local_password:secureBytes = InputKey; +inputKeyRegular key:key local_password:secureBytes = InputKey; +inputKeyFake = InputKey; exportedKey word_list:vector = ExportedKey; exportedPemKey pem:secureString = ExportedPemKey; exportedEncryptedKey data:secureBytes = ExportedEncryptedKey; @@ -68,29 +69,39 @@ sendGramsResult sent_until:int53 body_hash:bytes = SendGramsResult; syncStateDone = SyncState; syncStateInProgress from_seqno:int32 to_seqno:int32 current_seqno:int32 = SyncState; +fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53= Fees; +query.fees source_fees:fees destination_fees:fees = query.Fees; +query.info id:int53 valid_until:int53 body_hash:bytes = query.Info; + +smc.info id:int53 = smc.Info; + updateSendLiteServerQuery id:int64 data:bytes = Update; updateSyncState sync_state:SyncState = Update; -//@class LogStream @description Describes a stream to which TDLib internal log is written +//@class LogStream @description Describes a stream to which tonlib internal log is written //@description The log is written to stderr or an OS specific log logStreamDefault = LogStream; -//@description The log is written to a file @path Path to the file to where the internal TDLib log will be written @max_file_size Maximum size of the file to where the internal TDLib log is written before the file will be auto-rotated +//@description The log is written to a file @path Path to the file to where the internal tonlib log will be written @max_file_size Maximum size of the file to where the internal tonlib log is written before the file will be auto-rotated logStreamFile path:string max_file_size:int53 = LogStream; //@description The log is written nowhere logStreamEmpty = LogStream; -//@description Contains a TDLib internal log verbosity level @verbosity_level Log verbosity level +//@description Contains a tonlib internal log verbosity level @verbosity_level Log verbosity level logVerbosityLevel verbosity_level:int32 = LogVerbosityLevel; -//@description Contains a list of available TDLib internal log tags @tags List of log tags +//@description Contains a list of available tonlib internal log tags @tags List of log tags logTags tags:vector = LogTags; data bytes:secureBytes = Data; +tvm.cell bytes:string = tvm.Cell; + +liteServer.info now:int53 version:int32 capabilities:int64 = liteServer.Info; + ---functions--- init options:options = Ok; @@ -101,13 +112,13 @@ options.setConfig config:config = Ok; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; deleteKey key:key = Ok; deleteAllKeys = Ok; -exportKey input_key:inputKey = ExportedKey; -exportPemKey input_key:inputKey key_password:secureBytes = ExportedPemKey; -exportEncryptedKey input_key:inputKey key_password:secureBytes = ExportedEncryptedKey; +exportKey input_key:InputKey = ExportedKey; +exportPemKey input_key:InputKey key_password:secureBytes = ExportedPemKey; +exportEncryptedKey input_key:InputKey key_password:secureBytes = ExportedEncryptedKey; importKey local_password:secureBytes mnemonic_password:secureBytes exported_key:exportedKey = Key; importPemKey local_password:secureBytes key_password:secureBytes exported_key:exportedPemKey = Key; importEncryptedKey local_password:secureBytes key_password:secureBytes exported_encrypted_key:exportedEncryptedKey = Key; -changeLocalPassword input_key:inputKey new_local_password:secureBytes = Key; +changeLocalPassword input_key:InputKey new_local_password:secureBytes = Key; encrypt decrypted_data:secureBytes secret:secureBytes = Data; decrypt encrypted_data:secureBytes secret:secureBytes = Data; @@ -121,18 +132,20 @@ getBip39Hints prefix:string = Bip39Hints; //raw.init initial_account_state:raw.initialAccountState = Ok; raw.getAccountAddress initital_account_state:raw.initialAccountState = AccountAddress; raw.getAccountState account_address:accountAddress = raw.AccountState; -raw.sendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok; raw.getTransactions account_address:accountAddress from_transaction_id:internal.transactionId = raw.Transactions; +raw.sendMessage body:bytes = Ok; +raw.createAndSendMessage destination:accountAddress initial_account_state:bytes data:bytes = Ok; +raw.createQuery destination:accountAddress init_code:bytes init_data:bytes body:bytes = query.Info; -testWallet.init private_key:inputKey = Ok; +testWallet.init private_key:InputKey = Ok; testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress; testWallet.getAccountState account_address:accountAddress = testWallet.AccountState; -testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult; +testWallet.sendGrams private_key:InputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult; -wallet.init private_key:inputKey = Ok; +wallet.init private_key:InputKey = Ok; wallet.getAccountAddress initital_account_state:wallet.initialAccountState = AccountAddress; wallet.getAccountState account_address:accountAddress = wallet.AccountState; -wallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult; +wallet.sendGrams private_key:InputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult; testGiver.getAccountState = testGiver.AccountState; testGiver.getAccountAddress = AccountAddress; @@ -142,36 +155,50 @@ sync = Ok; //generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress; generic.getAccountState account_address:accountAddress = generic.AccountState; -generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = SendGramsResult; +generic.sendGrams private_key:InputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = SendGramsResult; + +generic.createSendGramsQuery private_key:InputKey source:accountAddress destination:accountAddress amount:int64 timeout:int32 allow_send_to_uninited:Bool message:bytes = query.Info; + +query.send id:int53 = Ok; +query.forget id:int53 = Ok; +query.estimateFees id:int53 ignore_chksig:Bool = query.Fees; +query.getInfo id:int53 = query.Info; + +smc.load account_address:accountAddress = smc.Info; +smc.getCode id:int53 = tvm.Cell; +smc.getData id:int53 = tvm.Cell; +smc.getState id:int53 = tvm.Cell; onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryError id:int64 error:error = Ok; runTests dir:string = Ok; -//@description Sets new log stream for internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously @log_stream New log stream +liteServer.getInfo = liteServer.Info; + +//@description Sets new log stream for internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously @log_stream New log stream setLogStream log_stream:LogStream = Ok; -//@description Returns information about currently used log stream for internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously +//@description Returns information about currently used log stream for internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously getLogStream = LogStream; -//@description Sets the verbosity level of the internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously +//@description Sets the verbosity level of the internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously //@new_verbosity_level New value of the verbosity level for logging. Value 0 corresponds to fatal errors, value 1 corresponds to errors, value 2 corresponds to warnings and debug warnings, value 3 corresponds to informational, value 4 corresponds to debug, value 5 corresponds to verbose debug, value greater than 5 and up to 1023 can be used to enable even more logging setLogVerbosityLevel new_verbosity_level:int32 = Ok; -//@description Returns current verbosity level of the internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously +//@description Returns current verbosity level of the internal logging of tonlib. This is an offline method. Can be called before authorization. Can be called synchronously getLogVerbosityLevel = LogVerbosityLevel; -//@description Returns list of available TDLib internal log tags, for example, ["actor", "binlog", "connections", "notifications", "proxy"]. This is an offline method. Can be called before authorization. Can be called synchronously +//@description Returns list of available tonlib internal log tags, for example, ["actor", "binlog", "connections", "notifications", "proxy"]. This is an offline method. Can be called before authorization. Can be called synchronously getLogTags = LogTags; -//@description Sets the verbosity level for a specified TDLib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously +//@description Sets the verbosity level for a specified tonlib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously //@tag Logging tag to change verbosity level @new_verbosity_level New verbosity level; 1-1024 setLogTagVerbosityLevel tag:string new_verbosity_level:int32 = Ok; -//@description Returns current verbosity level for a specified TDLib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously @tag Logging tag to change verbosity level +//@description Returns current verbosity level for a specified tonlib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously @tag Logging tag to change verbosity level getLogTagVerbosityLevel tag:string = LogVerbosityLevel; -//@description Adds a message to TDLib internal log. This is an offline method. Can be called before authorization. Can be called synchronously +//@description Adds a message to tonlib internal log. This is an offline method. Can be called before authorization. Can be called synchronously //@verbosity_level Minimum verbosity level needed for the message to be logged, 0-1023 @text Text of a message to log addLogMessage verbosity_level:int32 text:string = Ok; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index c48574b2d510507701d3edaa452b167321fe7267..d7c0e04957ea94a8765b1d6349812bb11a8c80ec 100644 GIT binary patch delta 1675 zcmaJ>Z)h837|$iw_VRDJwkb`Q*k(&&Ggs2OwCkK1+vw<4tb(>8vT42M-lpf0yYB9~ zQWV;usE8ZHhe@HLg{|P|mq|JtBAe4OPzr9KAIj7g#Ts#wfeeO%_&%4r7=qjT;kftt z`+I)R^SleWm08C7(|qS{n|YESdv7w%&o+Kbv;BlJ`Fyaz z+Xo$f?VndJ__C5thKPEcsEGl2Ovz6KHg#Uuz1?+elBm-O8X+%l_PNiUf6@6GpdS}tvP!VRV|Kg+>^+Oyu}`Kv3Oems}xBiXE_DAvv~slti6gKWEjZj7R% znDKx0TEieq|AQNA+gy@7GBzcSP&KUaEpTzOABsHB)nx?D5U%q97;fRI&;u(WE{*}L z;=gNk9r>XDmmhtSS?n_;Gno}sBmlm552x^_()iM2e_!@k#{I%kLX~KPh|o^$tL@H|>9j|6hqO73|Jsi{nYjDWQ6i}R89pzP%6d7I$2YjBiFd>XelyM0whrNwW zGgf?zfcfxX5fwc!c)~kouv0x|aBIq>nkGmHl4wJWNCblydeDX!iKa`kpi=;4L#nH= z-G{d2A~A^}pdSQL(RQrThWE-c?UHD;sAFdo?(uOJ^@Z-{bv9C-48zZ6!#jE+JcL&A zY{LGrs4H-xfZbmnn=~C8YKeDPafaZnu%E?C!QaZ;nH0prZ?czr`68ST@2W9cTDTov zva>TzyBFS%m@?QC?}R(90IRJIehATMSIHEfkG8R&p804MUX6B}qLL*Cx1#m!e~o)W z86p!^N+;4P5pX|b@t`W?w5M??C@?G(W@BN~dolL2Q9Im;>g-OReGSHA7OutEP)*k# zhfhA+P_bzlqT?Rfthu2f%S}L8;npKpnb7{H?p|vs?IAWYN0IbnyRl8s)fm9F*yNQm zvZ9ia^!QOt%D^dS>z3*^j=cNDC!SKAF-4`hVM}MDqoM`CNBH)eTMu9NmaHsz<%a)H z+mgru;>Lr+qBT9-YrYX0<;x~ry8Z2aT8{a`i(lQl5m1FGljHreh%`S*G)>6h7D7BR z_t4VYo|C@+j12y*DP-Z%#CE4sk|lj4t%z_e5pfjI)xN)<+x4f%%sZsh&eNC=@Q+se@4X)T+IX6?iXc*7Tryawi^9H`zqE0~UE_B4 z9q;Q)@c9mF4VXw9JG#7MD}<6MbFW@V{>reA+g>ihp&o02(CqH=#d=uiu~zMeo-h9e DqA@+m delta 375 zcmX@pz_=in_t9*9D+Vag-N<{Jk$0=h1!E4+yn@ma@6^i49~q@Ln=tt>PyWODVlof= zlF2vNXE2&>F63}$5do{rgsKFZ0pd^Y;0<8`F*Zx^@o;axBXxyklL8OZ<^&}cCPstF zsVeO(%7vY?Cx1|}UZ+f@+=I;KekTZqhW%qkYlorrr_jSt#lTFxkoQeexU_5dUB`c8X=hOlEABF<-#Ana3)IiKWu* zm&D{88>m$d>Iy84H=g=TK4D`6v3!Ay!X)dK&1-C37+KDFi$qWUVrKx60jbUUQ9Nm~ Qk3G~!V6_I5Z`hXr0I~mv<^TWy diff --git a/tonlib/CMakeLists.txt b/tonlib/CMakeLists.txt index 42b909ca27..26b14c3d50 100644 --- a/tonlib/CMakeLists.txt +++ b/tonlib/CMakeLists.txt @@ -5,42 +5,34 @@ if (NOT OPENSSL_FOUND) endif() set(TONLIB_SOURCE - tonlib/CellString.cpp tonlib/Client.cpp tonlib/Config.cpp tonlib/ExtClient.cpp tonlib/ExtClientLazy.cpp tonlib/ExtClientOutbound.cpp - tonlib/GenericAccount.cpp tonlib/KeyStorage.cpp tonlib/KeyValue.cpp tonlib/LastBlock.cpp tonlib/LastBlockStorage.cpp + tonlib/LastConfig.cpp tonlib/Logging.cpp - tonlib/TestGiver.cpp - tonlib/TestWallet.cpp tonlib/TonlibClient.cpp tonlib/utils.cpp - tonlib/Wallet.cpp - tonlib/CellString.h tonlib/Client.h tonlib/Config.h tonlib/ExtClient.h tonlib/ExtClientLazy.h tonlib/ExtClientOutbound.h - tonlib/GenericAccount.h tonlib/KeyStorage.h tonlib/KeyValue.h tonlib/LastBlock.h tonlib/LastBlockStorage.h + tonlib/LastConfig.h tonlib/Logging.h - tonlib/TestGiver.h - tonlib/TestWallet.h tonlib/TonlibCallback.h tonlib/TonlibClient.h tonlib/utils.h - tonlib/Wallet.h tonlib/keys/bip39.cpp tonlib/keys/DecryptedKey.cpp @@ -64,7 +56,7 @@ target_include_directories(tonlib PUBLIC $/.. $ ) -target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common) +target_link_libraries(tonlib PRIVATE tdactor adnllite tl_lite_api tl-lite-utils ton_crypto ton_block lite-client-common smc-envelope) target_link_libraries(tonlib PUBLIC tdutils tl_tonlib_api) if (TONLIB_ENABLE_JNI AND NOT ANDROID) # jni is available by default on Android @@ -134,7 +126,7 @@ if (NOT TON_USE_ABSEIL) if (WIN32) set(WINGETOPT_TARGET wingetopt) endif() -install(TARGETS tdnet keys crc32c tdactor adnllite tl_api tl-utils tl_lite_api tl-lite-utils ton_crypto ton_block ${WINGETOPT_TARGET} +install(TARGETS tdnet keys crc32c tdactor adnllite tl_api tl-utils tl_lite_api tl-lite-utils ton_crypto ton_block smc-envelope ${WINGETOPT_TARGET} tdutils tl_tonlib_api tonlib lite-client-common Tonlib EXPORT Tonlib LIBRARY DESTINATION lib ARCHIVE DESTINATION lib diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index 209b01d235..53d08bdbc8 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -17,23 +17,14 @@ Copyright 2017-2019 Telegram Systems LLP */ -#include "fift/Fift.h" -#include "fift/words.h" -#include "fift/utils.h" - #include "block/block.h" #include "block/block-auto.h" #include "vm/cells.h" #include "vm/boc.h" -#include "vm/cells/MerkleProof.h" +#include "vm/cells/CellString.h" -#include "tonlib/CellString.h" #include "tonlib/utils.h" -#include "tonlib/TestGiver.h" -#include "tonlib/TestWallet.h" -#include "tonlib/Wallet.h" -#include "tonlib/GenericAccount.h" #include "tonlib/TonlibClient.h" #include "tonlib/Client.h" @@ -70,181 +61,6 @@ TEST(Tonlib, CellString) { using namespace tonlib; -std::string current_dir() { - return td::PathView(td::realpath(__FILE__).move_as_ok()).parent_dir().str(); -} - -std::string load_source(std::string name) { - return td::read_file_str(current_dir() + "../../crypto/" + name).move_as_ok(); -} - -td::Ref get_test_wallet_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 -512 INT LDSLICEX DUP 32 PLDU // sign cs cnt -c4 PUSHCTR CTOS 32 LDU 256 LDU ENDS // sign cs cnt cnt' pubk -s1 s2 XCPU // sign cs cnt pubk cnt' cnt -EQUAL 33 THROWIFNOT // ( seqno mismatch? ) -s2 PUSH HASHSU // sign cs cnt pubk hash -s0 s4 s4 XC2PU // pubk cs cnt hash sign pubk -CHKSIGNU // pubk cs cnt ? -34 THROWIFNOT // signature mismatch -ACCEPT -SWAP 32 LDU NIP -DUP SREFS IF:<{ - // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance - 8 LDU LDREF // pubk cnt mode msg cs - s0 s2 XCHG SENDRAWMSG // pubk cnt cs ; ( message sent ) -}> -ENDS -INC NEWC 32 STU 256 STU ENDC c4 POPCTR -)ABCD"; - return fift::compile_asm(code).move_as_ok(); -} - -td::Ref get_wallet_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 // signature in_msg msg_seqno valid_until cs - SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs - c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key - s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno - EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno - s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash - s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key - ACCEPT - s0 s2 XCHG // public_key stored_seqno cs - WHILE:<{ - DUP SREFS // public_key stored_seqno cs _40 - }>DO<{ // public_key stored_seqno cs - // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance - 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode - SENDRAWMSG // public_key stored_seqno cs - }> - ENDS INC // public_key seqno' - NEWC 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()); - CHECK(get_test_wallet_source()->get_hash() == TestWallet::get_init_code()->get_hash()); - auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet.fif"), {"aba", "0"}).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 = TestWallet::get_init_state(pub_key); - auto init_message = TestWallet::get_init_message(priv_key); - auto address = GenericAccount::get_address(0, init_state); - - CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); - - td::Ref res = 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.fif")).ensure(); - 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", "123", - "321", "-C", "TEST"}) - .move_as_ok(); - auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; - auto gift_message = GenericAccount::create_ext_message( - address, {}, TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", 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()); -} - -td::Ref get_wallet_source_fc() { - return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok(); -} - -TEST(Tonlib, Wallet) { - LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok()); - CHECK(get_wallet_source()->get_hash() == Wallet::get_init_code()->get_hash()); - - auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.fif"), {"aba", "0"}).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 = Wallet::get_init_state(pub_key); - auto init_message = Wallet::get_init_message(priv_key); - auto address = GenericAccount::get_address(0, init_state); - - CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); - - td::Ref res = 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-v2.fif")).ensure(); - class ZeroOsTime : public fift::OsTime { - public: - td::uint32 now() override { - return 0; - } - }; - fift_output.source_lookup.set_os_time(std::make_unique()); - 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", "123", "321"}) - .move_as_ok(); - auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; - auto gift_message = GenericAccount::create_ext_message( - address, {}, Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", 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(); - LOG(ERROR) << address.bounceable; - auto fift_output = fift::mem_run_fift(load_source("smartcont/testgiver.fif"), - {"aba", address.rserialize(), "0", "6.666", "wallet-query"}) - .move_as_ok(); - LOG(ERROR) << fift_output.output; - - auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; - - auto res = GenericAccount::create_ext_message( - TestGiver::address(), {}, TestGiver::make_a_gift_message(0, 1000000000ll * 6666 / 1000, "GIFT", address)); - vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr); - CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash()); -} TEST(Tonlib, PublicKey) { block::PublicKey::parse("pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").ensure_error(); auto key = block::PublicKey::parse("Pubjns2gp7DGCnEH7EOWeCnb6Lw1akm538YYaz6sdLVHfRB2").move_as_ok(); @@ -480,15 +296,15 @@ TEST(Tonlib, KeysApi) { td::SecureString{})) .move_as_ok(); - sync_send(client, make_object(make_object( + sync_send(client, make_object(make_object( make_object(key->public_key_, key->secret_.copy()), td::SecureString("wrong password")))) .ensure_error(); - //exportKey input_key:inputKey = ExportedKey; + //exportKey input_key:inputKeyRegular = ExportedKey; auto exported_key = sync_send(client, - make_object(make_object( + make_object(make_object( make_object(key->public_key_, key->secret_.copy()), local_password.copy()))) .move_as_ok(); LOG(ERROR) << to_string(exported_key); @@ -500,20 +316,20 @@ TEST(Tonlib, KeysApi) { return word_list_copy; }; - //changeLocalPassword input_key:inputKey new_local_password:bytes = Key; + //changeLocalPassword input_key:inputKeyRegular new_local_password:bytes = Key; auto new_key = sync_send(client, make_object( - make_object( + make_object( make_object(key->public_key_, key->secret_.copy()), local_password.copy()), td::SecureString("tmp local password"))) .move_as_ok(); sync_send(client, - make_object(make_object( + make_object(make_object( make_object(key->public_key_, new_key->secret_.copy()), local_password.copy()))) .ensure_error(); - auto exported_key2 = sync_send(client, make_object(make_object( + auto exported_key2 = sync_send(client, make_object(make_object( make_object(key->public_key_, new_key->secret_.copy()), td::SecureString("tmp local password")))) .move_as_ok(); @@ -531,7 +347,7 @@ TEST(Tonlib, KeysApi) { auto wrong_export_password = td::SecureString("wrong_export password"); auto exported_encrypted_key = sync_send(client, make_object( - make_object( + make_object( make_object(key->public_key_, new_key->secret_.copy()), td::SecureString("tmp local password")), export_password.copy())) @@ -576,12 +392,12 @@ TEST(Tonlib, KeysApi) { CHECK(imported_key->public_key_ == key->public_key_); CHECK(imported_key->secret_ != key->secret_); - //exportPemKey input_key:inputKey key_password:bytes = ExportedPemKey; + //exportPemKey input_key:inputKeyRegular key_password:bytes = ExportedPemKey; auto pem_password = td::SecureString("pem password"); auto r_exported_pem_key = sync_send( client, make_object( - make_object( + make_object( make_object(key->public_key_, imported_key->secret_.copy()), new_local_password.copy()), pem_password.copy())); if (r_exported_pem_key.is_error() && r_exported_pem_key.error().message() == "INTERNAL Not supported") { diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 6ac47921df..08a3c36af4 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -34,10 +34,12 @@ #include "ton/ton-tl.hpp" #include "block/block.h" #include "block/block-auto.h" +#include "Ed25519.h" -#include "tonlib/GenericAccount.h" -#include "tonlib/TestGiver.h" -#include "tonlib/TestWallet.h" +#include "smc-envelope/GenericAccount.h" +#include "smc-envelope/MultisigWallet.h" +#include "smc-envelope/TestGiver.h" +#include "smc-envelope/TestWallet.h" #include "tonlib/LastBlock.h" #include "tonlib/ExtClient.h" #include "tonlib/utils.h" @@ -57,18 +59,21 @@ #include "td/utils/optional.h" #include "td/utils/overloaded.h" #include "td/utils/MpscPollableQueue.h" +#include "td/utils/port/path.h" #include "td/utils/port/signals.h" using namespace tonlib; +constexpr td::int64 Gramm = 1000000000; + auto sync_send = [](auto& client, auto query) { using ReturnTypePtr = typename std::decay_t::ReturnType; using ReturnType = typename ReturnTypePtr::element_type; client.send({1, std::move(query)}); while (true) { auto response = client.receive(100); - if (response.object) { + if (response.object && response.id != 0) { CHECK(response.id == 1); if (response.object->get_id() == tonlib_api::error::ID) { auto error = tonlib_api::move_object_as(response.object); @@ -78,87 +83,295 @@ auto sync_send = [](auto& client, auto query) { } } }; +auto static_send = [](auto query) { + using ReturnTypePtr = typename std::decay_t::ReturnType; + using ReturnType = typename ReturnTypePtr::element_type; + auto response = Client::execute({1, std::move(query)}); + if (response.object->get_id() == tonlib_api::error::ID) { + auto error = tonlib_api::move_object_as(response.object); + return td::Result(td::Status::Error(error->code_, error->message_)); + } + return td::Result(tonlib_api::move_object_as(response.object)); +}; struct Key { std::string public_key; td::SecureString secret; - tonlib_api::object_ptr get_input_key() const { - return tonlib_api::make_object( + tonlib_api::object_ptr get_input_key() const { + return tonlib_api::make_object( tonlib_api::make_object(public_key, secret.copy()), td::SecureString("local")); } + tonlib_api::object_ptr get_fake_input_key() const { + return tonlib_api::make_object(); + } }; struct Wallet { std::string address; Key key; }; +struct TransactionId { + td::int64 lt{0}; + std::string hash; +}; + +struct AccountState { + enum Type { Empty, Wallet, Unknown } type{Empty}; + td::int64 sync_utime{-1}; + td::int64 balance{-1}; + TransactionId last_transaction_id; + std::string address; + + bool is_inited() const { + return type != Empty; + } +}; + +using tonlib_api::make_object; + +void sync(Client& client) { + sync_send(client, make_object()).ensure(); +} + +std::string wallet_address(Client& client, const Key& key) { + return sync_send(client, make_object( + make_object(key.public_key))) + .move_as_ok() + ->account_address_; +} + +Wallet import_wallet_from_pkey(Client& client, std::string pkey, std::string password) { + auto key = sync_send(client, make_object( + td::SecureString("local"), td::SecureString(password), + make_object(td::SecureString(pkey)))) + .move_as_ok(); + Wallet wallet{"", {key->public_key_, std::move(key->secret_)}}; + wallet.address = wallet_address(client, wallet.key); + return wallet; +} + std::string test_giver_address(Client& client) { using tonlib_api::make_object; return sync_send(client, make_object()).move_as_ok()->account_address_; } -td::int64 get_balance(Client& client, std::string address) { +AccountState get_account_state(Client& client, std::string address) { auto generic_state = sync_send(client, tonlib_api::make_object( tonlib_api::make_object(address))) .move_as_ok(); - td::int64 res = 0; - tonlib_api::downcast_call(*generic_state, [&](auto& state) { res = state.account_state_->balance_; }); + AccountState res; + tonlib_api::downcast_call(*generic_state, [&](auto& state) { + res.balance = state.account_state_->balance_; + res.sync_utime = state.account_state_->sync_utime_; + res.last_transaction_id.lt = state.account_state_->last_transaction_id_->lt_; + res.last_transaction_id.hash = state.account_state_->last_transaction_id_->hash_; + }); + res.address = address; + switch (generic_state->get_id()) { + case tonlib_api::generic_accountStateUninited::ID: + res.type = AccountState::Empty; + break; + case tonlib_api::generic_accountStateWallet::ID: + res.type = AccountState::Wallet; + break; + default: + res.type = AccountState::Unknown; + break; + } return res; } -bool is_inited(Client& client, std::string address) { - auto generic_state = sync_send(client, tonlib_api::make_object( - tonlib_api::make_object(address))) - .move_as_ok(); - return generic_state->get_id() != tonlib_api::generic_accountStateUninited::ID; +struct QueryId { + td::int64 id; +}; + +struct Fee { + td::int64 in_fwd_fee{0}; + td::int64 storage_fee{0}; + td::int64 gas_fee{0}; + td::int64 fwd_fee{0}; + td::int64 sum() const { + return in_fwd_fee + storage_fee + gas_fee + fwd_fee; + } +}; + +template +auto to_fee(const T& fee) { + Fee res; + res.in_fwd_fee = fee->in_fwd_fee_; + res.storage_fee = fee->storage_fee_; + res.gas_fee = fee->gas_fee_; + res.fwd_fee = fee->fwd_fee_; + return res; } -void transfer_grams(Client& client, std::string from, std::string to, td::int64 amount, - tonlib_api::object_ptr input_key) { - auto balance = get_balance(client, to); - sync_send(client, tonlib_api::make_object( - std::move(input_key), tonlib_api::make_object(from), - tonlib_api::make_object(to), amount, 0, true, "GIFT")) - .ensure(); - while (balance == get_balance(client, to)) { +td::StringBuilder& operator<<(td::StringBuilder& sb, const Fee& fees) { + return sb << td::tag("in_fwd_fee", fees.in_fwd_fee) << td::tag("storage_fee", fees.storage_fee) + << td::tag("gas_fee", fees.gas_fee) << td::tag("fwd_fee", fees.fwd_fee); +} + +struct QueryInfo { + td::int64 valid_until; + std::string body_hash; +}; + +td::Result create_send_grams_query(Client& client, const Wallet& source, std::string destination, + td::int64 amount, std::string message, bool force = false, int timeout = 0, + bool fake = false) { + auto r_id = sync_send(client, tonlib_api::make_object( + fake ? source.key.get_fake_input_key() : source.key.get_input_key(), + tonlib_api::make_object(source.address), + tonlib_api::make_object(destination), amount, timeout, + force, std::move(message))); + TRY_RESULT(id, std::move(r_id)); + return QueryId{id->id_}; +} + +td::Result create_raw_query(Client& client, std::string source, std::string init_code, std::string init_data, + std::string body) { + auto r_id = + sync_send(client, tonlib_api::make_object( + tonlib_api::make_object(source), init_code, init_data, body)); + TRY_RESULT(id, std::move(r_id)); + return QueryId{id->id_}; +} + +std::pair query_estimate_fees(Client& client, QueryId query_id, bool ignore_chksig = false) { + auto fees = sync_send(client, tonlib_api::make_object(query_id.id, ignore_chksig)) + .move_as_ok(); + return std::make_pair(to_fee(fees->source_fees_), to_fee(fees->destination_fees_)); +} + +void query_send(Client& client, QueryId query_id) { + sync_send(client, tonlib_api::make_object(query_id.id)).ensure(); +} +QueryInfo query_get_info(Client& client, QueryId query_id) { + auto info = sync_send(client, tonlib_api::make_object(query_id.id)).move_as_ok(); + return QueryInfo{info->valid_until_, info->body_hash_}; +} + +td::Result wait_state_change(Client& client, const AccountState& old_state, td::int64 valid_until) { + while (true) { + auto new_state = get_account_state(client, old_state.address); + if (new_state.last_transaction_id.lt != old_state.last_transaction_id.lt) { + return new_state; + } + if (valid_until != 0 && new_state.sync_utime >= valid_until) { + return td::Status::Error("valid_until expired"); + } client.receive(1); } +}; + +td::Result> get_transactions(Client& client, std::string address, + const TransactionId& from) { + auto got_transactions = sync_send(client, make_object( + make_object(address), + make_object(from.lt, from.hash))) + .move_as_ok(); + return std::move(got_transactions); +} + +td::Status transfer_grams(Client& client, const Wallet& wallet, std::string address, td::int64 amount) { + auto src_state = get_account_state(client, wallet.address); + auto dst_state = get_account_state(client, address); + auto message = td::rand_string('a', 'z', 500); + + LOG(INFO) << "Transfer: create query " << (double)amount / Gramm << " from " << wallet.address << " to " << address; + auto r_query_id = create_send_grams_query(client, wallet, address, amount, message); + if (r_query_id.is_error() && td::begins_with(r_query_id.error().message(), "DANGEROUS_TRANSACTION")) { + ASSERT_TRUE(dst_state.type == AccountState::Empty); + LOG(INFO) << "Transfer: recreate query due to DANGEROUS_TRANSACTION error"; + r_query_id = create_send_grams_query(client, wallet, address, amount, message, true); + } + + r_query_id.ensure(); + QueryId query_id = r_query_id.move_as_ok(); + auto query_info = query_get_info(client, query_id); + auto fees = query_estimate_fees(client, query_id); + + LOG(INFO) << "Expected src fees: " << fees.first; + LOG(INFO) << "Expected dst fees: " << fees.second; + + bool transfer_all = amount == src_state.balance; + if (!transfer_all && amount + fees.first.sum() + 10 > src_state.balance) { + return td::Status::Error("Not enough balance for query"); + } + + LOG(INFO) << "Transfer: send query"; + + query_send(client, query_id); + td::Timer timer; + TRY_RESULT(new_src_state, wait_state_change(client, src_state, query_info.valid_until)); + LOG(INFO) << "Transfer: reached source in " << timer; + + td::int64 lt; + td::int64 first_fee; + { + auto tr = get_transactions(client, src_state.address, new_src_state.last_transaction_id).move_as_ok(); + CHECK(tr->transactions_.size() > 0); + const auto& txn = tr->transactions_[0]; + CHECK(txn->in_msg_->body_hash_ == query_info.body_hash); + ASSERT_EQ(1u, txn->out_msgs_.size()); + ASSERT_EQ(message, txn->out_msgs_[0]->message_); + lt = txn->out_msgs_[0]->created_lt_; + auto fee_difference = fees.first.sum() - txn->fee_; + first_fee = txn->fee_; + auto desc = PSTRING() << fee_difference << " storage:[" << fees.first.storage_fee << " vs " << txn->storage_fee_ + << "] other:[" << fees.first.sum() - fees.first.storage_fee << " vs " << txn->other_fee_ + << "]"; + LOG(INFO) << "Source fee difference " << desc; + LOG_IF(ERROR, std::abs(fee_difference) > 1) << "Too big source fee difference " << desc; + } + + TRY_RESULT(new_dst_state, wait_state_change(client, dst_state, new_src_state.sync_utime + 30)); + LOG(INFO) << "Transfer: reached destination in " << timer; + + { + auto tr = get_transactions(client, dst_state.address, new_dst_state.last_transaction_id).move_as_ok(); + CHECK(tr->transactions_.size() > 0); + const auto& txn = tr->transactions_[0]; + ASSERT_EQ(lt, txn->in_msg_->created_lt_); + if (transfer_all) { + ASSERT_EQ(amount - first_fee, txn->in_msg_->value_); + } else { + ASSERT_EQ(new_src_state.address, txn->in_msg_->source_); + } + ASSERT_EQ(new_src_state.address, txn->in_msg_->source_); + ASSERT_EQ(message, txn->in_msg_->message_); + auto fee_difference = fees.second.sum() - txn->fee_; + auto desc = PSTRING() << fee_difference << " storage:[" << fees.second.storage_fee << " vs " << txn->storage_fee_ + << "] other:[" << fees.second.sum() - fees.second.storage_fee << " vs " << txn->other_fee_ + << "]"; + LOG(INFO) << "Destination fee difference " << desc; + LOG_IF(ERROR, std::abs(fee_difference) > 1) << "Too big destination fee difference " << desc; + } + + return td::Status::OK(); } + Wallet create_empty_wallet(Client& client) { using tonlib_api::make_object; - auto key = sync_send(client, make_object(td::SecureString("local"), - td::SecureString("mnemonic"), td::SecureString())) + auto key = sync_send(client, make_object(td::SecureString("local"), td::SecureString(), + td::SecureString())) .move_as_ok(); Wallet wallet{"", {key->public_key_, std::move(key->secret_)}}; auto account_address = - sync_send(client, make_object( - make_object(wallet.key.public_key))) + sync_send(client, make_object( + make_object(wallet.key.public_key))) .move_as_ok(); wallet.address = account_address->account_address_; - return wallet; -} -Wallet create_wallet(Client& client) { - using tonlib_api::make_object; - auto wallet = create_empty_wallet(client); + // get state of empty account + auto state = get_account_state(client, wallet.address); + ASSERT_EQ(-1, state.balance); + ASSERT_EQ(AccountState::Empty, state.type); - transfer_grams(client, test_giver_address(client), wallet.address, 6000000000, {}); - sync_send(client, make_object(wallet.key.get_input_key())).ensure(); - while (!is_inited(client, wallet.address)) { - client.receive(1); - } - LOG(ERROR) << get_balance(client, wallet.address); return wallet; } -std::string get_test_giver_address(Client& client) { - return sync_send(client, tonlib_api::make_object()) - .move_as_ok() - ->account_address_; -} - void dump_transaction_history(Client& client, std::string address) { using tonlib_api::make_object; auto state = sync_send(client, make_object()).move_as_ok(); @@ -180,166 +393,156 @@ void dump_transaction_history(Client& client, std::string address) { LOG(ERROR) << cnt; } +void test_estimate_fees_without_key(Client& client, const Wallet& wallet_a, const Wallet& wallet_b) { + LOG(ERROR) << " SUBTEST: estimate fees without key"; + { + auto query_id = create_send_grams_query(client, wallet_a, wallet_b.address, 0, "???", true, 0, true).move_as_ok(); + auto fees1 = query_estimate_fees(client, query_id, false); + auto fees2 = query_estimate_fees(client, query_id, true); + LOG(INFO) << "Fee without ignore_chksig\t" << fees1; + LOG(INFO) << "Fee with ignore_chksig\t" << fees2; + CHECK(fees1.first.gas_fee == 0); + CHECK(fees2.first.gas_fee != 0); + } +} + +void test_back_and_forth_transfer(Client& client, const Wallet& giver_wallet, bool flag) { + LOG(ERROR) << "TEST: back and forth transfer"; + // just generate private key and address + auto wallet_a = create_empty_wallet(client); + LOG(INFO) << wallet_a.address; + + // get state of empty account + auto state = get_account_state(client, wallet_a.address); + ASSERT_EQ(-1, state.balance); + ASSERT_EQ(AccountState::Empty, state.type); + + test_estimate_fees_without_key(client, giver_wallet, wallet_a); + + // transfer from giver to a + transfer_grams(client, giver_wallet, wallet_a.address, 1 * Gramm).ensure(); + state = get_account_state(client, wallet_a.address); + ASSERT_EQ(1 * Gramm, state.balance); + ASSERT_EQ(AccountState::Empty, state.type); + + test_estimate_fees_without_key(client, wallet_a, giver_wallet); + + if (flag) { + // transfer from a to giver + transfer_grams(client, wallet_a, giver_wallet.address, 5 * Gramm / 10).ensure(); + state = get_account_state(client, wallet_a.address); + ASSERT_TRUE(state.balance < 5 * Gramm / 10); + ASSERT_EQ(AccountState::Wallet, state.type); + } + + // transfer all remaining balance (test flag 128) + transfer_grams(client, wallet_a, giver_wallet.address, state.balance).ensure(); + state = get_account_state(client, wallet_a.address); + ASSERT_TRUE(state.balance == 0); + ASSERT_EQ(AccountState::Wallet, state.type); +} + +void test_multisig(Client& client, const Wallet& giver_wallet) { + LOG(ERROR) << "TEST: multisig"; + + int n = 16; + int k = 10; + std::vector private_keys; + for (int i = 0; i < n; i++) { + private_keys.push_back(td::Ed25519::generate_private_key().move_as_ok()); + } + + auto ms = ton::MultisigWallet::create(); + auto init_data = ms->create_init_data( + td::transform(private_keys, [](const auto& pk) { return pk.get_public_key().move_as_ok().as_octet_string(); }), + k); + ms = ton::MultisigWallet::create(init_data); + auto raw_address = ms->get_address(ton::basechainId); + auto address = raw_address.rserialize(); + transfer_grams(client, giver_wallet, address, 1 * Gramm).ensure(); + auto init_state = ms->get_init_state(); + + // Just transfer all (some) money back in one query + vm::CellBuilder icb; + ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), + 5 * Gramm / 10); + icb.store_bytes("\0\0\0\0", 4); + vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure(); + ton::MultisigWallet::QueryBuilder qb(-1, icb.finalize()); + for (int i = 0; i < k - 1; i++) { + qb.sign(i, private_keys[i]); + } + + auto query_id = + create_raw_query(client, address, vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str(), + vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str(), + vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str()) + .move_as_ok(); + auto fees = query_estimate_fees(client, query_id); + + LOG(INFO) << "Expected src fees: " << fees.first; + LOG(INFO) << "Expected dst fees: " << fees.second; + auto a_state = get_account_state(client, address); + query_send(client, query_id); + auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok(); +} + int main(int argc, char* argv[]) { td::set_default_failure_signal_handler(); using tonlib_api::make_object; td::OptionsParser p; std::string global_config_str; + std::string giver_key_str; + std::string giver_key_pwd = "cucumber"; + std::string keystore_dir = "test-keystore"; + bool reset_keystore_dir = false; p.add_option('C', "global-config", "file to read global config", [&](td::Slice fname) { TRY_RESULT(str, td::read_file_str(fname.str())); global_config_str = std::move(str); - LOG(ERROR) << global_config_str; + return td::Status::OK(); + }); + p.add_option('G', "giver-key", "file with a wallet key that should be used as a giver", [&](td::Slice fname) { + TRY_RESULT(str, td::read_file_str(fname.str())); + giver_key_str = std::move(str); + return td::Status::OK(); + }); + p.add_option('f', "force", "reser keystore dir", [&]() { + reset_keystore_dir = true; return td::Status::OK(); }); p.run(argc, argv).ensure(); + if (reset_keystore_dir) { + td::rmrf(keystore_dir).ignore(); + td::mkdir(keystore_dir).ensure(); + } + + SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO)); + static_send(make_object("tonlib_query", 4)).ensure(); + auto tags = static_send(make_object()).move_as_ok()->tags_; + for (auto& tag : tags) { + static_send(make_object(tag, 4)).ensure(); + } + Client client; { sync_send(client, make_object(make_object( make_object(global_config_str, "", false, false), - make_object(".")))) + make_object(keystore_dir)))) .ensure(); } - //dump_transaction_history(client, get_test_giver_address(client)); - auto wallet_a = create_wallet(client); - auto wallet_b = create_empty_wallet(client); - transfer_grams(client, wallet_a.address, wallet_b.address, 3000000000, wallet_a.key.get_input_key()); - auto a = get_balance(client, wallet_a.address); - auto b = get_balance(client, wallet_b.address); - LOG(ERROR) << a << " " << b; - return 0; - { - // init - sync_send(client, make_object(make_object( - make_object(global_config_str, "", false, false), - make_object(".")))) - .ensure(); - - auto key = sync_send(client, make_object( - td::SecureString("local"), td::SecureString("mnemonic"), td::SecureString())) - .move_as_ok(); - - auto create_input_key = [&] { - return make_object(make_object(key->public_key_, key->secret_.copy()), - td::SecureString("local")); - }; - - auto public_key_raw = key->public_key_; - td::Ed25519::PublicKey public_key_std(td::SecureString{public_key_raw}); - - sync_send(client, make_object( - make_object(global_config_str, "", false, false))) - .ensure(); - auto wallet_addr = GenericAccount::get_address(0, TestWallet::get_init_state(public_key_std)); - { - auto account_address = - sync_send(client, make_object( - make_object(public_key_raw))) - .move_as_ok(); - ASSERT_EQ(wallet_addr.rserialize(), account_address->account_address_); - } + // wait till client is synchronized with blockchain. + // not necessary, but synchronized will be trigged anyway later + sync(client); - std::string test_giver_address; - { - auto account_address = sync_send(client, make_object()).move_as_ok(); - test_giver_address = account_address->account_address_; - ASSERT_EQ(TestGiver::address().rserialize(), test_giver_address); - } - - { - auto account_address = - sync_send( - client, - make_object(make_object( - vm::std_boc_serialize(TestWallet::get_init_code()).move_as_ok().as_slice().str(), - vm::std_boc_serialize(TestWallet::get_init_data(public_key_std)).move_as_ok().as_slice().str()))) - .move_as_ok(); - ASSERT_EQ(wallet_addr.rserialize(), account_address->account_address_); - } - - { - auto state = sync_send(client, make_object( - make_object(wallet_addr.rserialize()))) - .move_as_ok(); - LOG(ERROR) << to_string(state); - } - - td::int32 seqno = 0; - { - auto state = sync_send(client, make_object()).move_as_ok(); - LOG(ERROR) << to_string(state); - seqno = state->seqno_; - } + // give wallet with some test grams to run test + auto giver_wallet = import_wallet_from_pkey(client, giver_key_str, giver_key_pwd); - { - sync_send(client, make_object( - make_object(wallet_addr.rserialize()), seqno, - 1000000000ll * 6666 / 1000, "GIFT")) - .ensure(); - } - - while (true) { - auto state = sync_send(client, make_object()).move_as_ok(); - if (state->seqno_ > seqno) { - break; - } - client.receive(1); - } - - while (true) { - auto state = sync_send(client, make_object( - make_object(wallet_addr.rserialize()))) - .move_as_ok(); - td::int64 grams_count = state->balance_; - if (grams_count > 0) { - LOG(ERROR) << "GOT " << grams_count; - break; - } - client.receive(1); - } - - { sync_send(client, make_object(create_input_key())).ensure(); } - - while (true) { - auto r_state = sync_send(client, make_object( - make_object(wallet_addr.rserialize()))); - if (r_state.is_ok()) { - LOG(ERROR) << to_string(r_state.ok()); - break; - } - client.receive(1); - } - - { - sync_send(client, make_object( - create_input_key(), make_object(wallet_addr.rserialize()), - make_object(test_giver_address), 1000000000ll * 3333 / 1000, 0, - true, "GIFT")) - .ensure(); - } - while (true) { - auto generic_state = sync_send(client, make_object( - make_object(wallet_addr.rserialize()))) - .move_as_ok(); - if (generic_state->get_id() == tonlib_api::generic_accountStateTestWallet::ID) { - auto state = tonlib_api::move_object_as(generic_state); - if (state->account_state_->balance_ < 5617007000) { - LOG(ERROR) << to_string(state); - break; - } - } - client.receive(1); - } - { - auto generic_state = sync_send(client, make_object( - make_object(test_giver_address))) - .move_as_ok(); - CHECK(generic_state->get_id() == tonlib_api::generic_accountStateTestGiver::ID); - LOG(ERROR) << to_string(generic_state); - } - } + test_back_and_forth_transfer(client, giver_wallet, false); + test_back_and_forth_transfer(client, giver_wallet, true); + test_multisig(client, giver_wallet); return 0; } diff --git a/tonlib/tonlib/Client.cpp b/tonlib/tonlib/Client.cpp index d22e7ac792..83b41ad2fc 100644 --- a/tonlib/tonlib/Client.cpp +++ b/tonlib/tonlib/Client.cpp @@ -134,7 +134,7 @@ class Client::Impl final { }; Client::Client() : impl_(std::make_unique()) { - // At least it should be enough for everybody who uses TDLib + // At least it should be enough for everybody who uses tonlib // FIXME //td::init_openssl_threads(); } diff --git a/tonlib/tonlib/ExtClient.cpp b/tonlib/tonlib/ExtClient.cpp index 1ce77337c6..6981078efb 100644 --- a/tonlib/tonlib/ExtClient.cpp +++ b/tonlib/tonlib/ExtClient.cpp @@ -19,12 +19,27 @@ #include "tonlib/ExtClient.h" #include "tonlib/LastBlock.h" +#include "tonlib/LastConfig.h" namespace tonlib { ExtClient::~ExtClient() { + last_config_queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); }); last_block_queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); }); queries_.for_each([](auto id, auto &promise) { promise.set_error(TonlibError::Cancelled()); }); } +void ExtClient::with_last_config(td::Promise promise) { + auto query_id = last_config_queries_.create(std::move(promise)); + td::Promise P = [query_id, self = this, + actor_id = td::actor::actor_id()](td::Result result) { + send_lambda(actor_id, [self, query_id, result = std::move(result)]() mutable { + self->last_config_queries_.extract(query_id).set_result(std::move(result)); + }); + }; + if (client_.last_block_actor_.empty()) { + return P.set_error(TonlibError::NoLiteServers()); + } + td::actor::send_closure(client_.last_config_actor_, &LastConfig::get_last_config, std::move(P)); +} void ExtClient::with_last_block(td::Promise promise) { auto query_id = last_block_queries_.create(std::move(promise)); td::Promise P = [query_id, self = this, diff --git a/tonlib/tonlib/ExtClient.h b/tonlib/tonlib/ExtClient.h index beb0671fbe..b3cbbaf09d 100644 --- a/tonlib/tonlib/ExtClient.h +++ b/tonlib/tonlib/ExtClient.h @@ -33,10 +33,13 @@ namespace tonlib { class LastBlock; +class LastConfig; struct LastBlockState; +struct LastConfigState; struct ExtClientRef { td::actor::ActorId andl_ext_client_; td::actor::ActorId last_block_actor_; + td::actor::ActorId last_config_actor_; }; class ExtClient { @@ -56,6 +59,7 @@ class ExtClient { ~ExtClient(); void with_last_block(td::Promise promise); + void with_last_config(td::Promise promise); template void send_query(QueryT query, td::Promise promise, td::int32 seq_no = -1) { @@ -94,6 +98,7 @@ class ExtClient { ExtClientRef client_; td::Container> queries_; td::Container> last_block_queries_; + td::Container> last_config_queries_; void send_raw_query(td::BufferSlice query, td::Promise promise); }; diff --git a/tonlib/tonlib/ExtClientLazy.cpp b/tonlib/tonlib/ExtClientLazy.cpp index d5adb770e1..cd83e3abde 100644 --- a/tonlib/tonlib/ExtClientLazy.cpp +++ b/tonlib/tonlib/ExtClientLazy.cpp @@ -17,6 +17,7 @@ Copyright 2017-2019 Telegram Systems LLP */ #include "ExtClientLazy.h" +#include "TonlibError.h" namespace tonlib { class ExtClientLazyImp : public ton::adnl::AdnlExtClient { @@ -28,12 +29,18 @@ class ExtClientLazyImp : public ton::adnl::AdnlExtClient { void check_ready(td::Promise promise) override { before_query(); + if (client_.empty()) { + return promise.set_error(TonlibError::Cancelled()); + } send_closure(client_, &ton::adnl::AdnlExtClient::check_ready, std::move(promise)); } void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout, td::Promise promise) override { before_query(); + if (client_.empty()) { + return promise.set_error(TonlibError::Cancelled()); + } send_closure(client_, &ton::adnl::AdnlExtClient::send_query, std::move(name), std::move(data), timeout, std::move(promise)); } diff --git a/tonlib/tonlib/KeyStorage.cpp b/tonlib/tonlib/KeyStorage.cpp index 6e01f2f846..f1ac827156 100644 --- a/tonlib/tonlib/KeyStorage.cpp +++ b/tonlib/tonlib/KeyStorage.cpp @@ -106,6 +106,9 @@ td::Result KeyStorage::export_key(InputKey input_key) { } td::Result KeyStorage::load_private_key(InputKey input_key) { + if (is_fake_input_key(input_key)) { + return fake_private_key(); + } TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key))); PrivateKey private_key; private_key.private_key = decrypted_key.private_key.as_octet_string(); @@ -166,20 +169,46 @@ td::Result KeyStorage::import_pem_key(td::Slice local_password, return save_key(DecryptedKey({}, std::move(key)), local_password); } -static std::string dummy_secret = "dummy secret of 32 bytes length!"; +td::SecureString get_dummy_secret() { + return td::SecureString("dummy secret of 32 bytes length!"); +} td::Result KeyStorage::export_encrypted_key(InputKey input_key, td::Slice key_password) { TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key))); - auto res = decrypted_key.encrypt(key_password, dummy_secret); + auto res = decrypted_key.encrypt(key_password, get_dummy_secret()); return ExportedEncryptedKey{std::move(res.encrypted_data)}; } td::Result KeyStorage::import_encrypted_key(td::Slice local_password, td::Slice key_password, ExportedEncryptedKey exported_key) { EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()), - td::SecureString(dummy_secret)}; + get_dummy_secret()}; TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(key_password, false), TonlibError::KeyDecrypt()); return save_key(std::move(decrypted_key), local_password); } +KeyStorage::PrivateKey KeyStorage::fake_private_key() { + return PrivateKey{td::SecureString(32, 0)}; +} + +KeyStorage::InputKey KeyStorage::fake_input_key() { + return InputKey{{td::SecureString(32, 0), td::SecureString(32, 0)}, {}}; +} + +bool KeyStorage::is_fake_input_key(InputKey &input_key) { + auto is_zero = [](td::Slice slice, size_t size) { + if (slice.size() != size) { + return false; + } + for (auto c : slice) { + if (c != 0) { + return false; + } + } + return true; + }; + return is_zero(input_key.local_password, 0) && is_zero(input_key.key.secret, 32) && + is_zero(input_key.key.public_key, 32); +} + } // namespace tonlib diff --git a/tonlib/tonlib/KeyStorage.h b/tonlib/tonlib/KeyStorage.h index 1e37082885..38478bf1f3 100644 --- a/tonlib/tonlib/KeyStorage.h +++ b/tonlib/tonlib/KeyStorage.h @@ -69,6 +69,10 @@ class KeyStorage { td::Result load_private_key(InputKey input_key); + static PrivateKey fake_private_key(); + static InputKey fake_input_key(); + static bool is_fake_input_key(InputKey& input_key); + private: std::shared_ptr kv_; diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index 9740a24abe..234af99719 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -17,6 +17,7 @@ Copyright 2017-2019 Telegram Systems LLP */ #include "tonlib/LastBlock.h" +#include "tonlib/LastConfig.h" #include "tonlib/utils.h" @@ -271,7 +272,7 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice s } if (!state_.zero_state_id.is_valid()) { - LOG(INFO) << "Init zerostate from " << source << ": " << zero_state_id.to_str(); + VLOG(last_block) << "Init zerostate from " << source << ": " << zero_state_id.to_str(); state_.zero_state_id = std::move(zero_state_id); return; } @@ -295,7 +296,7 @@ bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) { } if (!state_.last_block_id.is_valid() || state_.last_block_id.id.seqno < mc_block_id.id.seqno) { state_.last_block_id = mc_block_id; - LOG(INFO) << "Update masterchain block id: " << state_.last_block_id.to_str(); + VLOG(last_block) << "Update masterchain block id: " << state_.last_block_id.to_str(); return true; } return false; @@ -311,7 +312,7 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) { } if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) { state_.last_key_block_id = mc_key_block_id; - LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str(); + VLOG(last_block) << "Update masterchain key block id: " << state_.last_key_block_id.to_str(); //LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " " //<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " " //<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice()); @@ -330,7 +331,7 @@ bool LastBlock::update_init_block(ton::BlockIdExt init_block_id) { } if (state_.init_block_id != init_block_id) { state_.init_block_id = init_block_id; - LOG(INFO) << "Update init block id: " << state_.init_block_id.to_str(); + VLOG(last_block) << "Update init block id: " << state_.init_block_id.to_str(); return true; } return false; diff --git a/tonlib/tonlib/LastConfig.cpp b/tonlib/tonlib/LastConfig.cpp new file mode 100644 index 0000000000..7207b6f0eb --- /dev/null +++ b/tonlib/tonlib/LastConfig.cpp @@ -0,0 +1,152 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "tonlib/LastConfig.h" + +#include "tonlib/utils.h" + +#include "ton/lite-tl.hpp" +#include "block/check-proof.h" +#include "block/mc-config.h" +#include "block/block-auto.h" + +#include "lite-client/lite-client-common.h" + +#include "LastBlock.h" + +namespace tonlib { + +// init_state <-> last_key_block +// state.valitated_init_state +// last_key_block -> +// +td::StringBuilder& operator<<(td::StringBuilder& sb, const LastConfigState& state) { + return sb; +} + +LastConfig::LastConfig(ExtClientRef client, td::unique_ptr callback) : callback_(std::move(callback)) { + client_.set_client(client); + VLOG(last_block) << "State: " << state_; +} + +void LastConfig::get_last_config(td::Promise promise) { + if (promises_.empty() && get_config_state_ == QueryState::Done) { + VLOG(last_config) << "start"; + VLOG(last_config) << "get_config: reset"; + get_config_state_ = QueryState::Empty; + } + + promises_.push_back(std::move(promise)); + loop(); +} + +void LastConfig::with_last_block(td::Result r_last_block) { + if (r_last_block.is_error()) { + on_error(r_last_block.move_as_error()); + return; + } + + auto last_block = r_last_block.move_as_ok(); + auto params = params_; + client_.send_query(ton::lite_api::liteServer_getConfigParams(0, create_tl_lite_block_id(last_block.last_block_id), + std::move(params)), + [this](auto r_config) { this->on_config(std::move(r_config)); }); +} + +void LastConfig::on_config(td::Result> r_config) { + auto status = process_config(std::move(r_config)); + if (status.is_ok()) { + on_ok(); + get_config_state_ = QueryState::Done; + } else { + on_error(std::move(status)); + get_config_state_ = QueryState::Empty; + } +} + +td::Status LastConfig::process_config( + td::Result> r_config) { + TRY_RESULT(raw_config, std::move(r_config)); + TRY_STATUS_PREFIX(TRY_VM(process_config_proof(std::move(raw_config))), TonlibError::ValidateConfig()); + return td::Status::OK(); +} + +td::Status LastConfig::process_config_proof(ton::ton_api::object_ptr raw_config) { + auto blkid = create_block_id(raw_config->id_); + if (!blkid.is_masterchain_ext()) { + return td::Status::Error(PSLICE() << "reference block " << blkid.to_str() + << " for the configuration is not a valid masterchain block"); + } + TRY_RESULT(state, block::check_extract_state_proof(blkid, raw_config->state_proof_.as_slice(), + raw_config->config_proof_.as_slice())); + TRY_RESULT(config, block::Config::extract_from_state(std::move(state), 0)); + + for (auto i : params_) { + VLOG(last_config) << "ConfigParam(" << i << ") = "; + auto value = config->get_config_param(i); + if (value.is_null()) { + VLOG(last_config) << "(null)\n"; + } else { + std::ostringstream os; + if (i >= 0) { + block::gen::ConfigParam{i}.print_ref(os, value); + os << std::endl; + } + vm::load_cell_slice(value).print_rec(os); + VLOG(last_config) << os.str(); + } + } + state_.config.reset(config.release()); + return td::Status::OK(); +} + +void LastConfig::loop() { + if (promises_.empty()) { + return; + } + + if (get_config_state_ == QueryState::Empty) { + VLOG(last_block) << "get_config: start"; + get_config_state_ = QueryState::Active; + client_.with_last_block( + [self = this](td::Result r_last_block) { self->with_last_block(std::move(r_last_block)); }); + } +} + +void LastConfig::on_ok() { + VLOG(last_block) << "ok " << state_; + for (auto& promise : promises_) { + auto state = state_; + promise.set_value(std::move(state)); + } + promises_.clear(); +} + +void LastConfig::on_error(td::Status status) { + VLOG(last_config) << "error " << status; + for (auto& promise : promises_) { + promise.set_error(status.clone()); + } + promises_.clear(); +} + +void LastConfig::tear_down() { + on_error(TonlibError::Cancelled()); +} + +} // namespace tonlib diff --git a/tonlib/tonlib/LastConfig.h b/tonlib/tonlib/LastConfig.h new file mode 100644 index 0000000000..2e0cf2a399 --- /dev/null +++ b/tonlib/tonlib/LastConfig.h @@ -0,0 +1,70 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once +#include "td/actor/actor.h" + +#include "tonlib/Config.h" +#include "tonlib/ExtClient.h" + +#include "td/utils/CancellationToken.h" +#include "td/utils/tl_helpers.h" + +#include "block/mc-config.h" + +namespace tonlib { +struct LastConfigState { + std::shared_ptr config; +}; + +td::StringBuilder& operator<<(td::StringBuilder& sb, const LastConfigState& state); + +class LastConfig : public td::actor::Actor { + public: + class Callback { + public: + virtual ~Callback() { + } + }; + + explicit LastConfig(ExtClientRef client, td::unique_ptr callback); + void get_last_config(td::Promise promise); + + private: + td::unique_ptr callback_; + ExtClient client_; + LastConfigState state_; + + enum class QueryState { Empty, Active, Done }; + QueryState get_config_state_{QueryState::Empty}; + + std::vector> promises_; + std::vector params_{18, 20, 21, 24, 25}; + + void with_last_block(td::Result r_last_block); + void on_config(td::Result> r_config); + td::Status process_config(td::Result> r_config); + td::Status process_config_proof(ton::ton_api::object_ptr config); + + void on_ok(); + void on_error(td::Status status); + + void loop() override; + void tear_down() override; +}; +} // namespace tonlib diff --git a/tonlib/tonlib/Logging.cpp b/tonlib/tonlib/Logging.cpp index 31dc275c00..a17b03ebe1 100644 --- a/tonlib/tonlib/Logging.cpp +++ b/tonlib/tonlib/Logging.cpp @@ -32,14 +32,22 @@ namespace tonlib { -static std::mutex logging_mutex; -static td::FileLog file_log; -static td::TsLog ts_log(&file_log); -static td::NullLog null_log; +struct LogData { + std::mutex logging_mutex; + td::FileLog file_log; + td::TsLog ts_log{&file_log}; + td::NullLog null_log; +}; + +auto &log_data() { + static LogData data; + return data; +} #define ADD_TAG(tag) \ { #tag, &VERBOSITY_NAME(tag) } -static const std::map log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block)}; +static const std::map log_tags{ADD_TAG(tonlib_query), ADD_TAG(last_block), ADD_TAG(last_config), + ADD_TAG(lite_server)}; #undef ADD_TAG td::Status Logging::set_current_stream(tonlib_api::object_ptr stream) { @@ -47,7 +55,7 @@ td::Status Logging::set_current_stream(tonlib_api::object_ptr lock(logging_mutex); + std::lock_guard lock(log_data().logging_mutex); switch (stream->get_id()) { case tonlib_api::logStreamDefault::ID: td::log_interface = td::default_log_interface; @@ -59,13 +67,13 @@ td::Status Logging::set_current_stream(tonlib_api::object_ptrpath_, max_log_file_size)); + TRY_STATUS(log_data().file_log.init(file_stream->path_, max_log_file_size)); std::atomic_thread_fence(std::memory_order_release); // better than nothing - td::log_interface = &ts_log; + td::log_interface = &log_data().ts_log; return td::Status::OK(); } case tonlib_api::logStreamEmpty::ID: - td::log_interface = &null_log; + td::log_interface = &log_data().null_log; return td::Status::OK(); default: UNREACHABLE(); @@ -74,22 +82,22 @@ td::Status Logging::set_current_stream(tonlib_api::object_ptr> Logging::get_current_stream() { - std::lock_guard lock(logging_mutex); + std::lock_guard lock(log_data().logging_mutex); if (td::log_interface == td::default_log_interface) { return tonlib_api::make_object(); } - if (td::log_interface == &null_log) { + if (td::log_interface == &log_data().null_log) { return tonlib_api::make_object(); } - if (td::log_interface == &ts_log) { - return tonlib_api::make_object(file_log.get_path().str(), - file_log.get_rotate_threshold()); + if (td::log_interface == &log_data().ts_log) { + return tonlib_api::make_object(log_data().file_log.get_path().str(), + log_data().file_log.get_rotate_threshold()); } return td::Status::Error("Log stream is unrecognized"); } td::Status Logging::set_verbosity_level(int new_verbosity_level) { - std::lock_guard lock(logging_mutex); + std::lock_guard lock(log_data().logging_mutex); if (0 <= new_verbosity_level && new_verbosity_level <= VERBOSITY_NAME(NEVER)) { SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + new_verbosity_level); return td::Status::OK(); @@ -99,7 +107,7 @@ td::Status Logging::set_verbosity_level(int new_verbosity_level) { } int Logging::get_verbosity_level() { - std::lock_guard lock(logging_mutex); + std::lock_guard lock(log_data().logging_mutex); return GET_VERBOSITY_LEVEL(); } @@ -113,7 +121,7 @@ td::Status Logging::set_tag_verbosity_level(td::Slice tag, int new_verbosity_lev return td::Status::Error("Log tag is not found"); } - std::lock_guard lock(logging_mutex); + std::lock_guard lock(log_data().logging_mutex); *it->second = td::clamp(new_verbosity_level, 1, VERBOSITY_NAME(NEVER)); return td::Status::OK(); } @@ -124,7 +132,7 @@ td::Result Logging::get_tag_verbosity_level(td::Slice tag) { return td::Status::Error("Log tag is not found"); } - std::lock_guard lock(logging_mutex); + std::lock_guard lock(log_data().logging_mutex); return *it->second; } diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 070630e737..edd8d6e751 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -20,18 +20,19 @@ #include "tonlib/ExtClientLazy.h" #include "tonlib/ExtClientOutbound.h" -#include "tonlib/GenericAccount.h" #include "tonlib/LastBlock.h" +#include "tonlib/LastConfig.h" #include "tonlib/Logging.h" -#include "tonlib/TestWallet.h" -#include "tonlib/Wallet.h" -#include "tonlib/TestGiver.h" #include "tonlib/utils.h" #include "tonlib/keys/Mnemonic.h" #include "tonlib/keys/SimpleEncryption.h" - #include "tonlib/TonlibError.h" +#include "smc-envelope/GenericAccount.h" +#include "smc-envelope/TestWallet.h" +#include "smc-envelope/Wallet.h" +#include "smc-envelope/TestGiver.h" + #include "auto/tl/tonlib_api.hpp" #include "block/block-auto.h" #include "block/check-proof.h" @@ -41,12 +42,43 @@ #include "vm/boc.h" #include "td/utils/Random.h" +#include "td/utils/optional.h" #include "td/utils/overloaded.h" #include "td/utils/tests.h" #include "td/utils/port/path.h" namespace tonlib { +namespace int_api { +struct GetAccountState { + block::StdAddress address; + using ReturnType = td::unique_ptr; +}; +struct GetPrivateKey { + KeyStorage::InputKey input_key; + using ReturnType = KeyStorage::PrivateKey; +}; +struct SendMessage { + td::Ref message; + using ReturnType = td::Unit; +}; +} // namespace int_api + +class TonlibQueryActor : public td::actor::Actor { + public: + TonlibQueryActor(td::actor::ActorShared client) : client_(std::move(client)) { + } + template + void send_query(QueryT query, td::Promise promise) { + td::actor::send_lambda(client_, + [self = client_.get(), query = std::move(query), promise = std::move(promise)]() mutable { + self.get_actor_unsafe().make_request(std::move(query), std::move(promise)); + }); + } + + private: + td::actor::ActorShared client_; +}; tonlib_api::object_ptr status_to_tonlib_api(const td::Status& status) { return tonlib_api::make_object(status.code(), status.message().str()); @@ -63,12 +95,427 @@ static block::AccountState create_account_state(ton::tl_object_ptr code; - td::Ref data; + + ton::UnixTime storage_last_paid{0}; + vm::CellStorageStat storage_stat; + + td::Ref code; + td::Ref data; + td::Ref state; std::string frozen_hash; block::AccountState::Info info; }; +tonlib_api::object_ptr empty_transaction_id() { + return tonlib_api::make_object(0, std::string(32, 0)); +} + +tonlib_api::object_ptr to_transaction_id(const block::AccountState::Info& info) { + return tonlib_api::make_object(info.last_trans_lt, + info.last_trans_hash.as_slice().str()); +} + +std::string to_bytes(td::Ref cell) { + return vm::std_boc_serialize(cell, vm::BagOfCells::Mode::WithCRC32C).move_as_ok().as_slice().str(); +} + +class AccountState { + public: + AccountState(block::StdAddress address, RawAccountState&& raw) : address_(std::move(address)), raw_(std::move(raw)) { + wallet_type_ = guess_type(); + } + + auto to_uninited_accountState() const { + return tonlib_api::make_object(get_balance(), to_transaction_id(raw().info), + raw().frozen_hash, get_sync_time()); + } + + td::Result> to_raw_accountState() const { + auto state = get_smc_state(); + std::string code; + if (state.code.not_null()) { + code = to_bytes(state.code); + } + std::string data; + if (state.data.not_null()) { + data = to_bytes(state.data); + } + return tonlib_api::make_object(get_balance(), std::move(code), std::move(data), + to_transaction_id(raw().info), raw().frozen_hash, + get_sync_time()); + } + + td::Result> to_testWallet_accountState() const { + if (wallet_type_ != SimpleWallet) { + return TonlibError::AccountTypeUnexpected("TestWallet"); + } + TRY_RESULT(seqno, ton::TestWallet(get_smc_state()).get_seqno()); + return tonlib_api::make_object(get_balance(), static_cast(seqno), + to_transaction_id(raw().info), get_sync_time()); + } + + td::Result> to_wallet_accountState() const { + if (wallet_type_ != Wallet) { + return TonlibError::AccountTypeUnexpected("Wallet"); + } + TRY_RESULT(seqno, ton::Wallet(get_smc_state()).get_seqno()); + return tonlib_api::make_object(get_balance(), static_cast(seqno), + to_transaction_id(raw().info), get_sync_time()); + } + + td::Result> to_testGiver_accountState() const { + if (wallet_type_ != Giver) { + return TonlibError::AccountTypeUnexpected("TestGiver"); + } + TRY_RESULT(seqno, ton::TestGiver(get_smc_state()).get_seqno()); + return tonlib_api::make_object(get_balance(), static_cast(seqno), + to_transaction_id(raw().info), get_sync_time()); + } + td::Result> to_generic_accountState() const { + switch (wallet_type_) { + case Empty: + return tonlib_api::make_object(to_uninited_accountState()); + case Unknown: { + TRY_RESULT(res, to_raw_accountState()); + return tonlib_api::make_object(std::move(res)); + } + case Giver: { + TRY_RESULT(res, to_testGiver_accountState()); + return tonlib_api::make_object(std::move(res)); + } + case SimpleWallet: { + TRY_RESULT(res, to_testWallet_accountState()); + return tonlib_api::make_object(std::move(res)); + } + case Wallet: { + TRY_RESULT(res, to_wallet_accountState()); + return tonlib_api::make_object(std::move(res)); + } + } + UNREACHABLE(); + } + + enum WalletType { Empty, Unknown, Giver, SimpleWallet, Wallet }; + WalletType get_wallet_type() const { + return wallet_type_; + } + bool is_frozen() const { + return !raw_.frozen_hash.empty(); + } + + const block::StdAddress& get_address() const { + return address_; + } + + void make_non_bounceable() { + address_.bounceable = false; + } + + td::uint32 get_sync_time() const { + return raw_.info.gen_utime; + } + + td::int64 get_balance() const { + return raw_.balance; + } + + const RawAccountState& raw() const { + return raw_; + } + + WalletType guess_type_by_public_key(td::Ed25519::PublicKey& key) { + if (wallet_type_ != WalletType::Empty) { + return wallet_type_; + } + if (ton::GenericAccount::get_address(address_.workchain, ton::TestWallet::get_init_state(key)).addr == + address_.addr) { + set_new_state({ton::TestWallet::get_init_code(), ton::TestWallet::get_init_data(key)}); + wallet_type_ = WalletType::SimpleWallet; + } else if (ton::GenericAccount::get_address(address_.workchain, ton::Wallet::get_init_state(key)).addr == + address_.addr) { + set_new_state({ton::Wallet::get_init_code(), ton::Wallet::get_init_data(key)}); + wallet_type_ = WalletType::Wallet; + } + return wallet_type_; + } + + WalletType guess_type_default(td::Ed25519::PublicKey& key) { + if (wallet_type_ != WalletType::Empty) { + return wallet_type_; + } + set_new_state({ton::Wallet::get_init_code(), ton::Wallet::get_init_data(key)}); + wallet_type_ = WalletType::Wallet; + return wallet_type_; + } + + ton::SmartContract::State get_smc_state() const { + return {raw_.code, raw_.data}; + } + + td::Ref get_raw_state() { + return raw_.state; + } + + void set_new_state(ton::SmartContract::State state) { + raw_.code = std::move(state.code); + raw_.data = std::move(state.data); + raw_.state = ton::GenericAccount::get_init_state(raw_.code, raw_.data); + has_new_state_ = true; + } + + td::Ref get_new_state() const { + if (!has_new_state_) { + return {}; + } + return raw_.state; + } + + private: + block::StdAddress address_; + RawAccountState raw_; + WalletType wallet_type_{Unknown}; + bool has_new_state_{false}; + + WalletType guess_type() const { + if (raw_.code.is_null()) { + return WalletType::Empty; + } + auto code_hash = raw_.code->get_hash(); + if (code_hash == ton::TestGiver::get_init_code_hash()) { + return WalletType::Giver; + } + if (code_hash == ton::TestWallet::get_init_code_hash()) { + return WalletType::SimpleWallet; + } + if (code_hash == ton::Wallet::get_init_code_hash()) { + return WalletType::Wallet; + } + LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice()); + return WalletType::Unknown; + } +}; + +class Query { + public: + struct Raw { + td::unique_ptr source; + td::unique_ptr destination; + + td::uint32 valid_until{std::numeric_limits::max()}; + + td::Ref message; + td::Ref new_state; + td::Ref message_body; + }; + + Query(Raw&& raw) : raw_(std::move(raw)) { + } + + td::Ref get_message() const { + return raw_.message; + } + + vm::CellHash get_body_hash() const { + return raw_.message_body->get_hash(); + } + + td::uint32 get_valid_until() const { + return raw_.valid_until; + } + + // ported from block/transaction.cpp + // TODO: reuse code + static td::RefInt256 compute_threshold(const block::GasLimitsPrices& cfg) { + auto gas_price256 = td::RefInt256{true, cfg.gas_price}; + if (cfg.gas_limit > cfg.flat_gas_limit) { + return td::rshift(gas_price256 * (cfg.gas_limit - cfg.flat_gas_limit), 16, 1) + + td::make_refint(cfg.flat_gas_price); + } else { + return td::make_refint(cfg.flat_gas_price); + } + } + + static td::uint64 gas_bought_for(td::RefInt256 nanograms, td::RefInt256 max_gas_threshold, + const block::GasLimitsPrices& cfg) { + if (nanograms.is_null() || sgn(nanograms) < 0) { + return 0; + } + if (nanograms >= max_gas_threshold) { + return cfg.gas_limit; + } + if (nanograms < cfg.flat_gas_price) { + return 0; + } + auto gas_price256 = td::RefInt256{true, cfg.gas_price}; + auto res = td::div((std::move(nanograms) - cfg.flat_gas_price) << 16, gas_price256); + return res->to_long() + cfg.flat_gas_limit; + } + + static td::RefInt256 compute_gas_price(td::uint64 gas_used, const block::GasLimitsPrices& cfg) { + auto gas_price256 = td::RefInt256{true, cfg.gas_price}; + return gas_used <= cfg.flat_gas_limit + ? td::make_refint(cfg.flat_gas_price) + : td::rshift(gas_price256 * (gas_used - cfg.flat_gas_limit), 16, 1) + cfg.flat_gas_price; + } + + static vm::GasLimits compute_gas_limits(td::RefInt256 balance, const block::GasLimitsPrices& cfg) { + vm::GasLimits res; + // Compute gas limits + if (false /*account.is_special*/) { + res.gas_max = cfg.special_gas_limit; + } else { + res.gas_max = gas_bought_for(balance, compute_threshold(cfg), cfg); + } + res.gas_credit = 0; + if (false /*trans_type != tr_ord*/) { + // may use all gas that can be bought using remaining balance + res.gas_limit = res.gas_max; + } else { + // originally use only gas bought using remaining message balance + // if the message is "accepted" by the smart contract, the gas limit will be set to gas_max + res.gas_limit = gas_bought_for(td::make_refint(0) /*msg balance remaining*/, compute_threshold(cfg), cfg); + if (true /*!block::tlb::t_Message.is_internal(in_msg)*/) { + // external messages carry no balance, give them some credit to check whether they are accepted + res.gas_credit = std::min(static_cast(cfg.gas_credit), static_cast(res.gas_max)); + } + } + LOG(DEBUG) << "gas limits: max=" << res.gas_max << ", limit=" << res.gas_limit << ", credit=" << res.gas_credit; + return res; + } + + struct Fee { + td::int64 in_fwd_fee{0}; + td::int64 storage_fee{0}; + td::int64 gas_fee{0}; + td::int64 fwd_fee{0}; + auto to_tonlib_api() const { + return tonlib_api::make_object(in_fwd_fee, storage_fee, gas_fee, fwd_fee); + } + }; + + td::Result calc_fwd_fees(td::Ref list, const block::MsgPrices& msg_prices) { + td::int64 res = 0; + std::vector> actions; + int n{0}; + int max_actions = 20; + while (true) { + actions.push_back(list); + auto cs = load_cell_slice(std::move(list)); + if (!cs.size_ext()) { + break; + } + if (!cs.have_refs()) { + return td::Status::Error("action list invalid: entry found with data but no next reference"); + } + list = cs.prefetch_ref(); + n++; + if (n > max_actions) { + return td::Status::Error(PSLICE() << "action list too long: more than " << max_actions << " actions"); + } + } + for (int i = n - 1; i >= 0; --i) { + vm::CellSlice cs = load_cell_slice(actions[i]); + CHECK(cs.fetch_ref().not_null()); + int tag = block::gen::t_OutAction.get_tag(cs); + CHECK(tag >= 0); + switch (tag) { + case block::gen::OutAction::action_set_code: + return td::Status::Error("estimate_fee: action_set_code unsupported"); + case block::gen::OutAction::action_send_msg: { + block::gen::OutAction::Record_action_send_msg act_rec; + // mode: +128 = attach all remaining balance, +64 = attach all remaining balance of the inbound message, +1 = pay message fees, +2 = skip if message cannot be sent + if (!tlb::unpack_exact(cs, act_rec) || (act_rec.mode & ~0xe3) || (act_rec.mode & 0xc0) == 0xc0) { + return td::Status::Error("estimate_fee: can't parse send_msg"); + } + block::gen::MessageRelaxed::Record msg; + if (!tlb::type_unpack_cell(act_rec.out_msg, block::gen::t_MessageRelaxed_Any, msg)) { + return td::Status::Error("estimate_fee: can't parse send_msg"); + } + vm::CellStorageStat sstat; // for message size + sstat.add_used_storage(msg.init, true, 3); // message init + sstat.add_used_storage(msg.body, true, 3); // message body (the root cell itself is not counted) + res += msg_prices.compute_fwd_fees(sstat.cells, sstat.bits); + break; + } + case block::gen::OutAction::action_reserve_currency: + return td::Status::Error("estimate_fee: action_reserve_currency unsupported"); + } + } + return res; + } + td::Result> estimate_fees(bool ignore_chksig, const block::Config& cfg) { + // gas fees + bool is_masterchain = raw_.source->get_address().workchain == ton::masterchainId; + bool dest_is_masterchain = raw_.destination && raw_.destination->get_address().workchain == ton::masterchainId; + TRY_RESULT(gas_limits_prices, cfg.get_gas_limits_prices(is_masterchain)); + TRY_RESULT(dest_gas_limits_prices, cfg.get_gas_limits_prices(dest_is_masterchain)); + TRY_RESULT(msg_prices, cfg.get_msg_prices(is_masterchain || dest_is_masterchain)); + TRY_RESULT(storage_prices, cfg.get_storage_prices()); + + auto storage_fee_256 = block::StoragePrices::compute_storage_fees( + raw_.source->get_sync_time(), storage_prices, raw_.source->raw().storage_stat, + raw_.source->raw().storage_last_paid, false, is_masterchain); + auto storage_fee = storage_fee_256.is_null() ? 0 : storage_fee_256->to_long(); + + auto dest_storage_fee_256 = + raw_.destination ? block::StoragePrices::compute_storage_fees( + raw_.destination->get_sync_time(), storage_prices, raw_.destination->raw().storage_stat, + raw_.destination->raw().storage_last_paid, false, is_masterchain) + : td::make_refint(0); + auto dest_storage_fee = dest_storage_fee_256.is_null() ? 0 : dest_storage_fee_256->to_long(); + + auto smc = ton::SmartContract::create(raw_.source->get_smc_state()); + + td::int64 in_fwd_fee = 0; + { + vm::CellStorageStat sstat; // for message size + sstat.add_used_storage(raw_.message, true, 3); // message init + in_fwd_fee += msg_prices.compute_fwd_fees(sstat.cells, sstat.bits); + } + + vm::GasLimits gas_limits = compute_gas_limits(td::make_refint(raw_.source->get_balance()), gas_limits_prices); + auto res = smc.write().send_external_message( + raw_.message_body, ton::SmartContract::Args().set_limits(gas_limits).set_ignore_chksig(ignore_chksig)); + td::int64 fwd_fee = 0; + if (res.success) { + //std::cerr << "new smart contract data: "; + //load_cell_slice(res.new_state.data).print_rec(std::cerr); + //std::cerr << "output actions: "; + //int out_act_num = output_actions_count(res.actions); + //block::gen::OutList{out_act_num}.print_ref(std::cerr, res.actions); + + TRY_RESULT_ASSIGN(fwd_fee, calc_fwd_fees(res.actions, msg_prices)); + } + + auto gas_fee = res.accepted ? compute_gas_price(res.gas_used, gas_limits_prices)->to_long() : 0; + LOG(ERROR) << storage_fee << " " << in_fwd_fee << " " << gas_fee << " " << fwd_fee; + + Fee fee; + fee.in_fwd_fee = in_fwd_fee; + fee.storage_fee = storage_fee; + fee.gas_fee = gas_fee; + fee.fwd_fee = fwd_fee; + + Fee dst_fee; + if (raw_.destination && raw_.destination->get_wallet_type() != AccountState::WalletType::Empty) { + dst_fee.gas_fee = dest_gas_limits_prices.flat_gas_price; + dst_fee.storage_fee = dest_storage_fee; + } + return std::make_pair(fee, dst_fee); + } + + private: + Raw raw_; + static int output_actions_count(td::Ref list) { + int i = -1; + do { + ++i; + list = load_cell_slice(std::move(list)).prefetch_ref(); + } while (list.not_null()); + return i; + } +}; // namespace tonlib + td::Result to_balance_or_throw(td::Ref balance_ref) { vm::CellSlice balance_slice = *balance_ref; auto balance = block::tlb::t_Grams.as_integer_skip(balance_slice); @@ -139,8 +586,8 @@ class GetTransactionHistory : public td::actor::Actor { td::Result do_with_transactions(std::vector blkids, td::BufferSlice transactions) { - LOG(INFO) << "got up to " << count_ << " transactions for " << address_ << " from last transaction " << lt_ << ":" - << hash_.to_hex(); + //LOG(INFO) << "got up to " << count_ << " transactions for " << address_ << " from last transaction " << lt_ << ":" + //<< hash_.to_hex(); block::TransactionList list; list.blkids = std::move(blkids); list.hash = hash_; @@ -211,7 +658,7 @@ class GetRawAccountState : public td::actor::Actor { auto cell = res.info.root; std::ostringstream outp; block::gen::t_Account.print_ref(outp, cell); - LOG(INFO) << outp.str(); + //LOG(INFO) << outp.str(); if (cell.is_null()) { return res; } @@ -219,6 +666,41 @@ class GetRawAccountState : public td::actor::Actor { if (!tlb::unpack_cell(cell, account)) { return td::Status::Error("Failed to unpack Account"); } + { + block::gen::StorageInfo::Record storage_info; + if (!tlb::csr_unpack(account.storage_stat, storage_info)) { + return td::Status::Error("Failed to unpack StorageInfo"); + } + res.storage_last_paid = storage_info.last_paid; + td::RefInt256 due_payment; + if (storage_info.due_payment->prefetch_ulong(1) == 1) { + vm::CellSlice& cs2 = storage_info.due_payment.write(); + cs2.advance(1); + due_payment = block::tlb::t_Grams.as_integer_skip(cs2); + if (due_payment.is_null() || !cs2.empty_ext()) { + return td::Status::Error("Failed to upack due_payment"); + } + } else { + due_payment = td::RefInt256{true, 0}; + } + block::gen::StorageUsed::Record storage_used; + if (!tlb::csr_unpack(storage_info.used, storage_used)) { + return td::Status::Error("Failed to unpack StorageInfo"); + } + unsigned long long u = 0; + vm::CellStorageStat storage_stat; + u |= storage_stat.cells = block::tlb::t_VarUInteger_7.as_uint(*storage_used.cells); + u |= storage_stat.bits = block::tlb::t_VarUInteger_7.as_uint(*storage_used.bits); + u |= storage_stat.public_cells = block::tlb::t_VarUInteger_7.as_uint(*storage_used.public_cells); + //LOG(DEBUG) << "last_paid=" << res.storage_last_paid << "; cells=" << storage_stat.cells + //<< " bits=" << storage_stat.bits << " public_cells=" << storage_stat.public_cells; + if (u == std::numeric_limits::max()) { + return td::Status::Error("Failed to unpack StorageStat"); + } + + res.storage_stat = storage_stat; + } + block::gen::AccountStorage::Record storage; if (!tlb::csr_unpack(account.storage, storage)) { return td::Status::Error("Failed to unpack AccountStorage"); @@ -245,12 +727,12 @@ class GetRawAccountState : public td::actor::Actor { return td::Status::Error("Failed to parse AccountState"); } block::gen::StateInit::Record state_init; + res.state = vm::CellBuilder().append_cellslice(state.x).finalize(); if (!tlb::csr_unpack(state.x, state_init)) { return td::Status::Error("Failed to parse StateInit"); } - res.code = std::move(state_init.code); - res.data = std::move(state_init.data); - + state_init.code->prefetch_maybe_ref(res.code); + state_init.data->prefetch_maybe_ref(res.data); return res; } @@ -295,6 +777,7 @@ void TonlibClient::hangup() { ref_cnt_--; raw_client_ = {}; raw_last_block_ = {}; + raw_last_config_ = {}; try_stop(); } @@ -302,6 +785,7 @@ ExtClientRef TonlibClient::get_client_ref() { ExtClientRef ref; ref.andl_ext_client_ = raw_client_.get(); ref.last_block_actor_ = raw_last_block_.get(); + ref.last_config_actor_ = raw_last_config_.get(); return ref; } @@ -413,6 +897,20 @@ void TonlibClient::init_last_block() { td::actor::ActorOptions().with_name("LastBlock").with_poll(false), get_client_ref(), std::move(state), config_, source_.get_cancellation_token(), td::make_unique(td::actor::actor_shared(this), config_generation_)); } +void TonlibClient::init_last_config() { + ref_cnt_++; + class Callback : public LastConfig::Callback { + public: + Callback(td::actor::ActorShared client) : client_(std::move(client)) { + } + + private: + td::actor::ActorShared client_; + }; + raw_last_config_ = + td::actor::create_actor(td::actor::ActorOptions().with_name("LastConfig").with_poll(false), + get_client_ref(), td::make_unique(td::actor::actor_shared(this))); +} void TonlibClient::on_result(td::uint64 id, tonlib_api::object_ptr response) { VLOG_IF(tonlib_query, id != 0) << "Tonlib answer query " << td::tag("id", id) << " " << to_string(response); @@ -461,11 +959,7 @@ void TonlibClient::request(td::uint64 id, tonlib_api::object_ptrdo_request(request, std::move(promise)); - if (status.is_error()) { - CHECK(promise); - promise.set_error(std::move(status)); - } + this->make_request(request, std::move(promise)); }); } void TonlibClient::close() { @@ -538,19 +1032,20 @@ td::Result get_public_key(td::Slice public_key) { td::Result get_account_address(const tonlib_api::raw_initialAccountState& raw_state) { TRY_RESULT_PREFIX(code, vm::std_boc_deserialize(raw_state.code_), TonlibError::InvalidBagOfCells("raw_state.code")); TRY_RESULT_PREFIX(data, vm::std_boc_deserialize(raw_state.data_), TonlibError::InvalidBagOfCells("raw_state.data")); - return GenericAccount::get_address(0 /*zerochain*/, GenericAccount::get_init_state(std::move(code), std::move(data))); + return ton::GenericAccount::get_address(0 /*zerochain*/, + ton::GenericAccount::get_init_state(std::move(code), std::move(data))); } td::Result get_account_address(const tonlib_api::testWallet_initialAccountState& test_wallet_state) { TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); - return GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)); + return ton::GenericAccount::get_address(0 /*zerochain*/, ton::TestWallet::get_init_state(key)); } td::Result get_account_address(const tonlib_api::wallet_initialAccountState& test_wallet_state) { TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); - return GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)); + return ton::GenericAccount::get_address(0 /*zerochain*/, ton::Wallet::get_init_state(key)); } td::Result get_account_address(td::Slice account_address) { @@ -587,7 +1082,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testGiver_getAccountAddress& request) { - return tonlib_api::make_object(TestGiver::address().rserialize(true)); + return tonlib_api::make_object(ton::TestGiver::address().rserialize(true)); } tonlib_api::object_ptr TonlibClient::do_static_request( @@ -675,6 +1170,7 @@ td::Status TonlibClient::set_config(object_ptr config) { ignore_cache_ = config->ignore_cache_; init_ext_client(); init_last_block(); + init_last_config(); client_.set_client(get_client_ref()); return td::Status::OK(); } @@ -698,35 +1194,6 @@ td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, return td::Status::OK(); } -tonlib_api::object_ptr empty_transaction_id() { - return tonlib_api::make_object(0, std::string(32, 0)); -} - -tonlib_api::object_ptr to_transaction_id(const block::AccountState::Info& info) { - return tonlib_api::make_object(info.last_trans_lt, - info.last_trans_hash.as_slice().str()); -} - -td::Result> to_raw_accountState(RawAccountState&& raw_state) { - std::string code; - if (raw_state.code.not_null()) { - code = vm::std_boc_serialize(vm::CellBuilder().append_cellslice(std::move(raw_state.code)).finalize()) - .move_as_ok() - .as_slice() - .str(); - } - std::string data; - if (raw_state.data.not_null()) { - data = vm::std_boc_serialize(vm::CellBuilder().append_cellslice(std::move(raw_state.data)).finalize()) - .move_as_ok() - .as_slice() - .str(); - } - return tonlib_api::make_object(raw_state.balance, std::move(code), std::move(data), - to_transaction_id(raw_state.info), raw_state.frozen_hash, - raw_state.info.gen_utime); -} - td::Result to_std_address_or_throw(td::Ref cs) { auto tag = block::gen::MsgAddressInt().get_tag(*cs); if (tag < 0) { @@ -833,8 +1300,7 @@ td::Result> to_raw_transacti td::int64 fees = 0; td::int64 storage_fee = 0; if (info.transaction.not_null()) { - TRY_RESULT(copy_data, vm::std_boc_serialize(info.transaction)); - data = copy_data.as_slice().str(); + data = to_bytes(info.transaction); block::gen::Transaction::Record trans; if (!tlb::unpack_cell(info.transaction, trans)) { return td::Status::Error("Failed to unpack Transaction"); @@ -842,9 +1308,9 @@ td::Result> to_raw_transacti TRY_RESULT_ASSIGN(fees, to_balance(trans.total_fees)); - std::ostringstream outp; - block::gen::t_Transaction.print_ref(outp, info.transaction); - LOG(INFO) << outp.str(); + //std::ostringstream outp; + //block::gen::t_Transaction.print_ref(outp, info.transaction); + //LOG(INFO) << outp.str(); auto is_just = trans.r1.in_msg->prefetch_long(1); if (is_just == trans.r1.in_msg->fetch_long_eof) { @@ -899,88 +1365,26 @@ td::Result> to_raw_transact return tonlib_api::make_object(std::move(transactions), std::move(transaction_id)); } -td::Result> to_testWallet_accountState( - RawAccountState&& raw_state) { - if (raw_state.code.is_null()) { - return TonlibError::AccountNotInited(); - } - if (raw_state.code->prefetch_ref()->get_hash() != TestWallet::get_init_code_hash()) { - return TonlibError::AccountTypeUnexpected("TestWallet"); - } - auto ref = raw_state.data->prefetch_ref(); - auto cs = vm::load_cell_slice(std::move(ref)); - auto seqno = cs.fetch_ulong(32); - if (seqno == cs.fetch_ulong_eof) { - return td::Status::Error("Failed to parse seq_no"); - } - return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); -} +// Raw -td::Result> to_wallet_accountState( - RawAccountState&& raw_state) { - if (raw_state.code.is_null()) { - return TonlibError::AccountNotInited(); - } - if (raw_state.code->prefetch_ref()->get_hash() != Wallet::get_init_code_hash()) { - return TonlibError::AccountTypeUnexpected("Wallet"); - } - auto ref = raw_state.data->prefetch_ref(); - auto cs = vm::load_cell_slice(std::move(ref)); - auto seqno = cs.fetch_ulong(32); - if (seqno == cs.fetch_ulong_eof) { - return td::Status::Error("Failed to parse seq_no"); - } - return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); +auto to_any_promise(td::Promise>&& promise) { + return promise.wrap([](auto x) { return tonlib_api::make_object(); }); } - -td::Result> to_testGiver_accountState( - RawAccountState&& raw_state) { - if (raw_state.code.is_null()) { - return TonlibError::AccountNotInited(); - } - if (raw_state.code->prefetch_ref()->get_hash() != TestGiver::get_init_code_hash()) { - return TonlibError::AccountTypeUnexpected("TestGiver"); - } - auto ref = raw_state.data->prefetch_ref(); - auto cs = vm::load_cell_slice(std::move(ref)); - auto seqno = cs.fetch_ulong(32); - if (seqno == cs.fetch_ulong_eof) { - return td::Status::Error("Failed to parse seq_no"); - } - return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); +auto to_any_promise(td::Promise&& promise) { + return promise.wrap([](auto x) { return td::Unit(); }); } -td::Result> to_generic_accountState( - RawAccountState&& raw_state) { - if (raw_state.code.is_null()) { - return tonlib_api::make_object( - tonlib_api::make_object(raw_state.balance, to_transaction_id(raw_state.info), - raw_state.frozen_hash, raw_state.info.gen_utime)); - } - - auto code_hash = raw_state.code->prefetch_ref()->get_hash(); - if (code_hash == TestWallet::get_init_code_hash()) { - TRY_RESULT(test_wallet, to_testWallet_accountState(std::move(raw_state))); - return tonlib_api::make_object(std::move(test_wallet)); - } - if (code_hash == Wallet::get_init_code_hash()) { - TRY_RESULT(wallet, to_wallet_accountState(std::move(raw_state))); - return tonlib_api::make_object(std::move(wallet)); - } - if (code_hash == TestGiver::get_init_code_hash()) { - TRY_RESULT(test_wallet, to_testGiver_accountState(std::move(raw_state))); - return tonlib_api::make_object(std::move(test_wallet)); - } - TRY_RESULT(raw, to_raw_accountState(std::move(raw_state))); - return tonlib_api::make_object(std::move(raw)); +td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, + td::Promise>&& promise) { + TRY_RESULT_PREFIX(body, vm::std_boc_deserialize(request.body_), TonlibError::InvalidBagOfCells("body")); + std::ostringstream os; + block::gen::t_Message_Any.print_ref(os, body); + LOG(ERROR) << os.str(); + make_request(int_api::SendMessage{std::move(body)}, to_any_promise(std::move(promise))); + return td::Status::OK(); } -// Raw - -td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, +td::Status TonlibClient::do_request(const tonlib_api::raw_createAndSendMessage& request, td::Promise>&& promise) { td::Ref init_state; if (!request.initial_account_state_.empty()) { @@ -990,12 +1394,9 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_sendMessage& request, } TRY_RESULT_PREFIX(data, vm::std_boc_deserialize(request.data_), TonlibError::InvalidBagOfCells("data")); TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); - auto message = GenericAccount::create_ext_message(account_address, std::move(init_state), std::move(data)); - client_.send_query(ton::lite_api::liteServer_sendMessage(vm::std_boc_serialize(message).move_as_ok()), - [promise = std::move(promise)](auto r_info) mutable { - TRY_RESULT_PROMISE(promise, info, std::move(r_info)); - promise.set_value(tonlib_api::make_object()); - }); + auto message = ton::GenericAccount::create_ext_message(account_address, std::move(init_state), std::move(data)); + + make_request(int_api::SendMessage{std::move(message)}, to_any_promise(std::move(promise))); return td::Status::OK(); } @@ -1005,13 +1406,8 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getAccountState& request, return TonlibError::EmptyField("account_address"); } TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); - auto actor_id = actor_id_++; - actors_[actor_id] = td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), - [promise = std::move(promise)](td::Result r_state) mutable { - TRY_RESULT_PROMISE(promise, state, std::move(r_state)); - promise.set_result(to_raw_accountState(std::move(state))); - }); + make_request(int_api::GetAccountState{std::move(account_address)}, + promise.wrap([](auto&& res) { return res->to_raw_accountState(); })); return td::Status::OK(); } @@ -1035,14 +1431,10 @@ td::Status TonlibClient::do_request(tonlib_api::raw_getTransactions& request, auto actor_id = actor_id_++; actors_[actor_id] = td::actor::create_actor( "GetTransactionHistory", client_.get_client(), account_address, lt, hash, actor_shared(this, actor_id), - [promise = std::move(promise)](td::Result r_info) mutable { - TRY_RESULT_PROMISE(promise, info, std::move(r_info)); - promise.set_result(to_raw_transactions(std::move(info))); - }); + promise.wrap(to_raw_transactions)); return td::Status::OK(); } - -td::Result from_tonlib(tonlib_api::inputKey& input_key) { +td::Result from_tonlib(tonlib_api::inputKeyRegular& input_key) { if (!input_key.key_) { return TonlibError::EmptyField("key"); } @@ -1052,22 +1444,28 @@ td::Result from_tonlib(tonlib_api::inputKey& input_key) { std::move(input_key.local_password_)}; } -// TestWallet +td::Result from_tonlib(tonlib_api::InputKey& input_key) { + td::Result r_key; + tonlib_api::downcast_call( + input_key, td::overloaded([&](tonlib_api::inputKeyRegular& input_key) { r_key = from_tonlib(input_key); }, + [&](tonlib_api::inputKeyFake&) { r_key = KeyStorage::fake_input_key(); })); + return r_key; +} + +// ton::TestWallet td::Status TonlibClient::do_request(const tonlib_api::testWallet_init& request, td::Promise>&& promise) { if (!request.private_key_) { return td::Status::Error(400, "Field private_key must not be empty"); } TRY_RESULT(input_key, from_tonlib(*request.private_key_)); - auto init_state = TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())); - auto address = GenericAccount::get_address(0 /*zerochain*/, init_state); + auto init_state = ton::TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())); + auto address = ton::GenericAccount::get_address(0 /*zerochain*/, init_state); TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); - auto init_message = TestWallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key))); - return do_request( - tonlib_api::raw_sendMessage(tonlib_api::make_object(address.rserialize(true)), - vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(), - vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()), - std::move(promise)); + auto init_message = ton::TestWallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key))); + auto message = ton::GenericAccount::create_ext_message(address, std::move(init_state), std::move(init_message)); + make_request(int_api::SendMessage{std::move(message)}, to_any_promise(std::move(promise))); + return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& request, @@ -1078,33 +1476,30 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ if (!request.private_key_) { return TonlibError::EmptyField("private_key"); } - if (request.message_.size() > TestWallet::max_message_size) { + if (request.message_.size() > ton::TestWallet::max_message_size) { return TonlibError::MessageTooLong(); } TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); TRY_RESULT(input_key, from_tonlib(*request.private_key_)); - auto address = GenericAccount::get_address( - 0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); + auto address = ton::GenericAccount::get_address( + 0 /*zerochain*/, ton::TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key))); auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); - std::string init_state; + td::Ref init_state; if (request.seqno_ == 0) { TRY_RESULT_PREFIX(public_key, private_key.get_public_key(), TonlibError::Internal()); - init_state = vm::std_boc_serialize(TestWallet::get_init_state(public_key)).move_as_ok().as_slice().str(); + init_state = ton::TestWallet::get_init_state(public_key); } - auto message = - TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, request.message_, account_address); + auto message = ton::TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, request.message_, + account_address); auto message_hash = message->get_hash().as_slice().str(); - td::Promise> new_promise = - [promise = std::move(promise), - message_hash = std::move(message_hash)](td::Result> res) mutable { - TRY_RESULT_PROMISE(promise, ok, std::move(res)); - promise.set_value(tonlib_api::make_object(0, std::move(message_hash))); - }; - return do_request(tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize(true)), - std::move(init_state), vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), - std::move(new_promise)); + auto new_promise = promise.wrap([message_hash = std::move(message_hash)](auto&&) { + return tonlib_api::make_object(0, std::move(message_hash)); + }); + + auto ext_message = ton::GenericAccount::create_ext_message(address, std::move(init_state), std::move(message)); + make_request(int_api::SendMessage{std::move(message)}, std::move(new_promise)); + return td::Status::OK(); } td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request, @@ -1113,32 +1508,27 @@ td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& requ return TonlibError::EmptyField("account_address"); } TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); - auto actor_id = actor_id_++; - actors_[actor_id] = td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), - [promise = std::move(promise)](td::Result r_state) mutable { - TRY_RESULT_PROMISE(promise, state, std::move(r_state)); - promise.set_result(to_testWallet_accountState(std::move(state))); - }); + make_request(int_api::GetAccountState{std::move(account_address)}, + promise.wrap([](auto&& res) { return res->to_testWallet_accountState(); })); return td::Status::OK(); } -// Wallet +// ton::Wallet td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request, td::Promise>&& promise) { if (!request.private_key_) { return TonlibError::EmptyField("private_key"); } TRY_RESULT(input_key, from_tonlib(*request.private_key_)); - auto init_state = Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())); - auto address = GenericAccount::get_address(0 /*zerochain*/, init_state); + auto init_state = ton::Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())); + auto address = ton::GenericAccount::get_address(0 /*zerochain*/, init_state); TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); - auto init_message = Wallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key))); - return do_request( - tonlib_api::raw_sendMessage(tonlib_api::make_object(address.rserialize(true)), - vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(), - vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()), - std::move(promise)); + auto init_message = ton::Wallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key))); + auto message = + ton::GenericAccount::create_ext_message(std::move(address), std::move(init_state), std::move(init_message)); + + make_request(int_api::SendMessage{std::move(message)}, to_any_promise(std::move(promise))); + return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, @@ -1149,35 +1539,31 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, if (!request.private_key_) { return TonlibError::EmptyField("private_key"); } - if (request.message_.size() > Wallet::max_message_size) { + if (request.message_.size() > ton::Wallet::max_message_size) { return TonlibError::MessageTooLong(); } TRY_RESULT_PREFIX(valid_until, td::narrow_cast_safe(request.valid_until_), TonlibError::InvalidField("valid_until", "overflow")); TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); TRY_RESULT(input_key, from_tonlib(*request.private_key_)); - auto address = GenericAccount::get_address( - 0 /*zerochain*/, Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); + auto address = ton::GenericAccount::get_address( + 0 /*zerochain*/, ton::Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key))); auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); - std::string init_state; + td::Ref init_state; if (request.seqno_ == 0) { TRY_RESULT_PREFIX(public_key, private_key.get_public_key(), TonlibError::Internal()); - init_state = vm::std_boc_serialize(Wallet::get_init_state(public_key)).move_as_ok().as_slice().str(); + init_state = ton::Wallet::get_init_state(public_key); } - auto message = Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, - request.message_, account_address); + auto message = ton::Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, + request.message_, account_address); auto message_hash = message->get_hash().as_slice().str(); - td::Promise> new_promise = - [promise = std::move(promise), valid_until, - message_hash = std::move(message_hash)](td::Result> res) mutable { - TRY_RESULT_PROMISE(promise, ok, std::move(res)); - promise.set_value(tonlib_api::make_object(valid_until, std::move(message_hash))); - }; - return do_request(tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize(true)), - std::move(init_state), vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), - std::move(new_promise)); + auto new_promise = promise.wrap([valid_until, message_hash = std::move(message_hash)](auto&&) { + return tonlib_api::make_object(valid_until, std::move(message_hash)); + }); + auto ext_message = ton::GenericAccount::create_ext_message(address, std::move(init_state), std::move(message)); + make_request(int_api::SendMessage{std::move(message)}, std::move(new_promise)); + return td::Status::OK(); } td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, @@ -1186,49 +1572,37 @@ td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, return TonlibError::EmptyField("account_address"); } TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); - auto actor_id = actor_id_++; - actors_[actor_id] = td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), - [promise = std::move(promise)](td::Result r_state) mutable { - TRY_RESULT_PROMISE(promise, state, std::move(r_state)); - promise.set_result(to_wallet_accountState(std::move(state))); - }); + make_request(int_api::GetAccountState{std::move(account_address)}, + promise.wrap([](auto&& res) { return res->to_wallet_accountState(); })); return td::Status::OK(); } -// TestGiver +// ton::TestGiver td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { return TonlibError::EmptyField("destination"); } - if (request.message_.size() > TestGiver::max_message_size) { + if (request.message_.size() > ton::TestGiver::max_message_size) { return TonlibError::MessageTooLong(); } TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); - auto message = TestGiver::make_a_gift_message(request.seqno_, request.amount_, request.message_, account_address); + auto message = + ton::TestGiver::make_a_gift_message(request.seqno_, request.amount_, request.message_, account_address); auto message_hash = message->get_hash().as_slice().str(); - td::Promise> new_promise = - [promise = std::move(promise), - message_hash = std::move(message_hash)](td::Result> res) mutable { - TRY_RESULT_PROMISE(promise, ok, std::move(res)); - promise.set_value(tonlib_api::make_object(0, std::move(message_hash))); - }; - return do_request(tonlib_api::raw_sendMessage( - tonlib_api::make_object(TestGiver::address().rserialize(true)), "", - vm::std_boc_serialize(std::move(message)).move_as_ok().as_slice().str()), - std::move(new_promise)); + auto new_promise = promise.wrap([message_hash = std::move(message_hash)](auto&&) { + return tonlib_api::make_object(0, std::move(message_hash)); + }); + + auto ext_message = ton::GenericAccount::create_ext_message(ton::TestGiver::address(), {}, std::move(message)); + make_request(int_api::SendMessage{std::move(message)}, std::move(new_promise)); + return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request, td::Promise>&& promise) { - auto actor_id = actor_id_++; - actors_[actor_id] = td::actor::create_actor( - "GetAccountState", client_.get_client(), TestGiver::address(), actor_shared(this, actor_id), - [promise = std::move(promise)](td::Result r_state) mutable { - TRY_RESULT_PROMISE(promise, state, std::move(r_state)); - promise.set_result(to_testGiver_accountState(std::move(state))); - }); + make_request(int_api::GetAccountState{ton::TestGiver::address()}, + promise.wrap([](auto&& res) { return res->to_testGiver_accountState(); })); return td::Status::OK(); } @@ -1238,51 +1612,29 @@ td::Status TonlibClient::do_request(const tonlib_api::generic_getAccountState& r return TonlibError::EmptyField("account_address"); } TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); - auto actor_id = actor_id_++; - actors_[actor_id] = td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), actor_shared(this, actor_id), - [promise = std::move(promise)](td::Result r_state) mutable { - TRY_RESULT_PROMISE(promise, state, std::move(r_state)); - promise.set_result(to_generic_accountState(std::move(state))); - }); + make_request(int_api::GetAccountState{std::move(account_address)}, + promise.wrap([](auto&& res) { return res->to_generic_accountState(); })); return td::Status::OK(); } -class TonlibQueryActor : public td::actor::Actor { +class GenericCreateSendGrams : public TonlibQueryActor { public: - TonlibQueryActor(td::actor::ActorShared client) : client_(std::move(client)) { - } - template - void send_query(QueryT query, td::Promise promise) { - td::actor::send_lambda(client_, - [self = client_.get(), query = std::move(query), promise = std::move(promise)]() mutable { - auto status = self.get_actor_unsafe().do_request(query, std::move(promise)); - if (status.is_error()) { - promise.set_error(std::move(status)); - } - }); - } - - private: - td::actor::ActorShared client_; -}; - -class GenericSendGrams : public TonlibQueryActor { - public: - GenericSendGrams(td::actor::ActorShared client, tonlib_api::generic_sendGrams send_grams, - td::Promise>&& promise) + GenericCreateSendGrams(td::actor::ActorShared client, + tonlib_api::generic_createSendGramsQuery send_grams, + td::Promise>&& promise) : TonlibQueryActor(std::move(client)), send_grams_(std::move(send_grams)), promise_(std::move(promise)) { } private: - tonlib_api::generic_sendGrams send_grams_; - td::Promise> promise_; + tonlib_api::generic_createSendGramsQuery send_grams_; + td::Promise> promise_; - tonlib_api::object_ptr source_state_; - block::StdAddress source_address_; - - tonlib_api::object_ptr destination_state_; - bool is_destination_bounceable_{false}; + td::unique_ptr source_; + td::unique_ptr destination_; + bool has_private_key_{false}; + bool is_fake_key_{false}; + td::optional private_key_; + td::optional public_key_; void check(td::Status status) { if (status.is_error()) { @@ -1299,182 +1651,380 @@ class GenericSendGrams : public TonlibQueryActor { } td::Status do_start_up() { + if (send_grams_.timeout_ < 0 || send_grams_.timeout_ > 300) { + return TonlibError::InvalidField("timeout", "must be between 0 and 300"); + } if (!send_grams_.destination_) { return TonlibError::EmptyField("destination"); } - TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_)); - is_destination_bounceable_ = destination_address.bounceable; - if (!send_grams_.source_) { - return TonlibError::EmptyField("destination"); + return TonlibError::EmptyField("source"); + } + if (send_grams_.amount_ < 0) { + return TonlibError::InvalidField("amount", "can't be negative"); } + // Use this limit as a preventive check + if (send_grams_.message_.size() > ton::Wallet::max_message_size) { + return TonlibError::MessageTooLong(); + } + TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_)); TRY_RESULT(source_address, get_account_address(send_grams_.source_->account_address_)); - source_address_ = std::move(source_address); - - send_query(tonlib_api::generic_getAccountState( - tonlib_api::make_object(send_grams_.source_->account_address_)), - [actor_id = actor_id(this)](auto r_res) { - send_closure(actor_id, &GenericSendGrams::on_source_state, std::move(r_res)); - }); - send_query(tonlib_api::generic_getAccountState( - tonlib_api::make_object(send_grams_.destination_->account_address_)), - [actor_id = actor_id(this)](auto r_res) { - send_closure(actor_id, &GenericSendGrams::on_destination_state, std::move(r_res)); - }); + + has_private_key_ = bool(send_grams_.private_key_); + if (has_private_key_) { + TRY_RESULT(input_key, from_tonlib(*send_grams_.private_key_)); + is_fake_key_ = send_grams_.private_key_->get_id() == tonlib_api::inputKeyFake::ID; + public_key_ = td::Ed25519::PublicKey(input_key.key.public_key.copy()); + send_query(int_api::GetPrivateKey{std::move(input_key)}, + promise_send_closure(actor_id(this), &GenericCreateSendGrams::on_private_key)); + } + + send_query(int_api::GetAccountState{source_address}, + promise_send_closure(actor_id(this), &GenericCreateSendGrams::on_source_state)); + + send_query(int_api::GetAccountState{destination_address}, + promise_send_closure(actor_id(this), &GenericCreateSendGrams::on_destination_state)); + return do_loop(); } - static tonlib_api::object_ptr clone(const tonlib_api::object_ptr& ptr) { - if (!ptr) { - return nullptr; - } - return tonlib_api::make_object(ptr->public_key_, ptr->secret_.copy()); + void on_private_key(td::Result r_key) { + check(do_on_private_key(std::move(r_key))); } - static tonlib_api::object_ptr clone(const tonlib_api::object_ptr& ptr) { - if (!ptr) { - return nullptr; - } - return tonlib_api::make_object(clone(ptr->key_), ptr->local_password_.copy()); + td::Status do_on_private_key(td::Result r_key) { + TRY_RESULT(key, std::move(r_key)); + private_key_ = td::Ed25519::PrivateKey(std::move(key.private_key)); + return do_loop(); } - void on_source_state(td::Result> r_state) { + void on_source_state(td::Result> r_state) { check(do_on_source_state(std::move(r_state))); } - td::Status do_on_source_state(td::Result> r_state) { + td::Status do_on_source_state(td::Result> r_state) { TRY_RESULT(state, std::move(r_state)); - source_state_ = std::move(state); - if (source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && send_grams_.private_key_ && - send_grams_.private_key_->key_) { - TRY_RESULT(key_bytes, get_public_key(send_grams_.private_key_->key_->public_key_)); - auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); - - if (GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)).addr == source_address_.addr) { - auto state = ton::move_tl_object_as(source_state_); - source_state_ = tonlib_api::make_object( - tonlib_api::make_object(state->account_state_->balance_, 0, nullptr, - state->account_state_->sync_utime_)); - } else if (GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)).addr == - source_address_.addr) { - auto state = ton::move_tl_object_as(source_state_); - source_state_ = tonlib_api::make_object( - tonlib_api::make_object(state->account_state_->balance_, 0, nullptr, - state->account_state_->sync_utime_)); - } + source_ = std::move(state); + if (source_->get_wallet_type() == AccountState::Empty && public_key_) { + source_->guess_type_by_public_key(public_key_.value()); + } + + //TODO: pass default type through api + if (source_->get_wallet_type() == AccountState::Empty && public_key_ && is_fake_key_) { + source_->guess_type_default(public_key_.value()); } + return do_loop(); } - void on_destination_state(td::Result> r_state) { + void on_destination_state(td::Result> r_state) { check(do_on_destination_state(std::move(r_state))); } - td::Status do_on_destination_state(td::Result> r_state) { + td::Status do_on_destination_state(td::Result> r_state) { TRY_RESULT(state, std::move(r_state)); - destination_state_ = std::move(state); - if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && is_destination_bounceable_) { + destination_ = std::move(state); + if (destination_->is_frozen()) { //FIXME: after restoration of frozen accounts will be supported - if (!static_cast(*destination_state_) - .account_state_->frozen_hash_.empty()) { - return TonlibError::TransferToFrozen(); - //return TonlibError::DangerousTransaction("Transfer to frozen wallet"); - } - if (send_grams_.allow_send_to_uninited_) { - TRY_RESULT(destination_address, get_account_address(send_grams_.destination_->account_address_)); - destination_address.bounceable = false; - auto new_destination_address = destination_address.rserialize(true); - LOG(INFO) << "Change destination address from bounceable to non-bounceable " - << send_grams_.destination_->account_address_ << " -> " << new_destination_address; - send_grams_.destination_->account_address_ = std::move(new_destination_address); - } else { + return TonlibError::TransferToFrozen(); + } + if (destination_->get_wallet_type() == AccountState::Empty && destination_->get_address().bounceable) { + if (!send_grams_.allow_send_to_uninited_) { return TonlibError::DangerousTransaction("Transfer to uninited wallet"); } + destination_->make_non_bounceable(); + LOG(INFO) << "Change destination address from bounceable to non-bounceable "; } return do_loop(); } td::Status do_loop() { - if (!source_state_ || !destination_state_) { + if (!source_ || !destination_) { + return td::Status::OK(); + } + if (has_private_key_ && !private_key_) { return td::Status::OK(); } - downcast_call( - *source_state_, - td::overloaded( - [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { - auto amount = send_grams_.amount_; - send_query(tonlib_api::testGiver_sendGrams(std::move(send_grams_.destination_), - test_giver_state.account_state_->seqno_, amount, - std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { - auto amount = send_grams_.amount_; - auto balance = test_wallet_state.account_state_->balance_; - if (false && amount == balance) { - amount = -1; - } else if (amount >= balance) { - promise_.set_error(TonlibError::NotEnoughFunds()); - return stop(); - } - send_query(tonlib_api::testWallet_sendGrams( - std::move(send_grams_.private_key_), std::move(send_grams_.destination_), - test_wallet_state.account_state_->seqno_, amount, std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateWallet& wallet_state) { - auto amount = send_grams_.amount_; - auto balance = wallet_state.account_state_->balance_; - if (false && amount == balance) { - amount = -1; - } else if (amount >= balance) { - promise_.set_error(TonlibError::NotEnoughFunds()); - return stop(); - } - send_query( - tonlib_api::wallet_sendGrams(std::move(send_grams_.private_key_), std::move(send_grams_.destination_), - wallet_state.account_state_->seqno_, - send_grams_.timeout_ == 0 - ? 60 + wallet_state.account_state_->sync_utime_ - : send_grams_.timeout_ + wallet_state.account_state_->sync_utime_, - amount, std::move(send_grams_.message_)), - std::move(promise_)); - stop(); - }, - [&](tonlib_api::generic_accountStateUninited&) { - promise_.set_error(TonlibError::AccountNotInited()); - stop(); - }, - [&](tonlib_api::generic_accountStateRaw&) { - promise_.set_error(TonlibError::AccountTypeUnknown()); - stop(); - })); + + Query::Raw raw; + + auto amount = send_grams_.amount_; + if (amount > source_->get_balance()) { + return TonlibError::NotEnoughFunds(); + } + if (amount == source_->get_balance()) { + amount = -1; + } + auto message = send_grams_.message_; + switch (source_->get_wallet_type()) { + case AccountState::Empty: + return TonlibError::AccountNotInited(); + case AccountState::Unknown: + return TonlibError::AccountTypeUnknown(); + case AccountState::Giver: { + raw.message_body = ton::TestGiver::make_a_gift_message(0, amount, message, destination_->get_address()); + break; + } + + case AccountState::SimpleWallet: { + if (!private_key_) { + return TonlibError::EmptyField("private_key"); + } + if (message.size() > ton::TestWallet::max_message_size) { + return TonlibError::MessageTooLong(); + } + TRY_RESULT(seqno, ton::TestWallet(source_->get_smc_state()).get_seqno()); + raw.message_body = ton::TestWallet::make_a_gift_message(private_key_.unwrap(), seqno, amount, message, + destination_->get_address()); + break; + } + case AccountState::Wallet: { + if (!private_key_) { + return TonlibError::EmptyField("private_key"); + } + if (message.size() > ton::Wallet::max_message_size) { + return TonlibError::MessageTooLong(); + } + TRY_RESULT(seqno, ton::Wallet(source_->get_smc_state()).get_seqno()); + auto valid_until = source_->get_sync_time(); + valid_until += send_grams_.timeout_ == 0 ? 60 : send_grams_.timeout_; + raw.valid_until = valid_until; + raw.message_body = ton::Wallet::make_a_gift_message(private_key_.unwrap(), seqno, valid_until, amount, message, + destination_->get_address()); + break; + } + } + + raw.new_state = source_->get_new_state(); + raw.message = ton::GenericAccount::create_ext_message(source_->get_address(), raw.new_state, raw.message_body); + raw.source = std::move(source_); + raw.destination = std::move(destination_); + + promise_.set_value(td::make_unique(std::move(raw))); + stop(); return td::Status::OK(); } }; +td::int64 TonlibClient::register_query(td::unique_ptr query) { + auto query_id = ++next_query_id_; + queries_[query_id] = std::move(query); + return query_id; +} + +td::Result> TonlibClient::get_query_info(td::int64 id) { + auto it = queries_.find(id); + if (it == queries_.end()) { + return TonlibError::InvalidQueryId(); + } + return tonlib_api::make_object(id, it->second->get_valid_until(), + it->second->get_body_hash().as_slice().str()); +} + +void TonlibClient::finish_create_query(td::Result> r_query, + td::Promise>&& promise) { + TRY_RESULT_PROMISE(promise, query, std::move(r_query)); + auto id = register_query(std::move(query)); + promise.set_result(get_query_info(id)); +} +void TonlibClient::finish_send_query(td::Result> r_query, + td::Promise>&& promise) { + TRY_RESULT_PROMISE(promise, query, std::move(r_query)); + auto result = tonlib_api::make_object(query->get_valid_until(), + query->get_body_hash().as_slice().str()); + auto id = register_query(std::move(query)); + make_request(tonlib_api::query_send(id), + promise.wrap([result = std::move(result)](auto&&) mutable { return std::move(result); })); +} +td::Status TonlibClient::do_request(tonlib_api::generic_createSendGramsQuery& request, + td::Promise>&& promise) { + auto id = actor_id_++; + actors_[id] = td::actor::create_actor( + "GenericSendGrams", actor_shared(this, id), std::move(request), + promise.send_closure(actor_id(this), &TonlibClient::finish_create_query)); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(const tonlib_api::raw_createQuery& request, + td::Promise>&& promise) { + if (!request.destination_) { + return TonlibError::EmptyField("destination"); + } + TRY_RESULT(account_address, get_account_address(request.destination_->account_address_)); + + td::optional smc_state; + if (!request.init_code_.empty()) { + TRY_RESULT_PREFIX(code, vm::std_boc_deserialize(request.init_code_), TonlibError::InvalidBagOfCells("init_code")); + TRY_RESULT_PREFIX(data, vm::std_boc_deserialize(request.init_data_), TonlibError::InvalidBagOfCells("init_data")); + smc_state = ton::SmartContract::State{std::move(code), std::move(data)}; + } + TRY_RESULT_PREFIX(body, vm::std_boc_deserialize(request.body_), TonlibError::InvalidBagOfCells("body")); + + td::Promise> new_promise = + promise.send_closure(actor_id(this), &TonlibClient::finish_create_query); + + make_request(int_api::GetAccountState{account_address}, + new_promise.wrap([smc_state = std::move(smc_state), body = std::move(body)](auto&& source) mutable { + Query::Raw raw; + if (smc_state) { + source->set_new_state(smc_state.unwrap()); + } + raw.new_state = source->get_new_state(); + raw.message_body = std::move(body); + raw.message = + ton::GenericAccount::create_ext_message(source->get_address(), raw.new_state, raw.message_body); + raw.source = std::move(source); + raw.destination = nullptr; + return td::make_unique(std::move(raw)); + })); + return td::Status::OK(); +} + td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, td::Promise>&& promise) { - if (request.timeout_ < 0 || request.timeout_ > 300) { - return TonlibError::InvalidField("timeout", "must be between 0 and 300"); - } auto id = actor_id_++; - actors_[id] = td::actor::create_actor("GenericSendGrams", actor_shared(this, id), - std::move(request), std::move(promise)); + actors_[id] = td::actor::create_actor( + "GenericSendGrams", actor_shared(this, id), + tonlib_api::generic_createSendGramsQuery(std::move(request.private_key_), std::move(request.source_), + std::move(request.destination_), request.amount_, request.timeout_, + request.allow_send_to_uninited_, std::move(request.message_)), + promise.send_closure(actor_id(this), &TonlibClient::finish_send_query)); return td::Status::OK(); } -td::Status TonlibClient::do_request(tonlib_api::sync& request, td::Promise>&& promise) { - client_.with_last_block([promise = std::move(promise)](td::Result r_last_block) mutable { - TRY_RESULT_PROMISE(promise, last_block, std::move(r_last_block)); - (void)last_block; - promise.set_value(tonlib_api::make_object()); +td::Status TonlibClient::do_request(const tonlib_api::query_getInfo& request, + td::Promise>&& promise) { + promise.set_result(get_query_info(request.id_)); + return td::Status::OK(); +} + +void TonlibClient::query_estimate_fees(td::int64 id, bool ignore_chksig, td::Result r_state, + td::Promise>&& promise) { + auto it = queries_.find(id); + if (it == queries_.end()) { + promise.set_error(TonlibError::InvalidQueryId()); + return; + } + TRY_RESULT_PROMISE(promise, state, std::move(r_state)); + TRY_RESULT_PROMISE_PREFIX(promise, fees, TRY_VM(it->second->estimate_fees(ignore_chksig, *state.config)), + TonlibError::Internal()); + promise.set_value( + tonlib_api::make_object(fees.first.to_tonlib_api(), fees.second.to_tonlib_api())); +} + +td::Status TonlibClient::do_request(const tonlib_api::query_estimateFees& request, + td::Promise>&& promise) { + auto it = queries_.find(request.id_); + if (it == queries_.end()) { + return TonlibError::InvalidQueryId(); + } + + client_.with_last_config([this, id = request.id_, ignore_chksig = request.ignore_chksig_, + promise = std::move(promise)](td::Result r_state) mutable { + this->query_estimate_fees(id, ignore_chksig, std::move(r_state), std::move(promise)); }); return td::Status::OK(); } +td::Status TonlibClient::do_request(const tonlib_api::query_send& request, + td::Promise>&& promise) { + auto it = queries_.find(request.id_); + if (it == queries_.end()) { + return TonlibError::InvalidQueryId(); + } + + auto message = it->second->get_message(); + if (GET_VERBOSITY_LEVEL() >= VERBOSITY_NAME(DEBUG)) { + std::ostringstream ss; + block::gen::t_Message_Any.print_ref(ss, message); + LOG(DEBUG) << ss.str(); + } + make_request(int_api::SendMessage{std::move(message)}, to_any_promise(std::move(promise))); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(tonlib_api::query_forget& request, + td::Promise>&& promise) { + auto it = queries_.find(request.id_); + if (it == queries_.end()) { + return TonlibError::InvalidQueryId(); + } + promise.set_value(tonlib_api::make_object()); + return td::Status::OK(); +} + +td::int64 TonlibClient::register_smc(td::unique_ptr smc) { + auto smc_id = ++next_smc_id_; + smcs_[smc_id] = std::move(smc); + return smc_id; +} + +td::Result> TonlibClient::get_smc_info(td::int64 id) { + auto it = smcs_.find(id); + if (it == smcs_.end()) { + return TonlibError::InvalidSmcId(); + } + return tonlib_api::make_object(id); +} + +void TonlibClient::finish_load_smc(td::unique_ptr smc, + td::Promise>&& promise) { + auto id = register_smc(std::move(smc)); + promise.set_result(get_smc_info(id)); +} + +td::Status TonlibClient::do_request(const tonlib_api::smc_load& request, + td::Promise>&& promise) { + if (!request.account_address_) { + return TonlibError::EmptyField("account_address"); + } + TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); + make_request(int_api::GetAccountState{std::move(account_address)}, + promise.send_closure(actor_id(this), &TonlibClient::finish_load_smc)); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(const tonlib_api::smc_getCode& request, + td::Promise>&& promise) { + auto it = smcs_.find(request.id_); + if (it == smcs_.end()) { + return TonlibError::InvalidSmcId(); + } + auto& acc = it->second; + auto code = acc->get_smc_state().code; + promise.set_value(tonlib_api::make_object(to_bytes(code))); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(const tonlib_api::smc_getData& request, + td::Promise>&& promise) { + auto it = smcs_.find(request.id_); + if (it == smcs_.end()) { + return TonlibError::InvalidSmcId(); + } + auto& acc = it->second; + auto data = acc->get_smc_state().data; + promise.set_value(tonlib_api::make_object(to_bytes(data))); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(const tonlib_api::smc_getState& request, + td::Promise>&& promise) { + auto it = smcs_.find(request.id_); + if (it == smcs_.end()) { + return TonlibError::InvalidSmcId(); + } + auto& acc = it->second; + auto data = acc->get_raw_state(); + promise.set_value(tonlib_api::make_object(to_bytes(data))); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(tonlib_api::sync& request, td::Promise>&& promise) { + client_.with_last_block(to_any_promise(std::move(promise))); + return td::Status::OK(); +} + td::Result public_key_from_bytes(td::Slice bytes) { TRY_RESULT_PREFIX(key_bytes, block::PublicKey::from_bytes(bytes), TonlibError::Internal()); return key_bytes; @@ -1541,13 +2091,7 @@ td::Status TonlibClient::do_request(const tonlib_api::exportPemKey& request, if (!request.input_key_) { return TonlibError::EmptyField("input_key"); } - if (!request.input_key_->key_) { - return TonlibError::EmptyField("key"); - } - - TRY_RESULT(key_bytes, get_public_key(request.input_key_->key_->public_key_)); - KeyStorage::InputKey input_key{{td::SecureString(key_bytes.key), std::move(request.input_key_->key_->secret_)}, - std::move(request.input_key_->local_password_)}; + TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(exported_pem_key, key_storage_.export_pem_key(std::move(input_key), std::move(request.key_password_))); promise.set_value(tonlib_api::make_object(std::move(exported_pem_key.pem))); return td::Status::OK(); @@ -1594,26 +2138,16 @@ td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& reque if (!request.input_key_) { return TonlibError::EmptyField("input_key"); } - if (!request.input_key_->key_) { - return TonlibError::EmptyField("key"); - } TRY_RESULT(input_key, from_tonlib(*request.input_key_)); TRY_RESULT(key, key_storage_.change_local_password(std::move(input_key), std::move(request.new_local_password_))); - promise.set_value( - tonlib_api::make_object(request.input_key_->key_->public_key_, std::move(key.secret))); + promise.set_value(tonlib_api::make_object(key.public_key.as_slice().str(), std::move(key.secret))); return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryResult& request, td::Promise>&& promise) { send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, td::BufferSlice(request.bytes_), - [promise = std::move(promise)](td::Result res) mutable { - if (res.is_ok()) { - promise.set_value(tonlib_api::make_object()); - } else { - promise.set_error(res.move_as_error()); - } - }); + to_any_promise(std::move(promise))); return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& request, @@ -1621,13 +2155,7 @@ td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& re send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, td::Status::Error(request.error_->code_, request.error_->message_) .move_as_error_prefix(TonlibError::LiteServerNetwork()), - [promise = std::move(promise)](td::Result res) mutable { - if (res.is_ok()) { - promise.set_value(tonlib_api::make_object()); - } else { - promise.set_error(res.move_as_error()); - } - }); + to_any_promise(std::move(promise))); return td::Status::OK(); } @@ -1710,4 +2238,131 @@ tonlib_api::object_ptr TonlibClient::do_static_request(const SimpleEncryption::kdf(request.password_, request.salt_, request.iterations_)); } +td::Status TonlibClient::do_request(int_api::GetAccountState request, + td::Promise>&& promise) { + auto actor_id = actor_id_++; + actors_[actor_id] = td::actor::create_actor( + "GetAccountState", client_.get_client(), request.address, actor_shared(this, actor_id), + promise.wrap([address = request.address](auto&& state) mutable { + return td::make_unique(std::move(address), std::move(state)); + })); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(int_api::GetPrivateKey request, td::Promise&& promise) { + TRY_RESULT(pk, key_storage_.load_private_key(std::move(request.input_key))); + promise.set_value(std::move(pk)); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(int_api::SendMessage request, td::Promise&& promise) { + client_.send_query(ton::lite_api::liteServer_sendMessage(vm::std_boc_serialize(request.message).move_as_ok()), + to_any_promise(std::move(promise))); + return td::Status::OK(); +} + +td::Status TonlibClient::do_request(const tonlib_api::liteServer_getInfo& request, + td::Promise>&& promise) { + client_.send_query(ton::lite_api::liteServer_getVersion(), promise.wrap([](auto&& version) { + return tonlib_api::make_object(version->now_, version->version_, + version->capabilities_); + })); + return td::Status::OK(); +} + +template +td::Status TonlibClient::do_request(const tonlib_api::runTests& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::raw_getAccountAddress& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::testWallet_getAccountAddress& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::wallet_getAccountAddress& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::packAccountAddress& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::unpackAccountAddress& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(tonlib_api::getBip39Hints& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(tonlib_api::setLogStream& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::getLogStream& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::setLogVerbosityLevel& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::setLogTagVerbosityLevel& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::getLogVerbosityLevel& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::getLogTagVerbosityLevel& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::getLogTags& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::addLogMessage& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::encrypt& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::decrypt& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template +td::Status TonlibClient::do_request(const tonlib_api::kdf& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} } // namespace tonlib diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 148f8bae7e..9a844fa360 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -34,6 +34,17 @@ #include namespace tonlib { +namespace int_api { +struct GetAccountState; +struct GetPrivateKey; +struct SendMessage; +inline std::string to_string(const int_api::SendMessage&) { + return "Send message"; +} +} // namespace int_api +class AccountState; +class Query; + class TonlibClient : public td::actor::Actor { public: template @@ -66,6 +77,7 @@ class TonlibClient : public td::actor::Actor { td::actor::ActorOwn raw_client_; td::actor::ActorId ext_client_outbound_; td::actor::ActorOwn raw_last_block_; + td::actor::ActorOwn raw_last_config_; ExtClient client_; td::CancellationTokenSource source_; @@ -76,6 +88,7 @@ class TonlibClient : public td::actor::Actor { ExtClientRef get_client_ref(); void init_ext_client(); void init_last_block(); + void init_last_config(); bool is_closing_{false}; td::uint32 ref_cnt_{1}; @@ -127,9 +140,53 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::decrypt& request); static object_ptr do_static_request(const tonlib_api::kdf& request); + template + td::Status do_request(const tonlib_api::runTests& request, P&&); + template + td::Status do_request(const tonlib_api::raw_getAccountAddress& request, P&&); + template + td::Status do_request(const tonlib_api::testWallet_getAccountAddress& request, P&&); + template + td::Status do_request(const tonlib_api::wallet_getAccountAddress& request, P&&); + template + td::Status do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&); + template + td::Status do_request(const tonlib_api::packAccountAddress& request, P&&); + template + td::Status do_request(const tonlib_api::unpackAccountAddress& request, P&&); + template + td::Status do_request(tonlib_api::getBip39Hints& request, P&&); + + template + td::Status do_request(tonlib_api::setLogStream& request, P&&); + template + td::Status do_request(const tonlib_api::getLogStream& request, P&&); + template + td::Status do_request(const tonlib_api::setLogVerbosityLevel& request, P&&); + template + td::Status do_request(const tonlib_api::setLogTagVerbosityLevel& request, P&&); + template + td::Status do_request(const tonlib_api::getLogVerbosityLevel& request, P&&); + template + td::Status do_request(const tonlib_api::getLogTagVerbosityLevel& request, P&&); + template + td::Status do_request(const tonlib_api::getLogTags& request, P&&); + template + td::Status do_request(const tonlib_api::addLogMessage& request, P&&); + + template + td::Status do_request(const tonlib_api::encrypt& request, P&&); + template + td::Status do_request(const tonlib_api::decrypt& request, P&&); + template + td::Status do_request(const tonlib_api::kdf& request, P&&); + template - td::Status do_request(const T& request, P&& promise) { - return td::Status::Error(400, "Function is unsupported"); + void make_request(T&& request, P&& promise) { + auto status = do_request(std::forward(request), std::move(promise)); + if (status.is_error()) { + promise.operator()(std::move(status)); + } } td::Status set_config(object_ptr config); @@ -138,6 +195,11 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::raw_createAndSendMessage& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::raw_createQuery& request, + td::Promise>&& promise); + td::Status do_request(tonlib_api::raw_getAccountState& request, td::Promise>&& promise); td::Status do_request(tonlib_api::raw_getTransactions& request, @@ -191,6 +253,48 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::onLiteServerQueryError& request, td::Promise>&& promise); + td::int64 next_query_id_{0}; + std::map> queries_; + td::int64 register_query(td::unique_ptr query); + td::Result> get_query_info(td::int64 id); + void finish_create_query(td::Result> r_query, + td::Promise>&& promise); + void finish_send_query(td::Result> r_query, + td::Promise>&& promise); + void query_estimate_fees(td::int64 id, bool ignore_chksig, td::Result r_state, + td::Promise>&& promise); + + td::Status do_request(const tonlib_api::query_getInfo& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::query_estimateFees& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::query_send& request, td::Promise>&& promise); + td::Status do_request(tonlib_api::query_forget& request, td::Promise>&& promise); + + td::Status do_request(tonlib_api::generic_createSendGramsQuery& request, + td::Promise>&& promise); + + td::int64 next_smc_id_{0}; + std::map> smcs_; + + td::int64 register_smc(td::unique_ptr smc); + td::Result> get_smc_info(td::int64 id); + void finish_load_smc(td::unique_ptr query, td::Promise>&& promise); + td::Status do_request(const tonlib_api::smc_load& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::smc_getCode& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::smc_getData& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::smc_getState& request, + td::Promise>&& promise); + + td::Status do_request(int_api::GetAccountState request, td::Promise>&&); + td::Status do_request(int_api::GetPrivateKey request, td::Promise&&); + td::Status do_request(int_api::SendMessage request, td::Promise&& promise); + + td::Status do_request(const tonlib_api::liteServer_getInfo& request, + td::Promise>&& promise); + void proxy_request(td::int64 query_id, std::string data); friend class TonlibQueryActor; diff --git a/tonlib/tonlib/TonlibError.h b/tonlib/tonlib/TonlibError.h index b521c13689..6568112302 100644 --- a/tonlib/tonlib/TonlibError.h +++ b/tonlib/tonlib/TonlibError.h @@ -27,6 +27,8 @@ // INVALID_MNEMONIC // INVALID_BAG_OF_CELLS // INVALID_PUBLIC_KEY +// INVALID_QUERY_ID +// INVALID_SMC_ID // INVALID_ACCOUNT_ADDRESS // INVALID_CONFIG // INVALID_PEM_KEY @@ -65,6 +67,12 @@ struct TonlibError { static td::Status InvalidAccountAddress() { return td::Status::Error(400, "INVALID_ACCOUNT_ADDRESS"); } + static td::Status InvalidQueryId() { + return td::Status::Error(400, "INVALID_QUERY_ID"); + } + static td::Status InvalidSmcId() { + return td::Status::Error(400, "INVALID_SMC_ID"); + } static td::Status InvalidConfig(td::Slice reason) { return td::Status::Error(400, PSLICE() << "INVALID_CONFIG: " << reason); } @@ -110,6 +118,9 @@ struct TonlibError { static td::Status ValidateTransactions() { return td::Status::Error(500, "VALIDATE_TRANSACTION"); } + static td::Status ValidateConfig() { + return td::Status::Error(500, "VALIDATE_CONFIG"); + } static td::Status ValidateZeroState(td::Slice message) { return td::Status::Error(500, PSLICE() << "VALIDATE_ZERO_STATE: " << message); } diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 21f68b4b6a..b401a027fa 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -20,6 +20,57 @@ #include #include +// Consider this as a TODO list: +// +// (from lite-client) +// SUPPORTED +// "time\tGet server time\n" +// "remote-version\tShows server time, version and capabilities\n" +// "help []\tThis help\n" // TODO: support [] +// "quit\tExit\n"; +// "sendfile \tLoad a serialized message from and send it to server\n" +// +// "saveaccount[code|data] []\tSaves into specified file the most recent state " +// "runmethod ...\tRuns GET method of account " +// "with specified parameters\n" +// +// WONTSUPPORT +// +// UNSUPPORTED +//"last\tGet last block and state info from server\n" +//"status\tShow connection and local database status\n" +//"getaccount []\tLoads the most recent state of specified account; is in " +//"[:] format\n" +//"(StateInit) or just the code or data of specified account; is in " +//"[:] format\n" +//"allshards []\tShows shard configuration from the most recent masterchain " +//"state or from masterchain state corresponding to \n" +//"getconfig [...]\tShows specified or all configuration parameters from the latest masterchain state\n" +//"getconfigfrom [...]\tShows specified or all configuration parameters from the " +//"masterchain state of \n" +//"saveconfig []\tSaves all configuration parameters into specified file\n" +//"gethead \tShows block header for \n" +//"getblock \tDownloads block\n" +//"dumpblock \tDownloads and dumps specified block\n" +//"getstate \tDownloads state corresponding to specified block\n" +//"dumpstate \tDownloads and dumps state corresponding to specified block\n" +//"dumptrans \tDumps one transaction of specified account\n" +//"lasttrans[dump] []\tShows or dumps specified transaction and " +//"several preceding " +//"ones\n" +//"listblocktrans[rev] [ ]\tLists block transactions, " +//"starting immediately after or before the specified one\n" +//"blkproofchain[step] []\tDownloads and checks proof of validity of the /"second " +//"indicated block (or the last known masterchain block) starting from given block\n" +//"byseqno \tLooks up a block by workchain, shard and seqno, and shows its " +//"header\n" +//"bylt \tLooks up a block by workchain, shard and logical time, and shows its " +//"header\n" +//"byutime \tLooks up a block by workchain, shard and creation time, and " +//"shows its header\n" +//"known\tShows the list of all known block ids\n" +//"privkey \tLoads a private key from file\n" + class TonlibCli : public td::actor::Actor { public: struct Options { @@ -201,8 +252,25 @@ class TonlibCli : public td::actor::Actor { } return true; }; + + td::Promise cmd_promise = [line = line.clone()](td::Result res) { + if (res.is_ok()) { + // on_ok + } else { + td::TerminalIO::out() << "Query {" << line.as_slice() << "} FAILED: \n\t" << res.error() << "\n"; + } + }; + if (cmd == "help") { - td::TerminalIO::out() << "help - show this help\n"; + td::TerminalIO::out() << "help\tThis help\n"; + td::TerminalIO::out() << "time\tGet server time\n"; + td::TerminalIO::out() << "remote-version\tShows server time, version and capabilities\n"; + td::TerminalIO::out() << "sendfile \tLoad a serialized message from and send it to server\n"; + td::TerminalIO::out() << "exit\tExit\n"; + td::TerminalIO::out() << "quit\tExit\n"; + td::TerminalIO::out() + << "saveaccount[code|data] \tSaves into specified file the most recent state\n"; + td::TerminalIO::out() << "genkey - generate new secret key\n"; td::TerminalIO::out() << "keys - show all stored keys\n"; td::TerminalIO::out() << "unpackaddress
- validate and parse address\n"; @@ -210,6 +278,7 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "importkey - import key\n"; td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n"; td::TerminalIO::out() << "exportkey [] - export key\n"; + td::TerminalIO::out() << "exportkeypem [] - export key\n"; td::TerminalIO::out() << "setconfig [] [] [] - set lite server config\n"; td::TerminalIO::out() << "getstate - get state of simple wallet with requested key\n"; td::TerminalIO::out() @@ -219,10 +288,9 @@ class TonlibCli : public td::actor::Actor { " to .\n" << "\t could also be 'giver'\n" << "\t could also be 'giver' or smartcontract address\n"; - td::TerminalIO::out() << "exit - exit from this programm\n"; } else if (cmd == "genkey") { generate_key(); - } else if (cmd == "exit") { + } else if (cmd == "exit" || cmd == "quit") { is_closing_ = true; client_.reset(); ref_cnt_--; @@ -233,8 +301,8 @@ class TonlibCli : public td::actor::Actor { //delete_key(parser.read_word()); } else if (cmd == "deletekeys") { delete_all_keys(); - } else if (cmd == "exportkey") { - export_key(parser.read_word()); + } else if (cmd == "exportkey" || cmd == "exportkeypem") { + export_key(cmd.str(), parser.read_word()); } else if (cmd == "importkey") { import_key(parser.read_all()); } else if (cmd == "setconfig") { @@ -265,20 +333,93 @@ class TonlibCli : public td::actor::Actor { set_bounceable(addr, to_bool(bounceable, true)); } else if (cmd == "netstats") { dump_netstats(); + // reviewed from here } else if (cmd == "sync") { - sync(); + sync(std::move(cmd_promise)); + } else if (cmd == "time") { + remote_time(std::move(cmd_promise)); + } else if (cmd == "remote-version") { + remote_version(std::move(cmd_promise)); + } else if (cmd == "sendfile") { + send_file(parser.read_word(), std::move(cmd_promise)); + } else if (cmd == "saveaccount" || cmd == "saveaccountdata" || cmd == "saveaccountcode") { + auto path = parser.read_word(); + auto address = parser.read_word(); + save_account(cmd, path, address, std::move(cmd_promise)); + } else { + cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`")); } + if (cmd_promise) { + cmd_promise.set_value(td::Unit()); + } + } + + void remote_time(td::Promise promise) { + send_query(tonlib_api::make_object(), promise.wrap([](auto&& info) { + td::TerminalIO::out() << "Lite server time is: " << info->now_ << "\n"; + return td::Unit(); + })); + } + + void remote_version(td::Promise promise) { + send_query(tonlib_api::make_object(), promise.wrap([](auto&& info) { + td::TerminalIO::out() << "Lite server time is: " << info->now_ << "\n"; + td::TerminalIO::out() << "Lite server version is: " << info->version_ << "\n"; + td::TerminalIO::out() << "Lite server capabilities are: " << info->capabilities_ << "\n"; + return td::Unit(); + })); + } + + void send_file(td::Slice name, td::Promise promise) { + TRY_RESULT_PROMISE(promise, data, td::read_file_str(name.str())); + send_query(tonlib_api::make_object(std::move(data)), promise.wrap([](auto&& info) { + td::TerminalIO::out() << "Query was sent\n"; + return td::Unit(); + })); + } + + void save_account(td::Slice cmd, td::Slice path, td::Slice address, td::Promise promise) { + TRY_RESULT_PROMISE(promise, addr, to_account_address(address, false)); + send_query(tonlib_api::make_object(std::move(addr.address)), + promise.send_closure(actor_id(this), &TonlibCli::save_account_2, cmd.str(), path.str(), address.str())); } - void sync() { + void save_account_2(std::string cmd, std::string path, std::string address, + tonlib_api::object_ptr info, td::Promise promise) { + auto with_query = [&, self = this](auto query, auto log) { + send_query(std::move(query), + promise.send_closure(actor_id(self), &TonlibCli::save_account_3, std::move(path), std::move(log))); + }; + if (cmd == "saveaccount") { + with_query(tonlib_api::make_object(info->id_), + PSTRING() << "StateInit of account " << address); + } else if (cmd == "saveaccountcode") { + with_query(tonlib_api::make_object(info->id_), PSTRING() + << "Code of account " << address); + } else if (cmd == "saveaccountdata") { + with_query(tonlib_api::make_object(info->id_), PSTRING() + << "Data of account " << address); + } else { + promise.set_error(td::Status::Error("Unknown query")); + } + } + + void save_account_3(std::string path, std::string log, tonlib_api::object_ptr cell, + td::Promise promise) { + TRY_STATUS_PROMISE(promise, td::write_file(path, cell->bytes_)); + td::TerminalIO::out() << log << " was successfully written to the disk(" << td::format::as_size(cell->bytes_.size()) + << ")\n"; + promise.set_value(td::Unit()); + } + + void sync(td::Promise promise) { using tonlib_api::make_object; - send_query(make_object(), [](auto r_ok) { - LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); - if (r_ok.is_ok()) { - td::TerminalIO::out() << "synchronized\n"; - } - }); + send_query(make_object(), promise.wrap([](auto&&) { + td::TerminalIO::out() << "synchronized\n"; + return td::Unit(); + })); } + void dump_netstats() { td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n"; td::TerminalIO::out() << td::tag("rcv", td::format::as_size(rcv_bytes_)) << "\n"; @@ -298,15 +439,41 @@ class TonlibCli : public td::actor::Actor { void on_tonlib_result(std::uint64_t id, tonlib_api::object_ptr result) { if (id == 0) { - if (result->get_id() == tonlib_api::updateSendLiteServerQuery::ID) { - auto update = tonlib_api::move_object_as(std::move(result)); - CHECK(!raw_client_.empty()); - snd_bytes_ += update->data_.size(); - send_closure(raw_client_, &ton::adnl::AdnlExtClient::send_query, "query", td::BufferSlice(update->data_), - td::Timestamp::in(5), - [actor_id = actor_id(this), id = update->id_](td::Result res) { - send_closure(actor_id, &TonlibCli::on_adnl_result, id, std::move(res)); - }); + switch (result->get_id()) { + case tonlib_api::updateSendLiteServerQuery::ID: { + auto update = tonlib_api::move_object_as(std::move(result)); + CHECK(!raw_client_.empty()); + snd_bytes_ += update->data_.size(); + send_closure(raw_client_, &ton::adnl::AdnlExtClient::send_query, "query", td::BufferSlice(update->data_), + td::Timestamp::in(5), + [actor_id = actor_id(this), id = update->id_](td::Result res) { + send_closure(actor_id, &TonlibCli::on_adnl_result, id, std::move(res)); + }); + return; + } + case tonlib_api::updateSyncState::ID: { + auto update = tonlib_api::move_object_as(std::move(result)); + switch (update->sync_state_->get_id()) { + case tonlib_api::syncStateDone::ID: { + td::TerminalIO::out() << "synchronization: DONE\n"; + break; + } + case tonlib_api::syncStateInProgress::ID: { + auto progress = tonlib_api::move_object_as(update->sync_state_); + auto from = progress->from_seqno_; + auto to = progress->to_seqno_; + auto at = progress->current_seqno_; + auto d = to - from; + if (d <= 0) { + td::TerminalIO::out() << "synchronization: ???\n"; + } else { + td::TerminalIO::out() << "synchronization: " << 100 * (at - from) / d << "%\n"; + } + break; + } + } + return; + } } } auto it = query_handlers_.find(id); @@ -407,7 +574,7 @@ class TonlibCli : public td::actor::Actor { info.public_key = key->public_key_; info.secret = std::move(key->secret_); keys_.push_back(std::move(info)); - export_key(key->public_key_, keys_.size() - 1, std::move(password)); + export_key("exportkey", key->public_key_, keys_.size() - 1, std::move(password)); store_keys(); }); } @@ -596,11 +763,11 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "Ok\n"; }); } - void export_key(td::Slice key) { + void export_key(std::string cmd, td::Slice key) { if (key.empty()) { dump_keys(); td::TerminalIO::out() << "Choose public key (hex prefix or #N)"; - cont_ = [this](td::Slice key) { this->export_key(key); }; + cont_ = [this, cmd](td::Slice key) { this->export_key(cmd, key); }; return; } auto r_key_i = to_key_i(key); @@ -614,24 +781,40 @@ class TonlibCli : public td::actor::Actor { << "public key: " << td::buffer_to_hex(keys_[key_i].public_key) << "\n"; td::TerminalIO::out() << "Enter password (could be empty)"; - cont_ = [this, key = key.str(), key_i](td::Slice password) { this->export_key(key, key_i, password); }; + cont_ = [this, cmd, key = key.str(), key_i](td::Slice password) { this->export_key(cmd, key, key_i, password); }; } - void export_key(std::string key, size_t key_i, td::Slice password) { + void export_key(std::string cmd, std::string key, size_t key_i, td::Slice password) { using tonlib_api::make_object; - send_query(make_object(make_object( - make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), - td::SecureString(password))), - [this, key = std::move(key), key_i](auto r_res) { - if (r_res.is_error()) { - td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n"; - return; - } - dump_key(key_i); - for (auto& word : r_res.ok()->word_list_) { - td::TerminalIO::out() << " " << word.as_slice() << "\n"; - } - }); + if (cmd == "exportkey") { + send_query(make_object(make_object( + make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), + td::SecureString(password))), + [this, key = std::move(key), key_i](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n"; + return; + } + dump_key(key_i); + for (auto& word : r_res.ok()->word_list_) { + td::TerminalIO::out() << " " << word.as_slice() << "\n"; + } + }); + } else { + send_query(make_object( + make_object( + make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), + td::SecureString(password)), + td::SecureString("cucumber")), + [this, key = std::move(key), key_i](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't export key id: [" << key << "] " << r_res.error() << "\n"; + return; + } + dump_key(key_i); + td::TerminalIO::out() << "\n" << r_res.ok()->pem_.as_slice() << "\n"; + }); + } } void import_key(td::Slice slice, std::vector words = {}) { @@ -669,7 +852,7 @@ class TonlibCli : public td::actor::Actor { info.public_key = key->public_key_; info.secret = std::move(key->secret_); keys_.push_back(std::move(info)); - export_key(key->public_key_, keys_.size() - 1, std::move(password)); + export_key("exportkey", key->public_key_, keys_.size() - 1, std::move(password)); store_keys(); }); } @@ -830,21 +1013,42 @@ class TonlibCli : public td::actor::Actor { } using tonlib_api::make_object; auto key = !from.secret.empty() - ? make_object( + ? make_object( make_object(from.public_key, from.secret.copy()), td::SecureString(password)) : nullptr; - send_query( - make_object(std::move(key), std::move(from.address), std::move(to.address), - grams, 60, allow_send_to_uninited, std::move(msg)), - [this](auto r_res) { - if (r_res.is_error()) { - td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; - on_error(); - return; - } - td::TerminalIO::out() << to_string(r_res.ok()); - on_ok(); - }); + send_query(make_object(std::move(key), std::move(from.address), + std::move(to.address), grams, 60, + allow_send_to_uninited, std::move(msg)), + [self = this](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; + self->on_error(); + return; + } + td::TerminalIO::out() << to_string(r_res.ok()); + self->send_query(make_object(r_res.ok()->id_, false), + [self](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; + self->on_error(); + return; + } + td::TerminalIO::out() << to_string(r_res.ok()); + self->on_ok(); + }); + + self->send_query(make_object(r_res.ok()->id_), [self](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; + self->on_error(); + return; + } + td::TerminalIO::out() << to_string(r_res.ok()); + self->on_ok(); + }); + + self->on_ok(); + }); } void init_simple_wallet(td::Slice key) { @@ -871,7 +1075,7 @@ class TonlibCli : public td::actor::Actor { void init_simple_wallet(std::string key, size_t key_i, td::Slice password) { using tonlib_api::make_object; if (options_.use_simple_wallet) { - send_query(make_object(make_object( + send_query(make_object(make_object( make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), td::SecureString(password))), [key = std::move(key)](auto r_res) { @@ -882,7 +1086,7 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << to_string(r_res.ok()); }); } else { - send_query(make_object(make_object( + send_query(make_object(make_object( make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), td::SecureString(password))), [key = std::move(key)](auto r_res) { diff --git a/tonlib/tonlib/utils.cpp b/tonlib/tonlib/utils.cpp index 5cef21f56d..fec9144311 100644 --- a/tonlib/tonlib/utils.cpp +++ b/tonlib/tonlib/utils.cpp @@ -20,20 +20,9 @@ #include "td/utils/misc.h" #include "vm/cellslice.h" namespace tonlib { -int VERBOSITY_NAME(tonlib_query) = VERBOSITY_NAME(INFO); -int VERBOSITY_NAME(last_block) = VERBOSITY_NAME(INFO); -int VERBOSITY_NAME(lite_server) = VERBOSITY_NAME(INFO); +int VERBOSITY_NAME(tonlib_query) = VERBOSITY_NAME(DEBUG); +int VERBOSITY_NAME(last_block) = VERBOSITY_NAME(DEBUG); +int VERBOSITY_NAME(last_config) = VERBOSITY_NAME(DEBUG); +int VERBOSITY_NAME(lite_server) = VERBOSITY_NAME(DEBUG); -td::Result> binary_bitstring_to_cellslice(td::Slice literal) { - unsigned char buff[128]; - if (!begins_with(literal, "b{") || !ends_with(literal, "}")) { - return td::Status::Error("Invalid binary bitstring constant"); - } - int bits = - (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), literal.begin() + 2, literal.end() - 1); - if (bits < 0) { - return td::Status::Error("Invalid binary bitstring constant"); - } - return td::Ref{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}; -} } // namespace tonlib diff --git a/tonlib/tonlib/utils.h b/tonlib/tonlib/utils.h index 840bc10d20..8bcf9ab9b8 100644 --- a/tonlib/tonlib/utils.h +++ b/tonlib/tonlib/utils.h @@ -23,19 +23,8 @@ #include "block/block-parse.h" namespace tonlib { -template -auto try_f(F&& f) noexcept -> decltype(f()) { - try { - return f(); - } catch (vm::VmError error) { - return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg()); - } -} - -#define TRY_VM(f) try_f([&] { return f; }) - extern int VERBOSITY_NAME(tonlib_query); extern int VERBOSITY_NAME(last_block); +extern int VERBOSITY_NAME(last_config); extern int VERBOSITY_NAME(lite_server); -td::Result> binary_bitstring_to_cellslice(td::Slice literal); } // namespace tonlib diff --git a/validator/CMakeLists.txt b/validator/CMakeLists.txt index e66b4bfe6f..73cf0c99e9 100644 --- a/validator/CMakeLists.txt +++ b/validator/CMakeLists.txt @@ -10,6 +10,8 @@ add_subdirectory(impl) set(VALIDATOR_DB_SOURCE db/archiver.cpp db/archiver.hpp + db/archive-db.cpp + db/archive-db.hpp db/blockdb.cpp db/blockdb.hpp db/celldb.cpp @@ -25,6 +27,9 @@ set(VALIDATOR_DB_SOURCE db/statedb.cpp db/staticfilesdb.cpp db/staticfilesdb.hpp + + db/package.hpp + db/package.cpp ) set(VALIDATOR_HEADERS diff --git a/validator/block-handle.hpp b/validator/block-handle.hpp index 42eddccb25..3cbaf2e108 100644 --- a/validator/block-handle.hpp +++ b/validator/block-handle.hpp @@ -59,6 +59,7 @@ struct BlockHandleImpl : public BlockHandleInterface { dbf_moved = 0x1000000, dbf_deleted = 0x2000000, dbf_deleted_boc = 0x4000000, + dbf_moved_new = 0x8000000, dbf_processed = 0x10000000, }; @@ -95,6 +96,9 @@ struct BlockHandleImpl : public BlockHandleInterface { bool moved_to_storage() const override { return flags_.load(std::memory_order_consume) & Flags::dbf_moved; } + bool moved_to_archive() const override { + return flags_.load(std::memory_order_consume) & Flags::dbf_moved_new; + } bool deleted() const override { return flags_.load(std::memory_order_consume) & Flags::dbf_deleted; } @@ -393,6 +397,15 @@ struct BlockHandleImpl : public BlockHandleInterface { flags_ |= Flags::dbf_moved; unlock(); } + void set_moved_to_archive() override { + if (flags_.load(std::memory_order_consume) & Flags::dbf_moved_new) { + return; + } + lock(); + flags_ |= Flags::dbf_moved_new; + flags_ &= ~Flags::dbf_moved; + unlock(); + } void set_deleted() override { if (flags_.load(std::memory_order_consume) & Flags::dbf_deleted) { return; diff --git a/validator/db/archive-db.cpp b/validator/db/archive-db.cpp new file mode 100644 index 0000000000..cf0dbed1c1 --- /dev/null +++ b/validator/db/archive-db.cpp @@ -0,0 +1,332 @@ +#include "archive-db.hpp" +#include "common/errorcode.h" + +#include "common/int-to-string.hpp" +#include "files-async.hpp" + +#include "td/db/RocksDb.h" +#include "validator/fabric.h" + +namespace ton { + +namespace validator { + +void PackageWriter::append(std::string filename, td::BufferSlice data, + td::Promise> promise) { + auto offset = package_->append(std::move(filename), std::move(data)); + auto size = package_->size(); + + promise.set_value(std::pair{offset, size}); +} + +class PackageReader : public td::actor::Actor { + public: + PackageReader(std::shared_ptr package, td::uint64 offset, + td::Promise> promise) + : package_(std::move(package)), offset_(offset), promise_(std::move(promise)) { + } + void start_up() { + promise_.set_result(package_->read(offset_)); + } + + private: + std::shared_ptr package_; + td::uint64 offset_; + td::Promise> promise_; +}; + +void ArchiveFile::start_up() { + auto R = Package::open(path_, false, true); + if (R.is_error()) { + LOG(FATAL) << "failed to open/create archive '" << path_ << "': " << R.move_as_error(); + return; + } + package_ = std::make_shared(R.move_as_ok()); + index_ = std::make_shared(td::RocksDb::open(path_ + ".index").move_as_ok()); + + std::string value; + auto R2 = index_->get("status", value); + R2.ensure(); + + if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) { + auto len = td::to_integer(value); + package_->truncate(len); + } else { + package_->truncate(0); + } + + writer_ = td::actor::create_actor("writer", package_); +} + +void ArchiveFile::write(FileDb::RefId ref_id, td::BufferSlice data, td::Promise promise) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, promise = std::move(promise)]( + td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + return; + } + auto v = R.move_as_ok(); + td::actor::send_closure(SelfId, &ArchiveFile::completed_write, std::move(ref_id), v.first, v.second, + std::move(promise)); + }); + + FileHash hash; + ref_id.visit([&](const auto &obj) { hash = obj.hash(); }); + + td::actor::send_closure(writer_, &PackageWriter::append, hash.to_hex(), std::move(data), std::move(P)); +} + +void ArchiveFile::write_handle(BlockHandle handle, td::Promise promise) { + FileHash hash = fileref::BlockInfo{handle->id()}.hash(); + index_->begin_transaction().ensure(); + do { + auto version = handle->version(); + index_->set(hash.to_hex(), handle->serialize().as_slice().str()).ensure(); + handle->flushed_upto(version); + } while (handle->need_flush()); + index_->commit_transaction().ensure(); + promise.set_value(td::Unit()); +} + +void ArchiveFile::completed_write(FileDb::RefId ref_id, td::uint64 offset, td::uint64 new_size, + td::Promise promise) { + FileHash hash; + ref_id.visit([&](const auto &obj) { hash = obj.hash(); }); + index_->begin_transaction().ensure(); + index_->set("status", td::to_string(new_size)).ensure(); + index_->set(hash.to_hex(), td::to_string(offset)).ensure(); + index_->commit_transaction().ensure(); + promise.set_value(td::Unit()); +} + +void ArchiveFile::read(FileDb::RefId ref_id, td::Promise promise) { + FileHash hash; + ref_id.visit([&](const auto &obj) { hash = obj.hash(); }); + std::string value; + auto R = index_->get(hash.to_hex(), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_error(td::Status::Error(ErrorCode::notready, "not in db (archive)")); + return; + } + auto offset = td::to_integer(value); + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + promise.set_value(std::move(R.move_as_ok().second)); + } + }); + td::actor::create_actor("reader", package_, offset, std::move(P)).release(); +} + +void ArchiveFile::read_handle(BlockIdExt block_id, td::Promise promise) { + FileHash hash = fileref::BlockInfo{block_id}.hash(); + + std::string value; + auto R = index_->get(hash.to_hex(), value); + R.ensure(); + + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_error(td::Status::Error(ErrorCode::notready, "not in archive db")); + return; + } + promise.set_result(create_block_handle(td::BufferSlice{value})); +} + +ArchiveManager::ArchiveManager(std::string db_root) : db_root_(db_root) { +} + +void ArchiveManager::write(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::BufferSlice data, + td::Promise promise) { + auto f = get_file(ts, key_block); + td::actor::send_closure(f->file_actor_id(), &ArchiveFile::write, std::move(ref_id), std::move(data), + std::move(promise)); +} + +void ArchiveManager::write_handle(BlockHandle handle, td::Promise promise) { + auto f = get_file(handle->unix_time(), handle->is_key_block()); + update_desc(*f, handle->id().shard_full(), handle->id().seqno(), handle->logical_time()); + td::actor::send_closure(f->file_actor_id(), &ArchiveFile::write_handle, std::move(handle), std::move(promise)); +} + +void ArchiveManager::read(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::Promise promise) { + auto f = get_file(ts, key_block); + td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read, std::move(ref_id), std::move(promise)); +} + +void ArchiveManager::read_handle(BlockIdExt block_id, td::Promise promise) { + if (block_id.is_masterchain()) { + auto f = get_file_by_seqno(block_id.shard_full(), block_id.seqno(), true); + if (!f) { + read_handle_cont(block_id, std::move(promise)); + return; + } + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), block_id, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveManager::read_handle_cont, block_id, std::move(promise)); + } else { + promise.set_value(R.move_as_ok()); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read_handle, block_id, std::move(P)); + } else { + read_handle_cont(block_id, std::move(promise)); + } +} + +void ArchiveManager::read_handle_cont(BlockIdExt block_id, td::Promise promise) { + auto f = get_file_by_seqno(block_id.shard_full(), block_id.seqno(), false); + if (!f) { + promise.set_error(td::Status::Error(ErrorCode::notready, "not in archive db")); + return; + } + td::actor::send_closure(f->file_actor_id(), &ArchiveFile::read_handle, block_id, std::move(promise)); +} + +UnixTime ArchiveManager::convert_ts(UnixTime ts, bool key_block) { + if (!key_block) { + return ts - (ts % (1 << 17)); + } else { + return ts - (ts % (1 << 22)); + } +} + +ArchiveManager::FileDescription *ArchiveManager::get_file(UnixTime ts, bool key_block, bool force) { + ts = convert_ts(ts, key_block); + auto &f = key_block ? key_files_ : files_; + auto it = f.find(ts); + if (it != f.end()) { + return &it->second; + } + if (!force) { + return nullptr; + } + + return add_file(ts, key_block); +} + +ArchiveManager::FileDescription *ArchiveManager::add_file(UnixTime ts, bool key_block) { + CHECK((key_block ? key_files_ : files_).count(ts) == 0); + index_->begin_transaction().ensure(); + { + std::vector t; + std::vector tk; + for (auto &e : files_) { + t.push_back(e.first); + } + for (auto &e : key_files_) { + tk.push_back(e.first); + } + (key_block ? tk : t).push_back(ts); + index_ + ->set(create_serialize_tl_object().as_slice(), + create_serialize_tl_object(std::move(t), std::move(tk)).as_slice()) + .ensure(); + } + { + index_ + ->set(create_serialize_tl_object(ts, key_block).as_slice(), + create_serialize_tl_object( + ts, key_block, std::vector>(), false) + .as_slice()) + .ensure(); + } + index_->commit_transaction().ensure(); + FileDescription desc{ts, key_block}; + auto w = td::actor::create_actor( + PSTRING() << "archivefile" << ts, + PSTRING() << db_root_ << "/packed/" << (key_block ? "key" : "") << ts << ".pack", ts); + desc.file = std::move(w); + + return &(key_block ? key_files_ : files_).emplace(ts, std::move(desc)).first->second; +} + +void ArchiveManager::load_package(UnixTime ts, bool key_block) { + auto key = create_serialize_tl_object(ts, key_block); + + std::string value; + auto v = index_->get(key.as_slice(), value); + v.ensure(); + CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok); + + auto R = fetch_tl_object(value, true); + R.ensure(); + auto x = R.move_as_ok(); + + if (x->deleted_) { + return; + } + + FileDescription desc{ts, key_block}; + for (auto &e : x->firstblocks_) { + desc.first_blocks[ShardIdFull{e->workchain_, static_cast(e->shard_)}] = + FileDescription::Desc{static_cast(e->seqno_), static_cast(e->lt_)}; + } + desc.file = td::actor::create_actor( + PSTRING() << "archivefile" << ts, + PSTRING() << db_root_ << "/packed/" << (key_block ? "key" : "") << ts << ".pack", ts); + + (key_block ? key_files_ : files_).emplace(ts, std::move(desc)); +} + +void ArchiveManager::update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, LogicalTime lt) { + auto it = desc.first_blocks.find(shard); + if (it != desc.first_blocks.end() && it->second.seqno <= seqno) { + return; + } + desc.first_blocks[shard] = FileDescription::Desc{seqno, lt}; + std::vector> vec; + for (auto &e : desc.first_blocks) { + vec.push_back(create_tl_object(e.first.workchain, e.first.shard, + e.second.seqno, e.second.lt)); + } + index_->begin_transaction().ensure(); + index_ + ->set(create_serialize_tl_object(desc.unix_time, desc.key_block).as_slice(), + create_serialize_tl_object(desc.unix_time, desc.key_block, + std::move(vec), false) + .as_slice()) + .ensure(); + index_->commit_transaction().ensure(); +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_by_seqno(ShardIdFull shard, BlockSeqno seqno, + bool key_block) { + auto &f = (key_block ? key_files_ : files_); + + for (auto it = f.rbegin(); it != f.rend(); it++) { + auto i = it->second.first_blocks.find(shard); + if (i != it->second.first_blocks.end() && i->second.seqno <= seqno) { + return &it->second; + } + } + return nullptr; +} + +void ArchiveManager::start_up() { + td::mkdir(db_root_).ensure(); + td::mkdir(db_root_ + "/packed").ensure(); + index_ = std::make_shared(td::RocksDb::open(db_root_ + "/packed/globalindex").move_as_ok()); + std::string value; + auto v = index_->get(create_serialize_tl_object().as_slice(), value); + v.ensure(); + if (v.move_as_ok() == td::KeyValue::GetStatus::Ok) { + auto R = fetch_tl_object(value, true); + R.ensure(); + auto x = R.move_as_ok(); + + for (auto &d : x->packages_) { + load_package(d, false); + } + for (auto &d : x->key_packages_) { + load_package(d, true); + } + } +} + +} // namespace validator + +} // namespace ton diff --git a/validator/db/archive-db.hpp b/validator/db/archive-db.hpp new file mode 100644 index 0000000000..d842d9f47d --- /dev/null +++ b/validator/db/archive-db.hpp @@ -0,0 +1,97 @@ +#pragma once + +#include "td/actor/actor.h" +#include "td/utils/buffer.h" +#include "ton/ton-types.h" +#include "td/utils/port/FileFd.h" +#include "package.hpp" +#include "filedb.hpp" +#include "validator/interfaces/block-handle.h" + +#include +#include + +namespace ton { + +namespace validator { + +class PackageWriter : public td::actor::Actor { + public: + PackageWriter(std::shared_ptr package) : package_(std::move(package)) { + } + + void append(std::string filename, td::BufferSlice data, td::Promise> promise); + + private: + std::shared_ptr package_; +}; + +class ArchiveFile : public td::actor::Actor { + public: + ArchiveFile(std::string path, UnixTime ts) : path_(std::move(path)), ts_(ts) { + } + void start_up() override; + void write(FileDb::RefId ref_id, td::BufferSlice data, td::Promise promise); + void write_handle(BlockHandle handle, td::Promise promise); + void read(FileDb::RefId ref_id, td::Promise promise); + void read_handle(BlockIdExt block_id, td::Promise promise); + + private: + void completed_write(FileDb::RefId ref_id, td::uint64 offset, td::uint64 new_size, td::Promise promise); + + std::shared_ptr package_; + std::shared_ptr index_; + + td::actor::ActorOwn writer_; + + std::string path_; + UnixTime ts_; +}; + +class ArchiveManager : public td::actor::Actor { + public: + ArchiveManager(std::string db_root); + void write(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::BufferSlice data, td::Promise promise); + void write_handle(BlockHandle handle, td::Promise promise); + void read(UnixTime ts, bool key_block, FileDb::RefId ref_id, td::Promise promise); + void read_handle(BlockIdExt block_id, td::Promise promise); + + void start_up() override; + + private: + void read_handle_cont(BlockIdExt block_id, td::Promise promise); + struct FileDescription { + struct Desc { + BlockSeqno seqno; + LogicalTime lt; + }; + FileDescription(UnixTime unix_time, bool key_block) : unix_time(unix_time), key_block(key_block) { + } + auto file_actor_id() const { + return file.get(); + } + UnixTime unix_time; + bool key_block; + + std::map first_blocks; + td::actor::ActorOwn file; + }; + + void load_package(UnixTime ts, bool key_block); + + UnixTime convert_ts(UnixTime ts, bool key_block); + FileDescription *get_file(UnixTime ts, bool key_block, bool force = true); + FileDescription *add_file(UnixTime ts, bool key_block); + void update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, LogicalTime lt); + FileDescription *get_file_by_seqno(ShardIdFull shard, BlockSeqno seqno, bool key_block); + + std::string db_root_; + std::map files_; + std::map key_files_; + + std::shared_ptr index_; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/db/archiver.cpp b/validator/db/archiver.cpp index 284eea79ab..f0f1692c5d 100644 --- a/validator/db/archiver.cpp +++ b/validator/db/archiver.cpp @@ -26,8 +26,13 @@ namespace validator { BlockArchiver::BlockArchiver(BlockIdExt block_id, td::actor::ActorId root_db, td::actor::ActorId file_db, td::actor::ActorId archive_db, - td::Promise promise) - : block_id_(block_id), root_db_(root_db), file_db_(file_db), archive_db_(archive_db), promise_(std::move(promise)) { + td::actor::ActorId archive, td::Promise promise) + : block_id_(block_id) + , root_db_(root_db) + , file_db_(file_db) + , archive_db_(archive_db) + , archive_(archive) + , promise_(std::move(promise)) { } void BlockArchiver::start_up() { @@ -40,7 +45,7 @@ void BlockArchiver::start_up() { void BlockArchiver::got_block_handle(BlockHandle handle) { handle_ = std::move(handle); - if (handle_->moved_to_storage()) { + if (handle_->moved_to_archive()) { finish_query(); return; } @@ -63,16 +68,21 @@ void BlockArchiver::got_block_handle(BlockHandle handle) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::got_proof, R.move_as_ok()); }); - td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P)); + + if (handle_->moved_to_storage()) { + td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P)); + } else { + td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P)); + } } void BlockArchiver::got_proof(td::BufferSlice data) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_proof); }); - td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(data), - std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(), + FileDb::RefId{fileref::Proof{block_id_}}, std::move(data), std::move(P)); } void BlockArchiver::written_proof() { @@ -85,16 +95,21 @@ void BlockArchiver::written_proof() { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::got_proof_link, R.move_as_ok()); }); - td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(P)); + if (handle_->moved_to_storage()) { + td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, + std::move(P)); + } else { + td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(P)); + } } void BlockArchiver::got_proof_link(td::BufferSlice data) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_proof_link); }); - td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ProofLink{block_id_}}, - std::move(data), std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(), + FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(data), std::move(P)); } void BlockArchiver::written_proof_link() { @@ -106,20 +121,24 @@ void BlockArchiver::written_proof_link() { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::got_block_data, R.move_as_ok()); }); - td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P)); + if (handle_->moved_to_storage()) { + td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P)); + } else { + td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P)); + } } void BlockArchiver::got_block_data(td::BufferSlice data) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_block_data); }); - td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(data), - std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(), + FileDb::RefId{fileref::Block{block_id_}}, std::move(data), std::move(P)); } void BlockArchiver::written_block_data() { - handle_->set_moved_to_storage(); + handle_->set_moved_to_archive(); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); diff --git a/validator/db/archiver.hpp b/validator/db/archiver.hpp index 01ff875274..3335d3aa05 100644 --- a/validator/db/archiver.hpp +++ b/validator/db/archiver.hpp @@ -22,6 +22,7 @@ #include "td/actor/actor.h" #include "validator/interfaces/block-handle.h" #include "ton/ton-io.hpp" +#include "archive-db.hpp" namespace ton { @@ -33,7 +34,8 @@ class FileDb; class BlockArchiver : public td::actor::Actor { public: BlockArchiver(BlockIdExt block_id, td::actor::ActorId root_db, td::actor::ActorId file_db, - td::actor::ActorId archive_db, td::Promise promise); + td::actor::ActorId archive_db, td::actor::ActorId archive, + td::Promise promise); void abort_query(td::Status error); @@ -52,6 +54,7 @@ class BlockArchiver : public td::actor::Actor { td::actor::ActorId root_db_; td::actor::ActorId file_db_; td::actor::ActorId archive_db_; + td::actor::ActorId archive_; td::Promise promise_; BlockHandle handle_; diff --git a/validator/db/filedb.cpp b/validator/db/filedb.cpp index 4532641ea4..371c08fc3a 100644 --- a/validator/db/filedb.cpp +++ b/validator/db/filedb.cpp @@ -331,6 +331,9 @@ FileDb::RefId FileDb::get_ref_from_tl(const ton_api::db_filedb_Key& from) { [&](const ton_api::db_filedb_key_candidate& key) { ref_id = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_), key.id_->collated_data_file_hash_}; + }, + [&](const ton_api::db_filedb_key_blockInfo& key) { + ref_id = fileref::BlockInfo{create_block_id(key.block_id_)}; })); return ref_id; } diff --git a/validator/db/filedb.hpp b/validator/db/filedb.hpp index 5bb1ea8f0b..3adc434781 100644 --- a/validator/db/filedb.hpp +++ b/validator/db/filedb.hpp @@ -130,6 +130,18 @@ class Candidate { BlockIdExt block_id; FileHash collated_data_file_hash; }; + +class BlockInfo { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + + BlockIdExt block_id; +}; }; // namespace fileref class RootDb; @@ -138,7 +150,7 @@ class FileDb : public td::actor::Actor { public: using RefId = td::Variant; + fileref::Proof, fileref::ProofLink, fileref::Signatures, fileref::Candidate, fileref::BlockInfo>; using RefIdHash = td::Bits256; void store_file(RefId ref_id, td::BufferSlice data, td::Promise promise); diff --git a/validator/db/files-async.hpp b/validator/db/files-async.hpp index 4f42077620..f2009fcd5d 100644 --- a/validator/db/files-async.hpp +++ b/validator/db/files-async.hpp @@ -52,7 +52,7 @@ class WriteFile : public td::actor::Actor { auto res = R.move_as_ok(); auto file = std::move(res.first); auto old_name = res.second; - size_t offset = 0; + td::uint64 offset = 0; while (data_.size() > 0) { auto R = file.pwrite(data_.as_slice(), offset); auto s = R.move_as_ok(); diff --git a/validator/db/package.cpp b/validator/db/package.cpp new file mode 100644 index 0000000000..76f504a36e --- /dev/null +++ b/validator/db/package.cpp @@ -0,0 +1,143 @@ +#include "package.hpp" +#include "common/errorcode.h" + +namespace ton { + +namespace { + +constexpr td::uint32 header_size() { + return 4; +} + +constexpr td::uint32 max_data_size() { + return (1u << 31) - 1; +} + +constexpr td::uint32 max_filename_size() { + return (1u << 16) - 1; +} + +constexpr td::uint16 entry_header_magic() { + return 0x1e8b; +} + +constexpr td::uint32 package_header_magic() { + return 0xae8fdd01; +} +} // namespace + +Package::Package(td::FileFd fd) : fd_(std::move(fd)) { +} + +td::Status Package::truncate(td::uint64 size) { + TRY_STATUS(fd_.seek(size + header_size())); + return fd_.truncate_to_current_position(size + header_size()); +} + +td::uint64 Package::append(std::string filename, td::Slice data) { + CHECK(data.size() <= max_data_size()); + CHECK(filename.size() <= max_filename_size()); + auto size = fd_.get_size().move_as_ok(); + auto orig_size = size; + td::uint32 header[2]; + header[0] = entry_header_magic() + (td::narrow_cast(filename.size()) << 16); + header[1] = td::narrow_cast(data.size()); + CHECK(fd_.pwrite(td::Slice(reinterpret_cast(header), 8), size).move_as_ok() == 8); + size += 8; + CHECK(fd_.pwrite(filename, size).move_as_ok() == filename.size()); + size += filename.size(); + CHECK(fd_.pwrite(data, size).move_as_ok() == data.size()); + size += data.size(); + fd_.sync().ensure(); + return orig_size - header_size(); +} + +td::uint64 Package::size() const { + return fd_.get_size().move_as_ok() - header_size(); +} + +td::Result> Package::read(td::uint64 offset) const { + offset += header_size(); + + td::uint32 header[2]; + TRY_RESULT(s1, fd_.pread(td::MutableSlice(reinterpret_cast(header), 8), offset)); + if (s1 != 8) { + return td::Status::Error(ErrorCode::notready, "too short read"); + } + if ((header[0] & 0xffff) != entry_header_magic()) { + return td::Status::Error(ErrorCode::notready, "bad entry magic"); + } + offset += 8; + auto fname_size = header[0] >> 16; + auto data_size = header[1]; + + std::string fname(fname_size, '\0'); + TRY_RESULT(s2, fd_.pread(fname, offset)); + if (s2 != fname_size) { + return td::Status::Error(ErrorCode::notready, "too short read (filename)"); + } + offset += fname_size; + + td::BufferSlice data{data_size}; + TRY_RESULT(s3, fd_.pread(data.as_slice(), offset)); + if (s3 != data_size) { + return td::Status::Error(ErrorCode::notready, "too short read (data)"); + } + return std::pair{std::move(fname), std::move(data)}; +} + +td::Result Package::advance(td::uint64 offset) { + offset += header_size(); + + td::uint32 header[2]; + TRY_RESULT(s1, fd_.pread(td::MutableSlice(reinterpret_cast(header), 8), offset)); + if (s1 != 8) { + return td::Status::Error(ErrorCode::notready, "too short read"); + } + if ((header[0] & 0xffff) != entry_header_magic()) { + return td::Status::Error(ErrorCode::notready, "bad entry magic"); + } + + offset += 8 + (header[0] >> 16) + header[1]; + if (offset > static_cast(fd_.get_size().move_as_ok())) { + return td::Status::Error(ErrorCode::notready, "truncated read"); + } + return offset - header_size(); +} + +td::Result Package::open(std::string path, bool read_only, bool create) { + td::uint32 flags = td::FileFd::Flags::Read; + if (!read_only) { + flags |= td::FileFd::Write; + } + if (create) { + flags |= td::FileFd::Create; + } + + TRY_RESULT(fd, td::FileFd::open(path, flags)); + TRY_RESULT(size, fd.get_size()); + + if (size < header_size()) { + if (!create) { + return td::Status::Error(ErrorCode::notready, "db is too short"); + } + td::uint32 header[1]; + header[0] = package_header_magic(); + TRY_RESULT(s, fd.pwrite(td::Slice(reinterpret_cast(header), header_size()), size)); + if (s != header_size()) { + return td::Status::Error(ErrorCode::notready, "db write is short"); + } + } else { + td::uint32 header[1]; + TRY_RESULT(s, fd.pread(td::MutableSlice(reinterpret_cast(header), header_size()), 0)); + if (s != header_size()) { + return td::Status::Error(ErrorCode::notready, "db read failed"); + } + if (header[0] != package_header_magic()) { + return td::Status::Error(ErrorCode::notready, "magic mismatch"); + } + } + return Package{std::move(fd)}; +} + +} // namespace ton diff --git a/validator/db/package.hpp b/validator/db/package.hpp new file mode 100644 index 0000000000..50d6ad0a3d --- /dev/null +++ b/validator/db/package.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "td/actor/actor.h" +#include "td/utils/port/FileFd.h" +#include "td/utils/buffer.h" + +namespace ton { + +class Package { + public: + static td::Result open(std::string path, bool read_only = false, bool create = false); + + Package(td::FileFd fd); + + td::Status truncate(td::uint64 size); + + td::uint64 append(std::string filename, td::Slice data); + td::uint64 size() const; + td::Result> read(td::uint64 offset) const; + td::Result advance(td::uint64 offset); + + struct Iterator { + td::uint64 offset; + Package &package; + + Iterator operator++(int); + const Iterator operator++(int) const; + td::Result> read() const; + }; + + Iterator begin(); + const Iterator begin() const; + Iterator end(); + const Iterator end() const; + + private: + td::FileFd fd_; +}; + +} // namespace ton diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index 2a21d0c272..a0e4d5217a 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -32,7 +32,7 @@ namespace ton { namespace validator { void RootDb::store_block_data(BlockHandle handle, td::Ref block, td::Promise promise) { - if (handle->moved_to_storage()) { + if (handle->moved_to_storage() || handle->moved_to_archive()) { promise.set_value(td::Unit()); return; } @@ -64,14 +64,19 @@ void RootDb::get_block_data(BlockHandle handle, td::Promise> } }); - td::actor::send_closure(handle->moved_to_storage() ? archive_db_.get() : file_db_.get(), &FileDb::load_file, - FileDb::RefId{fileref::Block{handle->id()}}, std::move(P)); + if (handle->moved_to_archive()) { + td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(), + FileDb::RefId{fileref::Block{handle->id()}}, std::move(P)); + } else { + td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file, + FileDb::RefId{fileref::Block{handle->id()}}, std::move(P)); + } } } void RootDb::store_block_signatures(BlockHandle handle, td::Ref data, td::Promise promise) { - if (handle->moved_to_storage()) { + if (handle->moved_to_storage() || handle->moved_to_archive()) { promise.set_value(td::Unit()); return; } @@ -94,7 +99,7 @@ void RootDb::get_block_signatures(BlockHandle handle, td::Promiseinited_signatures()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { - if (handle->moved_to_storage()) { + if (handle->moved_to_storage() || handle->moved_to_archive()) { promise.set_error(td::Status::Error(ErrorCode::error, "signatures already gc'd")); return; } @@ -111,7 +116,7 @@ void RootDb::get_block_signatures(BlockHandle handle, td::Promise proof, td::Promise promise) { - if (handle->moved_to_storage()) { + if (handle->moved_to_storage() || handle->moved_to_archive()) { promise.set_value(td::Unit()); return; } @@ -142,13 +147,18 @@ void RootDb::get_block_proof(BlockHandle handle, td::Promise> pro promise.set_result(create_proof(id, R.move_as_ok())); } }); - td::actor::send_closure(handle->moved_to_storage() ? archive_db_.get() : file_db_.get(), &FileDb::load_file, - FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P)); + if (handle->moved_to_archive()) { + td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(), + FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P)); + } else { + td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file, + FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P)); + } } } void RootDb::store_block_proof_link(BlockHandle handle, td::Ref proof, td::Promise promise) { - if (handle->moved_to_storage()) { + if (handle->moved_to_storage() || handle->moved_to_archive()) { promise.set_value(td::Unit()); return; } @@ -179,8 +189,13 @@ void RootDb::get_block_proof_link(BlockHandle handle, td::Promisemoved_to_storage() ? archive_db_.get() : file_db_.get(), &FileDb::load_file, - FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P)); + if (handle->moved_to_archive()) { + td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(), + FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P)); + } else { + td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file, + FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P)); + } } } @@ -225,7 +240,7 @@ void RootDb::get_block_candidate(PublicKey source, BlockIdExt id, FileHash colla void RootDb::store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) { - if (handle->moved_to_storage()) { + if (handle->moved_to_storage() || handle->moved_to_archive()) { promise.set_value(std::move(state)); return; } @@ -290,27 +305,27 @@ void RootDb::store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterc } }); - td::actor::send_closure(archive_db_, &FileDb::store_file, + td::actor::send_closure(old_archive_db_, &FileDb::store_file, FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(state), std::move(P)); } void RootDb::get_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { - td::actor::send_closure(archive_db_, &FileDb::load_file, + td::actor::send_closure(old_archive_db_, &FileDb::load_file, FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise)); } void RootDb::get_persistent_state_file_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset, td::int64 max_size, td::Promise promise) { - td::actor::send_closure(archive_db_, &FileDb::load_file_slice, + td::actor::send_closure(old_archive_db_, &FileDb::load_file_slice, FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, offset, max_size, std::move(promise)); } void RootDb::check_persistent_state_file_exists(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { - td::actor::send_closure(archive_db_, &FileDb::check_file, + td::actor::send_closure(old_archive_db_, &FileDb::check_file, FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise)); } @@ -325,26 +340,38 @@ void RootDb::store_zero_state_file(BlockIdExt block_id, td::BufferSlice state, t } }); - td::actor::send_closure(archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ZeroState{block_id}}, + td::actor::send_closure(old_archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ZeroState{block_id}}, std::move(state), std::move(P)); } void RootDb::get_zero_state_file(BlockIdExt block_id, td::Promise promise) { - td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ZeroState{block_id}}, + td::actor::send_closure(old_archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ZeroState{block_id}}, std::move(promise)); } void RootDb::check_zero_state_file_exists(BlockIdExt block_id, td::Promise promise) { - td::actor::send_closure(archive_db_, &FileDb::check_file, FileDb::RefId{fileref::ZeroState{block_id}}, + td::actor::send_closure(old_archive_db_, &FileDb::check_file, FileDb::RefId{fileref::ZeroState{block_id}}, std::move(promise)); } void RootDb::store_block_handle(BlockHandle handle, td::Promise promise) { - td::actor::send_closure(block_db_, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); + if (handle->moved_to_archive()) { + td::actor::send_closure(new_archive_db_, &ArchiveManager::write_handle, std::move(handle), std::move(promise)); + } else { + td::actor::send_closure(block_db_, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); + } } void RootDb::get_block_handle(BlockIdExt id, td::Promise promise) { - td::actor::send_closure(block_db_, &BlockDb::get_block_handle, id, std::move(promise)); + auto P = td::PromiseCreator::lambda( + [db = block_db_.get(), id, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + td::actor::send_closure(db, &BlockDb::get_block_handle, id, std::move(promise)); + } else { + promise.set_value(R.move_as_ok()); + } + }); + td::actor::send_closure(new_archive_db_, &ArchiveManager::read_handle, id, std::move(P)); } void RootDb::try_get_static_file(FileHash file_hash, td::Promise promise) { @@ -426,16 +453,17 @@ void RootDb::start_up() { cell_db_ = td::actor::create_actor("celldb", actor_id(this), root_path_ + "/celldb/"); block_db_ = td::actor::create_actor("blockdb", actor_id(this), root_path_ + "/blockdb/"); file_db_ = td::actor::create_actor("filedb", actor_id(this), root_path_ + "/files/", depth_, false); - archive_db_ = + old_archive_db_ = td::actor::create_actor("filedbarchive", actor_id(this), root_path_ + "/archive/", depth_, true); lt_db_ = td::actor::create_actor("ltdb", actor_id(this), root_path_ + "/ltdb/"); state_db_ = td::actor::create_actor("statedb", actor_id(this), root_path_ + "/state/"); static_files_db_ = td::actor::create_actor("staticfilesdb", actor_id(this), root_path_ + "/static/"); + new_archive_db_ = td::actor::create_actor("archivemanager", root_path_ + "/archive/"); } void RootDb::archive(BlockIdExt block_id, td::Promise promise) { - td::actor::create_actor("archiveblock", block_id, actor_id(this), file_db_.get(), archive_db_.get(), - std::move(promise)) + td::actor::create_actor("archiveblock", block_id, actor_id(this), file_db_.get(), + old_archive_db_.get(), new_archive_db_.get(), std::move(promise)) .release(); } @@ -480,14 +508,15 @@ void RootDb::allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise p CHECK(!is_archive); td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_candidate_gc, key.block_id, std::move(promise)); - })); + }, + [&](const fileref::BlockInfo &key) { UNREACHABLE(); })); } void RootDb::prepare_stats(td::Promise>> promise) { auto merger = StatsMerger::create(std::move(promise)); td::actor::send_closure(file_db_, &FileDb::prepare_stats, merger.make_promise("filedb.")); - td::actor::send_closure(archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb.")); + td::actor::send_closure(old_archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb.")); } void RootDb::truncate(td::Ref state, td::Promise promise) { diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index a584ac9746..47cd31be18 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -28,6 +28,7 @@ #include "ltdb.hpp" #include "statedb.hpp" #include "staticfilesdb.hpp" +#include "archive-db.hpp" namespace ton { @@ -126,10 +127,11 @@ class RootDb : public Db { td::actor::ActorOwn cell_db_; td::actor::ActorOwn block_db_; td::actor::ActorOwn file_db_; - td::actor::ActorOwn archive_db_; + td::actor::ActorOwn old_archive_db_; td::actor::ActorOwn lt_db_; td::actor::ActorOwn state_db_; td::actor::ActorOwn static_files_db_; + td::actor::ActorOwn new_archive_db_; }; } // namespace validator diff --git a/validator/interfaces/block-handle.h b/validator/interfaces/block-handle.h index 1f6ad877fe..b30269cf77 100644 --- a/validator/interfaces/block-handle.h +++ b/validator/interfaces/block-handle.h @@ -33,6 +33,7 @@ struct BlockHandleInterface { virtual BlockIdExt id() const = 0; virtual bool received() const = 0; virtual bool moved_to_storage() const = 0; + virtual bool moved_to_archive() const = 0; virtual bool deleted() const = 0; virtual bool inited_next_left() const = 0; virtual bool inited_next_right() const = 0; @@ -83,6 +84,7 @@ struct BlockHandleInterface { virtual void set_prev(BlockIdExt prev) = 0; virtual void set_received() = 0; virtual void set_moved_to_storage() = 0; + virtual void set_moved_to_archive() = 0; virtual void set_deleted() = 0; virtual void set_split(bool value) = 0; virtual void set_merge(bool value) = 0; diff --git a/validator/manager.cpp b/validator/manager.cpp index eba3f24363..fc1edd9889 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1842,6 +1842,27 @@ void ValidatorManagerImpl::allow_block_state_gc(BlockIdExt block_id, td::Promise UNREACHABLE(); } +void ValidatorManagerImpl::allow_block_info_gc(BlockIdExt block_id, td::Promise promise) { + auto P = + td::PromiseCreator::lambda([db = db_.get(), promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_result(false); + } else { + auto handle = R.move_as_ok(); + if (!handle->moved_to_archive()) { + promise.set_result(false); + } else { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + promise.set_result(true); + }); + td::actor::send_closure(db, &Db::store_block_handle, handle, std::move(P)); + } + } + }); + get_block_handle(block_id, false, std::move(P)); +} + void ValidatorManagerImpl::got_next_gc_masterchain_handle(BlockHandle handle) { CHECK(gc_advancing_); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle](td::Result> R) { diff --git a/validator/manager.hpp b/validator/manager.hpp index 6ce036ca74..669f1c6694 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -463,11 +463,7 @@ class ValidatorManagerImpl : public ValidatorManager { void allow_delete(BlockIdExt block_id, td::Promise promise); void allow_archive(BlockIdExt block_id, td::Promise promise); void allow_block_data_gc(BlockIdExt block_id, bool is_archive, td::Promise promise) override { - if (!is_archive) { - allow_archive(block_id, std::move(promise)); - } else { - allow_delete(block_id, std::move(promise)); - } + allow_archive(block_id, std::move(promise)); } void allow_block_state_gc(BlockIdExt block_id, td::Promise promise) override; void allow_zero_state_file_gc(BlockIdExt block_id, td::Promise promise) override { @@ -479,25 +475,15 @@ class ValidatorManagerImpl : public ValidatorManager { allow_archive(block_id, std::move(promise)); } void allow_block_proof_gc(BlockIdExt block_id, bool is_archive, td::Promise promise) override { - if (!is_archive) { - allow_archive(block_id, std::move(promise)); - } else { - allow_delete(block_id, std::move(promise)); - } + allow_archive(block_id, std::move(promise)); } void allow_block_proof_link_gc(BlockIdExt block_id, bool is_archive, td::Promise promise) override { - if (!is_archive) { - allow_archive(block_id, std::move(promise)); - } else { - allow_delete(block_id, std::move(promise)); - } + allow_archive(block_id, std::move(promise)); } void allow_block_candidate_gc(BlockIdExt block_id, td::Promise promise) override { allow_block_state_gc(block_id, std::move(promise)); } - void allow_block_info_gc(BlockIdExt block_id, td::Promise promise) override { - allow_delete(block_id, std::move(promise)); - } + void allow_block_info_gc(BlockIdExt block_id, td::Promise promise) override; void send_peek_key_block_request(); void got_next_key_blocks(std::vector vec); diff --git a/validator/net/download-state.cpp b/validator/net/download-state.cpp index 6fc8c366fb..abda77a948 100644 --- a/validator/net/download-state.cpp +++ b/validator/net/download-state.cpp @@ -188,7 +188,7 @@ void DownloadState::got_block_state_part(td::BufferSlice data, td::uint32 reques parts_.push_back(std::move(data)); if (last_part) { - td::BufferSlice res{sum_}; + td::BufferSlice res{td::narrow_cast(sum_)}; auto S = res.as_slice(); for (auto &p : parts_) { S.copy_from(p.as_slice()); From 3002321eb779e9936243e3b5f00be7579fb07654 Mon Sep 17 00:00:00 2001 From: ton Date: Thu, 31 Oct 2019 14:26:04 +0400 Subject: [PATCH 015/667] updated tonlib --- crypto/CMakeLists.txt | 5 +- crypto/block/mc-config.cpp | 19 ++- crypto/block/mc-config.h | 1 + crypto/parser/symtable.cpp | 2 +- crypto/smartcont/config-code.fc | 8 + crypto/smartcont/new-wallet-v3.fif | 63 ++++++++ crypto/smartcont/wallet3-code.fc | 35 +++++ crypto/tl/tlbc-gen-cpp.cpp | 8 +- crypto/tl/tlbc.cpp | 2 +- crypto/vm/stack.hpp | 29 ++-- tdutils/td/utils/JsonBuilder.h | 2 +- tdutils/td/utils/StringBuilder.h | 2 + tl/generate/scheme/tonlib_api.tl | 19 ++- tl/generate/scheme/tonlib_api.tlo | Bin 16460 -> 17828 bytes tonlib/test/offline.cpp | 12 ++ tonlib/test/online.cpp | 48 +++--- tonlib/tonlib/ExtClientOutbound.cpp | 1 + tonlib/tonlib/LastBlock.cpp | 14 ++ tonlib/tonlib/TonlibClient.cpp | 233 +++++++++++++++++++++++++--- tonlib/tonlib/TonlibClient.h | 18 ++- tonlib/tonlib/tonlib-cli.cpp | 158 +++++++++++++++---- validator/manager.cpp | 6 +- 22 files changed, 576 insertions(+), 109 deletions(-) create mode 100644 crypto/smartcont/new-wallet-v3.fif create mode 100644 crypto/smartcont/wallet3-code.fc diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 8b8f147ab4..22c05fba05 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -350,6 +350,7 @@ if (NOT CMAKE_CROSSCOMPILING) GenFif(DEST smartcont/auto/config-code SOURCE smartcont/config-code.fc NAME config) GenFif(DEST smartcont/auto/wallet-code SOURCE smartcont/wallet-code.fc NAME wallet) + GenFif(DEST smartcont/auto/wallet3-code SOURCE smartcont/wallet3-code.fc NAME wallet3) GenFif(DEST smartcont/auto/simple-wallet-code SOURCE smartcont/simple-wallet-code.fc NAME simple-wallet) GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet) GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2) @@ -364,7 +365,9 @@ endif() add_library(smc-envelope ${SMC_ENVELOPE_SOURCE}) target_include_directories(smc-envelope PUBLIC $) target_link_libraries(smc-envelope PUBLIC ton_crypto PRIVATE tdutils ton_block) -add_dependencies(smc-envelope gen_fif) +if (NOT CMAKE_CROSSCOMPILING) + add_dependencies(smc-envelope gen_fif) +endif() add_executable(create-state block/create-state.cpp) target_include_directories(create-state PUBLIC $ diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 80e399eed1..b5959f7557 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -534,19 +534,16 @@ td::Result> Config::get_storage_prices() const { return std::move(res); } -td::Result Config::get_gas_limits_prices(bool is_masterchain) const { +td::Result Config::do_get_gas_limits_prices(td::Ref cell, int id) { GasLimitsPrices res; - auto id = is_masterchain ? 20 : 21; - auto cell = get_config_param(id); - if (cell.is_null()) { - return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent"); - } - auto cs = vm::load_cell_slice(std::move(cell)); + auto cs = vm::load_cell_slice(cell); block::gen::GasLimitsPrices::Record_gas_flat_pfx flat; if (tlb::unpack(cs, flat)) { cs = *flat.other; res.flat_gas_limit = flat.flat_gas_limit; res.flat_gas_price = flat.flat_gas_price; + } else { + cs = vm::load_cell_slice(cell); } auto f = [&](const auto& r, td::uint64 spec_limit) { res.gas_limit = r.gas_limit; @@ -570,6 +567,14 @@ td::Result Config::get_gas_limits_prices(bool is_masterchain) c } return res; } +td::Result Config::get_gas_limits_prices(bool is_masterchain) const { + auto id = is_masterchain ? 20 : 21; + auto cell = get_config_param(id); + if (cell.is_null()) { + return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent"); + } + return do_get_gas_limits_prices(std::move(cell), id); +} td::Result Config::get_msg_prices(bool is_masterchain) const { auto id = is_masterchain ? 24 : 25; diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 018a47adbe..241ae88c55 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -540,6 +540,7 @@ class Config { static td::Result> unpack_validator_set(Ref valset_root); td::Result> get_storage_prices() const; td::Result get_gas_limits_prices(bool is_masterchain = false) const; + static td::Result do_get_gas_limits_prices(td::Ref cell, int id); td::Result get_msg_prices(bool is_masterchain = false) const; static CatchainValidatorsConfig unpack_catchain_validators_config(Ref cell); CatchainValidatorsConfig get_catchain_validators_config() const; diff --git a/crypto/parser/symtable.cpp b/crypto/parser/symtable.cpp index 157b4a2b06..939bb29ca9 100644 --- a/crypto/parser/symtable.cpp +++ b/crypto/parser/symtable.cpp @@ -68,7 +68,7 @@ sym_idx_t SymTableBase::gen_lookup(std::string str, int mode, sym_idx_t idx) { if (!(mode & 1)) { return not_found; } - if (def_sym >= ((long)p * 3) / 4) { + if (def_sym >= ((long long)p * 3) / 4) { throw SymTableOverflow{def_sym}; } sym_table[h1] = std::make_unique(str, idx <= 0 ? sym_idx_t(h1) : -idx); diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index a49ad1378a..3a897d4cd0 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -163,6 +163,14 @@ slice create_new_entry(cs) inline { return begin_cell().store_int(false, 1).store_uint(0, 64).store_uint(0, 256).store_slice(cs).end_cell().begin_parse(); } +(cell, int, int, slice) unpack_suggestion(slice cs) inline { + return (cs~load_dict(), cs~load_uint(64), cs~load_uint(256), cs); +} + +builder pack_suggestion(cell voters, int sum_weight, int vset_id, slice body) inline { + return begin_cell().store_dict(voters).store_uint(sum_weight, 64).store_uint(vset_id, 256).store_slice(body); +} + cell register_vote(vote_dict, action, cs, idx, weight) { int hash = 0; var entry = null(); diff --git a/crypto/smartcont/new-wallet-v3.fif b/crypto/smartcont/new-wallet-v3.fif new file mode 100644 index 0000000000..187519c9cd --- /dev/null +++ b/crypto/smartcont/new-wallet-v3.fif @@ -0,0 +1,63 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a new advanced wallet with unique 32-bit identifier in specified workchain, with private key saved to or loaded from .pk" cr + ."('new-wallet.pk' by default)" cr 1 halt +} : usage +$# 2- -2 and ' usage if + +$1 parse-workchain-id =: wc // set workchain id from command line argument +$2 parse-int =: subwallet-id +def? $3 { @' $3 } { "new-wallet" } cond constant file-base + +."Creating new advanced v3 wallet in workchain " wc . cr +."with unique wallet id " subwallet-id . cr + +// Create new advanced wallet; code adapted from `auto/wallet3-code.fif` +<{ 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 +}>c // >libref +// code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."signing message: " +dup ."External message for initialization is " B dup Bx. cr +file-base +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/crypto/smartcont/wallet3-code.fc b/crypto/smartcont/wallet3-code.fc new file mode 100644 index 0000000000..a52cb1ec04 --- /dev/null +++ b/crypto/smartcont/wallet3-code.fc @@ -0,0 +1,35 @@ +;; Simple wallet smart contract + +() recv_internal(slice in_msg) impure { + ;; do nothing for internal messages +} + +() recv_external(slice in_msg) impure { + var signature = in_msg~load_bits(512); + var cs = in_msg; + var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)); + throw_if(35, valid_until <= now()); + var ds = get_data().begin_parse(); + var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256)); + ds.end_parse(); + throw_unless(33, msg_seqno == stored_seqno); + throw_unless(34, subwallet_id == stored_subwallet); + throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key)); + accept_message(); + cs~touch(); + while (cs.slice_refs()) { + var mode = cs~load_uint(8); + send_raw_message(cs~load_ref(), mode); + } + set_data(begin_cell() + .store_uint(stored_seqno + 1, 32) + .store_uint(stored_subwallet, 32) + .store_uint(public_key, 256) + .end_cell()); +} + +;; Get methods + +int seqno() method_id { + return get_data().begin_parse().preload_uint(32); +} diff --git a/crypto/tl/tlbc-gen-cpp.cpp b/crypto/tl/tlbc-gen-cpp.cpp index 5ab0620cce..bbf3adf79c 100644 --- a/crypto/tl/tlbc-gen-cpp.cpp +++ b/crypto/tl/tlbc-gen-cpp.cpp @@ -1021,9 +1021,9 @@ void CppTypeCode::generate_tag_pfx_selector(std::ostream& os, std::string nl, co } os << "};" << nl << "return ctab[1 + "; if (simple) { - os << "(long)cs.prefetch_ulong(" << d << ")];"; + os << "(long long)cs.prefetch_ulong(" << d << ")];"; } else { - os << "(long)cs.bselect" << (d >= min_size ? "(" : "_ext(") << d << ", " << HexConstWriter{mask} << ")];"; + os << "(long long)cs.bselect" << (d >= min_size ? "(" : "_ext(") << d << ", " << HexConstWriter{mask} << ")];"; } } @@ -1153,7 +1153,7 @@ void CppTypeCode::generate_get_tag_body(std::ostream& os, std::string nl) { os << ")) {"; for (int i = 0; i < l; i++) { if (A[i] != 0) { - if ((long)A[i] > 0) { + if ((long long)A[i] > 0) { int j; for (j = 0; j < i; j++) { if (A[j] == A[i]) { @@ -1165,7 +1165,7 @@ void CppTypeCode::generate_get_tag_body(std::ostream& os, std::string nl) { } } os << nl << "case " << i << ":"; - if ((long)A[i] > 0) { + if ((long long)A[i] > 0) { int j; for (j = i + 1; j < l; j++) { if (A[j] == A[i]) { diff --git a/crypto/tl/tlbc.cpp b/crypto/tl/tlbc.cpp index 5d3d0fe4cb..ff2e19e60e 100644 --- a/crypto/tl/tlbc.cpp +++ b/crypto/tl/tlbc.cpp @@ -687,7 +687,7 @@ unsigned long long BinTrie::build_submap(int depth, unsigned long long A[]) cons } else { std::memset(A + n, 0, n * 8); } - if (A[n] != A[n - 1] || (long)A[n] < 0) { + if (A[n] != A[n - 1] || (long long)A[n] < 0) { r2 |= 1; } else { r2 &= ~1; diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index c87bd4d937..0b16f59c39 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -33,6 +33,8 @@ #include "vm/cellslice.h" #include "vm/excno.hpp" +#include "td/utils/Span.h" + namespace td { extern template class td::Cnt; extern template class td::Ref>; @@ -156,7 +158,7 @@ class StackEntry { private: template - Ref dynamic_as() const & { + Ref dynamic_as() const& { return tp == tag ? static_cast>(ref) : td::Ref{}; } template @@ -168,7 +170,7 @@ class StackEntry { return tp == tag ? static_cast>(std::move(ref)) : td::Ref{}; } template - Ref as() const & { + Ref as() const& { return tp == tag ? Ref{td::static_cast_ref(), ref} : td::Ref{}; } template @@ -189,31 +191,31 @@ class StackEntry { return ref; } } - td::RefInt256 as_int() const & { + td::RefInt256 as_int() const& { return as(); } td::RefInt256 as_int() && { return move_as(); } - Ref as_cell() const & { + Ref as_cell() const& { return as(); } Ref as_cell() && { return move_as(); } - Ref as_builder() const & { + Ref as_builder() const& { return as(); } Ref as_builder() && { return move_as(); } - Ref as_slice() const & { + Ref as_slice() const& { return as(); } Ref as_slice() && { return move_as(); } - Ref as_cont() const &; + Ref as_cont() const&; Ref as_cont() &&; Ref> as_string_ref() const { return as, t_string>(); @@ -228,16 +230,16 @@ class StackEntry { std::string as_bytes() const { return tp == t_bytes ? *as_bytes_ref() : ""; } - Ref as_box() const &; + Ref as_box() const&; Ref as_box() &&; - Ref as_tuple() const &; + Ref as_tuple() const&; Ref as_tuple() &&; - Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const &; + Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const&; Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) &&; - Ref as_atom() const &; + Ref as_atom() const&; Ref as_atom() &&; template - Ref as_object() const & { + Ref as_object() const& { return dynamic_as(); } template @@ -361,6 +363,9 @@ class Stack : public td::CntObject { std::vector::const_iterator from_top(int offs) const { return stack.cend() - offs; } + td::Span as_span() const { + return stack; + } bool at_least(int req) const { return depth() >= req; } diff --git a/tdutils/td/utils/JsonBuilder.h b/tdutils/td/utils/JsonBuilder.h index f2df4e6022..be089ca6c0 100644 --- a/tdutils/td/utils/JsonBuilder.h +++ b/tdutils/td/utils/JsonBuilder.h @@ -193,7 +193,7 @@ class JsonObjectScope; class JsonBuilder { public: - explicit JsonBuilder(StringBuilder &&sb, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) { + explicit JsonBuilder(StringBuilder &&sb = {}, int32 offset = -1) : sb_(std::move(sb)), offset_(offset) { } StringBuilder &string_builder() { return sb_; diff --git a/tdutils/td/utils/StringBuilder.h b/tdutils/td/utils/StringBuilder.h index 7415aa2a19..229b967e80 100644 --- a/tdutils/td/utils/StringBuilder.h +++ b/tdutils/td/utils/StringBuilder.h @@ -31,6 +31,8 @@ namespace td { class StringBuilder { public: explicit StringBuilder(MutableSlice slice, bool use_buffer = false); + StringBuilder() : StringBuilder({}, true) { + } void clear() { current_ptr_ = begin_ptr_; diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 2a3f0f52c3..01ab5c628e 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -73,8 +73,22 @@ fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53= Fees; query.fees source_fees:fees destination_fees:fees = query.Fees; query.info id:int53 valid_until:int53 body_hash:bytes = query.Info; +tvm.slice bytes:string = tvm.Slice; +tvm.cell bytes:string = tvm.Cell; +tvm.numberDecimal number:string = tvm.Number; + +tvm.stackEntrySlice slice:tvm.slice = tvm.StackEntry; +tvm.stackEntryCell cell:tvm.cell = tvm.StackEntry; +tvm.stackEntryNumber number:tvm.Number = tvm.StackEntry; +tvm.stackEntryUnsupported = tvm.StackEntry; + smc.info id:int53 = smc.Info; +smc.methodIdNumber number:int32 = smc.MethodId; +smc.methodIdName name:string = smc.MethodId; + +smc.runResult gas_used:int53 stack:vector exit_code:int32 = smc.RunResult; + updateSendLiteServerQuery id:int64 data:bytes = Update; updateSyncState sync_state:SyncState = Update; @@ -98,8 +112,6 @@ logTags tags:vector = LogTags; data bytes:secureBytes = Data; -tvm.cell bytes:string = tvm.Cell; - liteServer.info now:int53 version:int32 capabilities:int64 = liteServer.Info; ---functions--- @@ -108,6 +120,7 @@ init options:options = Ok; close = Ok; options.setConfig config:config = Ok; +options.validateConfig config:config = Ok; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; deleteKey key:key = Ok; @@ -126,7 +139,6 @@ kdf password:secureBytes salt:secureBytes iterations:int32 = Data; unpackAccountAddress account_address:string = UnpackedAccountAddress; packAccountAddress account_address:unpackedAccountAddress = AccountAddress; - getBip39Hints prefix:string = Bip39Hints; //raw.init initial_account_state:raw.initialAccountState = Ok; @@ -168,6 +180,7 @@ smc.load account_address:accountAddress = smc.Info; smc.getCode id:int53 = tvm.Cell; smc.getData id:int53 = tvm.Cell; smc.getState id:int53 = tvm.Cell; +smc.runGetMethod id:int53 method:smc.MethodId stack:vector = smc.RunResult; onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryError id:int64 error:error = Ok; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index d7c0e04957ea94a8765b1d6349812bb11a8c80ec..0e727228133b3324ac3f085fbc973500a6484d3b 100644 GIT binary patch delta 991 zcmX@pz__HFk@wMTeJchiu-M4^pG`2sjU|+)I5%0(H?<@qKgBa;;zViT&!1gFctL_e zrFlWA#icnV3={JuC-boHnEZoLgn#q2N3L8YWx0BOrMXF|ML;RuzmqKwaDs$_b25`B ze`J>y^{n_5!3P#8Nleam%_}LYWMG+`$Ra-Z2Kx*~*Ug0-{o)3Hb}BCs0$G#`wH;z2 z1ISf3j|urO0^KV8?7k*j9@r*^$rCJO>_L(+Ph6Gbikb3X{i6ViA&I%EKn3WAu;hWo zC*N0;#V`Zz<080^89-tnM=(Pj!I7R=9A8?Tn!>;^`96;%8*4GhXOsVN$jN{_3v&|4 zy_~5PnI-Yb`6)nmfZWUiGHde}`5Y#FP~ZuI!Uf_bm(=9U+{7FP29P)wFN0jG0+NFn z5ArcQw2OhE4YUHppRAx{P!D1tD^$(a4HgDj26ieq5!N8CNaXoTy;d1>a$EjkHs1}6g6_*wiilA2t9XWaccount_address_); } +TEST(Tonlib, ConfigParseBug) { + td::Slice literal = + "D1000000000000006400000000000186A0DE0000000003E8000000000000000F424000000000000F42400000000000002710000000000098" + "96800000000005F5E100000000003B9ACA00"; + unsigned char buff[128]; + int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), literal.begin(), literal.end()); + CHECK(bits >= 0); + auto slice = vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize(); + block::Config::do_get_gas_limits_prices(std::move(slice), 21).ensure(); +} + TEST(Tonlib, EncryptionApi) { using tonlib_api::make_object; Client client; diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 08a3c36af4..c96fc75e0f 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -462,29 +462,31 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { transfer_grams(client, giver_wallet, address, 1 * Gramm).ensure(); auto init_state = ms->get_init_state(); - // Just transfer all (some) money back in one query - vm::CellBuilder icb; - ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), - 5 * Gramm / 10); - icb.store_bytes("\0\0\0\0", 4); - vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure(); - ton::MultisigWallet::QueryBuilder qb(-1, icb.finalize()); - for (int i = 0; i < k - 1; i++) { - qb.sign(i, private_keys[i]); - } - - auto query_id = - create_raw_query(client, address, vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str(), - vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str(), - vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str()) - .move_as_ok(); - auto fees = query_estimate_fees(client, query_id); + for (int i = 0; i < 2; i++) { + // Just transfer all (some) money back in one query + vm::CellBuilder icb; + ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), 1); + icb.store_bytes("\0\0\0\0", 4); + vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure(); + ton::MultisigWallet::QueryBuilder qb(-1 - i, icb.finalize()); + for (int i = 0; i < k - 1; i++) { + qb.sign(i, private_keys[i]); + } - LOG(INFO) << "Expected src fees: " << fees.first; - LOG(INFO) << "Expected dst fees: " << fees.second; - auto a_state = get_account_state(client, address); - query_send(client, query_id); - auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok(); + auto query_id = + create_raw_query(client, address, + i == 0 ? vm::std_boc_serialize(ms->get_state().code).move_as_ok().as_slice().str() : "", + i == 0 ? vm::std_boc_serialize(ms->get_state().data).move_as_ok().as_slice().str() : "", + vm::std_boc_serialize(qb.create(k - 1, private_keys[k - 1])).move_as_ok().as_slice().str()) + .move_as_ok(); + auto fees = query_estimate_fees(client, query_id); + + LOG(INFO) << "Expected src fees: " << fees.first; + LOG(INFO) << "Expected dst fees: " << fees.second; + auto a_state = get_account_state(client, address); + query_send(client, query_id); + auto new_a_state = wait_state_change(client, a_state, a_state.sync_utime + 30).move_as_ok(); + } } int main(int argc, char* argv[]) { @@ -515,8 +517,8 @@ int main(int argc, char* argv[]) { if (reset_keystore_dir) { td::rmrf(keystore_dir).ignore(); - td::mkdir(keystore_dir).ensure(); } + td::mkdir(keystore_dir).ensure(); SET_VERBOSITY_LEVEL(VERBOSITY_NAME(INFO)); static_send(make_object("tonlib_query", 4)).ensure(); diff --git a/tonlib/tonlib/ExtClientOutbound.cpp b/tonlib/tonlib/ExtClientOutbound.cpp index f1746af5d6..ee6ddc4fa8 100644 --- a/tonlib/tonlib/ExtClientOutbound.cpp +++ b/tonlib/tonlib/ExtClientOutbound.cpp @@ -42,6 +42,7 @@ class ExtClientOutboundImp : public ExtClientOutbound { auto it = queries_.find(id); if (it == queries_.end()) { promise.set_error(TonlibError::Internal("Unknown query id")); + return; } it->second.set_result(std::move(r_data)); queries_.erase(it); diff --git a/tonlib/tonlib/LastBlock.cpp b/tonlib/tonlib/LastBlock.cpp index 234af99719..a2f94787b2 100644 --- a/tonlib/tonlib/LastBlock.cpp +++ b/tonlib/tonlib/LastBlock.cpp @@ -25,6 +25,8 @@ #include "lite-client/lite-client-common.h" +#include "td/utils/JsonBuilder.h" + namespace tonlib { // init_state <-> last_key_block @@ -313,6 +315,18 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) { if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) { state_.last_key_block_id = mc_key_block_id; VLOG(last_block) << "Update masterchain key block id: " << state_.last_key_block_id.to_str(); + if (true) { + td::JsonBuilder jb; + auto jo = jb.enter_object(); + jo("workchain", state_.last_key_block_id.id.workchain); + jo("shard", static_cast(state_.last_key_block_id.id.shard)); + jo("seqno", static_cast(state_.last_key_block_id.id.seqno)); + jo("root_hash", td::base64_encode(as_slice(state_.last_key_block_id.root_hash))); + jo("file_hash", td::base64_encode(as_slice(state_.last_key_block_id.file_hash))); + jo.leave(); + LOG(INFO) << jb.string_builder().as_cslice(); + } + //LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " " //<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " " //<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice()); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index edd8d6e751..74483fa3ad 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -798,18 +798,22 @@ void TonlibClient::init_ext_client() { if (use_callbacks_for_network_) { class Callback : public ExtClientOutbound::Callback { public: - explicit Callback(td::actor::ActorShared parent) : parent_(std::move(parent)) { + explicit Callback(td::actor::ActorShared parent, td::uint32 config_generation) + : parent_(std::move(parent)), config_generation_(config_generation) { } void request(td::int64 id, std::string data) override { - send_closure(parent_, &TonlibClient::proxy_request, id, std::move(data)); + send_closure(parent_, &TonlibClient::proxy_request, (id << 16) | (config_generation_ & 0xffff), + std::move(data)); } private: td::actor::ActorShared parent_; + td::uint32 config_generation_; }; ref_cnt_++; - auto client = ExtClientOutbound::create(td::make_unique(td::actor::actor_shared(this))); + auto client = + ExtClientOutbound::create(td::make_unique(td::actor::actor_shared(this), config_generation_)); ext_client_outbound_ = client.get(); raw_client_ = std::move(client); } else { @@ -836,6 +840,7 @@ void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 conf if (config_generation != config_generation_) { return; } + last_block_storage_.save_state(blockchain_name_, state); } @@ -858,7 +863,7 @@ void TonlibClient::update_sync_state(LastBlockSyncState state, td::uint32 config } } -void TonlibClient::init_last_block() { +void TonlibClient::init_last_block(td::optional o_master_config) { ref_cnt_++; class Callback : public LastBlock::Callback { public: @@ -893,6 +898,15 @@ void TonlibClient::init_last_block() { state = r_state.move_as_ok(); } + if (o_master_config) { + auto master_config = o_master_config.unwrap(); + if (master_config.init_block_id.is_valid() && + state.last_key_block_id.id.seqno < master_config.init_block_id.id.seqno) { + state.last_key_block_id = master_config.init_block_id; + LOG(INFO) << "Use init block from MASTER config: " << master_config.init_block_id.to_str(); + } + } + raw_last_block_ = td::actor::create_actor( td::actor::ActorOptions().with_name("LastBlock").with_poll(false), get_client_ref(), std::move(state), config_, source_.get_cancellation_token(), td::make_unique(td::actor::actor_shared(this), config_generation_)); @@ -988,6 +1002,7 @@ bool TonlibClient::is_static_request(td::int32 id) { case tonlib_api::testGiver_getAccountAddress::ID: case tonlib_api::packAccountAddress::ID: case tonlib_api::unpackAccountAddress::ID: + case tonlib_api::options_validateConfig::ID: case tonlib_api::getBip39Hints::ID: case tonlib_api::setLogStream::ID: case tonlib_api::getLogStream::ID: @@ -1142,37 +1157,112 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, key_storage_.set_key_value(kv_); last_block_storage_.set_key_value(kv_); if (request.options_->config_) { - TRY_STATUS(set_config(std::move(request.options_->config_))); + TRY_RESULT(full_config, validate_config(std::move(request.options_->config_))); + set_config(std::move(full_config)); } state_ = State::Running; promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } -td::Status TonlibClient::set_config(object_ptr config) { - CHECK(config); +class MasterConfig { + public: + void add_config(std::string name, std::string json) { + auto config = std::make_shared(Config::parse(json).move_as_ok()); + if (!name.empty()) { + by_name_[name] = config; + } + by_root_hash_[config->zero_state_id.root_hash] = config; + } + td::optional by_name(std::string name) const { + auto it = by_name_.find(name); + if (it == by_name_.end()) { + return {}; + } + return *it->second; + } + + td::optional by_root_hash(const ton::RootHash& root_hash) const { + auto it = by_root_hash_.find(root_hash); + if (it == by_root_hash_.end()) { + return {}; + } + return *it->second; + } + + private: + size_t next_id_{0}; + std::map> by_name_; + std::map> by_root_hash_; +}; + +const MasterConfig& get_default_master_config() { + static MasterConfig config = [] { + MasterConfig res; + res.add_config("testnet", R"abc({ + "liteservers": [ + ], + "validator": { + "@type": "validator.config.global", + "zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=", + "file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" + }, + "init_block": +{"workchain":-1,"shard":-9223372036854775808,"seqno":870721,"root_hash":"jYKhSQ1xeSPprzgjqiUOnAWwc2yqs7nCVAU21k922s4=","file_hash":"kHidF02CZpaz2ia9jtXUJLp0AiWMWwfzprTUIsddHSo="} + } +})abc"); + return res; + }(); + return config; +} + +td::Result TonlibClient::validate_config(tonlib_api::object_ptr config) { + if (!config) { + return TonlibError::EmptyField("config"); + } if (config->config_.empty()) { return TonlibError::InvalidConfig("config is empty"); } TRY_RESULT_PREFIX(new_config, Config::parse(std::move(config->config_)), TonlibError::InvalidConfig("can't parse config")); + if (new_config.lite_clients.empty() && !config->use_callbacks_for_network_) { return TonlibError::InvalidConfig("no lite clients"); } - config_ = std::move(new_config); - config_generation_++; + + td::optional o_master_config; if (config->blockchain_name_.empty()) { - blockchain_name_ = td::sha256(config_.zero_state_id.to_str()).substr(0, 16); + o_master_config = get_default_master_config().by_root_hash(new_config.zero_state_id.root_hash); } else { - blockchain_name_ = config->blockchain_name_; + o_master_config = get_default_master_config().by_name(config->blockchain_name_); + } + + if (o_master_config && o_master_config.value().zero_state_id != new_config.zero_state_id) { + return TonlibError::InvalidConfig("zero_state differs from embedded zero_state"); } - use_callbacks_for_network_ = config->use_callbacks_for_network_; - ignore_cache_ = config->ignore_cache_; + FullConfig res; + res.config = std::move(new_config); + res.o_master_config = std::move(o_master_config); + res.ignore_cache = config->ignore_cache_; + res.use_callbacks_for_network = config->use_callbacks_for_network_; + return std::move(res); +} + +void TonlibClient::set_config(FullConfig full_config) { + config_ = std::move(full_config.config); + config_generation_++; + blockchain_name_ = config_.zero_state_id.root_hash.as_slice().str(); + + use_callbacks_for_network_ = full_config.use_callbacks_for_network; + ignore_cache_ = full_config.ignore_cache; init_ext_client(); - init_last_block(); + init_last_block(std::move(full_config.o_master_config)); init_last_config(); client_.set_client(get_client_ref()); - return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::close& request, @@ -1184,12 +1274,22 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request, return td::Status::OK(); } +tonlib_api::object_ptr TonlibClient::do_static_request( + tonlib_api::options_validateConfig& request) { + auto r_config = validate_config(std::move(request.config_)); + if (r_config.is_error()) { + return status_to_tonlib_api(r_config.move_as_error()); + } + return tonlib_api::make_object(); +} + td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise) { if (!request.config_) { return TonlibError::EmptyField("config"); } - TRY_STATUS(set_config(std::move(request.config_))); + TRY_RESULT(config, validate_config(std::move(request.config_))); + set_config(std::move(config)); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } @@ -2020,6 +2120,84 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_getState& request, return td::Status::OK(); } +td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request, + td::Promise>&& promise) { + auto it = smcs_.find(request.id_); + if (it == smcs_.end()) { + return TonlibError::InvalidSmcId(); + } + + td::Ref smc(true, it->second->get_smc_state()); + ton::SmartContract::Args args; + downcast_call(*request.method_, + td::overloaded([&](tonlib_api::smc_methodIdNumber& number) { args.set_method_id(number.number_); }, + [&](tonlib_api::smc_methodIdName& name) { args.set_method_id(name.name_); })); + td::Ref stack(true); + td::Status status; + // TODO: error codes + // downcast_call + for (auto& entry : request.stack_) { + downcast_call(*entry, td::overloaded( + [&](tonlib_api::tvm_stackEntryUnsupported& cell) { + status = td::Status::Error("Unsuppored stack entry"); + }, + [&](tonlib_api::tvm_stackEntrySlice& cell) { + auto r_cell = vm::std_boc_deserialize(cell.slice_->bytes_); + if (r_cell.is_error()) { + status = r_cell.move_as_error(); + return; + } + stack.write().push_cell(r_cell.move_as_ok()); + }, + [&](tonlib_api::tvm_stackEntryCell& cell) { + auto r_cell = vm::std_boc_deserialize(cell.cell_->bytes_); + if (r_cell.is_error()) { + status = r_cell.move_as_error(); + return; + } + stack.write().push_cell(r_cell.move_as_ok()); + }, + [&](tonlib_api::tvm_stackEntryNumber& number) { + [&](tonlib_api::tvm_numberDecimal& dec) { + auto num = td::dec_string_to_int256(dec.number_); + if (num.is_null()) { + status = td::Status::Error("Failed to parse dec string to int256"); + return; + } + stack.write().push_int(std::move(num)); + }(*number.number_); + })); + } + TRY_STATUS(std::move(status)); + args.set_stack(std::move(stack)); + auto res = smc->run_get_method(std::move(args)); + + // smc.runResult gas_used:int53 stack:vector exit_code:int32 = smc.RunResult; + std::vector> res_stack; + for (auto& entry : res.stack->as_span()) { + switch (entry.type()) { + case vm::StackEntry::Type::t_int: + res_stack.push_back(tonlib_api::make_object( + tonlib_api::make_object(dec_string(entry.as_int())))); + break; + case vm::StackEntry::Type::t_slice: + res_stack.push_back( + tonlib_api::make_object(tonlib_api::make_object( + to_bytes(vm::CellBuilder().append_cellslice(entry.as_slice()).finalize())))); + break; + case vm::StackEntry::Type::t_cell: + res_stack.push_back(tonlib_api::make_object( + tonlib_api::make_object(to_bytes(entry.as_cell())))); + break; + default: + res_stack.push_back(tonlib_api::make_object()); + break; + } + } + promise.set_value(tonlib_api::make_object(res.gas_used, std::move(res_stack), res.code)); + return td::Status::OK(); +} + td::Status TonlibClient::do_request(tonlib_api::sync& request, td::Promise>&& promise) { client_.with_last_block(to_any_promise(std::move(promise))); return td::Status::OK(); @@ -2146,13 +2324,25 @@ td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& reque td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryResult& request, td::Promise>&& promise) { - send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, td::BufferSlice(request.bytes_), - to_any_promise(std::move(promise))); + if (ext_client_outbound_.empty()) { + return TonlibError::InvalidQueryId(); + } + if (((request.id_ ^ config_generation_) & 0xffff) != 0) { + return TonlibError::InvalidQueryId(); + } + send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_ >> 16, + td::BufferSlice(request.bytes_), to_any_promise(std::move(promise))); return td::Status::OK(); } td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& request, td::Promise>&& promise) { - send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_, + if (ext_client_outbound_.empty()) { + return TonlibError::InvalidQueryId(); + } + if (((request.id_ ^ config_generation_) & 0xffff) != 0) { + return TonlibError::InvalidQueryId(); + } + send_closure(ext_client_outbound_, &ExtClientOutbound::on_query_result, request.id_ >> 16, td::Status::Error(request.error_->code_, request.error_->message_) .move_as_error_prefix(TonlibError::LiteServerNetwork()), to_any_promise(std::move(promise))); @@ -2306,6 +2496,11 @@ td::Status TonlibClient::do_request(const tonlib_api::unpackAccountAddress& requ return TonlibError::Internal(); } template +td::Status TonlibClient::do_request(const tonlib_api::options_validateConfig& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template td::Status TonlibClient::do_request(tonlib_api::getBip39Hints& request, P&&) { UNREACHABLE(); return TonlibError::Internal(); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 9a844fa360..fe006620aa 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -30,6 +30,7 @@ #include "td/actor/actor.h" #include "td/utils/CancellationToken.h" +#include "td/utils/optional.h" #include @@ -87,7 +88,7 @@ class TonlibClient : public td::actor::Actor { ExtClientRef get_client_ref(); void init_ext_client(); - void init_last_block(); + void init_last_block(td::optional o_master_config); void init_last_config(); bool is_closing_{false}; @@ -125,6 +126,7 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::testGiver_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::packAccountAddress& request); static object_ptr do_static_request(const tonlib_api::unpackAccountAddress& request); + static object_ptr do_static_request(tonlib_api::options_validateConfig& request); static object_ptr do_static_request(tonlib_api::getBip39Hints& request); static object_ptr do_static_request(tonlib_api::setLogStream& request); @@ -155,6 +157,8 @@ class TonlibClient : public td::actor::Actor { template td::Status do_request(const tonlib_api::unpackAccountAddress& request, P&&); template + td::Status do_request(const tonlib_api::options_validateConfig& request, P&&); + template td::Status do_request(tonlib_api::getBip39Hints& request, P&&); template @@ -189,7 +193,14 @@ class TonlibClient : public td::actor::Actor { } } - td::Status set_config(object_ptr config); + struct FullConfig { + Config config; + td::optional o_master_config; + bool use_callbacks_for_network; + bool ignore_cache; + }; + static td::Result validate_config(tonlib_api::object_ptr config); + void set_config(FullConfig config); td::Status do_request(const tonlib_api::init& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::close& request, td::Promise>&& promise); td::Status do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise); @@ -288,6 +299,9 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::smc_getState& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::smc_runGetMethod& request, + td::Promise>&& promise); + td::Status do_request(int_api::GetAccountState request, td::Promise>&&); td::Status do_request(int_api::GetPrivateKey request, td::Promise&&); td::Status do_request(int_api::SendMessage request, td::Promise&& promise); diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index b401a027fa..8e1e74d3da 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -29,20 +29,21 @@ // "help []\tThis help\n" // TODO: support [] // "quit\tExit\n"; // "sendfile \tLoad a serialized message from and send it to server\n" -// // "saveaccount[code|data] []\tSaves into specified file the most recent state " +// "(StateInit) or just the code or data of specified account; is in " +// "[:] format\n" +// // "runmethod ...\tRuns GET method of account " // "with specified parameters\n" // +// "getaccount []\tLoads the most recent state of specified account; is in " +// "[:] format\n" +// // WONTSUPPORT // // UNSUPPORTED //"last\tGet last block and state info from server\n" //"status\tShow connection and local database status\n" -//"getaccount []\tLoads the most recent state of specified account; is in " -//"[:] format\n" -//"(StateInit) or just the code or data of specified account; is in " -//"[:] format\n" //"allshards []\tShows shard configuration from the most recent masterchain " //"state or from masterchain state corresponding to \n" //"getconfig [...]\tShows specified or all configuration parameters from the latest masterchain state\n" @@ -266,6 +267,8 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "time\tGet server time\n"; td::TerminalIO::out() << "remote-version\tShows server time, version and capabilities\n"; td::TerminalIO::out() << "sendfile \tLoad a serialized message from and send it to server\n"; + td::TerminalIO::out() << "setconfig|validateconfig [] [] [] - set or validate " + "lite server config\n"; td::TerminalIO::out() << "exit\tExit\n"; td::TerminalIO::out() << "quit\tExit\n"; td::TerminalIO::out() @@ -279,7 +282,6 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n"; td::TerminalIO::out() << "exportkey [] - export key\n"; td::TerminalIO::out() << "exportkeypem [] - export key\n"; - td::TerminalIO::out() << "setconfig [] [] [] - set lite server config\n"; td::TerminalIO::out() << "getstate - get state of simple wallet with requested key\n"; td::TerminalIO::out() << "gethistory - get history fo simple wallet with requested key (last 10 transactions)\n"; @@ -305,12 +307,6 @@ class TonlibCli : public td::actor::Actor { export_key(cmd.str(), parser.read_word()); } else if (cmd == "importkey") { import_key(parser.read_all()); - } else if (cmd == "setconfig") { - auto config = parser.read_word(); - auto name = parser.read_word(); - auto use_callback = parser.read_word(); - auto force = parser.read_word(); - set_config(config, name, to_bool(use_callback), to_bool(force)); } else if (cmd == "getstate") { get_state(parser.read_word()); } else if (cmd == "gethistory") { @@ -346,6 +342,14 @@ class TonlibCli : public td::actor::Actor { auto path = parser.read_word(); auto address = parser.read_word(); save_account(cmd, path, address, std::move(cmd_promise)); + } else if (cmd == "runmethod") { + run_method(parser, std::move(cmd_promise)); + } else if (cmd == "setconfig" || cmd == "validateconfig") { + auto config = parser.read_word(); + auto name = parser.read_word(); + auto use_callback = parser.read_word(); + auto force = parser.read_word(); + set_validate_config(cmd, config, name, to_bool(use_callback), to_bool(force), std::move(cmd_promise)); } else { cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`")); } @@ -419,6 +423,107 @@ class TonlibCli : public td::actor::Actor { return td::Unit(); })); } + td::Result> parse_stack_entry(td::Slice str) { + if (str.empty() || str.size() > 65535) { + return td::Status::Error("String is or empty or too big"); + } + int l = (int)str.size(); + if (str[0] == '"') { + vm::CellBuilder cb; + if (l == 1 || str.back() != '"' || l >= 127 + 2 || !cb.store_bytes_bool(str.data() + 1, l - 2)) { + return td::Status::Error("Failed to parse slice"); + } + return tonlib_api::make_object( + tonlib_api::make_object(vm::std_boc_serialize(cb.finalize()).ok().as_slice().str())); + } + if (l >= 3 && (str[0] == 'x' || str[0] == 'b') && str[1] == '{' && str.back() == '}') { + unsigned char buff[128]; + int bits = + (str[0] == 'x') + ? (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1) + : (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1); + if (bits < 0) { + return td::Status::Error("Failed to parse slice"); + } + return tonlib_api::make_object(tonlib_api::make_object( + vm::std_boc_serialize(vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()) + .ok() + .as_slice() + .str())); + } + auto num = td::RefInt256{true}; + auto& x = num.unique_write(); + if (l >= 3 && str[0] == '0' && str[1] == 'x') { + if (x.parse_hex(str.data() + 2, l - 2) != l - 2) { + return td::Status::Error("Failed to parse a number"); + } + } else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') { + if (x.parse_hex(str.data() + 3, l - 3) != l - 3) { + return td::Status::Error("Failed to parse a number"); + } + x.negate().normalize(); + } else if (!l || x.parse_dec(str.data(), l) != l) { + return td::Status::Error("Failed to parse a number"); + } + return tonlib_api::make_object( + tonlib_api::make_object(dec_string(num))); + } + + void run_method(td::ConstParser& parser, td::Promise promise) { + TRY_RESULT_PROMISE(promise, addr, to_account_address(parser.read_word(), false)); + + auto method_str = parser.read_word(); + tonlib_api::object_ptr method; + if (std::all_of(method_str.begin(), method_str.end(), [](auto c) { return c >= '0' && c <= '9'; })) { + method = tonlib_api::make_object(td::to_integer(method_str.str())); + } else { + method = tonlib_api::make_object(method_str.str()); + } + std::vector> stack; + while (true) { + auto word = parser.read_word(); + if (word.empty()) { + break; + } + TRY_RESULT_PROMISE(promise, stack_entry, parse_stack_entry(word)); + stack.push_back(std::move(stack_entry)); + } + auto to_run = + tonlib_api::make_object(0 /*fixme*/, std::move(method), std::move(stack)); + + send_query(tonlib_api::make_object(std::move(addr.address)), + promise.send_closure(actor_id(this), &TonlibCli::run_method_2, std::move(to_run))); + } + + void run_method_2(tonlib_api::object_ptr to_run, + tonlib_api::object_ptr info, td::Promise promise) { + to_run->id_ = info->id_; + send_query(std::move(to_run), promise.send_closure(actor_id(this), &TonlibCli::run_method_3)); + } + + void run_method_3(tonlib_api::object_ptr info, td::Promise promise) { + td::TerminalIO::out() << "Got smc result " << to_string(info); + promise.set_value({}); + } + + void set_validate_config(td::Slice cmd, td::Slice path, td::Slice name, bool use_callback, bool ignore_cache, + td::Promise promise) { + TRY_RESULT_PROMISE(promise, data, td::read_file_str(path.str())); + using tonlib_api::make_object; + + auto config = make_object(std::move(data), name.str(), use_callback, ignore_cache); + if (cmd == "setconfig") { + send_query(make_object(std::move(config)), promise.wrap([](auto&& info) { + td::TerminalIO::out() << "Config is set\n"; + return td::Unit(); + })); + } else { + send_query(make_object(std::move(config)), promise.wrap([](auto&& info) { + td::TerminalIO::out() << "Config is valid\n"; + return td::Unit(); + })); + } + } void dump_netstats() { td::TerminalIO::out() << td::tag("snd", td::format::as_size(snd_bytes_)) << "\n"; @@ -437,6 +542,8 @@ class TonlibCli : public td::actor::Actor { } } + td::Timestamp sync_started_; + void on_tonlib_result(std::uint64_t id, tonlib_api::object_ptr result) { if (id == 0) { switch (result->get_id()) { @@ -455,10 +562,15 @@ class TonlibCli : public td::actor::Actor { auto update = tonlib_api::move_object_as(std::move(result)); switch (update->sync_state_->get_id()) { case tonlib_api::syncStateDone::ID: { - td::TerminalIO::out() << "synchronization: DONE\n"; + td::TerminalIO::out() << "synchronization: DONE in " + << td::format::as_time(td::Time::now() - sync_started_.at()) << "\n"; + sync_started_ = {}; break; } case tonlib_api::syncStateInProgress::ID: { + if (!sync_started_) { + sync_started_ = td::Timestamp::now(); + } auto progress = tonlib_api::move_object_as(update->sync_state_); auto from = progress->from_seqno_; auto to = progress->to_seqno_; @@ -857,26 +969,6 @@ class TonlibCli : public td::actor::Actor { }); } - void set_config(td::Slice path, td::Slice name, bool use_callback, bool ignore_cache) { - auto r_data = td::read_file_str(path.str()); - if (r_data.is_error()) { - td::TerminalIO::out() << "Can't read file [" << path << "] : " << r_data.error() << "\n"; - return; - } - - auto data = r_data.move_as_ok(); - using tonlib_api::make_object; - send_query(make_object( - make_object(std::move(data), name.str(), use_callback, ignore_cache)), - [](auto r_res) { - if (r_res.is_error()) { - td::TerminalIO::out() << "Can't set config: " << r_res.error() << "\n"; - return; - } - td::TerminalIO::out() << to_string(r_res.ok()); - }); - } - void get_state(td::Slice key) { if (key.empty()) { dump_keys(); diff --git a/validator/manager.cpp b/validator/manager.cpp index fc1edd9889..2198d42a5a 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1843,7 +1843,9 @@ void ValidatorManagerImpl::allow_block_state_gc(BlockIdExt block_id, td::Promise } void ValidatorManagerImpl::allow_block_info_gc(BlockIdExt block_id, td::Promise promise) { - auto P = + promise.set_result(false); + return; + /*auto P = td::PromiseCreator::lambda([db = db_.get(), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_result(false); @@ -1860,7 +1862,7 @@ void ValidatorManagerImpl::allow_block_info_gc(BlockIdExt block_id, td::Promise< } } }); - get_block_handle(block_id, false, std::move(P)); + get_block_handle(block_id, false, std::move(P));*/ } void ValidatorManagerImpl::got_next_gc_masterchain_handle(BlockHandle handle) { From 11bd640ee035fd74ca91cb1534c3823f6379c940 Mon Sep 17 00:00:00 2001 From: ton Date: Thu, 31 Oct 2019 22:14:52 +0400 Subject: [PATCH 016/667] updated tonlib --- crypto/CMakeLists.txt | 2 + crypto/smartcont/wallet-v3.fif | 50 ++++++++++++ crypto/smc-envelope/WalletV3.cpp | 130 ++++++++++++++++++++++++++++++ crypto/smc-envelope/WalletV3.h | 50 ++++++++++++ crypto/test/test-smartcont.cpp | 77 ++++++++++++++++++ tl/generate/scheme/tonlib_api.tl | 9 ++- tl/generate/scheme/tonlib_api.tlo | Bin 17828 -> 18708 bytes tonlib/test/online.cpp | 16 +++- tonlib/tonlib/TonlibClient.cpp | 94 +++++++++++++++++++-- tonlib/tonlib/TonlibClient.h | 5 ++ tonlib/tonlib/TonlibError.h | 3 + tonlib/tonlib/tonlib-cli.cpp | 41 +++++++--- 12 files changed, 455 insertions(+), 22 deletions(-) create mode 100644 crypto/smartcont/wallet-v3.fif create mode 100644 crypto/smc-envelope/WalletV3.cpp create mode 100644 crypto/smc-envelope/WalletV3.h diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 22c05fba05..10977b44b3 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -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 diff --git a/crypto/smartcont/wallet-v3.fif b/crypto/smartcont/wallet-v3.fif new file mode 100644 index 0000000000..b23f941adf --- /dev/null +++ b/crypto/smartcont/wallet-v3.fif @@ -0,0 +1,50 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include + +{ ."usage: " @' $0 type ." [-B ] []" cr + ."Creates a request to advanced wallet created by new-wallet-v3.fif, with private key loaded from file .pk " + ."and address from .addr, and saves it into .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 } { } 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 + +dup ."signing message: " +dup ."resulting external message: " B dup Bx. cr +savefile +".boc" tuck B>file +."Query expires in " timeout . ."seconds" cr +."(Saved to file " type .")" cr diff --git a/crypto/smc-envelope/WalletV3.cpp b/crypto/smc-envelope/WalletV3.cpp new file mode 100644 index 0000000000..db39c725ef --- /dev/null +++ b/crypto/smc-envelope/WalletV3.cpp @@ -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 . + + 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 + +namespace ton { +td::Ref 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 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::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 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 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 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 WalletV3::get_seqno() const { + return TRY_VM(get_seqno_or_throw()); +} + +td::Result WalletV3::get_seqno_or_throw() const { + if (state_.data.is_null()) { + return 0; + } + //FIXME use get method + return static_cast(vm::load_cell_slice(state_.data).fetch_ulong(32)); +} + +td::Result WalletV3::get_wallet_id() const { + return TRY_VM(get_wallet_id_or_throw()); +} + +td::Result 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(cs.fetch_ulong(32)); +} + +} // namespace ton diff --git a/crypto/smc-envelope/WalletV3.h b/crypto/smc-envelope/WalletV3.h new file mode 100644 index 0000000000..a6e4162df2 --- /dev/null +++ b/crypto/smc-envelope/WalletV3.h @@ -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 . + + 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 get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id) noexcept; + static td::Ref 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 get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; + + td::Result get_seqno() const; + td::Result get_wallet_id() const; + + private: + td::Result get_seqno_or_throw() const; + td::Result get_wallet_id_or_throw() const; +}; +} // namespace ton diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp index f9dd86522f..c0b991b620 100644 --- a/crypto/test/test-smartcont.cpp +++ b/crypto/test/test-smartcont.cpp @@ -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 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 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()); + 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(); diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 01ab5c628e..e6778df578 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -22,6 +22,7 @@ keyStoreTypeInMemory = KeyStoreType; config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config; options config:config keystore_type:KeyStoreType = Options; +options.configInfo default_wallet_id:int53 = options.ConfigInfo; key public_key:string secret:secureBytes = Key; inputKeyRegular key:key local_password:secureBytes = InputKey; @@ -50,6 +51,9 @@ testWallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.t wallet.initialAccountState public_key:string = wallet.InitialAccountState; wallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = wallet.AccountState; +wallet.v3.initialAccountState public_key:string wallet_id:int53 = wallet.v3.InitialAccountState; +wallet.v3.accountState balance:int64 wallet_id:int53 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = wallet.v3.AccountState; + testGiver.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53= testGiver.AccountState; uninited.accountState balance:int64 last_transaction_id:internal.transactionId frozen_hash:bytes sync_utime:int53 = uninited.AccountState; @@ -61,6 +65,7 @@ uninited.accountState balance:int64 last_transaction_id:internal.transactionId f generic.accountStateRaw account_state:raw.accountState = generic.AccountState; generic.accountStateTestWallet account_state:testWallet.accountState = generic.AccountState; generic.accountStateWallet account_state:wallet.accountState = generic.AccountState; +generic.accountStateWalletV3 account_state:wallet.v3.accountState = generic.AccountState; generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState; generic.accountStateUninited account_state:uninited.accountState = generic.AccountState; @@ -120,7 +125,7 @@ init options:options = Ok; close = Ok; options.setConfig config:config = Ok; -options.validateConfig config:config = Ok; +options.validateConfig config:config = options.ConfigInfo; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; deleteKey key:key = Ok; @@ -159,6 +164,8 @@ wallet.getAccountAddress initital_account_state:wallet.initialAccountState = Acc wallet.getAccountState account_address:accountAddress = wallet.AccountState; wallet.sendGrams private_key:InputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = SendGramsResult; +wallet.v3.getAccountAddress initital_account_state:wallet.v3.initialAccountState = AccountAddress; + testGiver.getAccountState = testGiver.AccountState; testGiver.getAccountAddress = AccountAddress; testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = SendGramsResult; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 0e727228133b3324ac3f085fbc973500a6484d3b..a670da5a67ca0761aba73e8ccd111a1ce2649bc0 100644 GIT binary patch delta 661 zcmZ3|%{XNeBk!Zx`c@23V7-x-o0a9(mn(*oWmqK`*(N8lh)?!mO%PtjaFbIgzn~;D zKd)HNIX^EgGkx-XR@u#KSZ6R#G+>d=a(`GVR-TxXlUkxzW~}F!oSa{pR}x&3Sdu#V zJfoxlzw1*0S%kdjqpe!*bGX)s6kc5En{N#gN;*$-`L?GT=VfKe_a)JSi!uQ2K gHqwa5Pfsm@rVq!Il%mw);z^DMEFf1*KHw|^07`-9j{pDw delta 230 zcmbO-iE&9cBk!Zx`c@23V6l;xo0a7?+q-*{WmqK`Stlp5h)?!mP1t;gbp|t|>*lAN z`g{;+2?+}bBSgXg!svjB9Ffq0sZp6MAt?gkERck%laNrE`~xN`AcZ8FAq7#pxj{;W zaWjvM3?HN2=C#_!Y!DL^+*Bq{aD*yRFfw45*4?*sw^)8bNoIatv0hnXPG(ACNvd;x SURq}QWNu5>%^NH~@BsiC{8jV- diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index c96fc75e0f..8eabc452e2 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -133,9 +133,11 @@ void sync(Client& client) { sync_send(client, make_object()).ensure(); } +static td::uint32 default_wallet_id{0}; std::string wallet_address(Client& client, const Key& key) { - return sync_send(client, make_object( - make_object(key.public_key))) + return sync_send(client, + make_object( + make_object(key.public_key, default_wallet_id))) .move_as_ok() ->account_address_; } @@ -171,6 +173,7 @@ AccountState get_account_state(Client& client, std::string address) { case tonlib_api::generic_accountStateUninited::ID: res.type = AccountState::Empty; break; + case tonlib_api::generic_accountStateWalletV3::ID: case tonlib_api::generic_accountStateWallet::ID: res.type = AccountState::Wallet; break; @@ -358,8 +361,9 @@ Wallet create_empty_wallet(Client& client) { Wallet wallet{"", {key->public_key_, std::move(key->secret_)}}; auto account_address = - sync_send(client, make_object( - make_object(wallet.key.public_key))) + sync_send(client, + make_object( + make_object(wallet.key.public_key, default_wallet_id))) .move_as_ok(); wallet.address = account_address->account_address_; @@ -529,6 +533,10 @@ int main(int argc, char* argv[]) { Client client; { + auto info = sync_send(client, make_object( + make_object(global_config_str, "", false, false))) + .move_as_ok(); + default_wallet_id = static_cast(info->default_wallet_id_); sync_send(client, make_object(make_object( make_object(global_config_str, "", false, false), make_object(keystore_dir)))) diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 74483fa3ad..95a089d720 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -31,6 +31,7 @@ #include "smc-envelope/GenericAccount.h" #include "smc-envelope/TestWallet.h" #include "smc-envelope/Wallet.h" +#include "smc-envelope/WalletV3.h" #include "smc-envelope/TestGiver.h" #include "auto/tl/tonlib_api.hpp" @@ -41,6 +42,7 @@ #include "vm/boc.h" +#include "td/utils/as.h" #include "td/utils/Random.h" #include "td/utils/optional.h" #include "td/utils/overloaded.h" @@ -121,7 +123,8 @@ std::string to_bytes(td::Ref cell) { class AccountState { public: - AccountState(block::StdAddress address, RawAccountState&& raw) : address_(std::move(address)), raw_(std::move(raw)) { + AccountState(block::StdAddress address, RawAccountState&& raw, td::uint32 wallet_id) + : address_(std::move(address)), raw_(std::move(raw)), wallet_id_(wallet_id) { wallet_type_ = guess_type(); } @@ -162,6 +165,17 @@ class AccountState { return tonlib_api::make_object(get_balance(), static_cast(seqno), to_transaction_id(raw().info), get_sync_time()); } + td::Result> to_wallet_v3_accountState() const { + if (wallet_type_ != WalletV3) { + return TonlibError::AccountTypeUnexpected("WalletV3"); + } + auto wallet = ton::WalletV3(get_smc_state()); + TRY_RESULT(seqno, wallet.get_seqno()); + TRY_RESULT(wallet_id, wallet.get_wallet_id()); + return tonlib_api::make_object( + get_balance(), static_cast(wallet_id), static_cast(seqno), + to_transaction_id(raw().info), get_sync_time()); + } td::Result> to_testGiver_accountState() const { if (wallet_type_ != Giver) { @@ -191,11 +205,15 @@ class AccountState { TRY_RESULT(res, to_wallet_accountState()); return tonlib_api::make_object(std::move(res)); } + case WalletV3: { + TRY_RESULT(res, to_wallet_v3_accountState()); + return tonlib_api::make_object(std::move(res)); + } } UNREACHABLE(); } - enum WalletType { Empty, Unknown, Giver, SimpleWallet, Wallet }; + enum WalletType { Empty, Unknown, Giver, SimpleWallet, Wallet, WalletV3 }; WalletType get_wallet_type() const { return wallet_type_; } @@ -235,6 +253,10 @@ class AccountState { address_.addr) { set_new_state({ton::Wallet::get_init_code(), ton::Wallet::get_init_data(key)}); wallet_type_ = WalletType::Wallet; + } else if (ton::GenericAccount::get_address(address_.workchain, ton::WalletV3::get_init_state(key, wallet_id_)) + .addr == address_.addr) { + set_new_state({ton::WalletV3::get_init_code(), ton::WalletV3::get_init_data(key, wallet_id_)}); + wallet_type_ = WalletType::WalletV3; } return wallet_type_; } @@ -243,8 +265,8 @@ class AccountState { if (wallet_type_ != WalletType::Empty) { return wallet_type_; } - set_new_state({ton::Wallet::get_init_code(), ton::Wallet::get_init_data(key)}); - wallet_type_ = WalletType::Wallet; + set_new_state({ton::WalletV3::get_init_code(), ton::WalletV3::get_init_data(key, wallet_id_)}); + wallet_type_ = WalletType::WalletV3; return wallet_type_; } @@ -274,6 +296,7 @@ class AccountState { block::StdAddress address_; RawAccountState raw_; WalletType wallet_type_{Unknown}; + td::uint32 wallet_id_{0}; bool has_new_state_{false}; WalletType guess_type() const { @@ -290,6 +313,9 @@ class AccountState { if (code_hash == ton::Wallet::get_init_code_hash()) { return WalletType::Wallet; } + if (code_hash == ton::WalletV3::get_init_code_hash()) { + return WalletType::WalletV3; + } LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice()); return WalletType::Unknown; } @@ -999,6 +1025,7 @@ bool TonlibClient::is_static_request(td::int32 id) { case tonlib_api::raw_getAccountAddress::ID: case tonlib_api::testWallet_getAccountAddress::ID: case tonlib_api::wallet_getAccountAddress::ID: + case tonlib_api::wallet_v3_getAccountAddress::ID: case tonlib_api::testGiver_getAccountAddress::ID: case tonlib_api::packAccountAddress::ID: case tonlib_api::unpackAccountAddress::ID: @@ -1062,6 +1089,12 @@ td::Result get_account_address(const tonlib_api::wallet_initi auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); return ton::GenericAccount::get_address(0 /*zerochain*/, ton::Wallet::get_init_state(key)); } +td::Result get_account_address(const tonlib_api::wallet_v3_initialAccountState& test_wallet_state) { + TRY_RESULT(key_bytes, get_public_key(test_wallet_state.public_key_)); + auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); + return ton::GenericAccount::get_address( + 0 /*zerochain*/, ton::WalletV3::get_init_state(key, static_cast(test_wallet_state.wallet_id_))); +} td::Result get_account_address(td::Slice account_address) { TRY_RESULT_PREFIX(address, block::StdAddress::parse(account_address), TonlibError::InvalidAccountAddress()); @@ -1070,6 +1103,9 @@ td::Result get_account_address(td::Slice account_address) { tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::raw_getAccountAddress& request) { + if (!request.initital_account_state_) { + return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state")); + } auto r_account_address = get_account_address(*request.initital_account_state_); if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.error()); @@ -1079,6 +1115,12 @@ tonlib_api::object_ptr TonlibClient::do_static_request( tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testWallet_getAccountAddress& request) { + if (!request.initital_account_state_) { + return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state")); + } + if (!request.initital_account_state_) { + return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state")); + } auto r_account_address = get_account_address(*request.initital_account_state_); if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.error()); @@ -1088,6 +1130,20 @@ tonlib_api::object_ptr TonlibClient::do_static_request( tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::wallet_getAccountAddress& request) { + if (!request.initital_account_state_) { + return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state")); + } + auto r_account_address = get_account_address(*request.initital_account_state_); + if (r_account_address.is_error()) { + return status_to_tonlib_api(r_account_address.error()); + } + return tonlib_api::make_object(r_account_address.ok().rserialize(true)); +} +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::wallet_v3_getAccountAddress& request) { + if (!request.initital_account_state_) { + return status_to_tonlib_api(TonlibError::EmptyField("initial_account_state")); + } auto r_account_address = get_account_address(*request.initital_account_state_); if (r_account_address.is_error()) { return status_to_tonlib_api(r_account_address.error()); @@ -1249,12 +1305,14 @@ td::Result TonlibClient::validate_config(tonlib_api::o res.o_master_config = std::move(o_master_config); res.ignore_cache = config->ignore_cache_; res.use_callbacks_for_network = config->use_callbacks_for_network_; + res.wallet_id = td::as(res.config.zero_state_id.root_hash.as_slice().data()); return std::move(res); } void TonlibClient::set_config(FullConfig full_config) { config_ = std::move(full_config.config); config_generation_++; + wallet_id_ = full_config.wallet_id; blockchain_name_ = config_.zero_state_id.root_hash.as_slice().str(); use_callbacks_for_network_ = full_config.use_callbacks_for_network; @@ -1280,7 +1338,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request( if (r_config.is_error()) { return status_to_tonlib_api(r_config.move_as_error()); } - return tonlib_api::make_object(); + return tonlib_api::make_object(r_config.ok().wallet_id); } td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, @@ -1893,6 +1951,23 @@ class GenericCreateSendGrams : public TonlibQueryActor { destination_->get_address()); break; } + case AccountState::WalletV3: { + if (!private_key_) { + return TonlibError::EmptyField("private_key"); + } + if (message.size() > ton::WalletV3::max_message_size) { + return TonlibError::MessageTooLong(); + } + auto wallet = ton::WalletV3(source_->get_smc_state()); + TRY_RESULT(seqno, wallet.get_seqno()); + TRY_RESULT(wallet_id, wallet.get_wallet_id()); + auto valid_until = source_->get_sync_time(); + valid_until += send_grams_.timeout_ == 0 ? 60 : send_grams_.timeout_; + raw.valid_until = valid_until; + raw.message_body = ton::WalletV3::make_a_gift_message(private_key_.unwrap(), wallet_id, seqno, valid_until, + amount, message, destination_->get_address()); + break; + } } raw.new_state = source_->get_new_state(); @@ -2433,8 +2508,8 @@ td::Status TonlibClient::do_request(int_api::GetAccountState request, auto actor_id = actor_id_++; actors_[actor_id] = td::actor::create_actor( "GetAccountState", client_.get_client(), request.address, actor_shared(this, actor_id), - promise.wrap([address = request.address](auto&& state) mutable { - return td::make_unique(std::move(address), std::move(state)); + promise.wrap([address = request.address, wallet_id = wallet_id_](auto&& state) mutable { + return td::make_unique(std::move(address), std::move(state), wallet_id); })); return td::Status::OK(); } @@ -2481,6 +2556,11 @@ td::Status TonlibClient::do_request(const tonlib_api::wallet_getAccountAddress& return TonlibError::Internal(); } template +td::Status TonlibClient::do_request(const tonlib_api::wallet_v3_getAccountAddress& request, P&&) { + UNREACHABLE(); + return TonlibError::Internal(); +} +template td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&) { UNREACHABLE(); return TonlibError::Internal(); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index fe006620aa..68936a4373 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -65,6 +65,7 @@ class TonlibClient : public td::actor::Actor { // Config Config config_; td::uint32 config_generation_{0}; + td::uint32 wallet_id_; std::string blockchain_name_; bool ignore_cache_{false}; bool use_callbacks_for_network_{false}; @@ -123,6 +124,7 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::raw_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::testWallet_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::wallet_getAccountAddress& request); + static object_ptr do_static_request(const tonlib_api::wallet_v3_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::testGiver_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::packAccountAddress& request); static object_ptr do_static_request(const tonlib_api::unpackAccountAddress& request); @@ -151,6 +153,8 @@ class TonlibClient : public td::actor::Actor { template td::Status do_request(const tonlib_api::wallet_getAccountAddress& request, P&&); template + td::Status do_request(const tonlib_api::wallet_v3_getAccountAddress& request, P&&); + template td::Status do_request(const tonlib_api::testGiver_getAccountAddress& request, P&&); template td::Status do_request(const tonlib_api::packAccountAddress& request, P&&); @@ -198,6 +202,7 @@ class TonlibClient : public td::actor::Actor { td::optional o_master_config; bool use_callbacks_for_network; bool ignore_cache; + td::uint32 wallet_id; }; static td::Result validate_config(tonlib_api::object_ptr config); void set_config(FullConfig config); diff --git a/tonlib/tonlib/TonlibError.h b/tonlib/tonlib/TonlibError.h index 6568112302..dca5eecfcb 100644 --- a/tonlib/tonlib/TonlibError.h +++ b/tonlib/tonlib/TonlibError.h @@ -79,6 +79,9 @@ struct TonlibError { static td::Status InvalidPemKey() { return td::Status::Error(400, "INVALID_PEM_KEY"); } + static td::Status NeedConfig() { + return td::Status::Error(400, "NeedConfig"); + } static td::Status MessageTooLong() { return td::Status::Error(400, "MESSAGE_TOO_LONG"); } diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 8e1e74d3da..716fae3070 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -81,7 +81,7 @@ class TonlibCli : public td::actor::Actor { std::string key_dir{"."}; bool in_memory{false}; bool use_callbacks_for_network{false}; - bool use_simple_wallet{false}; + td::int32 wallet_version = 2; bool ignore_cache{false}; bool one_shot{false}; @@ -96,6 +96,7 @@ class TonlibCli : public td::actor::Actor { td::actor::ActorOwn client_; std::uint64_t next_query_id_{1}; td::Promise cont_; + td::uint32 wallet_id_; struct KeyInfo { std::string public_key; @@ -176,6 +177,10 @@ class TonlibCli : public td::actor::Actor { ? make_object(options_.config, options_.name, options_.use_callbacks_for_network, options_.ignore_cache) : nullptr; + auto config2 = !options_.config.empty() + ? make_object(options_.config, options_.name, + options_.use_callbacks_for_network, options_.ignore_cache) + : nullptr; tonlib_api::object_ptr ks_type; if (options_.in_memory) { @@ -183,6 +188,14 @@ class TonlibCli : public td::actor::Actor { } else { ks_type = make_object(options_.key_dir); } + auto obj = + tonlib::TonlibClient::static_request(make_object(std::move(config2))); + if (obj->get_id() != tonlib_api::error::ID) { + auto info = ton::move_tl_object_as(obj); + wallet_id_ = static_cast(info->default_wallet_id_); + } else { + LOG(ERROR) << "Invalid config"; + } send_query(make_object(make_object(std::move(config), std::move(ks_type))), [](auto r_ok) { LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); @@ -519,7 +532,7 @@ class TonlibCli : public td::actor::Actor { })); } else { send_query(make_object(std::move(config)), promise.wrap([](auto&& info) { - td::TerminalIO::out() << "Config is valid\n"; + td::TerminalIO::out() << "Config is valid: " << to_string(info) << "\n"; return td::Unit(); })); } @@ -824,11 +837,19 @@ class TonlibCli : public td::actor::Actor { auto r_key_i = to_key_i(key); using tonlib_api::make_object; if (r_key_i.is_ok()) { - auto obj = options_.use_simple_wallet - ? tonlib::TonlibClient::static_request(make_object( - make_object(keys_[r_key_i.ok()].public_key))) - : tonlib::TonlibClient::static_request(make_object( - make_object(keys_[r_key_i.ok()].public_key))); + auto obj = [&](td::int32 version) { + if (version == 1) { + return tonlib::TonlibClient::static_request(make_object( + make_object(keys_[r_key_i.ok()].public_key))); + } + if (version == 2) { + return tonlib::TonlibClient::static_request(make_object( + make_object(keys_[r_key_i.ok()].public_key))); + } + return tonlib::TonlibClient::static_request(make_object( + make_object(keys_[r_key_i.ok()].public_key, wallet_id_))); + UNREACHABLE(); + }(options_.wallet_version); if (obj->get_id() != tonlib_api::error::ID) { Address res; res.address = ton::move_tl_object_as(obj); @@ -1166,7 +1187,7 @@ class TonlibCli : public td::actor::Actor { void init_simple_wallet(std::string key, size_t key_i, td::Slice password) { using tonlib_api::make_object; - if (options_.use_simple_wallet) { + if (options_.wallet_version == 1) { send_query(make_object(make_object( make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), td::SecureString(password))), @@ -1259,8 +1280,8 @@ int main(int argc, char* argv[]) { options.use_callbacks_for_network = true; return td::Status::OK(); }); - p.add_option('S', "use-simple-wallet", "do not use this", [&]() { - options.use_simple_wallet = true; + p.add_option('W', "wallet-version", "do not use this", [&](td::Slice arg) { + options.wallet_version = td::to_integer(arg); return td::Status::OK(); }); From 950e29226425e8f0452d6ecc55c64446f0f77b70 Mon Sep 17 00:00:00 2001 From: ton Date: Fri, 1 Nov 2019 22:15:04 +0400 Subject: [PATCH 017/667] do not use builtin popcnt --- tdutils/td/utils/bits.h | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tdutils/td/utils/bits.h b/tdutils/td/utils/bits.h index d4ab932e31..1197b9db23 100644 --- a/tdutils/td/utils/bits.h +++ b/tdutils/td/utils/bits.h @@ -139,7 +139,13 @@ inline uint64 bswap64(uint64 x) { } inline int32 count_bits32(uint32 x) { - return __popcnt(x); + // Do not use __popcnt because it will fail on some platforms. + x -= (x >> 1) & 0x55555555; + x = (x & 0x33333333) + ((x >> 2) & 0x33333333); + x = (x + (x >> 4)) & 0x0F0F0F0F; + x += x >> 8; + return (x + (x >> 16)) & 0x3F; + //return __popcnt(x); } inline int32 count_bits64(uint64 x) { From e30d98eb30d35b6808d2511c93631a72e5882514 Mon Sep 17 00:00:00 2001 From: ton Date: Fri, 15 Nov 2019 18:02:37 +0400 Subject: [PATCH 018/667] new db new database fift/func bugfixes --- CMakeLists.txt | 2 +- crypto/CMakeLists.txt | 10 + crypto/block/adjust-block.cpp | 203 +++++ crypto/block/block-parse.cpp | 17 + crypto/block/block-parse.h | 3 + crypto/block/block.cpp | 7 +- crypto/block/dump-block.cpp | 4 +- crypto/fift/lib/Color.fif | 21 + crypto/fift/words.cpp | 10 + crypto/func/builtins.cpp | 2 +- crypto/smartcont/elector-code.fc | 20 + crypto/smartcont/gen-zerostate.fif | 28 +- crypto/smc-envelope/HighloadWallet.cpp | 126 +++ crypto/smc-envelope/HighloadWallet.h | 54 ++ crypto/smc-envelope/SmartContract.cpp | 2 +- crypto/smc-envelope/SmartContractCode.cpp | 5 + crypto/smc-envelope/SmartContractCode.h | 1 + crypto/test/test-smartcont.cpp | 68 ++ crypto/vm/continuation.cpp | 2 +- crypto/vm/debugops.cpp | 7 +- crypto/vm/dict.cpp | 3 +- crypto/vm/stack.cpp | 94 ++- crypto/vm/stack.hpp | 41 +- lite-client/lite-client-common.cpp | 3 + lite-client/lite-client-common.h | 1 + lite-client/lite-client.cpp | 105 ++- lite-client/lite-client.h | 19 +- tdutils/CMakeLists.txt | 2 + tdutils/td/utils/misc.h | 14 + tdutils/td/utils/port/rlimit.cpp | 84 ++ tdutils/td/utils/port/rlimit.h | 14 + test/regression-tests.ans | 2 +- test/test-hello-world.cpp | 22 + test/test-ton-collator.cpp | 4 + tl/generate/scheme/ton_api.tl | 21 +- tl/generate/scheme/ton_api.tlo | Bin 55652 -> 56528 bytes tl/generate/scheme/tonlib_api.tl | 9 +- tl/generate/scheme/tonlib_api.tlo | Bin 18708 -> 19324 bytes ton/ton-types.h | 21 + tonlib/test/offline.cpp | 91 ++ tonlib/test/online.cpp | 12 +- tonlib/tonlib/TonlibClient.cpp | 279 +++--- tonlib/tonlib/TonlibClient.h | 32 +- tonlib/tonlib/tonlib-cli.cpp | 120 ++- utils/CMakeLists.txt | 5 + utils/pack-viewer.cpp | 61 ++ validator-engine/validator-engine.cpp | 3 + validator/CMakeLists.txt | 17 +- validator/apply-block.cpp | 30 +- validator/apply-block.hpp | 12 +- validator/block-handle.cpp | 9 +- validator/block-handle.hpp | 37 +- validator/db/archive-manager.cpp | 986 ++++++++++++++++++++++ validator/db/archive-manager.hpp | 155 ++++ validator/db/archive-mover.cpp | 475 +++++++++++ validator/db/archive-mover.hpp | 148 ++++ validator/db/archive-slice.cpp | 470 +++++++++++ validator/db/archive-slice.hpp | 84 ++ validator/db/archiver.cpp | 77 +- validator/db/archiver.hpp | 15 +- validator/db/blockdb.cpp | 301 ------- validator/db/blockdb.hpp | 88 -- validator/db/filedb.cpp | 400 --------- validator/db/filedb.hpp | 218 ----- validator/db/fileref.cpp | 471 +++++++++++ validator/db/fileref.hpp | 372 ++++++++ validator/db/ltdb.cpp | 310 ------- validator/db/ltdb.hpp | 67 -- validator/db/package.cpp | 34 +- validator/db/package.hpp | 21 +- validator/db/rootdb.cpp | 351 ++++---- validator/db/rootdb.hpp | 35 +- validator/db/statedb.cpp | 16 + validator/db/statedb.hpp | 3 + validator/downloaders/download-state.cpp | 6 +- validator/fabric.h | 5 +- validator/full-node-master.cpp | 20 + validator/full-node-master.hpp | 4 + validator/full-node-shard.cpp | 33 +- validator/full-node-shard.h | 2 + validator/full-node-shard.hpp | 8 +- validator/full-node.cpp | 13 + validator/full-node.hpp | 2 + validator/impl/accept-block.cpp | 121 ++- validator/impl/accept-block.hpp | 9 +- validator/impl/check-proof.cpp | 2 +- validator/impl/fabric.cpp | 16 +- validator/impl/liteserver.cpp | 56 +- validator/impl/proof.cpp | 1 + validator/impl/validate-query.cpp | 6 +- validator/import-db-slice.cpp | 354 ++++++++ validator/import-db-slice.hpp | 54 ++ validator/interfaces/block-handle.h | 7 +- validator/interfaces/db.h | 22 +- validator/interfaces/proof.h | 1 + validator/interfaces/validator-manager.h | 2 + validator/invariants.hpp | 2 +- validator/manager-disk.cpp | 42 +- validator/manager-disk.hpp | 19 +- validator/manager-init.cpp | 18 +- validator/manager.cpp | 144 +++- validator/manager.hpp | 23 +- validator/net/download-archive-slice.cpp | 177 ++++ validator/net/download-archive-slice.hpp | 83 ++ validator/net/download-proof.cpp | 27 + validator/net/download-proof.hpp | 1 + validator/shard-client.cpp | 17 +- validator/shard-client.hpp | 2 + validator/validate-broadcast.cpp | 8 +- validator/validator.h | 14 +- 110 files changed, 6102 insertions(+), 2075 deletions(-) create mode 100644 crypto/block/adjust-block.cpp create mode 100644 crypto/fift/lib/Color.fif create mode 100644 crypto/smc-envelope/HighloadWallet.cpp create mode 100644 crypto/smc-envelope/HighloadWallet.h create mode 100644 tdutils/td/utils/port/rlimit.cpp create mode 100644 tdutils/td/utils/port/rlimit.h create mode 100644 utils/pack-viewer.cpp create mode 100644 validator/db/archive-manager.cpp create mode 100644 validator/db/archive-manager.hpp create mode 100644 validator/db/archive-mover.cpp create mode 100644 validator/db/archive-mover.hpp create mode 100644 validator/db/archive-slice.cpp create mode 100644 validator/db/archive-slice.hpp delete mode 100644 validator/db/blockdb.cpp delete mode 100644 validator/db/blockdb.hpp delete mode 100644 validator/db/filedb.cpp delete mode 100644 validator/db/filedb.hpp create mode 100644 validator/db/fileref.cpp create mode 100644 validator/db/fileref.hpp delete mode 100644 validator/db/ltdb.cpp delete mode 100644 validator/db/ltdb.hpp create mode 100644 validator/import-db-slice.cpp create mode 100644 validator/import-db-slice.hpp create mode 100644 validator/net/download-archive-slice.cpp create mode 100644 validator/net/download-archive-slice.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 583578c6b3..8146bef970 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -441,7 +441,7 @@ if (USE_LIBRAPTORQ) endif() add_executable(test-hello-world test/test-hello-world.cpp ) -target_link_libraries(test-hello-world tl_api) +target_link_libraries(test-hello-world tl_api crypto ton_crypto) add_executable(test-adnl test/test-adnl.cpp) target_link_libraries(test-adnl adnl adnltest dht tl_api) diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 10977b44b3..38f65500c4 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -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 $ + $) +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) diff --git a/crypto/block/adjust-block.cpp b/crypto/block/adjust-block.cpp new file mode 100644 index 0000000000..ba9d22cdae --- /dev/null +++ b/crypto/block/adjust-block.cpp @@ -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 . + + 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 +#include "block-db.h" +#include "block-auto.h" +#include "block-parse.h" +#include "vm/cp0.h" +#include "td/utils/crypto.h" +#include + +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 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 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 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 adjust_block(Ref root, int vseqno_incr, const ton::BlockIdExt& id) { + std::vector 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] \n\tor adjust-block -h\n\tAdjusts block " + "loaded from by incrementing vert_seqno by (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(td::Slice(optarg)); + CHECK(vseqno_incr > 0 && vseqno_incr < 1000); + break; + case 'v': + new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer(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; +} diff --git a/crypto/block/block-parse.cpp b/crypto/block/block-parse.cpp index c410e2cb53..29d6ebea0e 100644 --- a/crypto/block/block-parse.cpp +++ b/crypto/block/block-parse.cpp @@ -2005,6 +2005,23 @@ bool ExtBlkRef::unpack(Ref 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 ExtBlkRef::pack_cell(const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const { + vm::CellBuilder cb; + return store(cb, blkid, end_lt) ? cb.finalize() : Ref{}; +} + +bool ExtBlkRef::pack_to(Ref& 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; diff --git a/crypto/block/block-parse.h b/crypto/block/block-parse.h index acbe9dd426..410a8ce575 100644 --- a/crypto/block/block-parse.h +++ b/crypto/block/block-parse.h @@ -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 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 pack_cell(const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const; + bool pack_to(Ref& cell, const ton::BlockIdExt& blkid, ton::LogicalTime end_lt) const; }; extern const ExtBlkRef t_ExtBlkRef; diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index 80723b5856..bdbf08a609 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -1745,7 +1745,7 @@ td::Status unpack_block_prev_blk_ext(Ref 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 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 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}; diff --git a/crypto/block/dump-block.cpp b/crypto/block/dump-block.cpp index ef4e1d8411..a29d5c0aa6 100644 --- a/crypto/block/dump-block.cpp +++ b/crypto/block/dump-block.cpp @@ -191,8 +191,8 @@ void test2(vm::CellSlice& cs) { } void usage() { - std::cout << "usage: test-block [-S][]\n\tor test-block -h\n\tDumps specified blockchain block or state " - "from , or runs some tests\n\t-S\tDump a blockchain state\n"; + std::cout << "usage: dump-block [-S][]\n\tor dump-block -h\n\tDumps specified blockchain block or state " + "from , or runs some tests\n\t-S\tDump a blockchain state instead of a block\n"; std::exit(2); } diff --git a/crypto/fift/lib/Color.fif b/crypto/fift/lib/Color.fif new file mode 100644 index 0000000000..bd8ed7af6d --- /dev/null +++ b/crypto/fift/lib/Color.fif @@ -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 diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 2c1a3083d6..ed703b1252 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -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)); diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index dd156f24c6..30beb7c82f 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -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)), diff --git a/crypto/smartcont/elector-code.fc b/crypto/smartcont/elector-code.fc index fe000c14d4..263aa463a8 100644 --- a/crypto/smartcont/elector-code.fc +++ b/crypto/smartcont/elector-code.fc @@ -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(); diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 66d686241e..196ca5def7 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -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 // 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 /* * diff --git a/crypto/smc-envelope/HighloadWallet.cpp b/crypto/smc-envelope/HighloadWallet.cpp new file mode 100644 index 0000000000..5f54078088 --- /dev/null +++ b/crypto/smc-envelope/HighloadWallet.cpp @@ -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 . + + 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 + +namespace ton { +td::Ref 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 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::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 HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id, + td::uint32 seqno, td::uint32 valid_until, + td::Span 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 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 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 HighloadWallet::get_seqno() const { + return TRY_VM(get_seqno_or_throw()); +} + +td::Result HighloadWallet::get_seqno_or_throw() const { + if (state_.data.is_null()) { + return 0; + } + //FIXME use get method + return static_cast(vm::load_cell_slice(state_.data).fetch_ulong(32)); +} + +td::Result HighloadWallet::get_wallet_id() const { + return TRY_VM(get_wallet_id_or_throw()); +} + +td::Result 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(cs.fetch_ulong(32)); +} + +} // namespace ton diff --git a/crypto/smc-envelope/HighloadWallet.h b/crypto/smc-envelope/HighloadWallet.h new file mode 100644 index 0000000000..aa1e3a4bcb --- /dev/null +++ b/crypto/smc-envelope/HighloadWallet.h @@ -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 . + + 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 get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; + static td::Ref 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 make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id, + td::uint32 seqno, td::uint32 valid_until, td::Span gifts) noexcept; + + static td::Ref get_init_code() noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; + + td::Result get_seqno() const; + td::Result get_wallet_id() const; + + private: + td::Result get_seqno_or_throw() const; + td::Result get_wallet_id_or_throw() const; +}; +} // namespace ton diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index ea76423763..aec7bed469 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -111,7 +111,7 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref= 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; diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp index c187131d71..4e17139be2 100644 --- a/crypto/smc-envelope/SmartContractCode.cpp +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -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 SmartContractCode::simple_wallet_ext() { static auto res = load("simple-wallet-ext").move_as_ok(); return res; } +td::Ref SmartContractCode::highload_wallet() { + static auto res = load("highload-wallet").move_as_ok(); + return res; +} } // namespace ton diff --git a/crypto/smc-envelope/SmartContractCode.h b/crypto/smc-envelope/SmartContractCode.h index 059215e445..0c9e476443 100644 --- a/crypto/smc-envelope/SmartContractCode.h +++ b/crypto/smc-envelope/SmartContractCode.h @@ -26,5 +26,6 @@ class SmartContractCode { static td::Ref wallet(); static td::Ref simple_wallet(); static td::Ref simple_wallet_ext(); + static td::Ref highload_wallet(); }; } // namespace ton diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp index c0b991b620..750ed94b73 100644 --- a/crypto/test/test-smartcont.cpp +++ b/crypto/test/test-smartcont.cpp @@ -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 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 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()); + 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(); diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index d4952769a5..5273e15972 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -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()) { diff --git a/crypto/vm/debugops.cpp b/crypto/vm/debugops.cpp index 911d6313b8..5c22740a42 100644 --- a/crypto/vm/debugops.cpp +++ b/crypto/vm/debugops.cpp @@ -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; } diff --git a/crypto/vm/dict.cpp b/crypto/vm/dict.cpp index 7037e7e41c..0b48f90eac 100644 --- a/crypto/vm/dict.cpp +++ b/crypto/vm/dict.cpp @@ -793,8 +793,7 @@ std::tuple, Ref, bool> dict_lookup_set(Ref dict, td:: std::pair, bool> pfx_dict_set(Ref 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{}, false); } diff --git a/crypto/vm/stack.cpp b/crypto/vm/stack.cpp index 0b63aec9cc..b2ba081fa8 100644 --- a/crypto/vm/stack.cpp +++ b/crypto/vm/stack.cpp @@ -45,6 +45,18 @@ const char* get_exception_msg(Excno exc_no) { } } +bool StackEntry::is_list(const StackEntry* se) { + Ref 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); + 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 << ')'; +void StackEntry::print_list_tail(std::ostream& os, const StackEntry* se) { + Ref tuple; + while (!se->empty()) { + tuple = se->as_tuple_range(2, 2); + if (tuple.is_null()) { + os << " . "; + se->print_list(os); break; - case t_tuple: { - const auto& tuple = *static_cast>(ref); - if (tuple.size() == 2) { - os << ' '; - tuple[0].print_list(os); - tuple[1].print_list_tail(os); - break; - } } - // fall through - default: - os << " . "; - print_list(os); - os << ')'; + os << ' '; + tuple->at(0).print_list(os); + se = &tuple->at(1); } + os << ')'; +} + +StackEntry StackEntry::make_list(std::vector&& 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& 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_ref) : ref(std::move(stack_ref)), tp(t_stack) { @@ -611,13 +645,21 @@ Ref 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; } } diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index 0b16f59c39..1c7fb55c62 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -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 - Ref dynamic_as() const& { + Ref dynamic_as() const & { return tp == tag ? static_cast>(ref) : td::Ref{}; } template @@ -170,7 +177,7 @@ class StackEntry { return tp == tag ? static_cast>(std::move(ref)) : td::Ref{}; } template - Ref as() const& { + Ref as() const & { return tp == tag ? Ref{td::static_cast_ref(), ref} : td::Ref{}; } template @@ -183,6 +190,8 @@ class StackEntry { } public: + static StackEntry make_list(std::vector&& elems); + static StackEntry make_list(const std::vector& elems); template static StackEntry maybe(Ref 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::RefInt256 as_int() && { return move_as(); } - Ref as_cell() const& { + Ref as_cell() const & { return as(); } Ref as_cell() && { return move_as(); } - Ref as_builder() const& { + Ref as_builder() const & { return as(); } Ref as_builder() && { return move_as(); } - Ref as_slice() const& { + Ref as_slice() const & { return as(); } Ref as_slice() && { return move_as(); } - Ref as_cont() const&; + Ref as_cont() const &; Ref as_cont() &&; Ref> as_string_ref() const { return as, t_string>(); @@ -230,16 +239,16 @@ class StackEntry { std::string as_bytes() const { return tp == t_bytes ? *as_bytes_ref() : ""; } - Ref as_box() const&; + Ref as_box() const &; Ref as_box() &&; - Ref as_tuple() const&; + Ref as_tuple() const &; Ref as_tuple() &&; - Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const&; + Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) const &; Ref as_tuple_range(unsigned max_len = 255, unsigned min_len = 0) &&; - Ref as_atom() const&; + Ref as_atom() const &; Ref as_atom() &&; template - Ref as_object() const& { + Ref as_object() const & { return dynamic_as(); } template @@ -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 diff --git a/lite-client/lite-client-common.cpp b/lite-client/lite-client-common.cpp index 82503faf35..184dfba3cb 100644 --- a/lite-client/lite-client-common.cpp +++ b/lite-client/lite-client-common.cpp @@ -9,6 +9,7 @@ using namespace std::literals::string_literals; namespace liteclient { + td::Result> deserialize_proof_chain( ton::lite_api::object_ptr f) { // deserialize proof chain @@ -75,6 +76,7 @@ td::Result> deserialize_proof_chain( LOG(DEBUG) << "deserialized a BlkProofChain of " << chain->link_count() << " links"; return std::move(chain); } + td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref my_addr, const block::CurrencyCollection& balance) { td::BitArray<256> rand_seed; @@ -96,4 +98,5 @@ td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); return vm::make_tuple_ref(std::move(tuple)); } + } // namespace liteclient diff --git a/lite-client/lite-client-common.h b/lite-client/lite-client-common.h index bca2c3079d..54948ead99 100644 --- a/lite-client/lite-client-common.h +++ b/lite-client/lite-client-common.h @@ -5,6 +5,7 @@ #include "auto/tl/lite_api.hpp" namespace liteclient { + td::Result> deserialize_proof_chain( ton::lite_api::object_ptr f); diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 54eb61b6ac..a4d3af516b 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -497,7 +497,7 @@ void TestNode::run_init_queries() { get_server_version(0x100); } -std::string TestNode::get_word(char delim) { +td::Slice TestNode::get_word(char delim) { if (delim == ' ' || !delim) { skipspc(); } @@ -506,10 +506,33 @@ std::string TestNode::get_word(char delim) { ptr++; } std::swap(ptr, parse_ptr_); - return std::string{ptr, parse_ptr_}; + return td::Slice{ptr, parse_ptr_}; +} + +td::Slice TestNode::get_word_ext(const char* delims, const char* specials) { + if (delims[0] == ' ') { + skipspc(); + } + const char* ptr = parse_ptr_; + while (ptr < parse_end_ && !strchr(delims, *ptr)) { + if (specials && strchr(specials, *ptr)) { + if (ptr == parse_ptr_) { + ptr++; + } + break; + } + ptr++; + } + std::swap(ptr, parse_ptr_); + return td::Slice{ptr, parse_ptr_}; } bool TestNode::get_word_to(std::string& str, char delim) { + str = get_word(delim).str(); + return !str.empty(); +} + +bool TestNode::get_word_to(td::Slice& str, char delim) { str = get_word(delim); return !str.empty(); } @@ -549,12 +572,12 @@ bool TestNode::parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr return block::parse_std_account_addr(get_word(), wc, addr) || set_error("cannot parse account address"); } -bool TestNode::convert_uint64(std::string word, td::uint64& val) { +bool TestNode::convert_uint64(td::Slice word, td::uint64& val) { val = ~0ULL; if (word.empty()) { return false; } - const char* ptr = word.c_str(); + const char* ptr = word.data(); char* end = nullptr; val = std::strtoull(ptr, &end, 10); if (end == ptr + word.size()) { @@ -565,12 +588,12 @@ bool TestNode::convert_uint64(std::string word, td::uint64& val) { } } -bool TestNode::convert_int64(std::string word, td::int64& val) { +bool TestNode::convert_int64(td::Slice word, td::int64& val) { val = (~0ULL << 63); if (word.empty()) { return false; } - const char* ptr = word.c_str(); + const char* ptr = word.data(); char* end = nullptr; val = std::strtoll(ptr, &end, 10); if (end == ptr + word.size()) { @@ -581,7 +604,7 @@ bool TestNode::convert_int64(std::string word, td::int64& val) { } } -bool TestNode::convert_uint32(std::string word, td::uint32& val) { +bool TestNode::convert_uint32(td::Slice word, td::uint32& val) { td::uint64 tmp; if (convert_uint64(word, tmp) && (td::uint32)tmp == tmp) { val = (td::uint32)tmp; @@ -591,7 +614,7 @@ bool TestNode::convert_uint32(std::string word, td::uint32& val) { } } -bool TestNode::convert_int32(std::string word, td::int32& val) { +bool TestNode::convert_int32(td::Slice word, td::int32& val) { td::int64 tmp; if (convert_int64(word, tmp) && (td::int32)tmp == tmp) { val = (td::int32)tmp; @@ -631,6 +654,10 @@ int TestNode::parse_hex_digit(int c) { return -1; } +bool TestNode::parse_hash(td::Slice str, ton::Bits256& hash) { + return str.size() == 64 && parse_hash(str.data(), hash); +} + bool TestNode::parse_hash(const char* str, ton::Bits256& hash) { unsigned char* data = hash.data(); for (int i = 0; i < 32; i++) { @@ -690,15 +717,15 @@ bool TestNode::parse_block_id_ext(std::string blkid_str, ton::BlockIdExt& blkid, } bool TestNode::parse_block_id_ext(ton::BlockIdExt& blk, bool allow_incomplete) { - return parse_block_id_ext(get_word(), blk, allow_incomplete) || set_error("cannot parse BlockIdExt"); + return parse_block_id_ext(get_word().str(), blk, allow_incomplete) || set_error("cannot parse BlockIdExt"); } bool TestNode::parse_hash(ton::Bits256& hash) { auto word = get_word(); - return (!word.empty() && parse_hash(word.c_str(), hash)) || set_error("cannot parse hash"); + return parse_hash(word, hash) || set_error("cannot parse hash"); } -bool TestNode::convert_shard_id(std::string str, ton::ShardIdFull& shard) { +bool TestNode::convert_shard_id(td::Slice str, ton::ShardIdFull& shard) { shard.workchain = ton::workchainInvalid; shard.shard = 0; auto pos = str.find(':'); @@ -774,7 +801,44 @@ bool TestNode::parse_stack_value(td::Slice str, vm::StackEntry& value) { } bool TestNode::parse_stack_value(vm::StackEntry& value) { - return parse_stack_value(td::Slice{get_word()}, value) || set_error("invalid vm stack value"); + auto word = get_word_ext(" \t", "[()]"); + if (word.empty()) { + return set_error("stack value expected instead of end-of-line"); + } + if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) { + int expected = (word[0] == '(' ? ')' : ']'); + std::vector values; + if (!parse_stack_values(values)) { + return false; + } + word = get_word_ext(" \t", "[()]"); + if (word.size() != 1 || word[0] != expected) { + return set_error("closing bracket expected"); + } + if (expected == ']') { + value = vm::StackEntry{std::move(values)}; + } else { + value = vm::StackEntry::make_list(std::move(values)); + } + return true; + } else { + return parse_stack_value(word, value) || set_error("invalid vm stack value"); + } +} + +bool TestNode::parse_stack_values(std::vector& values) { + values.clear(); + while (!seekeoln()) { + if (cur() == ']' || cur() == ')') { + break; + } + values.emplace_back(); + if (!parse_stack_value(values.back())) { + values.pop_back(); + return false; + } + } + return true; } bool TestNode::set_error(std::string err_msg) { @@ -865,7 +929,7 @@ bool TestNode::do_parse_line() { ton::BlockSeqno seqno{}; ton::UnixTime utime{}; unsigned count{}; - std::string word = get_word(); + std::string word = get_word().str(); skipspc(); if (word == "time") { return eoln() && get_server_time(); @@ -1027,12 +1091,11 @@ bool TestNode::get_account_state(ton::WorkchainId workchain, ton::StdSmcAddress bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid, std::string method_name) { std::vector params; - while (!seekeoln()) { - vm::StackEntry param; - if (!parse_stack_value(param)) { - return false; - } - params.push_back(std::move(param)); + if (!parse_stack_values(params)) { + return set_error("cannot parse list of TVM stack values"); + } + if (!seekeoln()) { + return set_error("extra characters after a list of TVM stack values"); } if (!ref_blkid.is_valid()) { return set_error("must obtain last block information before making other queries"); @@ -1278,7 +1341,7 @@ void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton: { std::ostringstream os; os << "arguments: "; - stack->dump(os); + stack->dump(os, 3); out << os.str(); } long long gas_limit = vm::GasLimits::infty; @@ -1302,7 +1365,7 @@ void TestNode::run_smc_method(ton::BlockIdExt ref_blk, ton::BlockIdExt blk, ton: { std::ostringstream os; os << "result: "; - stack->dump(os); + stack->dump(os, 3); out << os.str(); } } diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index 36830e8d5b..bb4f31dfc7 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -93,6 +93,9 @@ class TestNode : public td::actor::Actor { }; void run_init_queries(); + char cur() const { + return *parse_ptr_; + } bool get_server_time(); bool get_server_version(int mode = 0); void got_server_version(td::Result res, int mode); @@ -152,8 +155,10 @@ class TestNode : public td::actor::Actor { void got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, td::BufferSlice res); bool do_parse_line(); bool show_help(std::string command); - std::string get_word(char delim = ' '); + td::Slice get_word(char delim = ' '); + td::Slice get_word_ext(const char* delims, const char* specials = nullptr); bool get_word_to(std::string& str, char delim = ' '); + bool get_word_to(td::Slice& str, char delim = ' '); int skipspc(); std::string get_line_tail(bool remove_spaces = true) const; bool eoln() const; @@ -164,11 +169,12 @@ class TestNode : public td::actor::Actor { bool parse_account_addr(ton::WorkchainId& wc, ton::StdSmcAddress& addr); static int parse_hex_digit(int c); static bool parse_hash(const char* str, ton::Bits256& hash); - static bool convert_uint64(std::string word, td::uint64& val); - static bool convert_int64(std::string word, td::int64& val); - static bool convert_uint32(std::string word, td::uint32& val); - static bool convert_int32(std::string word, td::int32& val); - static bool convert_shard_id(std::string str, ton::ShardIdFull& shard); + static bool parse_hash(td::Slice str, ton::Bits256& hash); + static bool convert_uint64(td::Slice word, td::uint64& val); + static bool convert_int64(td::Slice word, td::int64& val); + static bool convert_uint32(td::Slice word, td::uint32& val); + static bool convert_int32(td::Slice word, td::int32& val); + static bool convert_shard_id(td::Slice str, ton::ShardIdFull& shard); bool parse_hash(ton::Bits256& hash); bool parse_lt(ton::LogicalTime& lt); bool parse_uint32(td::uint32& val); @@ -177,6 +183,7 @@ class TestNode : public td::actor::Actor { bool parse_block_id_ext(std::string blk_id_string, ton::BlockIdExt& blkid, bool allow_incomplete = false) const; bool parse_stack_value(td::Slice str, vm::StackEntry& value); bool parse_stack_value(vm::StackEntry& value); + bool parse_stack_values(std::vector& values); bool register_blkid(const ton::BlockIdExt& blkid); bool show_new_blkids(bool all = false); bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const; diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index ed82dbb12f..8927bd0533 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -56,6 +56,7 @@ set(TDUTILS_SOURCE td/utils/port/MemoryMapping.cpp td/utils/port/path.cpp td/utils/port/PollFlags.cpp + td/utils/port/rlimit.cpp td/utils/port/ServerSocketFd.cpp td/utils/port/signals.cpp td/utils/port/sleep.cpp @@ -129,6 +130,7 @@ set(TDUTILS_SOURCE td/utils/port/Poll.h td/utils/port/PollBase.h td/utils/port/PollFlags.h + td/utils/port/rlimit.h td/utils/port/RwMutex.h td/utils/port/ServerSocketFd.h td/utils/port/signals.h diff --git a/tdutils/td/utils/misc.h b/tdutils/td/utils/misc.h index d747313eb8..e8eea6589f 100644 --- a/tdutils/td/utils/misc.h +++ b/tdutils/td/utils/misc.h @@ -301,6 +301,20 @@ typename std::enable_if::value, T>::type hex_to_integer(Slic return integer_value; } +template +Result::value, T>::type> hex_to_integer_safe(Slice str) { + T integer_value = 0; + auto begin = str.begin(); + auto end = str.end(); + while (begin != end) { + if (!is_hex_digit(*begin)) { + return Status::Error("not a hex digit"); + } + integer_value = static_cast(integer_value * 16 + hex_to_int(*begin++)); + } + return integer_value; +} + double to_double(Slice str); template diff --git a/tdutils/td/utils/port/rlimit.cpp b/tdutils/td/utils/port/rlimit.cpp new file mode 100644 index 0000000000..49216cc2c6 --- /dev/null +++ b/tdutils/td/utils/port/rlimit.cpp @@ -0,0 +1,84 @@ +#include "rlimit.h" +#if TD_LINUX +#include +#include +#include +#include +#endif + +namespace td { + +#if TD_PORT_POSIX + +namespace { + +int get_rlimit_type(RlimitType rlim_type) { + switch (rlim_type) { + case RlimitType::nofile: + return RLIMIT_NOFILE; + case RlimitType::rss: + return RLIMIT_RSS; + default: + UNREACHABLE(); + } +} + +} // namespace + +td::Status change_rlimit(RlimitType rlim_type, td::uint64 value, td::uint64 cap) { + if (cap && value > cap) { + return td::Status::Error("setrlimit(): bad argument"); + } + int resource = get_rlimit_type(rlim_type); + + struct rlimit r; + if (getrlimit(resource, &r) < 0) { + return td::Status::PosixError(errno, "failed getrlimit()"); + } + + if (cap) { + r.rlim_max = cap; + } else if (r.rlim_max < value) { + r.rlim_max = value; + } + r.rlim_cur = value; + if (setrlimit(resource, &r) < 0) { + return td::Status::PosixError(errno, "failed setrlimit()"); + } + return td::Status::OK(); +} + +td::Status change_maximize_rlimit(RlimitType rlim_type, td::uint64 value) { + int resource = get_rlimit_type(rlim_type); + + struct rlimit r; + if (getrlimit(resource, &r) < 0) { + return td::Status::PosixError(errno, "failed getrlimit()"); + } + + if (r.rlim_max < value) { + auto t = r; + t.rlim_cur = value; + t.rlim_max = value; + if (setrlimit(resource, &t) >= 0) { + return td::Status::OK(); + } + } + + r.rlim_cur = value < r.rlim_max ? value : r.rlim_max; + if (setrlimit(resource, &r) < 0) { + return td::Status::PosixError(errno, "failed setrlimit()"); + } + return td::Status::OK(); +} +#else +td::Status change_rlimit(RlimitType rlim, td::uint64 value) { + return td::Status::Error("setrlimit not implemented on WINDOWS"); +} +td::Status change_maximize_rlimit(RlimitType rlim, td::uint64 value) { + return td::Status::OK(); +} +#endif + +} // namespace td + diff --git a/tdutils/td/utils/port/rlimit.h b/tdutils/td/utils/port/rlimit.h new file mode 100644 index 0000000000..b217dac81f --- /dev/null +++ b/tdutils/td/utils/port/rlimit.h @@ -0,0 +1,14 @@ +#pragma once + +#include "td/utils/port/config.h" +#include "td/utils/port/platform.h" +#include "td/utils/Status.h" + +namespace td { + +enum class RlimitType { nofile, rss }; + +td::Status change_rlimit(RlimitType rlim_type, td::uint64 value, td::uint64 cap = 0); +td::Status change_maximize_rlimit(RlimitType rlim, td::uint64 value); + +} // namespace td diff --git a/test/regression-tests.ans b/test/regression-tests.ans index 29e26328dc..b86b76c31c 100644 --- a/test/regression-tests.ans +++ b/test/regression-tests.ans @@ -7,7 +7,7 @@ Test_Fift_bug_newlize_default e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca4 Test_Fift_bug_ufits_default 51bf5a9f1ed7633a193f6fdd17a7a3af8e032dfe72a9669c85e8639aa8a7c195 Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96 Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d -Test_Fift_test_dict_default 480d22a6ec25a232febf4eec8ff64747573f79721327e7ff3b1aa7ea4921bbb4 +Test_Fift_test_dict_default 1879f03e5fb25dcdd33f40120a6d352c246481895c9f8e45975bf3e101d9ee70 Test_Fift_test_fixed_default 278a19d56b773102caf5c1fe2997ea6c8d0d9e720eff8503feede6398a197eec Test_Fift_test_sort2_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a Test_Fift_test_sort_default 9b57d47e6a10e7d1bbb565db35400debf2f963031f434742a702ec76555a5d3a diff --git a/test/test-hello-world.cpp b/test/test-hello-world.cpp index 427528f108..3a659f578d 100644 --- a/test/test-hello-world.cpp +++ b/test/test-hello-world.cpp @@ -31,6 +31,7 @@ #include "auto/tl/ton_api_json.h" #include "tl/tl_json.h" +#include "td/utils/Random.h" namespace { std::string config = R"json( @@ -135,4 +136,25 @@ int main() { return res; }; test_tl_json(ton::ton_api::make_object(create_vector_bytes())); + + td::Bits256 x; + td::Random::secure_bytes(x.as_slice()); + + auto s = x.to_hex(); + + auto v = td::hex_decode(s).move_as_ok(); + + auto w = td::buffer_to_hex(x.as_slice()); + + td::Bits256 y; + y.as_slice().copy_from(v); + + CHECK(x == y); + + auto w2 = td::hex_decode(w).move_as_ok(); + td::Bits256 z; + z.as_slice().copy_from(w2); + + LOG_CHECK(x == z) << s << " " << w; + return 0; } diff --git a/test/test-ton-collator.cpp b/test/test-ton-collator.cpp index 326c82be44..1fd6c091a5 100644 --- a/test/test-ton-collator.cpp +++ b/test/test-ton-collator.cpp @@ -303,6 +303,10 @@ class TestNode : public td::actor::Actor { void get_next_key_blocks(ton::BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) override { } + void download_archive(ton::BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + + td::Promise promise) override { + } void new_key_block(ton::validator::BlockHandle handle) override { } diff --git a/tl/generate/scheme/ton_api.tl b/tl/generate/scheme/ton_api.tl index 38b16a84ea..2460201a62 100644 --- a/tl/generate/scheme/ton_api.tl +++ b/tl/generate/scheme/ton_api.tl @@ -362,6 +362,9 @@ tonNode.capabilities version:int capabilities:long = tonNode.Capabilities; tonNode.success = tonNode.Success; +tonNode.archiveNotFound = tonNode.ArchiveInfo; +tonNode.archiveInfo id:long = tonNode.ArchiveInfo; + ---functions--- tonNode.getNextBlockDescription prev_block:tonNode.blockIdExt = tonNode.BlockDescription; @@ -385,6 +388,8 @@ tonNode.downloadBlockProof block:tonNode.blockIdExt = tonNode.Data; tonNode.downloadBlockProofs blocks:(vector tonNode.blockIdExt) = tonNode.DataList; tonNode.downloadBlockProofLink block:tonNode.blockIdExt = tonNode.Data; tonNode.downloadBlockProofLinks blocks:(vector tonNode.blockIdExt) = tonNode.DataList; +tonNode.getArchiveInfo masterchain_seqno:int = tonNode.ArchiveInfo; +tonNode.getArchiveSlice archive_id:long offset:long max_size:int = tonNode.Data; tonNode.getCapabilities = tonNode.Capabilities; @@ -417,7 +422,8 @@ db.block.info#4ac6e727 id:tonNode.blockIdExt flags:# prev_left:flags.1?tonNode.b next_right:flags.4?tonNode.blockIdExt lt:flags.13?long ts:flags.14?int - state:flags.17?int256 = db.block.Info; + state:flags.17?int256 + masterchain_ref_seqno:flags.23?int = db.block.Info; db.block.packedInfo id:tonNode.blockIdExt unixtime:int offset:long = db.block.Info; db.block.archivedInfo id:tonNode.blockIdExt flags:# next:flags.0?tonNode.blockIdExt = db.block.Info; @@ -447,6 +453,7 @@ db.state.gcBlockId block:tonNode.blockIdExt = db.state.GcBlockId; db.state.shardClient block:tonNode.blockIdExt = db.state.ShardClient; db.state.asyncSerializer block:tonNode.blockIdExt last:tonNode.blockIdExt last_ts:int = db.state.AsyncSerializer; db.state.hardforks blocks:(vector tonNode.blockIdExt) = db.state.Hardforks; +db.state.dbVersion version:int = db.state.DbVersion; db.state.key.destroyedSessions = db.state.Key; db.state.key.initBlockId = db.state.Key; @@ -454,6 +461,7 @@ db.state.key.gcBlockId = db.state.Key; db.state.key.shardClient = db.state.Key; db.state.key.asyncSerializer = db.state.Key; db.state.key.hardforks = db.state.Key; +db.state.key.dbVersion = db.state.Key; db.lt.el.key workchain:int shard:long idx:int = db.lt.Key; db.lt.desc.key workchain:int shard:long = db.lt.Key; @@ -464,12 +472,13 @@ db.lt.desc.value first_idx:int last_idx:int last_seqno:int last_lt:long last_ts: db.lt.shard.value workchain:int shard:long = db.lt.shard.Value; db.lt.status.value total_shards:int = db.lt.status.Value; -db.archive.index.key = db.archive.Key; -db.archive.package.key unixtime:int key:Bool = db.archive.Key; +db.files.index.key = db.files.Key; +db.files.package.key package_id:int key:Bool temp:Bool = db.files.Key; -db.archive.index.value packages:(vector int) key_packages:(vector int) = db.archive.index.Value; -db.archive.package.firstBlock workchain:int shard:long seqno:int lt:long = db.archive.package.FirstBlock; -db.archive.package.value unixtime:int key:Bool firstblocks:(vector db.archive.package.firstBlock) deleted:Bool = db.archive.package.Value; +db.files.index.value packages:(vector int) key_packages:(vector int) temp_packages:(vector int) = db.files.index.Value; +db.files.package.firstBlock workchain:int shard:long seqno:int unixtime:int lt:long = db.files.package.FirstBlock; +db.files.package.value package_id:int key:Bool temp:Bool firstblocks:(vector db.files.package.firstBlock) deleted:Bool + = db.files.package.Value; ---functions--- diff --git a/tl/generate/scheme/ton_api.tlo b/tl/generate/scheme/ton_api.tlo index 2f8e13c965126a74eca7e65c6148a2fac64fd483..40363811fd1a67ddd544514484a45502d20d64e5 100644 GIT binary patch delta 1287 zcmaE|iTT1#X5L4$^{p77VADq4-|U<3a7=W{A5JkJy=kcNvFBqnDkrl;z;Wfm2eIOXIgXHVQ9A+3GY=ZP4G zny|#2($vZOnI$(Da4lgJ|E^$uQwXT2xFoS8RnH|UEVZaOGd~ZcV6q6$8Bw9*OpH;+YW-npr$Zf zmE(%BV`f>32t!y{fpjy#^)mz6($DT|azUlzGgH7ufXur!J7Dr|72e4^^m!(4@E2fZ zDM`&On9QgyK6#HXhbg*kSYk}C3>4--^FVSS*D_E3>03Vef*K1iFUYKTsHKx9mdQ`v z;4ESZQU|skNPv8RJ+{-}u?><3naes^FwT8)z+{!l96>6R75o+4K{CiDVNVkf_krZV zrT{gdg~jCcDv}ufm!5pUUxNi?!{j&q1}q@$o6khuVUs~kQz@`C#Q+kYtdn5C0%B~Q zlX!zs0+hVa;}0Y@xhGi%x8#Ln4cw9fDR^{-q!{4VIVVLU0+)l312II=zX+7EAbA3q zkn|9K#HD}pos=Gpo#3u8k>fn|PNY%hCIRT%}E2##!b;_jSHg@xzv>v0)2WGyIW%S& = tvm.Tuple; +tvm.list elements:vector = tvm.List; tvm.stackEntrySlice slice:tvm.slice = tvm.StackEntry; tvm.stackEntryCell cell:tvm.cell = tvm.StackEntry; tvm.stackEntryNumber number:tvm.Number = tvm.StackEntry; +tvm.stackEntryTuple tuple:tvm.Tuple = tvm.StackEntry; +tvm.stackEntryList list:tvm.List = tvm.StackEntry; tvm.stackEntryUnsupported = tvm.StackEntry; smc.info id:int53 = smc.Info; @@ -121,10 +126,10 @@ liteServer.info now:int53 version:int32 capabilities:int64 = liteServer.Info; ---functions--- -init options:options = Ok; +init options:options = options.Info; close = Ok; -options.setConfig config:config = Ok; +options.setConfig config:config = options.ConfigInfo; options.validateConfig config:config = options.ConfigInfo; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index a670da5a67ca0761aba73e8ccd111a1ce2649bc0..82702148d4e32c17992b8bf5010bf852689c701e 100644 GIT binary patch delta 720 zcmbO-iSf@gM&3uW^{p77z-}Y&Zq~_H7)5v*WK{p~W^u`6MRw`Q2^;~Ua%LLhd?3-_lEma}*SwOVN(Q#ci7ev$$-6eM;{?fs zlosTqGEB^u+$(@{^mzJ3x z50z%%ux&fIjNv9HNFD@=oY{0hVjz8+WD3|>K(5(bqP&Dj1Mb=!plg9jL40JRIZ|^{ zb5rw5CJQJkiUW0l%|kL0q<%7w+7+;olb@&=Ko}D0DiDT)I#i@YT>~PrK;57oWIxQL zt8!d1carCQ69$DrF+6m@p#f5d>>Ab*a2SELf; z8_og^0+4|q!$DrdX87bQ>N-9!%^(H1Y=*}y)YTw9vTq25ElB-l35_Fs0^5{gj<96r zWrAV|6p54Hm>IBu7@HN$uP}-qeqEL?2u+B3#i=FE;N-wCIor~8bAaU=4sgJ4c5u4F F1^^Wy(T)HB delta 247 zcmew}jd98(M&3uW^{p77zRItAg0G}3S1-6EF*(~cucWAw zfn{aUP7X#!&&`Qk>Oz~J$fU5dfRt|bQCY$?IY#{im^WEN!vMmF(NKXfT3{kO zG&CS04$3lGMr^uO2hr_n*AlT>zts{H_+?!vuvt;IFmH=fl z_r1L`ImFUnlA*xnDV8S~S#BQgP@9}!Ey4npsjvpi?67{r!3r{fVRE60{N@ubTi5`J CKUzKj diff --git a/ton/ton-types.h b/ton/ton-types.h index fac94dc769..d4c7e33c20 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -23,6 +23,7 @@ #include "td/utils/bits.h" #include "td/utils/Slice.h" #include "td/utils/UInt.h" +#include "td/utils/misc.h" namespace ton { @@ -158,6 +159,9 @@ struct BlockId { explicit operator ShardIdFull() const { return ShardIdFull{workchain, shard}; } + ShardIdFull shard_full() const { + return ShardIdFull{workchain, shard}; + } bool is_valid() const { return workchain != workchainInvalid; } @@ -274,6 +278,23 @@ struct BlockIdExt { std::string to_str() const { return id.to_str() + ':' + root_hash.to_hex() + ':' + file_hash.to_hex(); } + static td::Result from_str(td::CSlice s) { + BlockIdExt v; + char rh[65]; + char fh[65]; + auto r = sscanf(s.begin(), "(%d,%lx,%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh); + if (r < 5) { + return td::Status::Error("failed to parse block id"); + } + if (strlen(rh) != 64 || strlen(fh) != 64) { + return td::Status::Error("failed to parse block id: bad roothash/filehash"); + } + TRY_RESULT(re, td::hex_decode(td::Slice(rh, 64))); + v.root_hash.as_slice().copy_from(td::Slice(re)); + TRY_RESULT(fe, td::hex_decode(td::Slice(fh, 64))); + v.file_hash.as_slice().copy_from(td::Slice(fe)); + return v; + } }; struct ZeroStateIdExt { diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index c615397e3c..4153e269bc 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -438,3 +438,94 @@ TEST(Tonlib, KeysApi) { CHECK(new_imported_key->public_key_ == key->public_key_); CHECK(new_imported_key->secret_ != key->secret_); } + +TEST(Tonlib, ConfigCache) { + using tonlib_api::make_object; + Client client; + + td::mkdir("testdir").ignore(); + // init + sync_send(client, make_object(make_object( + nullptr, make_object("testdir")))) + .ensure(); + + auto testnet = R"abc({ + "liteservers": [ + ], + "validator": { + "@type": "validator.config.global", + "zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "root_hash": "VCSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=", + "file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" + } + } +})abc"; + auto testnet2 = R"abc({ + "liteservers": [ + ], + "validator": { + "@type": "validator.config.global", + "zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "root_hash": "VXSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=", + "file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" + } + } +})abc"; + auto testnet3 = R"abc({ + "liteservers": [ + ], + "validator": { + "@type": "validator.config.global", + "zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "root_hash": "ZXSXxDHhTALFxReyTZRd8E4Ya3ySOmpOWAS4rBX9XBY=", + "file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" + } + } +})abc"; + auto bad = R"abc({ + "liteservers": [ + ], + "validator": { + "@type": "validator.config.global", + "zero_state": { + "workchain": -1, + "shard": -9223372036854775808, + "seqno": 0, + "file_hash": "eh9yveSz1qMdJ7mOsO+I+H77jkLr9NpAuEkoJuseXBo=" + } + } +})abc"; + sync_send(client, + make_object(make_object(bad, "", true, false))) + .ensure_error(); + + sync_send(client, + make_object(make_object(testnet, "", true, false))) + .ensure(); + sync_send(client, + make_object(make_object(testnet2, "", true, false))) + .ensure(); + sync_send(client, + make_object(make_object(testnet3, "", true, false))) + .ensure(); + + sync_send(client, make_object( + make_object(testnet2, "testnet", true, false))) + .ensure_error(); + + sync_send(client, make_object( + make_object(testnet2, "testnet2", true, false))) + .ensure(); + sync_send(client, make_object( + make_object(testnet3, "testnet2", true, false))) + .ensure_error(); +} diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 8eabc452e2..65c6f6f8fe 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -533,14 +533,12 @@ int main(int argc, char* argv[]) { Client client; { - auto info = sync_send(client, make_object( - make_object(global_config_str, "", false, false))) + auto info = sync_send(client, make_object(make_object( + make_object(global_config_str, "", false, false), + make_object(keystore_dir)))) .move_as_ok(); - default_wallet_id = static_cast(info->default_wallet_id_); - sync_send(client, make_object(make_object( - make_object(global_config_str, "", false, false), - make_object(keystore_dir)))) - .ensure(); + default_wallet_id = static_cast(info->config_info_->default_wallet_id_); + LOG(ERROR) << default_wallet_id; } // wait till client is synchronized with blockchain. diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 95a089d720..a26392104d 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -66,6 +66,16 @@ struct SendMessage { }; } // namespace int_api +template +R downcast_call2(O&& o, F&& f, R res = {}) { + downcast_call(o, [&](auto& x) { res = f(x); }); + return res; +} + +tonlib_api::object_ptr to_tonlib_api(const TonlibClient::FullConfig& full_config) { + return tonlib_api::make_object(full_config.wallet_id); +} + class TonlibQueryActor : public td::actor::Actor { public: TonlibQueryActor(td::actor::ActorShared client) : client_(std::move(client)) { @@ -867,7 +877,7 @@ void TonlibClient::update_last_block_state(LastBlockState state, td::uint32 conf return; } - last_block_storage_.save_state(blockchain_name_, state); + last_block_storage_.save_state(last_state_key_, state); } void TonlibClient::update_sync_state(LastBlockSyncState state, td::uint32 config_generation) { @@ -889,7 +899,7 @@ void TonlibClient::update_sync_state(LastBlockSyncState state, td::uint32 config } } -void TonlibClient::init_last_block(td::optional o_master_config) { +void TonlibClient::init_last_block(LastBlockState state) { ref_cnt_++; class Callback : public LastBlock::Callback { public: @@ -907,31 +917,8 @@ void TonlibClient::init_last_block(td::optional o_master_config) { td::actor::ActorShared client_; td::uint32 config_generation_; }; - LastBlockState state; - td::Result r_state; - if (!ignore_cache_) { - r_state = last_block_storage_.get_state(blockchain_name_); - } - if (ignore_cache_ || r_state.is_error()) { - LOG_IF(WARNING, !ignore_cache_) << "Unknown LastBlockState: " << r_state.error(); - state.zero_state_id = ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash, - config_.zero_state_id.file_hash), - state.last_block_id = config_.zero_state_id; - state.last_key_block_id = config_.zero_state_id; - last_block_storage_.save_state(blockchain_name_, state); - } else { - state = r_state.move_as_ok(); - } - - if (o_master_config) { - auto master_config = o_master_config.unwrap(); - if (master_config.init_block_id.is_valid() && - state.last_key_block_id.id.seqno < master_config.init_block_id.id.seqno) { - state.last_key_block_id = master_config.init_block_id; - LOG(INFO) << "Use init block from MASTER config: " << master_config.init_block_id.to_str(); - } - } + last_block_storage_.save_state(last_state_key_, state); raw_last_block_ = td::actor::create_actor( td::actor::ActorOptions().with_name("LastBlock").with_poll(false), get_client_ref(), std::move(state), config_, @@ -1013,8 +1000,8 @@ tonlib_api::object_ptr TonlibClient::static_request( return tonlib_api::make_object(400, "Request is empty"); } - tonlib_api::object_ptr response; - downcast_call(*function, [&response](auto& request) { response = TonlibClient::do_static_request(request); }); + auto response = downcast_call2>( + *function, [](auto& request) { return TonlibClient::do_static_request(request); }); VLOG(tonlib_query) << " answer static query " << to_string(response); return response; } @@ -1029,7 +1016,6 @@ bool TonlibClient::is_static_request(td::int32 id) { case tonlib_api::testGiver_getAccountAddress::ID: case tonlib_api::packAccountAddress::ID: case tonlib_api::unpackAccountAddress::ID: - case tonlib_api::options_validateConfig::ID: case tonlib_api::getBip39Hints::ID: case tonlib_api::setLogStream::ID: case tonlib_api::getLogStream::ID: @@ -1190,7 +1176,7 @@ tonlib_api::object_ptr TonlibClient::do_static_request(tonli } td::Status TonlibClient::do_request(const tonlib_api::init& request, - td::Promise>&& promise) { + td::Promise>&& promise) { if (state_ != State::Uninited) { return td::Status::Error(400, "Tonlib is already inited"); } @@ -1201,23 +1187,24 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request, return TonlibError::EmptyField("options.keystore_type"); } - td::Result> r_kv; - downcast_call( + auto r_kv = downcast_call2>>( *request.options_->keystore_type_, td::overloaded( - [&](tonlib_api::keyStoreTypeDirectory& directory) { r_kv = KeyValue::create_dir(directory.directory_); }, - [&](tonlib_api::keyStoreTypeInMemory& inmemory) { r_kv = KeyValue::create_inmemory(); })); + [](tonlib_api::keyStoreTypeDirectory& directory) { return KeyValue::create_dir(directory.directory_); }, + [](tonlib_api::keyStoreTypeInMemory& inmemory) { return KeyValue::create_inmemory(); })); TRY_RESULT(kv, std::move(r_kv)); kv_ = std::shared_ptr(kv.release()); key_storage_.set_key_value(kv_); last_block_storage_.set_key_value(kv_); + auto res = tonlib_api::make_object(); if (request.options_->config_) { TRY_RESULT(full_config, validate_config(std::move(request.options_->config_))); + res->config_info_ = to_tonlib_api(full_config); set_config(std::move(full_config)); } state_ = State::Running; - promise.set_value(tonlib_api::make_object()); + promise.set_value(std::move(res)); return td::Status::OK(); } @@ -1291,21 +1278,55 @@ td::Result TonlibClient::validate_config(tonlib_api::o } td::optional o_master_config; + std::string last_state_key; if (config->blockchain_name_.empty()) { + last_state_key = new_config.zero_state_id.root_hash.as_slice().str(); o_master_config = get_default_master_config().by_root_hash(new_config.zero_state_id.root_hash); } else { + last_state_key = config->blockchain_name_; o_master_config = get_default_master_config().by_name(config->blockchain_name_); } if (o_master_config && o_master_config.value().zero_state_id != new_config.zero_state_id) { return TonlibError::InvalidConfig("zero_state differs from embedded zero_state"); } + + LastBlockState state; + td::Result r_state; + if (!config->ignore_cache_) { + r_state = last_block_storage_.get_state(last_state_key); + } + auto zero_state = ton::ZeroStateIdExt(new_config.zero_state_id.id.workchain, new_config.zero_state_id.root_hash, + new_config.zero_state_id.file_hash); + if (config->ignore_cache_ || r_state.is_error()) { + LOG_IF(WARNING, !config->ignore_cache_) << "Unknown LastBlockState: " << r_state.error(); + state.zero_state_id = zero_state; + state.last_block_id = new_config.zero_state_id; + state.last_key_block_id = new_config.zero_state_id; + } else { + state = r_state.move_as_ok(); + if (state.zero_state_id != zero_state) { + LOG(ERROR) << state.zero_state_id.to_str() << " " << zero_state.to_str(); + return TonlibError::InvalidConfig("zero_state differs from cached zero_state"); + } + } + + if (o_master_config) { + auto master_config = o_master_config.unwrap(); + if (master_config.init_block_id.is_valid() && + state.last_key_block_id.id.seqno < master_config.init_block_id.id.seqno) { + state.last_key_block_id = master_config.init_block_id; + LOG(INFO) << "Use init block from MASTER config: " << master_config.init_block_id.to_str(); + } + } + FullConfig res; res.config = std::move(new_config); - res.o_master_config = std::move(o_master_config); - res.ignore_cache = config->ignore_cache_; res.use_callbacks_for_network = config->use_callbacks_for_network_; res.wallet_id = td::as(res.config.zero_state_id.root_hash.as_slice().data()); + res.last_state_key = std::move(last_state_key); + res.last_state = std::move(state); + return std::move(res); } @@ -1313,12 +1334,11 @@ void TonlibClient::set_config(FullConfig full_config) { config_ = std::move(full_config.config); config_generation_++; wallet_id_ = full_config.wallet_id; - blockchain_name_ = config_.zero_state_id.root_hash.as_slice().str(); + last_state_key_ = full_config.last_state_key; use_callbacks_for_network_ = full_config.use_callbacks_for_network; - ignore_cache_ = full_config.ignore_cache; init_ext_client(); - init_last_block(std::move(full_config.o_master_config)); + init_last_block(std::move(full_config.last_state)); init_last_config(); client_.set_client(get_client_ref()); } @@ -1332,23 +1352,23 @@ td::Status TonlibClient::do_request(const tonlib_api::close& request, return td::Status::OK(); } -tonlib_api::object_ptr TonlibClient::do_static_request( - tonlib_api::options_validateConfig& request) { - auto r_config = validate_config(std::move(request.config_)); - if (r_config.is_error()) { - return status_to_tonlib_api(r_config.move_as_error()); - } - return tonlib_api::make_object(r_config.ok().wallet_id); +td::Status TonlibClient::do_request(tonlib_api::options_validateConfig& request, + td::Promise>&& promise) { + TRY_RESULT(config, validate_config(std::move(request.config_))); + auto res = to_tonlib_api(config); + promise.set_value(std::move(res)); + return td::Status::OK(); } td::Status TonlibClient::do_request(tonlib_api::options_setConfig& request, - td::Promise>&& promise) { + td::Promise>&& promise) { if (!request.config_) { return TonlibError::EmptyField("config"); } TRY_RESULT(config, validate_config(std::move(request.config_))); + auto res = to_tonlib_api(config); set_config(std::move(config)); - promise.set_value(tonlib_api::make_object()); + promise.set_value(std::move(res)); return td::Status::OK(); } @@ -1603,11 +1623,9 @@ td::Result from_tonlib(tonlib_api::inputKeyRegular& input_ } td::Result from_tonlib(tonlib_api::InputKey& input_key) { - td::Result r_key; - tonlib_api::downcast_call( - input_key, td::overloaded([&](tonlib_api::inputKeyRegular& input_key) { r_key = from_tonlib(input_key); }, - [&](tonlib_api::inputKeyFake&) { r_key = KeyStorage::fake_input_key(); })); - return r_key; + return downcast_call2>( + input_key, td::overloaded([&](tonlib_api::inputKeyRegular& input_key) { return from_tonlib(input_key); }, + [&](tonlib_api::inputKeyFake&) { return KeyStorage::fake_input_key(); })); } // ton::TestWallet @@ -2195,6 +2213,98 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_getState& request, return td::Status::OK(); } +bool is_list(vm::StackEntry entry) { + while (true) { + if (entry.type() == vm::StackEntry::Type::t_null) { + return true; + } + if (entry.type() != vm::StackEntry::Type::t_tuple) { + return false; + } + if (entry.as_tuple()->size() != 2) { + return false; + } + entry = entry.as_tuple()->at(1); + } +}; +auto to_tonlib_api(const vm::StackEntry& entry) -> tonlib_api::object_ptr { + switch (entry.type()) { + case vm::StackEntry::Type::t_int: + return tonlib_api::make_object( + tonlib_api::make_object(dec_string(entry.as_int()))); + case vm::StackEntry::Type::t_slice: + return tonlib_api::make_object(tonlib_api::make_object( + to_bytes(vm::CellBuilder().append_cellslice(entry.as_slice()).finalize()))); + case vm::StackEntry::Type::t_cell: + return tonlib_api::make_object( + tonlib_api::make_object(to_bytes(entry.as_cell()))); + case vm::StackEntry::Type::t_null: + case vm::StackEntry::Type::t_tuple: { + std::vector> elements; + if (is_list(entry)) { + auto node = entry; + while (node.type() == vm::StackEntry::Type::t_tuple) { + elements.push_back(to_tonlib_api(node.as_tuple()->at(0))); + node = node.as_tuple()->at(1); + } + return tonlib_api::make_object( + tonlib_api::make_object(std::move(elements))); + + } else { + for (auto& element : *entry.as_tuple()) { + elements.push_back(to_tonlib_api(element)); + } + return tonlib_api::make_object( + tonlib_api::make_object(std::move(elements))); + } + } + + default: + return tonlib_api::make_object(); + } +}; + +td::Result from_tonlib_api(tonlib_api::tvm_StackEntry& entry) { + // TODO: error codes + // downcast_call + return downcast_call2>( + entry, + td::overloaded( + [&](tonlib_api::tvm_stackEntryUnsupported& cell) { return td::Status::Error("Unsuppored stack entry"); }, + [&](tonlib_api::tvm_stackEntrySlice& cell) -> td::Result { + TRY_RESULT(res, vm::std_boc_deserialize(cell.slice_->bytes_)); + return vm::StackEntry{std::move(res)}; + }, + [&](tonlib_api::tvm_stackEntryCell& cell) -> td::Result { + TRY_RESULT(res, vm::std_boc_deserialize(cell.cell_->bytes_)); + return vm::StackEntry{std::move(res)}; + }, + [&](tonlib_api::tvm_stackEntryTuple& tuple) -> td::Result { + std::vector elements; + for (auto& element : tuple.tuple_->elements_) { + TRY_RESULT(new_element, from_tonlib_api(*element)); + elements.push_back(std::move(new_element)); + } + return td::Ref(true, std::move(elements)); + }, + [&](tonlib_api::tvm_stackEntryList& tuple) -> td::Result { + vm::StackEntry tail; + for (auto& element : td::reversed(tuple.list_->elements_)) { + TRY_RESULT(new_element, from_tonlib_api(*element)); + tail = vm::make_tuple_ref(std::move(new_element), std::move(tail)); + } + return tail; + }, + [&](tonlib_api::tvm_stackEntryNumber& number) -> td::Result { + auto& dec = *number.number_; + auto num = td::dec_string_to_int256(dec.number_); + if (num.is_null()) { + return td::Status::Error("Failed to parse dec string to int256"); + } + return num; + })); +} + td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request, td::Promise>&& promise) { auto it = smcs_.find(request.id_); @@ -2209,65 +2319,17 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_runGetMethod& request, [&](tonlib_api::smc_methodIdName& name) { args.set_method_id(name.name_); })); td::Ref stack(true); td::Status status; - // TODO: error codes - // downcast_call for (auto& entry : request.stack_) { - downcast_call(*entry, td::overloaded( - [&](tonlib_api::tvm_stackEntryUnsupported& cell) { - status = td::Status::Error("Unsuppored stack entry"); - }, - [&](tonlib_api::tvm_stackEntrySlice& cell) { - auto r_cell = vm::std_boc_deserialize(cell.slice_->bytes_); - if (r_cell.is_error()) { - status = r_cell.move_as_error(); - return; - } - stack.write().push_cell(r_cell.move_as_ok()); - }, - [&](tonlib_api::tvm_stackEntryCell& cell) { - auto r_cell = vm::std_boc_deserialize(cell.cell_->bytes_); - if (r_cell.is_error()) { - status = r_cell.move_as_error(); - return; - } - stack.write().push_cell(r_cell.move_as_ok()); - }, - [&](tonlib_api::tvm_stackEntryNumber& number) { - [&](tonlib_api::tvm_numberDecimal& dec) { - auto num = td::dec_string_to_int256(dec.number_); - if (num.is_null()) { - status = td::Status::Error("Failed to parse dec string to int256"); - return; - } - stack.write().push_int(std::move(num)); - }(*number.number_); - })); - } - TRY_STATUS(std::move(status)); + TRY_RESULT(e, from_tonlib_api(*entry)); + stack.write().push(std::move(e)); + } args.set_stack(std::move(stack)); auto res = smc->run_get_method(std::move(args)); // smc.runResult gas_used:int53 stack:vector exit_code:int32 = smc.RunResult; std::vector> res_stack; for (auto& entry : res.stack->as_span()) { - switch (entry.type()) { - case vm::StackEntry::Type::t_int: - res_stack.push_back(tonlib_api::make_object( - tonlib_api::make_object(dec_string(entry.as_int())))); - break; - case vm::StackEntry::Type::t_slice: - res_stack.push_back( - tonlib_api::make_object(tonlib_api::make_object( - to_bytes(vm::CellBuilder().append_cellslice(entry.as_slice()).finalize())))); - break; - case vm::StackEntry::Type::t_cell: - res_stack.push_back(tonlib_api::make_object( - tonlib_api::make_object(to_bytes(entry.as_cell())))); - break; - default: - res_stack.push_back(tonlib_api::make_object()); - break; - } + res_stack.push_back(to_tonlib_api(entry)); } promise.set_value(tonlib_api::make_object(res.gas_used, std::move(res_stack), res.code)); return td::Status::OK(); @@ -2576,11 +2638,6 @@ td::Status TonlibClient::do_request(const tonlib_api::unpackAccountAddress& requ return TonlibError::Internal(); } template -td::Status TonlibClient::do_request(const tonlib_api::options_validateConfig& request, P&&) { - UNREACHABLE(); - return TonlibError::Internal(); -} -template td::Status TonlibClient::do_request(tonlib_api::getBip39Hints& request, P&&) { UNREACHABLE(); return TonlibError::Internal(); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 68936a4373..f624440ad5 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -58,6 +58,14 @@ class TonlibClient : public td::actor::Actor { ~TonlibClient(); + struct FullConfig { + Config config; + bool use_callbacks_for_network; + LastBlockState last_state; + std::string last_state_key; + td::uint32 wallet_id; + }; + private: enum class State { Uninited, Running, Closed } state_ = State::Uninited; td::unique_ptr callback_; @@ -66,8 +74,7 @@ class TonlibClient : public td::actor::Actor { Config config_; td::uint32 config_generation_{0}; td::uint32 wallet_id_; - std::string blockchain_name_; - bool ignore_cache_{false}; + std::string last_state_key_; bool use_callbacks_for_network_{false}; // KeyStorage @@ -89,7 +96,7 @@ class TonlibClient : public td::actor::Actor { ExtClientRef get_client_ref(); void init_ext_client(); - void init_last_block(td::optional o_master_config); + void init_last_block(LastBlockState state); void init_last_config(); bool is_closing_{false}; @@ -128,7 +135,6 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::testGiver_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::packAccountAddress& request); static object_ptr do_static_request(const tonlib_api::unpackAccountAddress& request); - static object_ptr do_static_request(tonlib_api::options_validateConfig& request); static object_ptr do_static_request(tonlib_api::getBip39Hints& request); static object_ptr do_static_request(tonlib_api::setLogStream& request); @@ -161,8 +167,6 @@ class TonlibClient : public td::actor::Actor { template td::Status do_request(const tonlib_api::unpackAccountAddress& request, P&&); template - td::Status do_request(const tonlib_api::options_validateConfig& request, P&&); - template td::Status do_request(tonlib_api::getBip39Hints& request, P&&); template @@ -197,18 +201,14 @@ class TonlibClient : public td::actor::Actor { } } - struct FullConfig { - Config config; - td::optional o_master_config; - bool use_callbacks_for_network; - bool ignore_cache; - td::uint32 wallet_id; - }; - static td::Result validate_config(tonlib_api::object_ptr config); + td::Result validate_config(tonlib_api::object_ptr config); void set_config(FullConfig config); - td::Status do_request(const tonlib_api::init& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::init& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::close& request, td::Promise>&& promise); - td::Status do_request(tonlib_api::options_setConfig& request, td::Promise>&& promise); + td::Status do_request(tonlib_api::options_validateConfig& request, + td::Promise>&& promise); + td::Status do_request(tonlib_api::options_setConfig& request, + td::Promise>&& promise); td::Status do_request(const tonlib_api::raw_sendMessage& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::raw_createAndSendMessage& request, diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 716fae3070..fec9f86c4d 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -2,6 +2,7 @@ #include "td/utils/filesystem.h" #include "td/utils/OptionsParser.h" +#include "td/utils/overloaded.h" #include "td/utils/Parser.h" #include "td/utils/port/signals.h" #include "td/utils/port/path.h" @@ -177,10 +178,6 @@ class TonlibCli : public td::actor::Actor { ? make_object(options_.config, options_.name, options_.use_callbacks_for_network, options_.ignore_cache) : nullptr; - auto config2 = !options_.config.empty() - ? make_object(options_.config, options_.name, - options_.use_callbacks_for_network, options_.ignore_cache) - : nullptr; tonlib_api::object_ptr ks_type; if (options_.in_memory) { @@ -188,18 +185,13 @@ class TonlibCli : public td::actor::Actor { } else { ks_type = make_object(options_.key_dir); } - auto obj = - tonlib::TonlibClient::static_request(make_object(std::move(config2))); - if (obj->get_id() != tonlib_api::error::ID) { - auto info = ton::move_tl_object_as(obj); - wallet_id_ = static_cast(info->default_wallet_id_); - } else { - LOG(ERROR) << "Invalid config"; - } send_query(make_object(make_object(std::move(config), std::move(ks_type))), - [](auto r_ok) { + [&](auto r_ok) { LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); - td::TerminalIO::out() << "Tonlib is inited\n"; + if (r_ok.is_ok()) { + wallet_id_ = static_cast(r_ok.ok()->config_info_->default_wallet_id_); + td::TerminalIO::out() << "Tonlib is inited\n"; + } }); if (options_.one_shot) { td::actor::send_closure(actor_id(this), &TonlibCli::parse_line, td::BufferSlice(options_.cmd)); @@ -482,6 +474,73 @@ class TonlibCli : public td::actor::Actor { tonlib_api::make_object(dec_string(num))); } + td::Result>> parse_stack(td::ConstParser& parser, + td::Slice end_token) { + std::vector> stack; + while (true) { + auto word = parser.read_word(); + LOG(ERROR) << word << " vs " << end_token; + if (word == end_token) { + break; + } + if (word == "[") { + TRY_RESULT(elements, parse_stack(parser, "]")); + stack.push_back(tonlib_api::make_object( + tonlib_api::make_object(std::move(elements)))); + } else if (word == "(") { + TRY_RESULT(elements, parse_stack(parser, ")")); + stack.push_back(tonlib_api::make_object( + tonlib_api::make_object(std::move(elements)))); + } else { + TRY_RESULT(stack_entry, parse_stack_entry(word)); + stack.push_back(std::move(stack_entry)); + } + } + return std::move(stack); + } + + static void store_entry(td::StringBuilder& sb, tonlib_api::tvm_StackEntry& entry) { + downcast_call(entry, td::overloaded( + [&](tonlib_api::tvm_stackEntryCell& cell) { + auto r_cell = vm::std_boc_deserialize(cell.cell_->bytes_); + if (r_cell.is_error()) { + sb << ""; + } + auto cs = vm::load_cell_slice(r_cell.move_as_ok()); + std::stringstream ss; + cs.print_rec(ss); + sb << ss.str(); + }, + [&](tonlib_api::tvm_stackEntrySlice& cell) { + auto r_cell = vm::std_boc_deserialize(cell.slice_->bytes_); + if (r_cell.is_error()) { + sb << ""; + } + auto cs = vm::load_cell_slice(r_cell.move_as_ok()); + std::stringstream ss; + cs.print_rec(ss); + sb << ss.str(); + }, + [&](tonlib_api::tvm_stackEntryNumber& cell) { sb << cell.number_->number_; }, + [&](tonlib_api::tvm_stackEntryTuple& cell) { + sb << "("; + for (auto& element : cell.tuple_->elements_) { + sb << " "; + store_entry(sb, *element); + } + sb << " )"; + }, + [&](tonlib_api::tvm_stackEntryList& cell) { + sb << "["; + for (auto& element : cell.list_->elements_) { + sb << " "; + store_entry(sb, *element); + } + sb << " ]"; + }, + [&](tonlib_api::tvm_stackEntryUnsupported& cell) { sb << ""; })); + } + void run_method(td::ConstParser& parser, td::Promise promise) { TRY_RESULT_PROMISE(promise, addr, to_account_address(parser.read_word(), false)); @@ -492,15 +551,15 @@ class TonlibCli : public td::actor::Actor { } else { method = tonlib_api::make_object(method_str.str()); } - std::vector> stack; - while (true) { - auto word = parser.read_word(); - if (word.empty()) { - break; - } - TRY_RESULT_PROMISE(promise, stack_entry, parse_stack_entry(word)); - stack.push_back(std::move(stack_entry)); + TRY_RESULT_PROMISE(promise, stack, parse_stack(parser, "")); + td::StringBuilder sb; + for (auto& entry : stack) { + store_entry(sb, *entry); + sb << "\n"; } + + td::TerminalIO::out() << "Run " << to_string(method) << "With stack:\n" << sb.as_cslice(); + auto to_run = tonlib_api::make_object(0 /*fixme*/, std::move(method), std::move(stack)); @@ -513,9 +572,16 @@ class TonlibCli : public td::actor::Actor { to_run->id_ = info->id_; send_query(std::move(to_run), promise.send_closure(actor_id(this), &TonlibCli::run_method_3)); } - void run_method_3(tonlib_api::object_ptr info, td::Promise promise) { - td::TerminalIO::out() << "Got smc result " << to_string(info); + td::StringBuilder sb; + for (auto& entry : info->stack_) { + store_entry(sb, *entry); + sb << "\n"; + } + + td::TerminalIO::out() << "Got smc result. exit code: " << info->exit_code_ << ", gas_used: " << info->gas_used_ + << "\n" + << sb.as_cslice(); promise.set_value({}); } @@ -972,7 +1038,7 @@ class TonlibCli : public td::actor::Actor { void import_key(std::vector words, td::Slice password) { using tonlib_api::make_object; - send_query(make_object(td::SecureString(password), td::SecureString(), + send_query(make_object(td::SecureString(password), td::SecureString(" test mnemonic"), make_object(std::move(words))), [this, password = td::SecureString(password)](auto r_res) { if (r_res.is_error()) { @@ -1147,7 +1213,7 @@ class TonlibCli : public td::actor::Actor { return; } td::TerminalIO::out() << to_string(r_res.ok()); - self->on_ok(); + //self->on_ok(); }); self->send_query(make_object(r_res.ok()->id_), [self](auto r_res) { @@ -1160,7 +1226,7 @@ class TonlibCli : public td::actor::Actor { self->on_ok(); }); - self->on_ok(); + //self->on_ok(); }); } diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt index c4207bbcde..82351ae750 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -14,3 +14,8 @@ add_executable(json2tlo json2tlo.cpp ) target_link_libraries(json2tlo tl_api ton_crypto keys ) target_include_directories(json2tlo PUBLIC $/..) + +add_executable(pack-viewer pack-viewer.cpp ) +target_link_libraries(pack-viewer tl_api ton_crypto keys validator tddb ) +target_include_directories(pack-viewer PUBLIC + $/..) diff --git a/utils/pack-viewer.cpp b/utils/pack-viewer.cpp new file mode 100644 index 0000000000..3897bf1841 --- /dev/null +++ b/utils/pack-viewer.cpp @@ -0,0 +1,61 @@ +/* + 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 . + + 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 +#include +#include +#include +#include +#include "td/utils/OptionsParser.h" + +#include "validator/db/package.hpp" +#include "validator/db/fileref.hpp" + +void run(std::string filename) { + auto R = ton::Package::open(filename, true, false); + if (R.is_error()) { + std::cerr << "failed to open archive '" << filename << "': " << R.move_as_error().to_string(); + std::_Exit(2); + } + auto p = R.move_as_ok(); + + p.iterate([&](std::string filename, td::BufferSlice data, td::uint64 offset) -> bool { + auto E = ton::validator::FileReference::create(filename); + if (E.is_error()) { + std::cout << "bad filename\n"; + } else { + std::cout << filename << " " << data.size() << "\n"; + } + return true; + }); +} + +int main(int argc, char **argv) { + run(argv[1]); + return 0; +} + diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 67171a1eae..542ecf000f 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -43,6 +43,7 @@ #include "td/utils/port/path.h" #include "td/utils/port/signals.h" #include "td/utils/port/user.h" +#include "td/utils/port/rlimit.h" #include "td/utils/ThreadSafeCounter.h" #include "td/utils/TsFileLog.h" #include "td/utils/Random.h" @@ -2842,6 +2843,8 @@ int main(int argc, char *argv[]) { td::log_interface = td::default_log_interface; }; + LOG_STATUS(td::change_maximize_rlimit(td::RlimitType::nofile, 65536)); + std::vector> acts; td::OptionsParser p; diff --git a/validator/CMakeLists.txt b/validator/CMakeLists.txt index 73cf0c99e9..564e21efd5 100644 --- a/validator/CMakeLists.txt +++ b/validator/CMakeLists.txt @@ -10,17 +10,17 @@ add_subdirectory(impl) set(VALIDATOR_DB_SOURCE db/archiver.cpp db/archiver.hpp - db/archive-db.cpp - db/archive-db.hpp + db/archive-manager.cpp + db/archive-manager.hpp + db/archive-slice.cpp + db/archive-slice.hpp db/blockdb.cpp db/blockdb.hpp db/celldb.cpp db/celldb.hpp db/files-async.hpp - db/filedb.hpp - db/filedb.cpp - db/ltdb.hpp - db/ltdb.cpp + db/fileref.hpp + db/fileref.cpp db/rootdb.cpp db/rootdb.hpp db/statedb.hpp @@ -52,6 +52,8 @@ set(VALIDATOR_HEADERS interfaces/validator-manager.h interfaces/validator-set.h invariants.hpp + + import-db-slice.hpp manager-disk.h manager-disk.hpp @@ -69,6 +71,7 @@ set(VALIDATOR_SOURCE apply-block.cpp block-handle.cpp get-next-key-blocks.cpp + import-db-slice.cpp shard-client.cpp state-serializer.cpp token-manager.cpp @@ -124,6 +127,8 @@ set(FULL_NODE_SOURCE net/download-block.cpp net/download-block-new.hpp net/download-block-new.cpp + net/download-archive-slice.hpp + net/download-archive-slice.cpp net/download-next-block.hpp net/download-next-block.cpp net/download-state.hpp diff --git a/validator/apply-block.cpp b/validator/apply-block.cpp index b5dfbd9d7c..8294928530 100644 --- a/validator/apply-block.cpp +++ b/validator/apply-block.cpp @@ -53,6 +53,10 @@ void ApplyBlock::alarm() { void ApplyBlock::start_up() { VLOG(VALIDATOR_DEBUG) << "running apply_block for " << id_; + if (id_.is_masterchain()) { + masterchain_block_id_ = id_; + } + alarm_timestamp() = timeout_; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { @@ -144,6 +148,7 @@ void ApplyBlock::got_block_handle(BlockHandle handle) { } void ApplyBlock::written_block_data() { + VLOG(VALIDATOR_DEBUG) << "apply block: written block data for " << id_; if (handle_->is_applied() && handle_->processed()) { finish_query(); } else { @@ -161,6 +166,7 @@ void ApplyBlock::written_block_data() { } void ApplyBlock::got_cur_state(td::Ref state) { + VLOG(VALIDATOR_DEBUG) << "apply block: received state for " << id_; state_ = std::move(state); CHECK(handle_->received_state()); written_state(); @@ -171,6 +177,7 @@ void ApplyBlock::written_state() { finish_query(); return; } + VLOG(VALIDATOR_DEBUG) << "apply block: setting next for parents of " << id_; if (handle_->id().id.seqno != 0 && !handle_->is_applied()) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { @@ -201,10 +208,12 @@ void ApplyBlock::written_next() { return; } - if (handle_->id().id.seqno != 0) { + VLOG(VALIDATOR_DEBUG) << "apply block: applying parents of " << id_; + + if (handle_->id().id.seqno != 0 && !handle_->is_applied()) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { - td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error()); + td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error_prefix("prev: ")); } else { td::actor::send_closure(SelfId, &ApplyBlock::applied_prev); } @@ -213,9 +222,13 @@ void ApplyBlock::written_next() { td::MultiPromise mp; auto g = mp.init_guard(); g.add_promise(std::move(P)); - run_apply_block_query(handle_->one_prev(true), td::Ref{}, manager_, timeout_, g.get_promise()); + BlockIdExt m = masterchain_block_id_; + if (id_.is_masterchain()) { + m = id_; + } + run_apply_block_query(handle_->one_prev(true), td::Ref{}, m, manager_, timeout_, g.get_promise()); if (handle_->merge_before()) { - run_apply_block_query(handle_->one_prev(false), td::Ref{}, manager_, timeout_, g.get_promise()); + run_apply_block_query(handle_->one_prev(false), td::Ref{}, m, manager_, timeout_, g.get_promise()); } } else { applied_prev(); @@ -223,6 +236,10 @@ void ApplyBlock::written_next() { } void ApplyBlock::applied_prev() { + VLOG(VALIDATOR_DEBUG) << "apply block: waiting manager's confirm for " << id_; + if (!id_.is_masterchain()) { + handle_->set_masterchain_ref_block(masterchain_block_id_.seqno()); + } auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ApplyBlock::abort_query, R.move_as_error()); @@ -234,7 +251,12 @@ void ApplyBlock::applied_prev() { } void ApplyBlock::applied_set() { + VLOG(VALIDATOR_DEBUG) << "apply block: setting apply bit for " << id_; handle_->set_applied(); + if (handle_->id().seqno() > 0) { + CHECK(handle_->handle_moved_to_archive()); + CHECK(handle_->moved_to_archive()); + } if (handle_->need_flush()) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { diff --git a/validator/apply-block.hpp b/validator/apply-block.hpp index e59c558e0d..b64ccce151 100644 --- a/validator/apply-block.hpp +++ b/validator/apply-block.hpp @@ -39,9 +39,14 @@ namespace validator { class ApplyBlock : public td::actor::Actor { public: - ApplyBlock(BlockIdExt id, td::Ref block, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise promise) - : id_(id), block_(std::move(block)), manager_(manager), timeout_(timeout), promise_(std::move(promise)) { + ApplyBlock(BlockIdExt id, td::Ref block, BlockIdExt masterchain_block_id, + td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise) + : id_(id) + , block_(std::move(block)) + , masterchain_block_id_(masterchain_block_id) + , manager_(manager) + , timeout_(timeout) + , promise_(std::move(promise)) { } static constexpr td::uint32 apply_block_priority() { @@ -65,6 +70,7 @@ class ApplyBlock : public td::actor::Actor { private: BlockIdExt id_; td::Ref block_; + BlockIdExt masterchain_block_id_; td::actor::ActorId manager_; td::Timestamp timeout_; td::Promise promise_; diff --git a/validator/block-handle.cpp b/validator/block-handle.cpp index 7c71630b6d..e1b0e514cb 100644 --- a/validator/block-handle.cpp +++ b/validator/block-handle.cpp @@ -32,18 +32,19 @@ void BlockHandleImpl::flush(td::actor::ActorId manage td::BufferSlice BlockHandleImpl::serialize() const { while (locked()) { } - auto flags = flags_.load(std::memory_order_consume) & ~Flags::dbf_processed; + auto flags = flags_.load(std::memory_order_consume) & ~(Flags::dbf_processed | Flags::dbf_moved_handle); return create_serialize_tl_object( create_tl_block_id(id_), flags, (flags & dbf_inited_prev_left) ? create_tl_block_id(prev_[0]) : nullptr, (flags & dbf_inited_prev_right) ? create_tl_block_id(prev_[1]) : nullptr, (flags & dbf_inited_next_left) ? create_tl_block_id(next_[0]) : nullptr, (flags & dbf_inited_next_right) ? create_tl_block_id(next_[1]) : nullptr, (flags & dbf_inited_lt) ? lt_ : 0, - (flags & dbf_inited_ts) ? ts_ : 0, (flags & dbf_inited_state) ? state_ : RootHash::zero()); + (flags & dbf_inited_ts) ? ts_ : 0, (flags & dbf_inited_state) ? state_ : RootHash::zero(), + (flags & dbf_inited_masterchain_ref_block) ? masterchain_ref_seqno_ : 0); } BlockHandleImpl::BlockHandleImpl(td::BufferSlice data) { auto obj = fetch_tl_object(std::move(data), true).move_as_ok(); - flags_ = obj->flags_ & ~Flags::dbf_processed; + flags_ = obj->flags_ & ~(Flags::dbf_processed | Flags::dbf_moved_handle); id_ = create_block_id(obj->id_); prev_[0] = (flags_ & dbf_inited_prev_left) ? create_block_id(obj->prev_left_) : BlockIdExt{}; prev_[1] = (flags_ & dbf_inited_prev_right) ? create_block_id(obj->prev_right_) : BlockIdExt{}; @@ -52,6 +53,8 @@ BlockHandleImpl::BlockHandleImpl(td::BufferSlice data) { lt_ = (flags_ & dbf_inited_lt) ? obj->lt_ : 0; ts_ = (flags_ & dbf_inited_ts) ? obj->ts_ : 0; state_ = (flags_ & dbf_inited_state) ? obj->state_ : RootHash::zero(); + masterchain_ref_seqno_ = + (flags_ & dbf_inited_masterchain_ref_block) ? static_cast(obj->masterchain_ref_seqno_) : 0; get_thread_safe_counter().add(1); } diff --git a/validator/block-handle.hpp b/validator/block-handle.hpp index 3cbaf2e108..06bd16137b 100644 --- a/validator/block-handle.hpp +++ b/validator/block-handle.hpp @@ -56,11 +56,12 @@ struct BlockHandleImpl : public BlockHandleInterface { dbf_inited_state_boc = 0x100000, dbf_archived = 0x200000, dbf_applied = 0x400000, - dbf_moved = 0x1000000, + dbf_inited_masterchain_ref_block = 0x800000, dbf_deleted = 0x2000000, dbf_deleted_boc = 0x4000000, dbf_moved_new = 0x8000000, dbf_processed = 0x10000000, + dbf_moved_handle = 0x20000000, }; std::atomic version_{0}; @@ -72,6 +73,7 @@ struct BlockHandleImpl : public BlockHandleInterface { LogicalTime lt_; UnixTime ts_; RootHash state_; + BlockSeqno masterchain_ref_seqno_; static constexpr td::uint64 lock_const() { return static_cast(1) << 32; @@ -93,12 +95,12 @@ struct BlockHandleImpl : public BlockHandleInterface { bool received() const override { return flags_.load(std::memory_order_consume) & Flags::dbf_received; } - bool moved_to_storage() const override { - return flags_.load(std::memory_order_consume) & Flags::dbf_moved; - } bool moved_to_archive() const override { return flags_.load(std::memory_order_consume) & Flags::dbf_moved_new; } + bool handle_moved_to_archive() const override { + return flags_.load(std::memory_order_consume) & Flags::dbf_moved_handle; + } bool deleted() const override { return flags_.load(std::memory_order_consume) & Flags::dbf_deleted; } @@ -199,6 +201,13 @@ struct BlockHandleImpl : public BlockHandleInterface { bool is_applied() const override { return flags_.load(std::memory_order_consume) & Flags::dbf_applied; } + bool inited_masterchain_ref_block() const override { + return id_.is_masterchain() || (flags_.load(std::memory_order_consume) & Flags::dbf_inited_masterchain_ref_block); + } + BlockSeqno masterchain_ref_block() const override { + CHECK(inited_masterchain_ref_block()); + return id_.is_masterchain() ? id_.seqno() : masterchain_ref_seqno_; + } std::vector prev() const override { if (is_zero()) { return {}; @@ -389,23 +398,17 @@ struct BlockHandleImpl : public BlockHandleInterface { flags_ |= Flags::dbf_received; unlock(); } - void set_moved_to_storage() override { - if (flags_.load(std::memory_order_consume) & Flags::dbf_moved) { - return; - } - lock(); - flags_ |= Flags::dbf_moved; - unlock(); - } void set_moved_to_archive() override { if (flags_.load(std::memory_order_consume) & Flags::dbf_moved_new) { return; } lock(); flags_ |= Flags::dbf_moved_new; - flags_ &= ~Flags::dbf_moved; unlock(); } + void set_handle_moved_to_archive() override { + flags_ |= Flags::dbf_moved_handle; + } void set_deleted() override { if (flags_.load(std::memory_order_consume) & Flags::dbf_deleted) { return; @@ -485,6 +488,14 @@ struct BlockHandleImpl : public BlockHandleInterface { unlock(); } } + void set_masterchain_ref_block(BlockSeqno seqno) override { + if (!inited_masterchain_ref_block()) { + lock(); + masterchain_ref_seqno_ = seqno; + flags_ |= Flags::dbf_inited_masterchain_ref_block; + unlock(); + } + } void unsafe_clear_applied() override { if (is_applied()) { diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp new file mode 100644 index 0000000000..72d56e56ee --- /dev/null +++ b/validator/db/archive-manager.cpp @@ -0,0 +1,986 @@ +#include "archive-manager.hpp" +#include "td/actor/MultiPromise.h" +#include "td/utils/overloaded.h" +#include "files-async.hpp" +#include "td/db/RocksDb.h" +#include "common/delay.h" + +namespace ton { + +namespace validator { + +std::string PackageId::path() const { + if (temp) { + return "files/packages/"; + } else if (key) { + char s[24]; + sprintf(s, "key%03d", id / 1000000); + return PSTRING() << "archive/packages/" << s << "/"; + } else { + char s[20]; + sprintf(s, "arch%04d", id / 100000); + return PSTRING() << "archive/packages/" << s << "/"; + } +} + +std::string PackageId::name() const { + if (temp) { + return PSTRING() << "temp.archive." << id; + } else if (key) { + char s[20]; + sprintf(s, "%06d", id); + return PSTRING() << "key.archive." << s; + } else { + char s[10]; + sprintf(s, "%05d", id); + return PSTRING() << "archive." << s; + } +} + +ArchiveManager::ArchiveManager(td::actor::ActorId root, std::string db_root) : db_root_(db_root) { +} + +void ArchiveManager::add_handle(BlockHandle handle, td::Promise promise) { + if (handle->handle_moved_to_archive()) { + update_handle(std::move(handle), std::move(promise)); + return; + } + auto p = get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(), + handle->unix_time(), handle->logical_time(), + handle->inited_is_key_block() && handle->is_key_block()); + auto f = get_file_desc(handle->id().shard_full(), p, handle->id().seqno(), handle->unix_time(), + handle->logical_time(), true); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_handle, std::move(handle), std::move(promise)); +} + +void ArchiveManager::update_handle(BlockHandle handle, td::Promise promise) { + FileDescription *f; + if (handle->handle_moved_to_archive()) { + CHECK(handle->inited_unix_time()); + f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), handle->id().seqno(), + handle->unix_time(), handle->logical_time(), true); + } else { + f = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true); + } + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::update_handle, std::move(handle), std::move(promise)); +} + +void ArchiveManager::add_file(BlockHandle handle, FileReference ref_id, td::BufferSlice data, + td::Promise promise) { + bool copy_to_key = false; + if (handle->inited_is_key_block() && handle->is_key_block() && handle->inited_unix_time() && + handle->inited_logical_time() && handle->inited_masterchain_ref_block()) { + copy_to_key = (ref_id.ref().get_offset() == ref_id.ref().offset()) || + (ref_id.ref().get_offset() == ref_id.ref().offset()); + } + if (!handle->handle_moved_to_archive()) { + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(promise)); + auto f1 = get_file_desc(handle->id().shard_full(), get_temp_package_id(), 0, 0, 0, true); + td::actor::send_closure(f1->file_actor_id(), &ArchiveSlice::add_file, std::move(ref_id), data.clone(), + ig.get_promise()); + if (copy_to_key) { + auto f2 = get_file_desc(handle->id().shard_full(), get_key_package_id(handle->masterchain_ref_block()), + handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); + td::actor::send_closure(f2->file_actor_id(), &ArchiveSlice::add_file, ref_id, std::move(data), ig.get_promise()); + } + return; + } + + CHECK(handle->inited_is_key_block()); + + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(promise)); + auto f1 = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), + handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); + td::actor::send_closure(f1->file_actor_id(), &ArchiveSlice::add_file, ref_id, data.clone(), ig.get_promise()); + if (copy_to_key) { + auto f2 = get_file_desc(handle->id().shard_full(), get_key_package_id(handle->masterchain_ref_block()), + handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); + td::actor::send_closure(f2->file_actor_id(), &ArchiveSlice::add_file, ref_id, std::move(data), ig.get_promise()); + } +} + +void ArchiveManager::add_key_block_proof(UnixTime ts, BlockSeqno seqno, LogicalTime lt, FileReference ref_id, + td::BufferSlice data, td::Promise promise) { + auto f = get_file_desc(ShardIdFull{masterchainId}, get_key_package_id(seqno), seqno, ts, lt, true); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_file, std::move(ref_id), std::move(data), + std::move(promise)); +} + +void ArchiveManager::add_temp_file_short(FileReference ref_id, td::BufferSlice data, td::Promise promise) { + auto f = get_file_desc(ref_id.shard(), get_temp_package_id(), 0, 0, 0, true); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_file, std::move(ref_id), std::move(data), + std::move(promise)); +} + +void ArchiveManager::get_handle(BlockIdExt block_id, td::Promise promise) { + auto f = get_file_desc_by_seqno(block_id.shard_full(), block_id.seqno(), false); + if (f) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block_id, idx = get_max_temp_file_desc_idx(), + promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok()) { + promise.set_value(R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &ArchiveManager::get_handle_cont, block_id, idx, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_handle, block_id, std::move(P)); + } else { + get_handle_cont(block_id, get_max_temp_file_desc_idx(), std::move(promise)); + } +} + +void ArchiveManager::get_handle_cont(BlockIdExt block_id, PackageId idx, td::Promise promise) { + if (idx.is_empty()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "block handle not in db")); + return; + } + auto f = get_temp_file_desc_by_idx(idx); + if (!f) { + promise.set_error(td::Status::Error(ErrorCode::notready, "block handle not in db")); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), block_id, idx = get_prev_temp_file_desc_idx(idx), + promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &ArchiveManager::get_handle_finish, R.move_as_ok(), std::move(promise)); + } else { + td::actor::send_closure(SelfId, &ArchiveManager::get_handle_cont, block_id, idx, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_handle, block_id, std::move(P)); +} + +void ArchiveManager::get_handle_finish(BlockHandle handle, td::Promise promise) { + auto f = get_file_desc_by_seqno(handle->id().shard_full(), handle->id().seqno(), false); + if (!f) { + promise.set_value(std::move(handle)); + return; + } + auto P = td::PromiseCreator::lambda([handle, promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok()) { + promise.set_value(R.move_as_ok()); + } else { + promise.set_value(std::move(handle)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_handle, handle->id(), std::move(P)); +} + +void ArchiveManager::get_file_short(FileReference ref_id, td::Promise promise) { + bool search_in_key = false; + BlockIdExt block_id; + ref_id.ref().visit(td::overloaded( + [&](const fileref::Proof &p) { + search_in_key = p.block_id.is_masterchain(); + block_id = p.block_id; + }, + [&](const fileref::ProofLink &p) { + search_in_key = p.block_id.is_masterchain(); + block_id = p.block_id; + }, + [&](const auto &p) {})); + if (search_in_key) { + auto f = get_file_desc_by_seqno(block_id.shard_full(), block_id.seqno(), true); + if (f) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, + promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok()) { + promise.set_value(R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &ArchiveManager::get_temp_file_short, std::move(ref_id), std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, ref_id, std::move(P)); + return; + } + } + get_temp_file_short(std::move(ref_id), std::move(promise)); +} + +void ArchiveManager::get_key_block_proof(FileReference ref_id, td::Promise promise) { + bool search_in_key = false; + BlockIdExt block_id; + ref_id.ref().visit(td::overloaded( + [&](const fileref::Proof &p) { + search_in_key = p.block_id.is_masterchain(); + block_id = p.block_id; + }, + [&](const fileref::ProofLink &p) { + search_in_key = p.block_id.is_masterchain(); + block_id = p.block_id; + }, + [&](const auto &p) {})); + if (search_in_key) { + auto f = get_file_desc_by_seqno(block_id.shard_full(), block_id.seqno(), true); + if (f) { + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, ref_id, std::move(promise)); + } else { + promise.set_error(td::Status::Error(ErrorCode::notready, "key proof not in db")); + } + } else { + promise.set_error( + td::Status::Error(ErrorCode::protoviolation, "only proof/prooflink supported in get_key_block_proof")); + } +} + +void ArchiveManager::get_temp_file_short(FileReference ref_id, td::Promise promise) { + get_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise)); +} + +void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise promise) { + auto f = get_temp_file_desc_by_idx(idx); + if (!f) { + promise.set_error(td::Status::Error(ErrorCode::notready, "file not in db")); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, idx = get_prev_temp_file_desc_idx(idx), + promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok()) { + promise.set_value(R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &ArchiveManager::get_file_short_cont, std::move(ref_id), idx, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(P)); +} + +void ArchiveManager::get_file(BlockHandle handle, FileReference ref_id, td::Promise promise) { + if (handle->moved_to_archive()) { + auto f = get_file_desc(handle->id().shard_full(), PackageId{handle->masterchain_ref_block(), false, false}, 0, 0, 0, + false); + if (f) { + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(promise)); + return; + } + } + get_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise)); +} + +void ArchiveManager::written_perm_state(FileReferenceShort id) { + perm_states_.emplace(id.hash(), id); +} + +void ArchiveManager::add_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise promise) { + auto id = FileReference{fileref::ZeroState{block_id}}; + auto hash = id.hash(); + if (perm_states_.find(hash) != perm_states_.end()) { + promise.set_value(td::Unit()); + return; + } + + auto path = db_root_ + "/archive/states/" + id.filename_short(); + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), id = id.shortref(), promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveManager::written_perm_state, id); + promise.set_value(td::Unit()); + } + }); + td::actor::create_actor("writefile", db_root_ + "/archive/tmp/", path, std::move(data), std::move(P)) + .release(); +} + +void ArchiveManager::add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data, + td::Promise promise) { + auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}}; + auto hash = id.hash(); + if (perm_states_.find(hash) != perm_states_.end()) { + promise.set_value(td::Unit()); + return; + } + + auto path = db_root_ + "/archive/states/" + id.filename_short(); + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), id = id.shortref(), promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveManager::written_perm_state, id); + promise.set_value(td::Unit()); + } + }); + td::actor::create_actor("writefile", db_root_ + "/archive/tmp/", path, std::move(data), std::move(P)) + .release(); +} + +void ArchiveManager::get_zero_state(BlockIdExt block_id, td::Promise promise) { + auto id = FileReference{fileref::ZeroState{block_id}}; + auto hash = id.hash(); + if (perm_states_.find(hash) == perm_states_.end()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "zerostate not in db")); + return; + } + + auto path = db_root_ + "/archive/states/" + id.filename_short(); + td::actor::create_actor("readfile", path, 0, -1, 0, std::move(promise)).release(); +} + +void ArchiveManager::check_zero_state(BlockIdExt block_id, td::Promise promise) { + auto id = FileReference{fileref::ZeroState{block_id}}; + auto hash = id.hash(); + if (perm_states_.find(hash) == perm_states_.end()) { + promise.set_result(false); + return; + } + promise.set_result(true); +} + +void ArchiveManager::get_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, + td::Promise promise) { + auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}}; + auto hash = id.hash(); + if (perm_states_.find(hash) == perm_states_.end()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "state file not in db")); + return; + } + + auto path = db_root_ + "/archive/states/" + id.filename_short(); + td::actor::create_actor("readfile", path, 0, -1, 0, std::move(promise)).release(); +} + +void ArchiveManager::get_persistent_state_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset, + td::int64 max_size, td::Promise promise) { + auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}}; + auto hash = id.hash(); + if (perm_states_.find(hash) == perm_states_.end()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "state file not in db")); + return; + } + + auto path = db_root_ + "/archive/states/" + id.filename_short(); + td::actor::create_actor("readfile", path, offset, max_size, 0, std::move(promise)).release(); +} + +void ArchiveManager::check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, + td::Promise promise) { + auto id = FileReference{fileref::PersistentState{block_id, masterchain_block_id}}; + auto hash = id.hash(); + if (perm_states_.find(hash) == perm_states_.end()) { + promise.set_result(false); + return; + } + promise.set_result(true); +} + +void ArchiveManager::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, + td::Promise promise) { + auto f = get_file_desc_by_unix_time(account_id, ts, false); + if (f) { + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts, + std::move(promise)); + } else { + promise.set_error(td::Status::Error(ErrorCode::notready, "ts not in db")); + } +} + +void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise) { + auto f = get_file_desc_by_lt(account_id, lt, false); + if (f) { + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise)); + } else { + promise.set_error(td::Status::Error(ErrorCode::notready, "lt not in db")); + } +} + +void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, + td::Promise promise) { + auto f = get_file_desc_by_seqno(account_id, seqno, false); + if (f) { + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_seqno, account_id, seqno, + std::move(promise)); + } else { + promise.set_error(td::Status::Error(ErrorCode::notready, "seqno not in db")); + } +} + +void ArchiveManager::delete_package(PackageId id) { + auto key = create_serialize_tl_object(id.id, id.key, id.temp); + + std::string value; + auto v = index_->get(key.as_slice(), value); + v.ensure(); + CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok); + + auto R = fetch_tl_object(value, true); + R.ensure(); + auto x = R.move_as_ok(); + + if (x->deleted_) { + return; + } + + auto &m = get_file_map(id); + auto it = m.find(id); + if (it == m.end() || it->second.deleted) { + return; + } + + it->second.deleted = true; + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id); + }); + td::actor::send_closure(it->second.file_actor_id(), &ArchiveSlice::destroy, std::move(P)); +} + +void ArchiveManager::deleted_package(PackageId id) { + auto key = create_serialize_tl_object(id.id, id.key, id.temp); + + std::string value; + auto v = index_->get(key.as_slice(), value); + v.ensure(); + CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok); + + auto R = fetch_tl_object(value, true); + R.ensure(); + auto x = R.move_as_ok(); + + if (x->deleted_) { + return; + } + x->deleted_ = true; + index_->begin_transaction().ensure(); + index_->set(key, serialize_tl_object(x, true)).ensure(); + index_->commit_transaction().ensure(); + + auto &m = get_file_map(id); + auto it = m.find(id); + CHECK(it != m.end()); + CHECK(it->second.deleted); + it->second.clear_actor_id(); +} + +void ArchiveManager::load_package(PackageId id) { + auto key = create_serialize_tl_object(id.id, id.key, id.temp); + + std::string value; + auto v = index_->get(key.as_slice(), value); + v.ensure(); + CHECK(v.move_as_ok() == td::KeyValue::GetStatus::Ok); + + auto R = fetch_tl_object(value, true); + R.ensure(); + auto x = R.move_as_ok(); + + if (x->deleted_) { + return; + } + + FileDescription desc{id, false}; + if (!id.temp) { + for (auto &e : x->firstblocks_) { + desc.first_blocks[ShardIdFull{e->workchain_, static_cast(e->shard_)}] = FileDescription::Desc{ + static_cast(e->seqno_), static_cast(e->unixtime_), static_cast(e->lt_)}; + } + } + + std::string prefix = PSTRING() << db_root_ << id.path() << id.name(); + desc.file = td::actor::create_actor("slice", id.key, id.temp, prefix); + + get_file_map(id).emplace(id, std::move(desc)); +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, + UnixTime ts, LogicalTime lt, bool force) { + auto &f = get_file_map(id); + auto it = f.find(id); + if (it != f.end()) { + if (it->second.deleted) { + CHECK(!force); + return nullptr; + } + if (force && !id.temp) { + update_desc(it->second, shard, seqno, ts, lt); + } + return &it->second; + } + if (!force) { + return nullptr; + } + + return add_file_desc(shard, id, seqno, ts, lt); +} + +ArchiveManager::FileDescription *ArchiveManager::add_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, + UnixTime ts, LogicalTime lt) { + auto &f = get_file_map(id); + CHECK(f.count(id) == 0); + + FileDescription desc{id, false}; + td::mkdir(db_root_ + id.path()).ensure(); + std::string prefix = PSTRING() << db_root_ << id.path() << id.name(); + desc.file = td::actor::create_actor("slice", id.key, id.temp, prefix); + if (!id.temp) { + update_desc(desc, shard, seqno, ts, lt); + } + + std::vector> vec; + for (auto &e : desc.first_blocks) { + vec.push_back(create_tl_object(e.first.workchain, e.first.shard, + e.second.seqno, e.second.ts, e.second.lt)); + } + + index_->begin_transaction().ensure(); + // add package info to list of packages + { + std::vector t; + std::vector tk; + std::vector tt; + for (auto &e : files_) { + t.push_back(e.first.id); + } + for (auto &e : key_files_) { + tk.push_back(e.first.id); + } + for (auto &e : temp_files_) { + tt.push_back(e.first.id); + } + (id.temp ? tt : (id.key ? tk : t)).push_back(id.id); + index_ + ->set(create_serialize_tl_object().as_slice(), + create_serialize_tl_object(std::move(t), std::move(tk), std::move(tt)) + .as_slice()) + .ensure(); + } + // add package info key + { + index_ + ->set(create_serialize_tl_object(id.id, id.key, id.temp).as_slice(), + create_serialize_tl_object(id.id, id.key, id.temp, std::move(vec), false) + .as_slice()) + .ensure(); + } + index_->commit_transaction().ensure(); + + return &f.emplace(id, std::move(desc)).first->second; +} + +void ArchiveManager::update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, UnixTime ts, + LogicalTime lt) { + auto it = desc.first_blocks.find(shard); + if (it != desc.first_blocks.end() && it->second.seqno <= seqno) { + return; + } + desc.first_blocks[shard] = FileDescription::Desc{seqno, ts, lt}; + std::vector> vec; + for (auto &e : desc.first_blocks) { + vec.push_back(create_tl_object(e.first.workchain, e.first.shard, + e.second.seqno, e.second.ts, e.second.lt)); + } + index_->begin_transaction().ensure(); + index_ + ->set(create_serialize_tl_object(desc.id.id, desc.id.key, desc.id.temp).as_slice(), + create_serialize_tl_object(desc.id.id, desc.id.key, desc.id.temp, + std::move(vec), false) + .as_slice()) + .ensure(); + index_->commit_transaction().ensure(); +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_seqno(ShardIdFull shard, BlockSeqno seqno, + bool key_block) { + auto &f = get_file_map(PackageId{0, key_block, false}); + for (auto it = f.rbegin(); it != f.rend(); it++) { + auto i = it->second.first_blocks.find(shard); + if (i != it->second.first_blocks.end() && i->second.seqno <= seqno) { + return &it->second; + } + } + return nullptr; +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_unix_time(ShardIdFull shard, UnixTime ts, + bool key_block) { + auto &f = get_file_map(PackageId{0, key_block, false}); + for (auto it = f.rbegin(); it != f.rend(); it++) { + auto i = it->second.first_blocks.find(shard); + if (i != it->second.first_blocks.end() && i->second.ts <= ts) { + return &it->second; + } + } + return nullptr; +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(ShardIdFull shard, LogicalTime lt, + bool key_block) { + auto &f = get_file_map(PackageId{0, key_block, false}); + for (auto it = f.rbegin(); it != f.rend(); it++) { + auto i = it->second.first_blocks.find(shard); + if (i != it->second.first_blocks.end() && i->second.lt <= lt) { + return &it->second; + } + } + return nullptr; +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, + bool key_block) { + auto &f = get_file_map(PackageId{0, key_block, false}); + if (account.is_masterchain()) { + return get_file_desc_by_seqno(ShardIdFull{masterchainId}, seqno, key_block); + } + for (auto it = f.rbegin(); it != f.rend(); it++) { + bool found = false; + for (int i = 0; i < 60; i++) { + auto shard = shard_prefix(account, i); + auto it2 = it->second.first_blocks.find(shard); + if (it2 != it->second.first_blocks.end()) { + if (it2->second.seqno <= seqno) { + return &it->second; + } + found = true; + } else if (found) { + break; + } + } + } + return nullptr; +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_unix_time(AccountIdPrefixFull account, UnixTime ts, + bool key_block) { + auto &f = get_file_map(PackageId{0, key_block, false}); + if (account.is_masterchain()) { + return get_file_desc_by_unix_time(ShardIdFull{masterchainId}, ts, key_block); + } + for (auto it = f.rbegin(); it != f.rend(); it++) { + bool found = false; + for (int i = 0; i < 60; i++) { + auto shard = shard_prefix(account, i); + auto it2 = it->second.first_blocks.find(shard); + if (it2 != it->second.first_blocks.end()) { + if (it2->second.ts <= ts) { + return &it->second; + } + found = true; + } else if (found) { + break; + } + } + } + return nullptr; +} + +ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(AccountIdPrefixFull account, LogicalTime lt, + bool key_block) { + auto &f = get_file_map(PackageId{0, key_block, false}); + if (account.is_masterchain()) { + return get_file_desc_by_lt(ShardIdFull{masterchainId}, lt, key_block); + } + for (auto it = f.rbegin(); it != f.rend(); it++) { + bool found = false; + for (int i = 0; i < 60; i++) { + auto shard = shard_prefix(account, i); + auto it2 = it->second.first_blocks.find(shard); + if (it2 != it->second.first_blocks.end()) { + if (it2->second.lt <= lt) { + return &it->second; + } + found = true; + } else if (found) { + break; + } + } + } + return nullptr; +} + +ArchiveManager::FileDescription *ArchiveManager::get_temp_file_desc_by_idx(PackageId idx) { + auto it = temp_files_.find(idx); + if (it != temp_files_.end()) { + if (it->second.deleted) { + return nullptr; + } else { + return &it->second; + } + } else { + return nullptr; + } +} + +PackageId ArchiveManager::get_max_temp_file_desc_idx() { + if (temp_files_.size() == 0) { + return PackageId::empty(false, true); + } else { + return temp_files_.rbegin()->first; + } +} + +PackageId ArchiveManager::get_prev_temp_file_desc_idx(PackageId idx) { + auto it = temp_files_.lower_bound(idx); + if (it == temp_files_.end()) { + return PackageId::empty(false, true); + } + if (it == temp_files_.begin()) { + return PackageId::empty(false, true); + } + it--; + return it->first; +} + +void ArchiveManager::start_up() { + td::mkdir(db_root_).ensure(); + td::mkdir(db_root_ + "/archive/").ensure(); + td::mkdir(db_root_ + "/archive/tmp/").ensure(); + td::mkdir(db_root_ + "/archive/packages/").ensure(); + td::mkdir(db_root_ + "/archive/states/").ensure(); + td::mkdir(db_root_ + "/files/").ensure(); + td::mkdir(db_root_ + "/files/packages/").ensure(); + index_ = std::make_shared(td::RocksDb::open(db_root_ + "/files/globalindex").move_as_ok()); + std::string value; + auto v = index_->get(create_serialize_tl_object().as_slice(), value); + v.ensure(); + if (v.move_as_ok() == td::KeyValue::GetStatus::Ok) { + auto R = fetch_tl_object(value, true); + R.ensure(); + auto x = R.move_as_ok(); + + for (auto &d : x->packages_) { + load_package(PackageId{static_cast(d), false, false}); + } + for (auto &d : x->key_packages_) { + load_package(PackageId{static_cast(d), true, false}); + } + for (auto &d : x->temp_packages_) { + load_package(PackageId{static_cast(d), false, true}); + } + } + + td::WalkPath::run(db_root_ + "/archive/states/", [&](td::CSlice fname, td::WalkPath::Type t) -> void { + if (t == td::WalkPath::Type::NotDir) { + LOG(ERROR) << "checking file " << fname; + auto pos = fname.rfind('/'); + if (pos != td::Slice::npos) { + fname.remove_prefix(pos + 1); + } + auto R = FileReferenceShort::create(fname.str()); + if (R.is_error()) { + auto R2 = FileReference::create(fname.str()); + if (R2.is_error()) { + LOG(ERROR) << "deleting bad state file '" << fname << "': " << R.move_as_error() << R2.move_as_error(); + td::unlink(db_root_ + "/archive/states/" + fname.str()).ignore(); + return; + } + auto d = R2.move_as_ok(); + auto newfname = d.filename_short(); + td::rename(db_root_ + "/archive/states/" + fname.str(), db_root_ + "/archive/states/" + newfname).ensure(); + R = FileReferenceShort::create(newfname); + R.ensure(); + } + auto f = R.move_as_ok(); + auto hash = f.hash(); + perm_states_[hash] = std::move(f); + } + }).ensure(); + + persistent_state_gc(FileHash::zero()); +} + +void ArchiveManager::run_gc(UnixTime ts) { + auto p = get_temp_package_id_by_unixtime(ts); + std::vector vec; + for (auto &x : temp_files_) { + if (x.first < p) { + vec.push_back(x.first); + } else { + break; + } + } + if (vec.size() <= 1) { + return; + } + vec.resize(vec.size() - 1, PackageId::empty(false, true)); + + for (auto &x : vec) { + delete_package(x); + } +} + +void ArchiveManager::persistent_state_gc(FileHash last) { + if (perm_states_.size() == 0) { + delay_action( + [hash = FileHash::zero(), SelfId = actor_id(this)]() { + td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, hash); + }, + td::Timestamp::in(1.0)); + return; + } + auto it = perm_states_.lower_bound(last); + if (it != perm_states_.end() && it->first == last) { + it++; + } + if (it == perm_states_.end()) { + it = perm_states_.begin(); + } + + auto &F = it->second; + auto hash = F.hash(); + + int res = 0; + BlockSeqno seqno = 0; + F.ref().visit(td::overloaded([&](const fileref::ZeroStateShort &x) { res = 1; }, + [&](const fileref::PersistentStateShort &x) { + res = 0; + seqno = x.masterchain_seqno; + }, + [&](const auto &obj) { res = -1; })); + + if (res == -1) { + td::unlink(db_root_ + "/archive/states/" + F.filename_short()).ignore(); + perm_states_.erase(it); + } + if (res != 0) { + delay_action([hash, SelfId = actor_id( + this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, hash); }, + td::Timestamp::in(1.0)); + return; + } + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveManager::got_gc_masterchain_handle, nullptr, hash); + } else { + td::actor::send_closure(SelfId, &ArchiveManager::got_gc_masterchain_handle, R.move_as_ok(), hash); + } + }); + + get_block_by_seqno(AccountIdPrefixFull{masterchainId, 0}, seqno, std::move(P)); +} + +void ArchiveManager::got_gc_masterchain_handle(BlockHandle handle, FileHash hash) { + bool to_del = false; + if (!handle || !handle->inited_unix_time() || !handle->unix_time()) { + to_del = true; + } else { + auto ttl = ValidatorManager::persistent_state_ttl(handle->unix_time()); + to_del = ttl < td::Clocks::system(); + } + auto it = perm_states_.find(hash); + CHECK(it != perm_states_.end()); + auto &F = it->second; + if (to_del) { + td::unlink(db_root_ + "/archive/states/" + F.filename_short()).ignore(); + perm_states_.erase(it); + } + delay_action([hash, SelfId = actor_id( + this)]() { td::actor::send_closure(SelfId, &ArchiveManager::persistent_state_gc, hash); }, + td::Timestamp::in(1.0)); +} + +PackageId ArchiveManager::get_temp_package_id() const { + return get_temp_package_id_by_unixtime(static_cast(td::Clocks::system())); +} + +PackageId ArchiveManager::get_temp_package_id_by_unixtime(UnixTime ts) const { + return PackageId{ts - (ts % 3600), false, true}; +} + +PackageId ArchiveManager::get_key_package_id(BlockSeqno seqno) const { + return PackageId{seqno - seqno % 200000, true, false}; +} + +PackageId ArchiveManager::get_package_id(BlockSeqno seqno) const { + auto it = files_.upper_bound(PackageId{seqno, false, false}); + CHECK(it != files_.begin()); + it--; + return it->first; +} + +PackageId ArchiveManager::get_package_id_force(BlockSeqno masterchain_seqno, ShardIdFull shard, BlockSeqno seqno, + UnixTime ts, LogicalTime lt, bool is_key) { + PackageId p = PackageId::empty(false, false); + if (!is_key) { + auto it = files_.upper_bound(PackageId{masterchain_seqno, false, false}); + p = PackageId{masterchain_seqno - (masterchain_seqno % 20000), false, false}; + if (it != files_.begin()) { + it--; + if (p < it->first) { + p = it->first; + } + } + } else { + p = PackageId{masterchain_seqno, false, false}; + } + auto it = files_.find(p); + if (it != files_.end()) { + return it->first; + } + add_file_desc(shard, p, seqno, ts, lt); + it = files_.find(p); + CHECK(it != files_.end()); + return it->first; +} + +void ArchiveManager::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { + auto F = get_file_desc_by_seqno(ShardIdFull{masterchainId}, masterchain_seqno, false); + if (!F) { + promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found")); + return; + } + + promise.set_result(F->id.id); +} + +void ArchiveManager::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) { + if (archive_id != static_cast(archive_id)) { + promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found")); + return; + } + auto F = get_file_desc(ShardIdFull{masterchainId}, PackageId{static_cast(archive_id), false, false}, 0, 0, + 0, false); + if (!F) { + promise.set_error(td::Status::Error(ErrorCode::notready, "archive not found")); + return; + } + + td::actor::send_closure(F->file_actor_id(), &ArchiveSlice::get_slice, offset, limit, std::move(promise)); +} + +void ArchiveManager::commit_transaction() { + if (!async_mode_ || huge_transaction_size_++ >= 100) { + index_->commit_transaction().ensure(); + if (async_mode_) { + huge_transaction_size_ = 0; + huge_transaction_started_ = false; + } + } +} + +void ArchiveManager::set_async_mode(bool mode, td::Promise promise) { + async_mode_ = mode; + if (!async_mode_ && huge_transaction_started_) { + index_->commit_transaction().ensure(); + huge_transaction_size_ = 0; + huge_transaction_started_ = false; + } + + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(promise)); + + for (auto &x : key_files_) { + if (!x.second.deleted) { + td::actor::send_closure(x.second.file_actor_id(), &ArchiveSlice::set_async_mode, mode, ig.get_promise()); + } + } + for (auto &x : temp_files_) { + if (!x.second.deleted) { + td::actor::send_closure(x.second.file_actor_id(), &ArchiveSlice::set_async_mode, mode, ig.get_promise()); + } + } + for (auto &x : files_) { + if (!x.second.deleted) { + td::actor::send_closure(x.second.file_actor_id(), &ArchiveSlice::set_async_mode, mode, ig.get_promise()); + } + } +} +} // namespace validator + +} // namespace ton diff --git a/validator/db/archive-manager.hpp b/validator/db/archive-manager.hpp new file mode 100644 index 0000000000..fc073b6b90 --- /dev/null +++ b/validator/db/archive-manager.hpp @@ -0,0 +1,155 @@ +#pragma once + +#include "archive-slice.hpp" + +namespace ton { + +namespace validator { + +struct PackageId { + td::uint32 id; + bool key; + bool temp; + + explicit PackageId(td::uint32 id, bool key, bool temp) : id(id), key(key), temp(temp) { + } + + bool operator<(const PackageId &with) const { + return id < with.id; + } + bool operator==(const PackageId &with) const { + return id == with.id; + } + + std::string path() const; + std::string name() const; + + bool is_empty() { + return id == std::numeric_limits::max(); + } + static PackageId empty(bool key, bool temp) { + return PackageId(std::numeric_limits::max(), key, temp); + } +}; + +class RootDb; + +class ArchiveManager : public td::actor::Actor { + public: + ArchiveManager(td::actor::ActorId root, std::string db_root); + + void add_handle(BlockHandle handle, td::Promise promise); + void update_handle(BlockHandle handle, td::Promise promise); + void add_file(BlockHandle handle, FileReference ref_id, td::BufferSlice data, td::Promise promise); + void add_key_block_proof(BlockSeqno seqno, UnixTime ts, LogicalTime lt, FileReference ref_id, td::BufferSlice data, + td::Promise promise); + void add_temp_file_short(FileReference ref_id, td::BufferSlice data, td::Promise promise); + void get_handle(BlockIdExt block_id, td::Promise promise); + void get_key_block_proof(FileReference ref_id, td::Promise promise); + void get_temp_file_short(FileReference ref_id, td::Promise promise); + void get_file_short(FileReference ref_id, td::Promise promise); + void get_file(BlockHandle handle, FileReference ref_id, td::Promise promise); + + void add_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise promise); + void add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data, + td::Promise promise); + void get_zero_state(BlockIdExt block_id, td::Promise promise); + void get_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise); + void get_persistent_state_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset, + td::int64 max_size, td::Promise promise); + void check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise); + void check_zero_state(BlockIdExt block_id, td::Promise promise); + + void run_gc(UnixTime ts); + + /* from LTDB */ + void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); + void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); + void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + + void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise); + void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise); + + void start_up() override; + + void begin_transaction(); + void commit_transaction(); + void set_async_mode(bool mode, td::Promise promise); + + private: + struct FileDescription { + struct Desc { + BlockSeqno seqno; + UnixTime ts; + LogicalTime lt; + }; + FileDescription(PackageId id, bool deleted) : id(id), deleted(deleted) { + } + auto file_actor_id() const { + return file.get(); + } + void clear_actor_id() { + file.reset(); + } + PackageId id; + bool deleted; + + std::map first_blocks; + td::actor::ActorOwn file; + }; + + std::map files_; + std::map key_files_; + std::map temp_files_; + bool async_mode_ = false; + bool huge_transaction_started_ = false; + td::uint32 huge_transaction_size_ = 0; + + auto &get_file_map(const PackageId &p) { + return p.key ? key_files_ : p.temp ? temp_files_ : files_; + } + + std::map perm_states_; + + void load_package(PackageId seqno); + void delete_package(PackageId seqno); + void deleted_package(PackageId seqno); + void get_handle_cont(BlockIdExt block_id, PackageId id, td::Promise promise); + void get_handle_finish(BlockHandle handle, td::Promise promise); + void get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise promise); + + FileDescription *get_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, UnixTime ts, LogicalTime lt, + bool force); + FileDescription *add_file_desc(ShardIdFull shard, PackageId id, BlockSeqno seqno, UnixTime ts, LogicalTime lt); + void update_desc(FileDescription &desc, ShardIdFull shard, BlockSeqno seqno, UnixTime ts, LogicalTime lt); + FileDescription *get_file_desc_by_seqno(ShardIdFull shard, BlockSeqno seqno, bool key_block); + FileDescription *get_file_desc_by_lt(ShardIdFull shard, LogicalTime lt, bool key_block); + FileDescription *get_file_desc_by_unix_time(ShardIdFull shard, UnixTime ts, bool key_block); + FileDescription *get_file_desc_by_seqno(AccountIdPrefixFull shard, BlockSeqno seqno, bool key_block); + FileDescription *get_file_desc_by_lt(AccountIdPrefixFull shard, LogicalTime lt, bool key_block); + FileDescription *get_file_desc_by_unix_time(AccountIdPrefixFull shard, UnixTime ts, bool key_block); + FileDescription *get_temp_file_desc_by_idx(PackageId idx); + PackageId get_max_temp_file_desc_idx(); + PackageId get_prev_temp_file_desc_idx(PackageId id); + + void written_perm_state(FileReferenceShort id); + + void persistent_state_gc(FileHash last); + void got_gc_masterchain_handle(BlockHandle handle, FileHash hash); + + std::string db_root_; + + std::shared_ptr index_; + + PackageId get_package_id(BlockSeqno seqno) const; + PackageId get_package_id_force(BlockSeqno masterchain_seqno, ShardIdFull shard, BlockSeqno seqno, UnixTime ts, + LogicalTime lt, bool is_key); + PackageId get_temp_package_id() const; + PackageId get_key_package_id(BlockSeqno seqno) const; + PackageId get_temp_package_id_by_unixtime(UnixTime ts) const; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/db/archive-mover.cpp b/validator/db/archive-mover.cpp new file mode 100644 index 0000000000..ec9448ab27 --- /dev/null +++ b/validator/db/archive-mover.cpp @@ -0,0 +1,475 @@ +#include "archive-mover.hpp" +#include "td/actor/MultiPromise.h" +#include "validator/fabric.h" + +namespace ton { + +namespace validator { + +void ArchiveFileMover::start_up() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_handle1, std::move(R)); + }); + td::actor::send_closure(archive_manager_, &ArchiveManager::get_handle, block_id_, std::move(P)); +} + +void ArchiveFileMover::got_block_handle0(td::Result R) { + if (R.is_ok()) { + handle_ = R.move_as_ok(); + CHECK(handle_->moved_to_archive()); + CHECK(handle_->handle_moved_to_archive()); + finish_query(); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_handle1, std::move(R)); + }); + td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read_handle, block_id_, std::move(P)); +} + +void ArchiveFileMover::got_block_handle1(td::Result R) { + if (R.is_ok()) { + handle_ = R.move_as_ok(); + got_block_handle(); + return; + } + + if (R.error().code() != ErrorCode::notready) { + abort_query(R.move_as_error()); + return; + } + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_handle1, std::move(R)); + }); + td::actor::send_closure(block_db_, &BlockDb::get_block_handle, std::move(P)); +} + +void ArchiveFileMover::got_block_handle2(td::Result R) { + if (R.is_ok()) { + handle_ = R.move_as_ok(); + got_block_handle(); + return; + } + + if (R.error().code() != ErrorCode::notready) { + abort_query(R.move_as_error()); + return; + } + + finish_query(); +} + +void ArchiveFileMover::got_block_handle() { + if (!handle_->is_applied()) { + finish_query(); + return; + } + if (handle_->id().seqno() == 0) { + processed_all_children(); + return; + } + + CHECK(handle_->inited_prev()); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveFileMover::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveFileMover::processed_child); + } + }); + + td::actor::create_actor("mover", handle_->one_prev(left_), mode_, block_db_, file_db_, + old_archive_db_, old_archive_manager_, archive_manager_, std::move(P)) + .release(); +} + +void ArchiveFileMover::processed_child() { + if (!left_ || !handle_->merge_before()) { + processed_all_children(); + return; + } + left_ = false; + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveFileMover::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveFileMover::processed_child); + } + }); + + td::actor::create_actor("mover", handle_->one_prev(left_), mode_, block_db_, file_db_, + old_archive_db_, old_archive_manager_, archive_manager_, std::move(P)) + .release(); +} + +void ArchiveFileMover::processed_all_children() { + if (!handle_->received()) { + got_block_data(td::BufferSlice{}); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_data, std::move(R)); + }); + + if (handle_->moved_to_archive()) { + CHECK(handle_->inited_unix_time()); + td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, handle_->unix_time(), + handle_->is_key_block(), FileDb::RefId{fileref::Block{handle_->id()}}, std::move(P)); + } else { + td::actor::send_closure(handle_->moved_to_storage() ? old_archive_db_ : file_db_, &FileDb::load_file, + FileDb::RefId{fileref::Block{handle_->id()}}, std::move(P)); + } + } +} + +void ArchiveFileMover::got_block_data(td::Result R) { + if (R.is_error()) { + if (R.error().code() != ErrorCode::notready) { + abort_query(R.move_as_error()); + return; + } + } else { + data_ = R.move_as_ok(); + } + if (!handle_->inited_proof()) { + got_block_proof(td::BufferSlice{}); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_proof, std::move(R)); + }); + + if (handle_->moved_to_archive()) { + CHECK(handle_->inited_unix_time()); + td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, handle_->unix_time(), + handle_->is_key_block(), FileDb::RefId{fileref::Proof{handle_->id()}}, std::move(P)); + } else { + td::actor::send_closure(handle_->moved_to_storage() ? old_archive_db_ : file_db_, &FileDb::load_file, + FileDb::RefId{fileref::Proof{handle_->id()}}, std::move(P)); + } + } +} + +void ArchiveFileMover::got_block_proof(td::Result R) { + if (R.is_error()) { + if (R.error().code() != ErrorCode::notready) { + abort_query(R.move_as_error()); + return; + } + } else { + proof_ = R.move_as_ok(); + } + if (!handle_->inited_proof_link()) { + got_block_proof_link(td::BufferSlice{}); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &ArchiveFileMover::got_block_proof_link, std::move(R)); + }); + + if (handle_->moved_to_archive()) { + CHECK(handle_->inited_unix_time()); + td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, handle_->unix_time(), + handle_->is_key_block(), FileDb::RefId{fileref::ProofLink{handle_->id()}}, std::move(P)); + } else { + td::actor::send_closure(handle_->moved_to_storage() ? old_archive_db_ : file_db_, &FileDb::load_file, + FileDb::RefId{fileref::ProofLink{handle_->id()}}, std::move(P)); + } + } +} + +void ArchiveFileMover::got_block_proof_link(td::Result R) { + if (R.is_error()) { + if (R.error().code() != ErrorCode::notready) { + abort_query(R.move_as_error()); + return; + } + } else { + proof_link_ = R.move_as_ok(); + } + + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveFileMover::written_data); + }); + + if (data_.size() > 0) { + td::actor::send_closure(archive_manager_, &ArchiveManager::add_file, handle_, fileref::Block{block_id_}, + std::move(data_), ig.get_promise()); + } + if (proof_.size() > 0) { + td::actor::send_closure(archive_manager_, &ArchiveManager::add_file, handle_, fileref::Proof{block_id_}, + std::move(proof_), ig.get_promise()); + } + if (proof_link_.size() > 0) { + td::actor::send_closure(archive_manager_, &ArchiveManager::add_file, handle_, fileref::ProofLink{block_id_}, + std::move(proof_link_), ig.get_promise()); + } +} + +void ArchiveFileMover::written_data() { + handle_->set_moved_to_archive(); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveFileMover::written_handle); + }); + td::actor::send_closure(archive_manager_, &ArchiveManager::add_handle, handle_, std::move(P)); +} + +void ArchiveFileMover::written_handle() { + CHECK(handle_->handle_moved_to_archive()); + finish_query(); +} + +void ArchiveFileMover::abort_query(td::Status error) { + if (promise_) { + promise_.set_error(std::move(error)); + } + stop(); +} + +void ArchiveFileMover::finish_query() { + if (promise_) { + promise_.set_value(td::Unit()); + } + stop(); +} + +void ArchiveKeyBlockMover::start_up() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::skip_block_proof, R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof0); + } + }); + if (proof_link_) { + td::actor::send_closure(archive_manager_, &ArchiveManager::get_file_short, fileref::ProofLink{block_id_}, + std::move(P)); + } else { + td::actor::send_closure(archive_manager_, &ArchiveManager::get_file_short, fileref::Proof{block_id_}, std::move(P)); + } +} + +void ArchiveKeyBlockMover::failed_to_get_proof0() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::got_block_proof, R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof1); + } + }); + if (proof_link_) { + td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, fileref::ProofLink{block_id_}, + std::move(P)); + } else { + td::actor::send_closure(old_archive_manager_, &OldArchiveManager::read, fileref::Proof{block_id_}, std::move(P)); + } +} + +void ArchiveKeyBlockMover::failed_to_get_proof1() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::got_block_proof, R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof2); + } + }); + if (proof_link_) { + td::actor::send_closure(old_archive_db_, &FileDb::load_file, fileref::ProofLink{block_id_}, std::move(P)); + } else { + td::actor::send_closure(old_archive_db_, &FileDb::load_file, fileref::Proof{block_id_}, std::move(P)); + } +} + +void ArchiveKeyBlockMover::failed_to_get_proof2() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_ok()) { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::got_block_proof, R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::failed_to_get_proof3); + } + }); + if (proof_link_) { + td::actor::send_closure(file_db_, &FileDb::load_file, fileref::ProofLink{block_id_}, std::move(P)); + } else { + td::actor::send_closure(file_db_, &FileDb::load_file, fileref::Proof{block_id_}, std::move(P)); + } +} + +void ArchiveKeyBlockMover::failed_to_get_proof3() { + if (proof_link_) { + written_data(); + } else { + proof_link_ = true; + start_up(); + } +} + +void ArchiveKeyBlockMover::got_block_proof(td::BufferSlice data) { + data_ = std::move(data); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveKeyBlockMover::written_data); + }); + + if (proof_link_) { + auto p = create_proof_link(block_id_, data_.clone()).move_as_ok(); + auto h = p->get_basic_header_info().move_as_ok(); + td::actor::send_closure(archive_manager_, &ArchiveManager::add_key_block_proof, h.utime, + fileref::ProofLink{block_id_}, std::move(data_), std::move(P)); + } else { + auto p = create_proof(block_id_, data_.clone()).move_as_ok(); + auto h = p->get_basic_header_info().move_as_ok(); + td::actor::send_closure(archive_manager_, &ArchiveManager::add_key_block_proof, h.utime, fileref::Proof{block_id_}, + std::move(data_), std::move(P)); + } +} + +void ArchiveKeyBlockMover::skip_block_proof(td::BufferSlice data) { + data_ = std::move(data); + written_data(); +} + +void ArchiveKeyBlockMover::written_data() { + td::Ref proof_link; + if (proof_link_) { + auto p = create_proof_link(block_id_, data_.clone()).move_as_ok(); + proof_link = std::move(p); + } else { + auto p = create_proof(block_id_, data_.clone()).move_as_ok(); + proof_link = std::move(p); + } + auto ts = proof_link->get_basic_header_info().move_as_ok().utime; + auto te = ValidatorManager::persistent_state_ttl(ts); + if (te < td::Clocks::system()) { + finish_query(); + return; + } +} + +void ArchiveKeyBlockMover::abort_query(td::Status error) { + if (promise_) { + promise_.set_error(std::move(error)); + } + stop(); +} + +void ArchiveKeyBlockMover::finish_query() { + if (promise_) { + promise_.set_value(td::Unit()); + } + stop(); +} + +void ArchiveMover::start_up() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveMover::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveMover::moved_blocks); + } + }); + td::actor::create_actor("fmover", masterchain_block_id_, block_db_.get(), file_db_.get(), + old_archive_db_.get(), old_archive_manager_.get(), archive_manager_.get(), + std::move(P)) + .release(); +} + +void ArchiveMover::moved_blocks() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveMover::got_handle, R.move_as_ok()); + }); + td::actor::send_closure(archive_manager_, &ArchiveManager::get_handle, masterchain_block_id_, std::move(P)); +} + +void ArchiveMover::got_handle(BlockHandle handle) { + handle_ = std::move(handle); + CHECK(handle_->is_applied()); + CHECK(handle_->inited_state_boc()); + CHECK(!handle_->deleted_state_boc()); + auto P = td::PromiseCreator::lambda( + [handle = handle_, SelfId = actor_id(this)](td::Result> R) mutable { + R.ensure(); + auto S = create_shard_state(handle->id(), R.move_as_ok()); + S.ensure(); + td::actor::send_closure(SelfId, &ArchiveMover::got_state, td::Ref{S.move_as_ok()}); + }); + td::actor::send_closure(cell_db_, &CellDb::load_cell, handle_->state(), std::move(P)); +} + +void ArchiveMover::got_state(td::Ref state) { + state_ = std::move(state); + + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveMover::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveMover::moved_key_blocks); + } + }); + + auto k = state_->prev_key_block_id(std::numeric_limits::max()); + while (k.is_valid() && k.seqno() > 0) { + td::actor::create_actor("keymover", k, block_db_.get(), file_db_.get(), old_archive_db_.get(), + old_archive_manager_.get(), archive_manager_.get(), ig.get_promise()) + .release(); + k = state_->prev_key_block_id(k.seqno()); + } +} + +void ArchiveMover::moved_key_blocks() { + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveMover::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveMover::moved_key_blocks); + } + }); + + auto k = state_->prev_key_block_id(std::numeric_limits::max()); + while (k.is_valid() && k.seqno() > 0) { + td::actor::create_actor("keymover", k, block_db_.get(), file_db_.get(), old_archive_db_.get(), + old_archive_manager_.get(), archive_manager_.get(), ig.get_promise()) + .release(); + k = state_->prev_key_block_id(k.seqno()); + } +} + +void ArchiveMover::run() { + if (to_move_.empty() && to_check_.empty()) { + completed(); + return; + } + if (!to_check_.empty()) { + auto B = to_check_.back(); + CHECK(to_check_set_.count(B) == 1); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(SelfId, &ArchiveMover::got_to_check_handle, std::move(R)); + }); + td::actor::send_closure(block_db_, &BlockDb::get_block_handle, B, std::move(P)); + return; + } + CHECK(!to_move_.empty()); +} + +void ArchiveMover::got_to_check_handle(td::Result R) { + if (R.is_error()) { + CHECK(R.error().code() == ErrorCode::notready); + run(); + return; + } + auto handle = R.move_as_ok(); +} + +} // namespace validator + +} // namespace ton diff --git a/validator/db/archive-mover.hpp b/validator/db/archive-mover.hpp new file mode 100644 index 0000000000..f81ee03a37 --- /dev/null +++ b/validator/db/archive-mover.hpp @@ -0,0 +1,148 @@ +#pragma once + +#include "td/actor/actor.h" +#include "filedb.hpp" +#include "blockdb.hpp" +#include "statedb.hpp" +#include "celldb.hpp" +#include "archive-db.hpp" +#include "archive-manager.hpp" + +#include +#include + +namespace ton { + +namespace validator { + +class ArchiveFileMover : public td::actor::Actor { + public: + ArchiveFileMover(BlockIdExt block_id, td::actor::ActorId block_db, td::actor::ActorId file_db, + td::actor::ActorId old_archive_db, td::actor::ActorId old_archive_manager, + td::actor::ActorId archive_manager, td::Promise promise) + : block_id_(block_id) + , block_db_(block_db) + , file_db_(file_db) + , old_archive_db_(old_archive_db) + , old_archive_manager_(old_archive_manager) + , archive_manager_(archive_manager) + , promise_(std::move(promise)) { + } + void start_up() override; + void got_block_handle0(td::Result R); + void got_block_handle1(td::Result R); + void got_block_handle2(td::Result R); + void got_block_handle(); + + void processed_child(); + void processed_all_children(); + + void got_block_data(td::Result R); + void got_block_proof(td::Result R); + void got_block_proof_link(td::Result R); + + void written_data(); + void written_handle(); + + void abort_query(td::Status error); + void finish_query(); + + private: + BlockIdExt block_id_; + BlockHandle handle_; + td::BufferSlice data_; + td::BufferSlice proof_; + td::BufferSlice proof_link_; + bool left_ = true; + + td::actor::ActorId block_db_; + td::actor::ActorId file_db_; + td::actor::ActorId old_archive_db_; + td::actor::ActorId old_archive_manager_; + td::actor::ActorId archive_manager_; + + td::Promise promise_; +}; + +class ArchiveKeyBlockMover : public td::actor::Actor { + public: + ArchiveKeyBlockMover(BlockIdExt block_id, td::actor::ActorId block_db, td::actor::ActorId file_db, + td::actor::ActorId old_archive_db, + td::actor::ActorId old_archive_manager, + td::actor::ActorId archive_manager, td::Promise promise) + : block_id_(block_id) + , block_db_(block_db) + , file_db_(file_db) + , old_archive_db_(old_archive_db) + , old_archive_manager_(old_archive_manager) + , archive_manager_(archive_manager) + , promise_(std::move(promise)) { + } + + void start_up() override; + void failed_to_get_proof0(); + void failed_to_get_proof1(); + void failed_to_get_proof2(); + void failed_to_get_proof3(); + void got_block_proof(td::BufferSlice data); + void skip_block_proof(td::BufferSlice data); + + void written_data(); + + void abort_query(td::Status error); + void finish_query(); + + private: + BlockIdExt block_id_; + td::BufferSlice data_; + bool proof_link_ = false; + + td::actor::ActorId block_db_; + td::actor::ActorId file_db_; + td::actor::ActorId old_archive_db_; + td::actor::ActorId old_archive_manager_; + td::actor::ActorId archive_manager_; + + td::Promise promise_; +}; + +class ArchiveMover : public td::actor::Actor { + public: + ArchiveMover(std::string db_root, BlockIdExt masterchain_block_id, BlockIdExt shard_block_id, + BlockIdExt key_block_id); + + void start_up() override; + void moved_blocks(); + void got_handle(BlockHandle handle); + void got_state(td::Ref state); + void moved_key_blocks(); + void run(); + void completed(); + void add_to_move(BlockIdExt block_id); + void add_to_check(BlockIdExt block_id); + + void got_to_check_handle(td::Result R); + + void abort_query(td::Status error); + void finish_query(); + + private: + std::string db_root_; + BlockHandle handle_; + td::Ref state_; + + td::actor::ActorOwn block_db_; + td::actor::ActorOwn file_db_; + td::actor::ActorOwn old_archive_db_; + td::actor::ActorOwn old_archive_manager_; + td::actor::ActorOwn archive_manager_; + td::actor::ActorOwn cell_db_; + + BlockIdExt masterchain_block_id_; + BlockIdExt shard_block_id_; + BlockIdExt key_block_id_; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/db/archive-slice.cpp b/validator/db/archive-slice.cpp new file mode 100644 index 0000000000..b623415a21 --- /dev/null +++ b/validator/db/archive-slice.cpp @@ -0,0 +1,470 @@ +#include "archive-slice.hpp" + +#include "td/actor/MultiPromise.h" +#include "validator/fabric.h" +#include "td/db/RocksDb.h" +#include "ton/ton-io.hpp" +#include "td/utils/port/path.h" +#include "common/delay.h" +#include "files-async.hpp" + +namespace ton { + +namespace validator { + +void PackageWriter::append(std::string filename, td::BufferSlice data, + td::Promise> promise) { + auto offset = package_->append(std::move(filename), std::move(data), !async_mode_); + auto size = package_->size(); + + promise.set_value(std::pair{offset, size}); +} + +class PackageReader : public td::actor::Actor { + public: + PackageReader(std::shared_ptr package, td::uint64 offset, + td::Promise> promise) + : package_(std::move(package)), offset_(offset), promise_(std::move(promise)) { + } + void start_up() { + promise_.set_result(package_->read(offset_)); + } + + private: + std::shared_ptr package_; + td::uint64 offset_; + td::Promise> promise_; +}; + +void ArchiveSlice::add_handle(BlockHandle handle, td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + if (handle->id().seqno() == 0) { + update_handle(std::move(handle), std::move(promise)); + return; + } + CHECK(!key_blocks_only_); + CHECK(!temp_); + CHECK(handle->inited_unix_time()); + CHECK(handle->inited_logical_time()); + + auto key = get_db_key_lt_desc(handle->id().shard_full()); + + std::string value; + auto R = kv_->get(key.as_slice(), value); + R.ensure(); + tl_object_ptr v; + bool add_shard = false; + if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) { + auto F = fetch_tl_object(td::BufferSlice{value}, true); + F.ensure(); + v = F.move_as_ok(); + } else { + v = create_tl_object(1, 1, 0, 0, 0); + add_shard = true; + } + if (handle->id().seqno() <= static_cast(v->last_seqno_) || + handle->logical_time() <= static_cast(v->last_lt_) || + handle->unix_time() <= static_cast(v->last_ts_)) { + update_handle(std::move(handle), std::move(promise)); + return; + } + auto db_value = create_serialize_tl_object(create_tl_block_id(handle->id()), + handle->logical_time(), handle->unix_time()); + auto db_key = get_db_key_lt_el(handle->id().shard_full(), v->last_idx_++); + auto status_key = create_serialize_tl_object(); + v->last_seqno_ = handle->id().seqno(); + v->last_lt_ = handle->logical_time(); + v->last_ts_ = handle->unix_time(); + + td::uint32 idx = 0; + if (add_shard) { + auto G = kv_->get(status_key.as_slice(), value); + G.ensure(); + if (G.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + idx = 0; + } else { + auto F = fetch_tl_object(value, true); + F.ensure(); + auto f = F.move_as_ok(); + idx = f->total_shards_; + } + } + + auto version = handle->version(); + + begin_transaction(); + kv_->set(key, serialize_tl_object(v, true)).ensure(); + kv_->set(db_key, db_value.as_slice()).ensure(); + if (add_shard) { + auto shard_key = create_serialize_tl_object(idx); + auto shard_value = + create_serialize_tl_object(handle->id().id.workchain, handle->id().id.shard); + kv_->set(status_key.as_slice(), create_serialize_tl_object(idx + 1)).ensure(); + kv_->set(shard_key.as_slice(), shard_value.as_slice()).ensure(); + } + kv_->set(get_db_key_block_info(handle->id()), handle->serialize().as_slice()).ensure(); + commit_transaction(); + + handle->flushed_upto(version); + handle->set_handle_moved_to_archive(); + + if (handle->need_flush()) { + update_handle(std::move(handle), std::move(promise)); + } else { + promise.set_value(td::Unit()); + } +} + +void ArchiveSlice::update_handle(BlockHandle handle, td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + if (!handle->need_flush() && (temp_ || handle->handle_moved_to_archive())) { + promise.set_value(td::Unit()); + return; + } + CHECK(!key_blocks_only_); + + begin_transaction(); + do { + auto version = handle->version(); + kv_->set(get_db_key_block_info(handle->id()), handle->serialize().as_slice()).ensure(); + handle->flushed_upto(version); + } while (handle->need_flush()); + commit_transaction(); + if (!temp_) { + handle->set_handle_moved_to_archive(); + } + + promise.set_value(td::Unit()); +} + +void ArchiveSlice::add_file(FileReference ref_id, td::BufferSlice data, td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + std::string value; + auto R = kv_->get(ref_id.hash().to_hex(), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) { + promise.set_value(td::Unit()); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_id, promise = std::move(promise)]( + td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + return; + } + auto v = R.move_as_ok(); + td::actor::send_closure(SelfId, &ArchiveSlice::add_file_cont, std::move(ref_id), v.first, v.second, + std::move(promise)); + }); + + td::actor::send_closure(writer_, &PackageWriter::append, ref_id.filename(), std::move(data), std::move(P)); +} + +void ArchiveSlice::add_file_cont(FileReference ref_id, td::uint64 offset, td::uint64 size, + td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + begin_transaction(); + kv_->set("status", td::to_string(size)).ensure(); + kv_->set(ref_id.hash().to_hex(), td::to_string(offset)).ensure(); + commit_transaction(); + promise.set_value(td::Unit()); +} + +void ArchiveSlice::get_handle(BlockIdExt block_id, td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + CHECK(!key_blocks_only_); + std::string value; + auto R = kv_->get(get_db_key_block_info(block_id), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_error(td::Status::Error(ErrorCode::notready, "handle not in archive slice")); + return; + } + auto E = create_block_handle(td::BufferSlice{value}); + E.ensure(); + auto handle = E.move_as_ok(); + if (!temp_) { + handle->set_handle_moved_to_archive(); + } + promise.set_value(std::move(handle)); +} + +void ArchiveSlice::get_file(FileReference ref_id, td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + std::string value; + auto R = kv_->get(ref_id.hash().to_hex(), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_error(td::Status::Error(ErrorCode::notready, "file not in archive slice")); + return; + } + auto offset = td::to_integer(value); + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + promise.set_value(std::move(R.move_as_ok().second)); + } + }); + td::actor::create_actor("reader", package_, offset, std::move(P)).release(); +} + +void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, + std::function compare_desc, + std::function compare, bool exact, + td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + bool f = false; + BlockIdExt block_id; + td::uint32 ls = 0; + for (td::uint32 len = 0; len <= 60; len++) { + auto s = shard_prefix(account_id, len); + auto key = get_db_key_lt_desc(s); + std::string value; + auto F = kv_->get(key, value); + F.ensure(); + if (F.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + if (!f) { + continue; + } else { + break; + } + } + f = true; + auto G = fetch_tl_object(td::BufferSlice{value}, true); + G.ensure(); + auto g = G.move_as_ok(); + if (compare_desc(*g.get()) > 0) { + continue; + } + td::uint32 l = g->first_idx_ - 1; + BlockIdExt lseq; + td::uint32 r = g->last_idx_; + BlockIdExt rseq; + while (r - l > 1) { + auto x = (r + l) / 2; + auto db_key = get_db_key_lt_el(s, x); + F = kv_->get(db_key, value); + F.ensure(); + CHECK(F.move_as_ok() == td::KeyValue::GetStatus::Ok); + auto E = fetch_tl_object(td::BufferSlice{value}, true); + E.ensure(); + auto e = E.move_as_ok(); + int cmp_val = compare(*e.get()); + + if (cmp_val < 0) { + rseq = create_block_id(e->id_); + r = x; + } else if (cmp_val > 0) { + lseq = create_block_id(e->id_); + l = x; + } else { + get_handle(create_block_id(e->id_), std::move(promise)); + return; + } + } + if (rseq.is_valid()) { + if (!block_id.is_valid()) { + block_id = rseq; + } else if (block_id.id.seqno > rseq.id.seqno) { + block_id = rseq; + } + } + if (lseq.is_valid()) { + if (ls < lseq.id.seqno) { + ls = lseq.id.seqno; + } + } + if (block_id.is_valid() && ls + 1 == block_id.id.seqno) { + if (!exact) { + get_handle(block_id, std::move(promise)); + } else { + promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); + } + return; + } + } + if (!exact && block_id.is_valid()) { + get_handle(block_id, std::move(promise)); + } else { + promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); + } +} + +void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise) { + return get_block_common( + account_id, + [lt](ton_api::db_lt_desc_value &w) { + return lt > static_cast(w.last_lt_) ? 1 : lt == static_cast(w.last_lt_) ? 0 : -1; + }, + [lt](ton_api::db_lt_el_value &w) { + return lt > static_cast(w.lt_) ? 1 : lt == static_cast(w.lt_) ? 0 : -1; + }, + false, std::move(promise)); +} + +void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, + td::Promise promise) { + return get_block_common( + account_id, + [seqno](ton_api::db_lt_desc_value &w) { + return seqno > static_cast(w.last_seqno_) + ? 1 + : seqno == static_cast(w.last_seqno_) ? 0 : -1; + }, + [seqno](ton_api::db_lt_el_value &w) { + return seqno > static_cast(w.id_->seqno_) + ? 1 + : seqno == static_cast(w.id_->seqno_) ? 0 : -1; + }, + true, std::move(promise)); +} + +void ArchiveSlice::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, + td::Promise promise) { + return get_block_common( + account_id, + [ts](ton_api::db_lt_desc_value &w) { + return ts > static_cast(w.last_ts_) ? 1 : ts == static_cast(w.last_ts_) ? 0 : -1; + }, + [ts](ton_api::db_lt_el_value &w) { + return ts > static_cast(w.ts_) ? 1 : ts == static_cast(w.ts_) ? 0 : -1; + }, + false, std::move(promise)); +} + +td::BufferSlice ArchiveSlice::get_db_key_lt_desc(ShardIdFull shard) { + return create_serialize_tl_object(shard.workchain, shard.shard); +} + +td::BufferSlice ArchiveSlice::get_db_key_lt_el(ShardIdFull shard, td::uint32 idx) { + return create_serialize_tl_object(shard.workchain, shard.shard, idx); +} + +td::BufferSlice ArchiveSlice::get_db_key_block_info(BlockIdExt block_id) { + return create_serialize_tl_object(create_tl_block_id(block_id)); +} + +void ArchiveSlice::get_slice(td::uint64 offset, td::uint32 limit, td::Promise promise) { + td::actor::create_actor("readfile", prefix_ + ".pack", offset, limit, 0, std::move(promise)).release(); +} + +void ArchiveSlice::start_up() { + auto R = Package::open(prefix_ + ".pack", false, true); + if (R.is_error()) { + LOG(FATAL) << "failed to open/create archive '" << prefix_ << ".pack" + << "': " << R.move_as_error(); + return; + } + package_ = std::make_shared(R.move_as_ok()); + kv_ = std::make_shared(td::RocksDb::open(prefix_ + ".index").move_as_ok()); + + std::string value; + auto R2 = kv_->get("status", value); + R2.ensure(); + + if (R2.move_as_ok() == td::KeyValue::GetStatus::Ok) { + auto len = td::to_integer(value); + package_->truncate(len); + } else { + package_->truncate(0); + } + + writer_ = td::actor::create_actor("writer", package_); +} + +void ArchiveSlice::begin_transaction() { + if (!async_mode_ || !huge_transaction_started_) { + kv_->begin_transaction().ensure(); + if (async_mode_) { + huge_transaction_started_ = true; + } + } +} + +void ArchiveSlice::commit_transaction() { + if (!async_mode_ || huge_transaction_size_++ >= 100) { + kv_->commit_transaction().ensure(); + if (async_mode_) { + huge_transaction_size_ = 0; + huge_transaction_started_ = false; + } + } +} + +void ArchiveSlice::set_async_mode(bool mode, td::Promise promise) { + async_mode_ = mode; + if (!async_mode_ && huge_transaction_started_) { + kv_->commit_transaction().ensure(); + huge_transaction_size_ = 0; + huge_transaction_started_ = false; + } + + td::actor::send_closure(writer_, &PackageWriter::set_async_mode, mode, std::move(promise)); +} + +ArchiveSlice::ArchiveSlice(bool key_blocks_only, bool temp, std::string prefix) + : key_blocks_only_(key_blocks_only), temp_(temp), prefix_(std::move(prefix)) { +} + +namespace { + +void destroy_db(std::string name, td::uint32 attempt, td::Promise promise) { + auto S = td::RocksDb::destroy(name); + if (S.is_ok()) { + promise.set_value(td::Unit()); + return; + } + if (S.is_error() && attempt > 0 && attempt % 64 == 0) { + LOG(ERROR) << "failed to destroy index " << name << ": " << S; + } else { + LOG(DEBUG) << "failed to destroy index " << name << ": " << S; + } + delay_action( + [name, attempt, promise = std::move(promise)]() mutable { destroy_db(name, attempt, std::move(promise)); }, + td::Timestamp::in(1.0)); +} +} // namespace + +void ArchiveSlice::destroy(td::Promise promise) { + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(promise)); + destroyed_ = true; + + writer_.reset(); + package_ = nullptr; + kv_ = nullptr; + + td::unlink(prefix_ + ".pack").ensure(); + + delay_action([name = prefix_ + ".index", attempt = 0, + promise = ig.get_promise()]() mutable { destroy_db(name, attempt, std::move(promise)); }, + td::Timestamp::in(0.0)); +} + +} // namespace validator + +} // namespace ton diff --git a/validator/db/archive-slice.hpp b/validator/db/archive-slice.hpp new file mode 100644 index 0000000000..b4e03e01db --- /dev/null +++ b/validator/db/archive-slice.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include "validator/interfaces/db.h" +#include "package.hpp" +#include "fileref.hpp" + +namespace ton { + +namespace validator { + +class PackageWriter : public td::actor::Actor { + public: + PackageWriter(std::shared_ptr package) : package_(std::move(package)) { + } + + void append(std::string filename, td::BufferSlice data, td::Promise> promise); + void set_async_mode(bool mode, td::Promise promise) { + async_mode_ = mode; + if (!async_mode_) { + package_->sync(); + } + promise.set_value(td::Unit()); + } + + private: + std::shared_ptr package_; + bool async_mode_ = false; +}; + +class ArchiveSlice : public td::actor::Actor { + public: + ArchiveSlice(bool key_blocks_only, bool temp, std::string prefix); + + void add_handle(BlockHandle handle, td::Promise promise); + void update_handle(BlockHandle handle, td::Promise promise); + void add_file(FileReference ref_id, td::BufferSlice data, td::Promise promise); + void get_handle(BlockIdExt block_id, td::Promise promise); + void get_file(FileReference ref_id, td::Promise promise); + + /* from LTDB */ + void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); + void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); + void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + void get_block_common(AccountIdPrefixFull account_id, + std::function compare_desc, + std::function compare, bool exact, + td::Promise promise); + + void get_slice(td::uint64 offset, td::uint32 limit, td::Promise promise); + + void start_up() override; + void destroy(td::Promise promise); + + void begin_transaction(); + void commit_transaction(); + void set_async_mode(bool mode, td::Promise promise); + + private: + void written_data(BlockHandle handle, td::Promise promise); + void add_file_cont(FileReference ref_id, td::uint64 offset, td::uint64 size, td::Promise promise); + + /* ltdb */ + td::BufferSlice get_db_key_lt_desc(ShardIdFull shard); + td::BufferSlice get_db_key_lt_el(ShardIdFull shard, td::uint32 idx); + td::BufferSlice get_db_key_block_info(BlockIdExt block_id); + td::BufferSlice get_lt_from_db(ShardIdFull shard, td::uint32 idx); + + bool key_blocks_only_; + bool temp_; + + bool destroyed_ = false; + bool async_mode_ = false; + bool huge_transaction_started_ = false; + td::uint32 huge_transaction_size_ = 0; + + std::string prefix_; + std::shared_ptr package_; + std::shared_ptr kv_; + td::actor::ActorOwn writer_; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/db/archiver.cpp b/validator/db/archiver.cpp index f0f1692c5d..5b0d1a8a38 100644 --- a/validator/db/archiver.cpp +++ b/validator/db/archiver.cpp @@ -24,41 +24,30 @@ namespace ton { namespace validator { -BlockArchiver::BlockArchiver(BlockIdExt block_id, td::actor::ActorId root_db, - td::actor::ActorId file_db, td::actor::ActorId archive_db, - td::actor::ActorId archive, td::Promise promise) - : block_id_(block_id) - , root_db_(root_db) - , file_db_(file_db) - , archive_db_(archive_db) - , archive_(archive) - , promise_(std::move(promise)) { +BlockArchiver::BlockArchiver(BlockHandle handle, td::actor::ActorId archive_db, + td::Promise promise) + : handle_(std::move(handle)), archive_(archive_db), promise_(std::move(promise)) { } void BlockArchiver::start_up() { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { - R.ensure(); - td::actor::send_closure(SelfId, &BlockArchiver::got_block_handle, R.move_as_ok()); - }); - td::actor::send_closure(root_db_, &RootDb::get_block_handle_external, block_id_, false, std::move(P)); + if (handle_->handle_moved_to_archive()) { + moved_handle(); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &BlockArchiver::moved_handle); + }); + td::actor::send_closure(archive_, &ArchiveManager::add_handle, handle_, std::move(P)); + } } -void BlockArchiver::got_block_handle(BlockHandle handle) { - handle_ = std::move(handle); +void BlockArchiver::moved_handle() { + CHECK(handle_->handle_moved_to_archive()); if (handle_->moved_to_archive()) { finish_query(); return; } - if (!handle_->is_applied() && !handle_->is_archived() && - (!handle_->inited_is_key_block() || !handle_->is_key_block())) { - // no need for this block - // probably this block not in final chain - // this will eventually delete all associated data - written_block_data(); - return; - } - if (!handle_->inited_proof()) { written_proof(); return; @@ -69,11 +58,7 @@ void BlockArchiver::got_block_handle(BlockHandle handle) { td::actor::send_closure(SelfId, &BlockArchiver::got_proof, R.move_as_ok()); }); - if (handle_->moved_to_storage()) { - td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P)); - } else { - td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Proof{block_id_}}, std::move(P)); - } + td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::Proof{handle_->id()}, std::move(P)); } void BlockArchiver::got_proof(td::BufferSlice data) { @@ -81,8 +66,8 @@ void BlockArchiver::got_proof(td::BufferSlice data) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_proof); }); - td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(), - FileDb::RefId{fileref::Proof{block_id_}}, std::move(data), std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::add_file, handle_, fileref::Proof{handle_->id()}, std::move(data), + std::move(P)); } void BlockArchiver::written_proof() { @@ -95,12 +80,9 @@ void BlockArchiver::written_proof() { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::got_proof_link, R.move_as_ok()); }); - if (handle_->moved_to_storage()) { - td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, - std::move(P)); - } else { - td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(P)); - } + + td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::ProofLink{handle_->id()}, + std::move(P)); } void BlockArchiver::got_proof_link(td::BufferSlice data) { @@ -108,8 +90,8 @@ void BlockArchiver::got_proof_link(td::BufferSlice data) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_proof_link); }); - td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(), - FileDb::RefId{fileref::ProofLink{block_id_}}, std::move(data), std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::add_file, handle_, fileref::ProofLink{handle_->id()}, + std::move(data), std::move(P)); } void BlockArchiver::written_proof_link() { @@ -121,11 +103,8 @@ void BlockArchiver::written_proof_link() { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::got_block_data, R.move_as_ok()); }); - if (handle_->moved_to_storage()) { - td::actor::send_closure(archive_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P)); - } else { - td::actor::send_closure(file_db_, &FileDb::load_file, FileDb::RefId{fileref::Block{block_id_}}, std::move(P)); - } + + td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::Block{handle_->id()}, std::move(P)); } void BlockArchiver::got_block_data(td::BufferSlice data) { @@ -133,8 +112,8 @@ void BlockArchiver::got_block_data(td::BufferSlice data) { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::written_block_data); }); - td::actor::send_closure(archive_, &ArchiveManager::write, handle_->unix_time(), handle_->is_key_block(), - FileDb::RefId{fileref::Block{block_id_}}, std::move(data), std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::add_file, handle_, fileref::Block{handle_->id()}, std::move(data), + std::move(P)); } void BlockArchiver::written_block_data() { @@ -144,7 +123,7 @@ void BlockArchiver::written_block_data() { R.ensure(); td::actor::send_closure(SelfId, &BlockArchiver::finish_query); }); - td::actor::send_closure(root_db_, &RootDb::store_block_handle, handle_, std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::update_handle, handle_, std::move(P)); } void BlockArchiver::finish_query() { @@ -156,7 +135,7 @@ void BlockArchiver::finish_query() { void BlockArchiver::abort_query(td::Status reason) { if (promise_) { - VLOG(VALIDATOR_WARNING) << "failed to archive block " << block_id_ << ": " << reason; + VLOG(VALIDATOR_WARNING) << "failed to archive block " << handle_->id() << ": " << reason; promise_.set_error(std::move(reason)); } stop(); diff --git a/validator/db/archiver.hpp b/validator/db/archiver.hpp index 3335d3aa05..054ab42bdd 100644 --- a/validator/db/archiver.hpp +++ b/validator/db/archiver.hpp @@ -22,7 +22,7 @@ #include "td/actor/actor.h" #include "validator/interfaces/block-handle.h" #include "ton/ton-io.hpp" -#include "archive-db.hpp" +#include "archive-manager.hpp" namespace ton { @@ -33,14 +33,12 @@ class FileDb; class BlockArchiver : public td::actor::Actor { public: - BlockArchiver(BlockIdExt block_id, td::actor::ActorId root_db, td::actor::ActorId file_db, - td::actor::ActorId archive_db, td::actor::ActorId archive, - td::Promise promise); + BlockArchiver(BlockHandle handle, td::actor::ActorId archive_db, td::Promise promise); void abort_query(td::Status error); void start_up() override; - void got_block_handle(BlockHandle handle); + void moved_handle(); void got_proof(td::BufferSlice data); void written_proof(); void got_proof_link(td::BufferSlice data); @@ -50,14 +48,9 @@ class BlockArchiver : public td::actor::Actor { void finish_query(); private: - BlockIdExt block_id_; - td::actor::ActorId root_db_; - td::actor::ActorId file_db_; - td::actor::ActorId archive_db_; + BlockHandle handle_; td::actor::ActorId archive_; td::Promise promise_; - - BlockHandle handle_; }; } // namespace validator diff --git a/validator/db/blockdb.cpp b/validator/db/blockdb.cpp deleted file mode 100644 index 1e1c0cf00a..0000000000 --- a/validator/db/blockdb.cpp +++ /dev/null @@ -1,301 +0,0 @@ -/* - 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 . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#include "blockdb.hpp" -#include "rootdb.hpp" -#include "validator/fabric.h" -#include "ton/ton-tl.hpp" -#include "td/utils/port/path.h" -#include "files-async.hpp" -#include "td/db/RocksDb.h" - -namespace ton { - -namespace validator { - -void BlockDb::store_block_handle(BlockHandle handle, td::Promise promise) { - if (!handle->id().is_valid()) { - promise.set_error(td::Status::Error(ErrorCode::protoviolation, "invalid block id")); - return; - } - auto lru_key = get_block_lru_key(handle->id()); - auto value_key = get_block_value_key(handle->id()); - while (handle->need_flush()) { - auto version = handle->version(); - auto R = get_block_value(value_key); - if (R.is_ok()) { - kv_->begin_transaction().ensure(); - set_block_value(value_key, handle->serialize()); - kv_->commit_transaction().ensure(); - } else { - CHECK(get_block_lru(lru_key).is_error()); - auto empty = get_block_lru_empty_key_hash(); - auto ER = get_block_lru(empty); - ER.ensure(); - auto E = ER.move_as_ok(); - - auto PR = get_block_lru(E.prev); - PR.ensure(); - auto P = PR.move_as_ok(); - CHECK(P.next == empty); - - DbEntry D{handle->id(), E.prev, empty}; - - E.prev = lru_key; - P.next = lru_key; - - if (P.is_empty()) { - E.next = lru_key; - P.prev = lru_key; - } - - kv_->begin_transaction().ensure(); - set_block_value(value_key, handle->serialize()); - set_block_lru(empty, std::move(E)); - set_block_lru(D.prev, std::move(P)); - set_block_lru(lru_key, std::move(D)); - kv_->commit_transaction().ensure(); - } - handle->flushed_upto(version); - } - promise.set_value(td::Unit()); -} - -void BlockDb::get_block_handle(BlockIdExt id, td::Promise promise) { - if (!id.is_valid()) { - promise.set_error(td::Status::Error(ErrorCode::protoviolation, "invalid block id")); - return; - } - CHECK(id.is_valid()); - auto key_hash = get_block_value_key(id); - auto B = get_block_value(key_hash); - if (B.is_error()) { - promise.set_error(B.move_as_error()); - return; - } - promise.set_result(create_block_handle(B.move_as_ok())); -} - -void BlockDb::start_up() { - kv_ = std::make_shared(td::RocksDb::open(db_path_).move_as_ok()); - alarm_timestamp() = td::Timestamp::in(0.1); - - auto empty = get_block_lru_empty_key_hash(); - if (get_block_lru(empty).is_error()) { - DbEntry e{BlockIdExt{}, empty, empty}; - kv_->begin_transaction().ensure(); - set_block_lru(empty, std::move(e)); - kv_->commit_transaction().ensure(); - } - last_gc_ = empty; -} - -BlockDb::BlockDb(td::actor::ActorId root_db, std::string db_path) : root_db_(root_db), db_path_(db_path) { -} - -BlockDb::KeyHash BlockDb::get_block_lru_key(BlockIdExt id) { - if (!id.is_valid()) { - return KeyHash::zero(); - } else { - auto obj = create_tl_object(create_tl_block_id(id)); - return get_tl_object_sha_bits256(obj); - } -} - -BlockDb::KeyHash BlockDb::get_block_value_key(BlockIdExt id) { - CHECK(id.is_valid()); - auto obj = create_tl_object(create_tl_block_id(id)); - return get_tl_object_sha_bits256(obj); -} - -td::Result BlockDb::get_block_lru(KeyHash key_hash) { - std::string value; - auto R = kv_->get(key_hash.as_slice(), value); - R.ensure(); - if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { - return td::Status::Error(ErrorCode::notready, "not in db"); - } - auto v = fetch_tl_object(td::BufferSlice{value}, true); - v.ensure(); - return DbEntry{v.move_as_ok()}; -} - -td::Result BlockDb::get_block_value(KeyHash key_hash) { - std::string value; - auto R = kv_->get(key_hash.as_slice(), value); - R.ensure(); - if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { - return td::Status::Error(ErrorCode::notready, "not in db"); - } - return td::BufferSlice{value}; -} - -void BlockDb::set_block_lru(KeyHash key_hash, DbEntry e) { - kv_->set(key_hash.as_slice(), serialize_tl_object(e.release(), true)).ensure(); -} - -void BlockDb::set_block_value(KeyHash key_hash, td::BufferSlice value) { - kv_->set(key_hash.as_slice(), std::move(value)).ensure(); -} - -void BlockDb::alarm() { - auto R = get_block_lru(last_gc_); - R.ensure(); - - auto N = R.move_as_ok(); - if (N.is_empty()) { - last_gc_ = N.next; - alarm_timestamp() = td::Timestamp::in(0.01); - return; - } - - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { - if (R.is_error()) { - td::actor::send_closure(SelfId, &BlockDb::skip_gc); - } else { - auto value = R.move_as_ok(); - if (!value) { - td::actor::send_closure(SelfId, &BlockDb::skip_gc); - } else { - td::actor::send_closure(SelfId, &BlockDb::gc); - } - } - }); - td::actor::send_closure(root_db_, &RootDb::allow_block_gc, N.block_id, std::move(P)); -} - -void BlockDb::gc() { - auto FR = get_block_lru(last_gc_); - FR.ensure(); - auto F = FR.move_as_ok(); - - auto PR = get_block_lru(F.prev); - PR.ensure(); - auto P = PR.move_as_ok(); - auto NR = get_block_lru(F.next); - NR.ensure(); - auto N = NR.move_as_ok(); - - P.next = F.next; - N.prev = F.prev; - if (P.is_empty() && N.is_empty()) { - P.prev = P.next; - N.next = N.prev; - } - - auto value_key = get_block_value_key(F.block_id); - - kv_->begin_transaction().ensure(); - kv_->erase(last_gc_.as_slice()).ensure(); - kv_->erase(value_key.as_slice()).ensure(); - set_block_lru(F.prev, std::move(P)); - set_block_lru(F.next, std::move(N)); - kv_->commit_transaction().ensure(); - alarm_timestamp() = td::Timestamp::now(); - - DCHECK(get_block_lru(last_gc_).is_error()); - last_gc_ = F.next; -} - -void BlockDb::skip_gc() { - auto R = get_block_lru(last_gc_); - R.ensure(); - - auto N = R.move_as_ok(); - last_gc_ = N.next; - alarm_timestamp() = td::Timestamp::in(0.01); -} - -BlockDb::DbEntry::DbEntry(tl_object_ptr entry) - : block_id(create_block_id(entry->id_)), prev(entry->prev_), next(entry->next_) { -} - -tl_object_ptr BlockDb::DbEntry::release() { - return create_tl_object(create_tl_block_id(block_id), prev, next); -} - -void BlockDb::truncate(td::Ref state, td::Promise promise) { - std::map max_seqno; - max_seqno.emplace(ShardIdFull{masterchainId}, state->get_seqno() + 1); - - auto shards = state->get_shards(); - auto it = KeyHash::zero(); - kv_->begin_transaction().ensure(); - while (true) { - auto R = get_block_lru(it); - R.ensure(); - auto v = R.move_as_ok(); - it = v.next; - R = get_block_lru(it); - R.ensure(); - v = R.move_as_ok(); - if (v.is_empty()) { - break; - } - - auto s = v.block_id.shard_full(); - if (!max_seqno.count(s)) { - bool found = false; - for (auto &shard : shards) { - if (shard_intersects(shard->shard(), s)) { - found = true; - max_seqno.emplace(s, shard->top_block_id().seqno() + 1); - break; - } - } - if (!found) { - max_seqno.emplace(s, 0); - } - } - - bool to_delete = v.block_id.seqno() >= max_seqno[s]; - if (to_delete) { - auto key_hash = get_block_value_key(v.block_id); - auto B = get_block_value(key_hash); - B.ensure(); - auto handleR = create_block_handle(B.move_as_ok()); - handleR.ensure(); - auto handle = handleR.move_as_ok(); - - handle->unsafe_clear_applied(); - handle->unsafe_clear_next(); - - if (handle->need_flush()) { - set_block_value(key_hash, handle->serialize()); - } - } else if (v.block_id.seqno() + 1 == max_seqno[s]) { - auto key_hash = get_block_value_key(v.block_id); - auto B = get_block_value(key_hash); - B.ensure(); - auto handleR = create_block_handle(B.move_as_ok()); - handleR.ensure(); - auto handle = handleR.move_as_ok(); - - handle->unsafe_clear_next(); - - if (handle->need_flush()) { - set_block_value(key_hash, handle->serialize()); - } - } - } - kv_->commit_transaction().ensure(); -} - -} // namespace validator - -} // namespace ton diff --git a/validator/db/blockdb.hpp b/validator/db/blockdb.hpp deleted file mode 100644 index e9e26c8c30..0000000000 --- a/validator/db/blockdb.hpp +++ /dev/null @@ -1,88 +0,0 @@ -/* - 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 . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#pragma once - -#include "td/actor/actor.h" -#include "ton/ton-types.h" -#include "validator/interfaces/block-handle.h" -#include "validator/interfaces/db.h" -#include "td/db/KeyValueAsync.h" - -namespace ton { - -namespace validator { - -class RootDb; - -class BlockDb : public td::actor::Actor { - public: - void store_block_handle(BlockHandle handle, td::Promise promise); - void get_block_handle(BlockIdExt id, td::Promise promise); - - void start_up() override; - void alarm() override; - - void gc(); - void skip_gc(); - - void truncate(td::Ref state, td::Promise promise); - - BlockDb(td::actor::ActorId root_db, std::string db_path); - - private: - using KeyHash = td::Bits256; - struct DbEntry { - BlockIdExt block_id; - KeyHash prev; - KeyHash next; - - DbEntry(tl_object_ptr entry); - DbEntry() { - } - DbEntry(BlockIdExt block_id, KeyHash prev, KeyHash next) : block_id(block_id), prev(prev), next(next) { - } - tl_object_ptr release(); - bool is_empty() const { - return !block_id.is_valid(); - } - }; - static KeyHash get_block_lru_key(BlockIdExt block_id); - static KeyHash get_block_value_key(BlockIdExt block_id); - static KeyHash get_block_lru_empty_key_hash() { - return KeyHash::zero(); - } - - td::Result get_block_lru(KeyHash key); - td::Result get_block_value(KeyHash key); - - void set_block_lru(KeyHash key_hash, DbEntry e); - void set_block_value(KeyHash key_hash, td::BufferSlice data); - - std::shared_ptr kv_; - - td::actor::ActorId root_db_; - - std::string db_path_; - - KeyHash last_gc_ = KeyHash::zero(); -}; - -} // namespace validator - -} // namespace ton diff --git a/validator/db/filedb.cpp b/validator/db/filedb.cpp deleted file mode 100644 index 371c08fc3a..0000000000 --- a/validator/db/filedb.cpp +++ /dev/null @@ -1,400 +0,0 @@ -/* - 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 . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#include "filedb.hpp" -#include "rootdb.hpp" -#include "td/utils/port/path.h" -#include "files-async.hpp" -#include "adnl/utils.hpp" -#include "tl-utils/tl-utils.hpp" -#include "td/utils/overloaded.h" - -#include "td/db/RocksDb.h" - -namespace ton { - -namespace validator { - -std::string FileDb::get_file_name(const RefId& ref_id, bool create_dirs) { - auto ref_id_hash = get_ref_id_hash(ref_id); - - auto s = ref_id_hash.to_hex(); - std::string path = root_path_ + "/files/"; - for (td::uint32 i = 0; i < depth_; i++) { - path = path + s[2 * i] + s[2 * i + 1] + "/"; - if (create_dirs) { - td::mkdir(path).ensure(); - } - } - return path + s; -} - -void FileDb::store_file(RefId ref_id, td::BufferSlice data, td::Promise promise) { - auto ref_id_hash = get_ref_id_hash(ref_id); - auto R = get_block(ref_id_hash); - if (R.is_ok()) { - auto val = R.move_as_ok(); - promise.set_result(val.file_hash); - return; - } - - auto file_hash = sha256_bits256(data.as_slice()); - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), file_hash, ref_id = std::move(ref_id), - promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - td::actor::send_closure(SelfId, &FileDb::store_file_continue, std::move(ref_id), file_hash, R.move_as_ok(), - std::move(promise)); - } - }); - td::actor::create_actor("writefile", root_path_ + "/tmp/", "", std::move(data), std::move(P)) - .release(); -} - -void FileDb::store_file_continue(RefId ref_id, FileHash file_hash, std::string res_path, - td::Promise promise) { - auto ref_id_hash = get_ref_id_hash(ref_id); - auto R = get_block(ref_id_hash); - if (R.is_ok()) { - td::unlink(res_path).ignore(); - auto val = R.move_as_ok(); - promise.set_result(val.file_hash); - return; - } - - auto path = get_file_name(ref_id, true); - td::rename(res_path, path).ensure(); - - auto empty = get_empty_ref_id_hash(); - auto ER = get_block(empty); - ER.ensure(); - auto E = ER.move_as_ok(); - - auto PR = get_block(E.prev); - PR.ensure(); - auto P = PR.move_as_ok(); - CHECK(P.next == empty); - - DbEntry D; - D.key = std::move(ref_id); - D.prev = E.prev; - D.next = empty; - D.file_hash = file_hash; - - E.prev = ref_id_hash; - P.next = ref_id_hash; - - if (P.is_empty()) { - E.next = ref_id_hash; - P.prev = ref_id_hash; - } - - kv_->begin_transaction().ensure(); - set_block(empty, std::move(E)); - set_block(D.prev, std::move(P)); - set_block(ref_id_hash, std::move(D)); - kv_->commit_transaction().ensure(); - - promise.set_value(std::move(file_hash)); -} - -void FileDb::load_file(RefId ref_id, td::Promise promise) { - auto ref_id_hash = get_ref_id_hash(ref_id); - auto R = get_block(ref_id_hash); - if (R.is_error()) { - promise.set_error(R.move_as_error()); - return; - } - - auto v = R.move_as_ok(); - - auto P = td::PromiseCreator::lambda( - [promise = std::move(promise), file_hash = v.file_hash](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - auto data = R.move_as_ok(); - if (file_hash != sha256_bits256(data.as_slice())) { - promise.set_error(td::Status::Error(ErrorCode::protoviolation, PSTRING() << "db error: bad file hash")); - } else { - promise.set_value(std::move(data)); - } - } - }); - - td::actor::create_actor("readfile", get_file_name(ref_id, false), 0, -1, 0, std::move(P)).release(); -} - -void FileDb::load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, td::Promise promise) { - auto ref_id_hash = get_ref_id_hash(ref_id); - auto R = get_block(ref_id_hash); - if (R.is_error()) { - promise.set_error(R.move_as_error()); - return; - } - - auto v = R.move_as_ok(); - - auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - promise.set_value(R.move_as_ok()); - } - }); - - td::actor::create_actor("readfile", get_file_name(ref_id, false), offset, max_size, 0, std::move(P)) - .release(); -} - -void FileDb::check_file(RefId ref_id, td::Promise promise) { - auto ref_id_hash = get_ref_id_hash(ref_id); - auto R = get_block(ref_id_hash); - if (R.is_error()) { - promise.set_result(false); - return; - } - promise.set_result(true); -} - -td::Slice FileDb::get_key(const RefIdHash& ref) { - return ref.as_slice(); -} - -td::Result FileDb::get_block(const RefIdHash& ref) { - std::string value; - auto R = kv_->get(get_key(ref), value); - R.ensure(); - if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { - return td::Status::Error(ErrorCode::notready, "not in db"); - } - auto val = fetch_tl_object(td::BufferSlice{value}, true); - val.ensure(); - return DbEntry{val.move_as_ok()}; -} - -void FileDb::set_block(const RefIdHash& ref, DbEntry entry) { - DCHECK(ref == get_ref_id_hash(entry.key)); - kv_->set(get_key(ref), entry.release()).ensure(); -} - -FileDb::FileDb(td::actor::ActorId root_db, std::string root_path, td::uint32 depth, bool is_archive) - : root_db_(root_db), root_path_(root_path), depth_(depth), is_archive_(is_archive) { -} - -void FileDb::start_up() { - td::mkdir(root_path_).ensure(); - db_path_ = root_path_ + "/db/"; - - kv_ = std::make_shared(td::RocksDb::open(db_path_).move_as_ok()); - td::mkdir(root_path_ + "/files/").ensure(); - td::mkdir(root_path_ + "/tmp/").ensure(); - - last_gc_ = get_empty_ref_id_hash(); - alarm_timestamp() = td::Timestamp::in(0.01); - - auto R = get_block(last_gc_); - if (R.is_error()) { - DbEntry e{get_empty_ref_id(), last_gc_, last_gc_, FileHash::zero()}; - kv_->begin_transaction().ensure(); - set_block(last_gc_, std::move(e)); - kv_->commit_transaction().ensure(); - } -} - -void FileDb::alarm() { - auto R = get_block(last_gc_); - R.ensure(); - auto N = R.move_as_ok(); - if (N.is_empty()) { - last_gc_ = N.next; - alarm_timestamp() = td::Timestamp::in(0.01); - return; - } - - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) mutable { - if (R.is_error()) { - td::actor::send_closure(SelfId, &FileDb::skip_gc); - } else { - auto value = R.move_as_ok(); - if (!value) { - td::actor::send_closure(SelfId, &FileDb::skip_gc); - } else { - td::actor::send_closure(SelfId, &FileDb::gc); - } - } - }); - td::actor::send_closure(root_db_, &RootDb::allow_gc, std::move(N.key), is_archive_, std::move(P)); -} - -void FileDb::gc() { - auto FR = get_block(last_gc_); - FR.ensure(); - auto F = FR.move_as_ok(); - - auto PR = get_block(F.prev); - PR.ensure(); - auto P = PR.move_as_ok(); - auto NR = get_block(F.next); - NR.ensure(); - auto N = NR.move_as_ok(); - - P.next = F.next; - N.prev = F.prev; - if (P.is_empty() && N.is_empty()) { - P.prev = P.next; - N.next = N.prev; - } - - auto name = get_file_name(F.key, false); - auto S = td::unlink(name); - if (S.is_error()) { - LOG(WARNING) << "failed to delete " << name; - } - - kv_->begin_transaction().ensure(); - kv_->erase(last_gc_.as_slice()).ensure(); - set_block(F.prev, std::move(P)); - set_block(F.next, std::move(N)); - kv_->commit_transaction().ensure(); - alarm_timestamp() = td::Timestamp::now(); - - DCHECK(get_block(last_gc_).is_error()); - last_gc_ = F.next; -} - -void FileDb::skip_gc() { - auto FR = get_block(last_gc_); - FR.ensure(); - auto F = FR.move_as_ok(); - last_gc_ = F.next; - alarm_timestamp() = td::Timestamp::in(0.01); -} - -td::BufferSlice FileDb::DbEntry::release() { - return create_serialize_tl_object(get_ref_id_tl(key), prev, next, file_hash); -} - -bool FileDb::DbEntry::is_empty() const { - return (key.get_offset() == key.offset()); -} - -FileDb::RefIdHash FileDb::get_ref_id_hash(const RefId& ref) { - FileHash x; - ref.visit([&](const auto& obj) { x = obj.hash(); }); - return x; -} - -tl_object_ptr FileDb::get_ref_id_tl(const RefId& ref) { - tl_object_ptr x; - ref.visit([&](const auto& obj) { x = obj.tl(); }); - return x; -} - -FileDb::RefId FileDb::get_ref_from_tl(const ton_api::db_filedb_Key& from) { - RefId ref_id{fileref::Empty{}}; - ton_api::downcast_call( - const_cast(from), - td::overloaded( - [&](const ton_api::db_filedb_key_empty& key) { ref_id = fileref::Empty{}; }, - [&](const ton_api::db_filedb_key_blockFile& key) { ref_id = fileref::Block{create_block_id(key.block_id_)}; }, - [&](const ton_api::db_filedb_key_zeroStateFile& key) { - ref_id = fileref::ZeroState{create_block_id(key.block_id_)}; - }, - [&](const ton_api::db_filedb_key_persistentStateFile& key) { - ref_id = - fileref::PersistentState{create_block_id(key.block_id_), create_block_id(key.masterchain_block_id_)}; - }, - [&](const ton_api::db_filedb_key_proof& key) { ref_id = fileref::Proof{create_block_id(key.block_id_)}; }, - [&](const ton_api::db_filedb_key_proofLink& key) { - ref_id = fileref::ProofLink{create_block_id(key.block_id_)}; - }, - [&](const ton_api::db_filedb_key_signatures& key) { - ref_id = fileref::Signatures{create_block_id(key.block_id_)}; - }, - [&](const ton_api::db_filedb_key_candidate& key) { - ref_id = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_), - key.id_->collated_data_file_hash_}; - }, - [&](const ton_api::db_filedb_key_blockInfo& key) { - ref_id = fileref::BlockInfo{create_block_id(key.block_id_)}; - })); - return ref_id; -} - -FileDb::RefId FileDb::get_empty_ref_id() { - return fileref::Empty(); -} - -FileDb::RefIdHash FileDb::get_empty_ref_id_hash() { - if (empty_.is_zero()) { - empty_ = get_ref_id_hash(get_empty_ref_id()); - } - return empty_; -} - -FileDb::DbEntry::DbEntry(tl_object_ptr entry) - : key(FileDb::get_ref_from_tl(*entry->key_.get())) - , prev(entry->prev_) - , next(entry->next_) - , file_hash(entry->file_hash_) { -} - -void FileDb::prepare_stats(td::Promise>> promise) { - std::vector> rocksdb_stats; - auto stats = kv_->stats(); - if (stats.size() == 0) { - promise.set_value(std::move(rocksdb_stats)); - return; - } - size_t pos = 0; - while (pos < stats.size()) { - while (pos < stats.size() && - (stats[pos] == ' ' || stats[pos] == '\n' || stats[pos] == '\r' || stats[pos] == '\t')) { - pos++; - } - auto p = pos; - if (pos == stats.size()) { - break; - } - while (stats[pos] != '\n' && stats[pos] != '\r' && stats[pos] != ' ' && stats[pos] != '\t' && pos < stats.size()) { - pos++; - } - auto name = stats.substr(p, pos - p); - if (stats[pos] == '\n' || pos == stats.size()) { - rocksdb_stats.emplace_back(name, ""); - continue; - } - while (pos < stats.size() && - (stats[pos] == ' ' || stats[pos] == '\n' || stats[pos] == '\r' || stats[pos] == '\t')) { - pos++; - } - p = pos; - while (stats[pos] != '\n' && stats[pos] != '\r' && pos < stats.size()) { - pos++; - } - auto value = stats.substr(p, pos - p); - rocksdb_stats.emplace_back(name, value); - } - promise.set_value(std::move(rocksdb_stats)); -} - -} // namespace validator - -} // namespace ton diff --git a/validator/db/filedb.hpp b/validator/db/filedb.hpp deleted file mode 100644 index 3adc434781..0000000000 --- a/validator/db/filedb.hpp +++ /dev/null @@ -1,218 +0,0 @@ -/* - 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 . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#pragma once - -#include "ton/ton-types.h" -#include "td/actor/actor.h" -#include "validator/interfaces/shard.h" -#include "td/db/KeyValue.h" -#include "ton/ton-tl.hpp" - -namespace ton { - -namespace validator { - -namespace fileref { - -class Empty { - public: - tl_object_ptr tl() const { - return create_tl_object(); - } - FileHash hash() const { - return create_hash_tl_object(); - } -}; - -class Block { - public: - tl_object_ptr tl() const { - return create_tl_object(create_tl_block_id(block_id)); - } - FileHash hash() const { - return create_hash_tl_object(create_tl_block_id(block_id)); - } - - BlockIdExt block_id; -}; - -class ZeroState { - public: - tl_object_ptr tl() const { - return create_tl_object(create_tl_block_id(block_id)); - } - FileHash hash() const { - return create_hash_tl_object(create_tl_block_id(block_id)); - } - - BlockIdExt block_id; -}; - -class PersistentState { - public: - tl_object_ptr tl() const { - return create_tl_object(create_tl_block_id(block_id), - create_tl_block_id(masterchain_block_id)); - } - FileHash hash() const { - return create_hash_tl_object(create_tl_block_id(block_id), - create_tl_block_id(masterchain_block_id)); - } - - BlockIdExt block_id; - BlockIdExt masterchain_block_id; -}; - -class Proof { - public: - tl_object_ptr tl() const { - return create_tl_object(create_tl_block_id(block_id)); - } - FileHash hash() const { - return create_hash_tl_object(create_tl_block_id(block_id)); - } - - BlockIdExt block_id; -}; - -class ProofLink { - public: - tl_object_ptr tl() const { - return create_tl_object(create_tl_block_id(block_id)); - } - FileHash hash() const { - return create_hash_tl_object(create_tl_block_id(block_id)); - } - - BlockIdExt block_id; -}; - -class Signatures { - public: - tl_object_ptr tl() const { - return create_tl_object(create_tl_block_id(block_id)); - } - FileHash hash() const { - return create_hash_tl_object(create_tl_block_id(block_id)); - } - - BlockIdExt block_id; -}; - -class Candidate { - public: - tl_object_ptr tl() const { - return create_tl_object( - create_tl_object(source.tl(), create_tl_block_id(block_id), collated_data_file_hash)); - } - FileHash hash() const { - return create_hash_tl_object( - create_tl_object(source.tl(), create_tl_block_id(block_id), collated_data_file_hash)); - } - - PublicKey source; - BlockIdExt block_id; - FileHash collated_data_file_hash; -}; - -class BlockInfo { - public: - tl_object_ptr tl() const { - return create_tl_object(create_tl_block_id(block_id)); - } - FileHash hash() const { - return create_hash_tl_object(create_tl_block_id(block_id)); - } - - BlockIdExt block_id; -}; -}; // namespace fileref - -class RootDb; - -class FileDb : public td::actor::Actor { - public: - using RefId = - td::Variant; - using RefIdHash = td::Bits256; - - void store_file(RefId ref_id, td::BufferSlice data, td::Promise promise); - void store_file_continue(RefId ref_id, FileHash file_hash, std::string path, td::Promise promise); - void load_file(RefId ref_id, td::Promise promise); - void load_file_slice(RefId ref_id, td::int64 offset, td::int64 max_size, td::Promise promise); - void check_file(RefId ref_id, td::Promise promise); - - void prepare_stats(td::Promise>> promise); - - void start_up() override; - void alarm() override; - - void gc(); - void skip_gc(); - - FileDb(td::actor::ActorId root_db, std::string root_path, td::uint32 depth, bool is_archive); - - private: - struct DbEntry { - RefId key; - RefIdHash prev; - RefIdHash next; - FileHash file_hash; - - DbEntry(tl_object_ptr entry); - DbEntry() { - } - DbEntry(RefId key, RefIdHash prev, RefIdHash next, FileHash file_hash) - : key(std::move(key)), prev(prev), next(next), file_hash(file_hash) { - } - td::BufferSlice release(); - bool is_empty() const; - }; - - static RefIdHash get_ref_id_hash(const RefId& ref); - static tl_object_ptr get_ref_id_tl(const RefId& ref); - static RefId get_ref_from_tl(const ton_api::db_filedb_Key& from); - static RefId get_empty_ref_id(); - RefIdHash get_empty_ref_id_hash(); - - std::string get_file_name(const RefId& ref, bool create_dirs); - td::Slice get_key(const RefIdHash& ref); - - td::Result get_block(const RefIdHash& ref_id); - void set_block(const RefIdHash& ref_id_hash, DbEntry entry); - - td::actor::ActorId root_db_; - - std::string root_path_; - std::string db_path_; - td::uint32 depth_; - - bool is_archive_; - - std::shared_ptr kv_; - - RefIdHash last_gc_; - RefIdHash empty_ = RefIdHash::zero(); -}; - -} // namespace validator - -} // namespace ton - diff --git a/validator/db/fileref.cpp b/validator/db/fileref.cpp new file mode 100644 index 0000000000..968ec55293 --- /dev/null +++ b/validator/db/fileref.cpp @@ -0,0 +1,471 @@ +#include "fileref.hpp" +#include "auto/tl/ton_api.hpp" +#include "td/utils/overloaded.h" +#include "td/utils/misc.h" + +namespace ton { + +namespace validator { + +namespace { + +td::Result get_block_id(std::stringstream& ss) { + std::string token; + BlockId block_id; + std::getline(ss, token, '_'); + TRY_RESULT(w, td::to_integer_safe(token)); + std::getline(ss, token, '_'); + TRY_RESULT(shard, td::hex_to_integer_safe(token)); + std::getline(ss, token, '_'); + TRY_RESULT(s, td::to_integer_safe(token)); + return BlockId{w, shard, s}; +} + +td::Result get_token_hash(std::stringstream& ss) { + std::string token; + std::getline(ss, token, '_'); + if (token.size() != 64) { + return td::Status::Error(ErrorCode::protoviolation, "hash must have exactly 64 hexdigits"); + } + + TRY_RESULT(v, td::hex_decode(token)); + + FileHash r; + r.as_slice().copy_from(v); + return r; +} + +} // namespace + +namespace fileref { + +std::string Empty::filename() const { + return "empty"; +} + +Empty Empty::shortref() const { + return *this; +} + +std::string Empty::filename_short() const { + return "empty"; +} + +BlockShort Block::shortref() const { + return BlockShort{block_id.id, hash()}; +} + +std::string Block::filename() const { + return PSTRING() << "block_" << block_id.to_str(); +} + +std::string Block::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "block_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" + << hash().to_hex(); +} + +std::string BlockShort::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.shard)); + return PSTRING() << "block_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); +} + +ZeroStateShort ZeroState::shortref() const { + return ZeroStateShort{block_id.id.workchain, hash()}; +} + +std::string ZeroState::filename() const { + return PSTRING() << "zerostate_" << block_id.to_str(); +} + +std::string ZeroState::filename_short() const { + return PSTRING() << "zerostate_" << block_id.id.workchain << "_" << hash().to_hex(); +} + +std::string ZeroStateShort::filename_short() const { + return PSTRING() << "zerostate_" << workchain << "_" << hash().to_hex(); +} + +PersistentStateShort PersistentState::shortref() const { + return PersistentStateShort{block_id.shard_full(), masterchain_block_id.seqno(), hash()}; +} + +std::string PersistentState::filename() const { + return PSTRING() << "state_" << masterchain_block_id.to_str() << "_" << block_id.to_str(); +} + +std::string PersistentState::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "state_" << masterchain_block_id.seqno() << "_" << block_id.id.workchain << "_" << s << "_" + << hash().to_hex(); +} + +std::string PersistentStateShort::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(shard_id.shard)); + return PSTRING() << "state_" << masterchain_seqno << "_" << shard_id.workchain << "_" << s << "_" << hash().to_hex(); +} + +ProofShort Proof::shortref() const { + return ProofShort{block_id.id, hash()}; +} + +std::string Proof::filename() const { + return PSTRING() << "proof_" << block_id.to_str(); +} + +std::string Proof::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "proof_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" + << hash().to_hex(); +} + +std::string ProofShort::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.shard)); + return PSTRING() << "proof_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); +} + +ProofLinkShort ProofLink::shortref() const { + return ProofLinkShort{block_id.id, hash()}; +} + +std::string ProofLink::filename() const { + return PSTRING() << "prooflink_" << block_id.to_str(); +} + +std::string ProofLink::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "prooflink_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" + << hash().to_hex(); +} + +std::string ProofLinkShort::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.shard)); + return PSTRING() << "prooflink_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); +} + +SignaturesShort Signatures::shortref() const { + return SignaturesShort{block_id.id, hash()}; +} + +std::string Signatures::filename() const { + return PSTRING() << "signatures_" << block_id.to_str(); +} + +std::string Signatures::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "signatures_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" + << hash().to_hex(); +} + +std::string SignaturesShort::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.shard)); + return PSTRING() << "signatures_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" + << hash().to_hex(); +} + +CandidateShort Candidate::shortref() const { + return CandidateShort{block_id.id, hash()}; +} + +std::string Candidate::filename() const { + return PSTRING() << "candidate_" << block_id.to_str() << "_" << collated_data_file_hash.to_hex() << "_" + << td::base64url_encode(source.export_as_slice()); +} + +std::string Candidate::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "candidate_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" + << hash().to_hex(); +} + +std::string CandidateShort::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.shard)); + return PSTRING() << "candidate_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); +} + +BlockInfoShort BlockInfo::shortref() const { + return BlockInfoShort{block_id.id, hash()}; +} + +std::string BlockInfo::filename() const { + return PSTRING() << "info_" << block_id.to_str(); +} + +std::string BlockInfo::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.id.shard)); + return PSTRING() << "info_" << block_id.id.workchain << "_" << s << "_" << block_id.id.seqno << "_" + << hash().to_hex(); +} + +std::string BlockInfoShort::filename_short() const { + char s[33]; + sprintf(s, "%llx", static_cast(block_id.shard)); + return PSTRING() << "info_" << block_id.workchain << "_" << s << "_" << block_id.seqno << "_" << hash().to_hex(); +} + +} // namespace fileref + +FileReference::FileReference(tl_object_ptr key) { + ton_api::downcast_call( + *key.get(), + td::overloaded( + [&](const ton_api::db_filedb_key_empty& key) { ref_ = fileref::Empty{}; }, + [&](const ton_api::db_filedb_key_blockFile& key) { ref_ = fileref::Block{create_block_id(key.block_id_)}; }, + [&](const ton_api::db_filedb_key_zeroStateFile& key) { + ref_ = fileref::ZeroState{create_block_id(key.block_id_)}; + }, + [&](const ton_api::db_filedb_key_persistentStateFile& key) { + ref_ = fileref::PersistentState{create_block_id(key.block_id_), create_block_id(key.masterchain_block_id_)}; + }, + [&](const ton_api::db_filedb_key_proof& key) { ref_ = fileref::Proof{create_block_id(key.block_id_)}; }, + [&](const ton_api::db_filedb_key_proofLink& key) { + ref_ = fileref::ProofLink{create_block_id(key.block_id_)}; + }, + [&](const ton_api::db_filedb_key_signatures& key) { + ref_ = fileref::Signatures{create_block_id(key.block_id_)}; + }, + [&](const ton_api::db_filedb_key_candidate& key) { + ref_ = fileref::Candidate{PublicKey{key.id_->source_}, create_block_id(key.id_->id_), + key.id_->collated_data_file_hash_}; + }, + [&](const ton_api::db_filedb_key_blockInfo& key) { + ref_ = fileref::BlockInfo{create_block_id(key.block_id_)}; + })); +} + +FileReferenceShort FileReference::shortref() const { + FileReferenceShort h; + ref_.visit([&](const auto& obj) { h = obj.shortref(); }); + return h; +} + +td::Bits256 FileReference::hash() const { + FileHash h; + ref_.visit([&](const auto& obj) { h = obj.hash(); }); + return h; +} + +td::Bits256 FileReferenceShort::hash() const { + FileHash h; + ref_.visit([&](const auto& obj) { h = obj.hash(); }); + return h; +} + +ShardIdFull FileReference::shard() const { + ShardIdFull h; + ref_.visit([&](const auto& obj) { h = obj.shard(); }); + return h; +} + +ShardIdFull FileReferenceShort::shard() const { + ShardIdFull h; + ref_.visit([&](const auto& obj) { h = obj.shard(); }); + return h; +} + +std::string FileReference::filename() const { + std::string h; + ref_.visit([&](const auto& obj) { h = obj.filename(); }); + return h; +} + +std::string FileReference::filename_short() const { + std::string h; + ref_.visit([&](const auto& obj) { h = obj.filename_short(); }); + return h; +} + +std::string FileReferenceShort::filename_short() const { + std::string h; + ref_.visit([&](const auto& obj) { h = obj.filename_short(); }); + return h; +} + +td::Result FileReference::create(std::string filename) { + std::stringstream ss{filename}; + + std::string token; + std::getline(ss, token, '_'); + + if (token == "empty") { + if (ss.eof()) { + return fileref::Empty{}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "block") { + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + if (ss.eof()) { + return fileref::Block{block_id}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "zerostate") { + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + if (ss.eof()) { + return fileref::ZeroState{block_id}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "state") { + std::getline(ss, token, '_'); + TRY_RESULT(masterchain_block_id, BlockIdExt::from_str(token)); + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + if (ss.eof()) { + return fileref::PersistentState{block_id, masterchain_block_id}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "proof") { + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + if (ss.eof()) { + return fileref::Proof{block_id}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "prooflink") { + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + if (ss.eof()) { + return fileref::ProofLink{block_id}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "signatures") { + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + if (ss.eof()) { + return fileref::Signatures{block_id}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "candidate") { + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + TRY_RESULT(col_hash, get_token_hash(ss)); + + std::string rem = ss.str(); + + TRY_RESULT(source_s, td::base64url_decode(rem)); + TRY_RESULT(source, PublicKey::import(source_s)); + return fileref::Candidate{source, block_id, col_hash}; + } else if (token == "info") { + std::getline(ss, token, '_'); + TRY_RESULT(block_id, BlockIdExt::from_str(token)); + if (ss.eof()) { + return fileref::BlockInfo{block_id}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else { + return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "unknown prefix '" << token << "'"); + } +} + +td::Result FileReferenceShort::create(std::string filename) { + std::stringstream ss{filename}; + + std::string token; + std::getline(ss, token, '_'); + + if (token == "empty") { + if (ss.eof()) { + return fileref::Empty{}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "block") { + TRY_RESULT(block_id, get_block_id(ss)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::BlockShort{block_id, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "zerostate") { + std::getline(ss, token, '_'); + TRY_RESULT(workchain, td::to_integer_safe(token)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::ZeroStateShort{workchain, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "state") { + std::getline(ss, token, '_'); + TRY_RESULT(masterchain_seqno, td::to_integer_safe(token)); + std::getline(ss, token, '_'); + TRY_RESULT(workchain, td::to_integer_safe(token)); + std::getline(ss, token, '_'); + TRY_RESULT(shard, td::hex_to_integer_safe(token)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::PersistentStateShort{ShardIdFull{workchain, shard}, masterchain_seqno, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "proof") { + TRY_RESULT(block_id, get_block_id(ss)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::ProofShort{block_id, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "prooflink") { + TRY_RESULT(block_id, get_block_id(ss)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::ProofLinkShort{block_id, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "signatures") { + TRY_RESULT(block_id, get_block_id(ss)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::SignaturesShort{block_id, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "candidate") { + TRY_RESULT(block_id, get_block_id(ss)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::CandidateShort{block_id, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else if (token == "info") { + TRY_RESULT(block_id, get_block_id(ss)); + TRY_RESULT(vhash, get_token_hash(ss)); + if (ss.eof()) { + return fileref::BlockInfoShort{block_id, vhash}; + } else { + return td::Status::Error(ErrorCode::protoviolation, "too big file name"); + } + } else { + return td::Status::Error(ErrorCode::protoviolation, PSTRING() << "unknown prefix '" << token << "'"); + } +} + +} // namespace validator + +} // namespace ton diff --git a/validator/db/fileref.hpp b/validator/db/fileref.hpp new file mode 100644 index 0000000000..9b012dc8e2 --- /dev/null +++ b/validator/db/fileref.hpp @@ -0,0 +1,372 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once + +#include "ton/ton-types.h" +#include "td/actor/actor.h" +#include "validator/interfaces/shard.h" +#include "td/db/KeyValue.h" +#include "ton/ton-tl.hpp" + +namespace ton { + +namespace validator { + +namespace fileref { + +class Empty { + public: + tl_object_ptr tl() const { + return create_tl_object(); + } + FileHash hash() const { + return create_hash_tl_object(); + } + ShardIdFull shard() const { + return ShardIdFull{masterchainId}; + } + std::string filename() const; + std::string filename_short() const; + Empty shortref() const; +}; + +class BlockShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + std::string filename_short() const; + + BlockId block_id; + FileHash hashv; +}; + +class Block { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + BlockShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; +}; + +class ZeroStateShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return ShardIdFull{workchain, shardIdAll}; + } + std::string filename_short() const; + + WorkchainId workchain; + FileHash hashv; +}; + +class ZeroState { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + ZeroStateShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; +}; + +class PersistentStateShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return shard_id; + } + std::string filename_short() const; + + ShardIdFull shard_id; + BlockSeqno masterchain_seqno; + FileHash hashv; +}; + +class PersistentState { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id), + create_tl_block_id(masterchain_block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id), + create_tl_block_id(masterchain_block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + PersistentStateShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; + BlockIdExt masterchain_block_id; +}; + +class ProofShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + std::string filename_short() const; + + BlockId block_id; + FileHash hashv; +}; + +class Proof { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + ProofShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; +}; + +class ProofLinkShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + std::string filename_short() const; + + BlockId block_id; + FileHash hashv; +}; + +class ProofLink { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + ProofLinkShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; +}; + +class SignaturesShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + std::string filename_short() const; + + BlockId block_id; + FileHash hashv; +}; + +class Signatures { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + SignaturesShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; +}; + +class CandidateShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + std::string filename_short() const; + + BlockId block_id; + FileHash hashv; +}; + +class Candidate { + public: + tl_object_ptr tl() const { + return create_tl_object( + create_tl_object(source.tl(), create_tl_block_id(block_id), collated_data_file_hash)); + } + FileHash hash() const { + return create_hash_tl_object( + create_tl_object(source.tl(), create_tl_block_id(block_id), collated_data_file_hash)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + CandidateShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + PublicKey source; + BlockIdExt block_id; + FileHash collated_data_file_hash; +}; + +class BlockInfoShort { + public: + FileHash hash() const { + return hashv; + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + std::string filename_short() const; + + BlockId block_id; + FileHash hashv; +}; + +class BlockInfo { + public: + tl_object_ptr tl() const { + return create_tl_object(create_tl_block_id(block_id)); + } + FileHash hash() const { + return create_hash_tl_object(create_tl_block_id(block_id)); + } + ShardIdFull shard() const { + return block_id.shard_full(); + } + BlockInfoShort shortref() const; + std::string filename() const; + std::string filename_short() const; + + BlockIdExt block_id; +}; + +}; // namespace fileref + +class FileReferenceShort { + private: + td::Variant + ref_; + + public: + template + FileReferenceShort(T x) : ref_(std::move(x)) { + } + FileReferenceShort() : ref_(fileref::Empty{}) { + } + + static td::Result create(std::string filename); + + auto &ref() { + return ref_; + } + + td::Bits256 hash() const; + ShardIdFull shard() const; + std::string filename_short() const; +}; + +class FileReference { + private: + td::Variant + ref_; + + public: + template + FileReference(T x) : ref_(std::move(x)) { + } + FileReference() : ref_(fileref::Empty{}) { + } + FileReference(tl_object_ptr key); + + static td::Result create(std::string filename); + + auto &ref() { + return ref_; + } + + FileReferenceShort shortref() const; + + tl_object_ptr tl() const; + td::Bits256 hash() const; + ShardIdFull shard() const; + std::string filename() const; + std::string filename_short() const; +}; + +} // namespace validator + +} // namespace ton + diff --git a/validator/db/ltdb.cpp b/validator/db/ltdb.cpp deleted file mode 100644 index eea2aa63ed..0000000000 --- a/validator/db/ltdb.cpp +++ /dev/null @@ -1,310 +0,0 @@ -/* - 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 . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#include "ltdb.hpp" -#include "rootdb.hpp" -#include "auto/tl/ton_api.h" -#include "ton/ton-tl.hpp" -#include "ton/ton-io.hpp" -#include "td/db/RocksDb.h" - -namespace ton { - -namespace validator { - -void LtDb::add_new_block(BlockIdExt block_id, LogicalTime lt, UnixTime ts, td::Promise promise) { - auto key = get_desc_key(block_id.shard_full()); - - std::string value; - auto R = kv_->get(key.as_slice(), value); - R.ensure(); - tl_object_ptr v; - bool add_shard = false; - if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) { - auto F = fetch_tl_object(td::BufferSlice{value}, true); - F.ensure(); - v = F.move_as_ok(); - } else { - v = create_tl_object(1, 1, 0, 0, 0); - add_shard = true; - } - if (block_id.id.seqno <= static_cast(v->last_seqno_) || lt <= static_cast(v->last_lt_) || - ts <= static_cast(v->last_ts_)) { - promise.set_value(td::Unit()); - return; - } - auto db_value = create_serialize_tl_object(create_tl_block_id(block_id), lt, ts); - auto db_key = get_el_key(block_id.shard_full(), v->last_idx_++); - auto status_key = create_serialize_tl_object(); - v->last_seqno_ = block_id.id.seqno; - v->last_lt_ = lt; - v->last_ts_ = ts; - - td::uint32 idx = 0; - if (add_shard) { - auto G = kv_->get(status_key.as_slice(), value); - G.ensure(); - if (G.move_as_ok() == td::KeyValue::GetStatus::NotFound) { - idx = 0; - } else { - auto F = fetch_tl_object(value, true); - F.ensure(); - auto f = F.move_as_ok(); - idx = f->total_shards_; - } - } - - kv_->begin_transaction().ensure(); - kv_->set(key, serialize_tl_object(v, true)).ensure(); - kv_->set(db_key, db_value.as_slice()).ensure(); - if (add_shard) { - auto shard_key = create_serialize_tl_object(idx); - auto shard_value = create_serialize_tl_object(block_id.id.workchain, block_id.id.shard); - kv_->set(status_key.as_slice(), create_serialize_tl_object(idx + 1)).ensure(); - kv_->set(shard_key.as_slice(), shard_value.as_slice()).ensure(); - } - kv_->commit_transaction().ensure(); - - promise.set_value(td::Unit()); -} - -void LtDb::get_block_common(AccountIdPrefixFull account_id, - std::function compare_desc, - std::function compare, bool exact, - td::Promise promise) { - bool f = false; - BlockIdExt block_id; - td::uint32 ls = 0; - for (td::uint32 len = 0; len <= 60; len++) { - auto s = shard_prefix(account_id, len); - auto key = get_desc_key(s); - std::string value; - auto F = kv_->get(key, value); - F.ensure(); - if (F.move_as_ok() == td::KeyValue::GetStatus::NotFound) { - if (!f) { - continue; - } else { - break; - } - } - f = true; - auto G = fetch_tl_object(td::BufferSlice{value}, true); - G.ensure(); - auto g = G.move_as_ok(); - if (compare_desc(*g.get()) > 0) { - continue; - } - td::uint32 l = g->first_idx_ - 1; - BlockIdExt lseq; - td::uint32 r = g->last_idx_; - BlockIdExt rseq; - while (r - l > 1) { - auto x = (r + l) / 2; - auto db_key = get_el_key(s, x); - F = kv_->get(db_key, value); - F.ensure(); - CHECK(F.move_as_ok() == td::KeyValue::GetStatus::Ok); - auto E = fetch_tl_object(td::BufferSlice{value}, true); - E.ensure(); - auto e = E.move_as_ok(); - int cmp_val = compare(*e.get()); - - if (cmp_val < 0) { - rseq = create_block_id(e->id_); - r = x; - } else if (cmp_val > 0) { - lseq = create_block_id(e->id_); - l = x; - } else { - promise.set_value(create_block_id(e->id_)); - return; - } - } - if (rseq.is_valid()) { - if (!block_id.is_valid()) { - block_id = rseq; - } else if (block_id.id.seqno > rseq.id.seqno) { - block_id = rseq; - } - } - if (lseq.is_valid()) { - if (ls < lseq.id.seqno) { - ls = lseq.id.seqno; - } - } - if (block_id.is_valid() && ls + 1 == block_id.id.seqno) { - if (!exact) { - promise.set_value(std::move(block_id)); - } else { - promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); - } - return; - } - } - if (!exact && block_id.is_valid()) { - promise.set_value(std::move(block_id)); - } else { - promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); - } -} - -void LtDb::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise) { - return get_block_common( - account_id, - [lt](ton_api::db_lt_desc_value &w) { - return lt > static_cast(w.last_lt_) ? 1 : lt == static_cast(w.last_lt_) ? 0 : -1; - }, - [lt](ton_api::db_lt_el_value &w) { - return lt > static_cast(w.lt_) ? 1 : lt == static_cast(w.lt_) ? 0 : -1; - }, - false, std::move(promise)); -} - -void LtDb::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise) { - return get_block_common( - account_id, - [seqno](ton_api::db_lt_desc_value &w) { - return seqno > static_cast(w.last_seqno_) - ? 1 - : seqno == static_cast(w.last_seqno_) ? 0 : -1; - }, - [seqno](ton_api::db_lt_el_value &w) { - return seqno > static_cast(w.id_->seqno_) - ? 1 - : seqno == static_cast(w.id_->seqno_) ? 0 : -1; - }, - true, std::move(promise)); -} - -void LtDb::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise) { - return get_block_common( - account_id, - [ts](ton_api::db_lt_desc_value &w) { - return ts > static_cast(w.last_ts_) ? 1 : ts == static_cast(w.last_ts_) ? 0 : -1; - }, - [ts](ton_api::db_lt_el_value &w) { - return ts > static_cast(w.ts_) ? 1 : ts == static_cast(w.ts_) ? 0 : -1; - }, - false, std::move(promise)); -} - -td::BufferSlice LtDb::get_desc_key(ShardIdFull shard) { - return create_serialize_tl_object(shard.workchain, shard.shard); -} - -td::BufferSlice LtDb::get_el_key(ShardIdFull shard, td::uint32 idx) { - return create_serialize_tl_object(shard.workchain, shard.shard, idx); -} - -void LtDb::start_up() { - kv_ = std::make_shared(td::RocksDb::open(db_path_).move_as_ok()); -} - -void LtDb::truncate_workchain(ShardIdFull shard, td::Ref state) { - auto key = get_desc_key(shard); - std::string value; - auto R = kv_->get(key, value); - R.ensure(); - CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok); - auto F = fetch_tl_object(td::BufferSlice{value}, true); - F.ensure(); - auto f = F.move_as_ok(); - - auto shards = state->get_shards(); - BlockSeqno seqno = 0; - if (shard.is_masterchain()) { - seqno = state->get_seqno(); - } else { - for (auto s : shards) { - if (shard_intersects(s->shard(), shard)) { - seqno = s->top_block_id().seqno(); - break; - } - } - } - - while (f->last_idx_ > f->first_idx_) { - auto db_key = get_el_key(shard, f->last_idx_ - 1); - R = kv_->get(db_key, value); - R.ensure(); - CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok); - auto E = fetch_tl_object(td::BufferSlice{value}, true); - E.ensure(); - auto e = E.move_as_ok(); - - bool to_delete = static_cast(e->id_->seqno_) > seqno; - - if (!to_delete) { - break; - } else { - f->last_idx_--; - kv_->erase(db_key).ensure(); - } - } - - if (f->first_idx_ == f->last_idx_) { - f->last_ts_ = 0; - f->last_lt_ = 0; - f->last_seqno_ = 0; - } - - kv_->set(key, serialize_tl_object(f, true)).ensure(); -} - -void LtDb::truncate(td::Ref state, td::Promise promise) { - auto status_key = create_serialize_tl_object(); - td::Result R; - td::uint32 total_shards = 0; - { - std::string value; - R = kv_->get(status_key.as_slice(), value); - R.ensure(); - if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { - promise.set_value(td::Unit()); - return; - } - auto F = fetch_tl_object(value, true); - F.ensure(); - auto f = F.move_as_ok(); - total_shards = f->total_shards_; - if (total_shards == 0) { - promise.set_value(td::Unit()); - return; - } - } - kv_->begin_transaction().ensure(); - for (td::uint32 idx = 0; idx < total_shards; idx++) { - auto shard_key = create_serialize_tl_object(idx); - std::string value; - R = kv_->get(shard_key.as_slice(), value); - R.ensure(); - CHECK(R.move_as_ok() == td::KeyValue::GetStatus::Ok); - auto F = fetch_tl_object(value, true); - F.ensure(); - auto f = F.move_as_ok(); - - truncate_workchain(ShardIdFull{f->workchain_, static_cast(f->shard_)}, state); - } - kv_->commit_transaction().ensure(); - promise.set_value(td::Unit()); -} - -} // namespace validator - -} // namespace ton diff --git a/validator/db/ltdb.hpp b/validator/db/ltdb.hpp deleted file mode 100644 index 4c7651ec5d..0000000000 --- a/validator/db/ltdb.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - 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 . - - Copyright 2017-2019 Telegram Systems LLP -*/ -#pragma once - -#include "td/actor/actor.h" -#include "td/db/KeyValueAsync.h" -#include "validator/interfaces/db.h" - -#include "ton/ton-types.h" - -#include "auto/tl/ton_api.h" - -namespace ton { - -namespace validator { - -class RootDb; - -class LtDb : public td::actor::Actor { - public: - void add_new_block(BlockIdExt block_id, LogicalTime lt, UnixTime ts, td::Promise promise); - void get_block_common(AccountIdPrefixFull account_id, - std::function compare_desc, - std::function compare, bool exact, - td::Promise promise); - void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); - void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); - void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); - - void truncate_workchain(ShardIdFull shard, td::Ref state); - void truncate(td::Ref state, td::Promise promise); - - void start_up() override; - - LtDb(td::actor::ActorId root_db, std::string db_path) : root_db_(root_db), db_path_(std::move(db_path)) { - } - - private: - td::BufferSlice get_desc_key(ShardIdFull shard); - td::BufferSlice get_el_key(ShardIdFull shard, td::uint32 idx); - td::BufferSlice get_from_db(ShardIdFull shard, td::uint32 idx); - - std::shared_ptr kv_; - - td::actor::ActorId root_db_; - std::string db_path_; -}; - -} // namespace validator - -} // namespace ton diff --git a/validator/db/package.cpp b/validator/db/package.cpp index 76f504a36e..e15767dfc6 100644 --- a/validator/db/package.cpp +++ b/validator/db/package.cpp @@ -34,7 +34,7 @@ td::Status Package::truncate(td::uint64 size) { return fd_.truncate_to_current_position(size + header_size()); } -td::uint64 Package::append(std::string filename, td::Slice data) { +td::uint64 Package::append(std::string filename, td::Slice data, bool sync) { CHECK(data.size() <= max_data_size()); CHECK(filename.size() <= max_filename_size()); auto size = fd_.get_size().move_as_ok(); @@ -48,10 +48,16 @@ td::uint64 Package::append(std::string filename, td::Slice data) { size += filename.size(); CHECK(fd_.pwrite(data, size).move_as_ok() == data.size()); size += data.size(); - fd_.sync().ensure(); + if (sync) { + fd_.sync().ensure(); + } return orig_size - header_size(); } +void Package::sync() { + fd_.sync().ensure(); +} + td::uint64 Package::size() const { return fd_.get_size().move_as_ok() - header_size(); } @@ -140,4 +146,28 @@ td::Result Package::open(std::string path, bool read_only, bool create) return Package{std::move(fd)}; } +void Package::iterate(std::function func) { + td::uint64 p = 0; + + td::uint64 size = fd_.get_size().move_as_ok(); + if (size < header_size()) { + LOG(ERROR) << "too short archive"; + return; + } + size -= header_size(); + while (p != size) { + auto R = read(p); + if (R.is_error()) { + LOG(ERROR) << "broken archive: " << R.move_as_error(); + return; + } + auto q = R.move_as_ok(); + if (!func(q.first, q.second.clone(), p)) { + break; + } + + p = advance(p).move_as_ok(); + } +} + } // namespace ton diff --git a/validator/db/package.hpp b/validator/db/package.hpp index 50d6ad0a3d..b4236d05d4 100644 --- a/validator/db/package.hpp +++ b/validator/db/package.hpp @@ -14,24 +14,17 @@ class Package { td::Status truncate(td::uint64 size); - td::uint64 append(std::string filename, td::Slice data); + td::uint64 append(std::string filename, td::Slice data, bool sync = true); + void sync(); td::uint64 size() const; td::Result> read(td::uint64 offset) const; - td::Result advance(td::uint64 offset); - - struct Iterator { - td::uint64 offset; - Package &package; - Iterator operator++(int); - const Iterator operator++(int) const; - td::Result> read() const; - }; + td::Result advance(td::uint64 offset); + void iterate(std::function func); - Iterator begin(); - const Iterator begin() const; - Iterator end(); - const Iterator end() const; + td::FileFd &fd() { + return fd_; + } private: td::FileFd fd_; diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index a0e4d5217a..21cc196d01 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -32,22 +32,22 @@ namespace ton { namespace validator { void RootDb::store_block_data(BlockHandle handle, td::Ref block, td::Promise promise) { - if (handle->moved_to_storage() || handle->moved_to_archive()) { + if (handle->received()) { promise.set_value(td::Unit()); return; } - auto id = block_db_.get(); - auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - handle->set_received(); - td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); - } - }); + auto P = td::PromiseCreator::lambda( + [id = archive_db_.get(), handle, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + handle->set_received(); + td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise)); + } + }); - td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::Block{handle->id()}}, block->data(), + td::actor::send_closure(archive_db_, &ArchiveManager::add_file, handle, fileref::Block{handle->id()}, block->data(), std::move(P)); } @@ -64,45 +64,34 @@ void RootDb::get_block_data(BlockHandle handle, td::Promise> } }); - if (handle->moved_to_archive()) { - td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(), - FileDb::RefId{fileref::Block{handle->id()}}, std::move(P)); - } else { - td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file, - FileDb::RefId{fileref::Block{handle->id()}}, std::move(P)); - } + td::actor::send_closure(archive_db_, &ArchiveManager::get_file, handle, fileref::Block{handle->id()}, std::move(P)); } } void RootDb::store_block_signatures(BlockHandle handle, td::Ref data, td::Promise promise) { - if (handle->moved_to_storage() || handle->moved_to_archive()) { + if (handle->inited_signatures() || handle->moved_to_archive()) { promise.set_value(td::Unit()); return; } - auto id = block_db_.get(); - - auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - handle->set_signatures(); - td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); - } - }); - td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::Signatures{handle->id()}}, + auto P = td::PromiseCreator::lambda( + [id = archive_db_.get(), handle, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + handle->set_signatures(); + td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise)); + } + }); + td::actor::send_closure(archive_db_, &ArchiveManager::add_temp_file_short, fileref::Signatures{handle->id()}, data->serialize(), std::move(P)); } void RootDb::get_block_signatures(BlockHandle handle, td::Promise> promise) { - if (!handle->inited_signatures()) { + if (!handle->inited_signatures() || handle->moved_to_archive()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { - if (handle->moved_to_storage() || handle->moved_to_archive()) { - promise.set_error(td::Status::Error(ErrorCode::error, "signatures already gc'd")); - return; - } auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); @@ -110,28 +99,28 @@ void RootDb::get_block_signatures(BlockHandle handle, td::Promiseid()}}, + td::actor::send_closure(archive_db_, &ArchiveManager::get_temp_file_short, fileref::Signatures{handle->id()}, std::move(P)); } } void RootDb::store_block_proof(BlockHandle handle, td::Ref proof, td::Promise promise) { - if (handle->moved_to_storage() || handle->moved_to_archive()) { + if (handle->inited_proof()) { promise.set_value(td::Unit()); return; } - auto id = block_db_.get(); - auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - handle->set_proof(); - td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); - } - }); + auto P = td::PromiseCreator::lambda( + [id = archive_db_.get(), handle, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + handle->set_proof(); + td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise)); + } + }); - td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::Proof{handle->id()}}, proof->data(), + td::actor::send_closure(archive_db_, &ArchiveManager::add_file, handle, fileref::Proof{handle->id()}, proof->data(), std::move(P)); } @@ -147,34 +136,27 @@ void RootDb::get_block_proof(BlockHandle handle, td::Promise> pro promise.set_result(create_proof(id, R.move_as_ok())); } }); - if (handle->moved_to_archive()) { - td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(), - FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P)); - } else { - td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file, - FileDb::RefId{fileref::Proof{handle->id()}}, std::move(P)); - } + td::actor::send_closure(archive_db_, &ArchiveManager::get_file, handle, fileref::Proof{handle->id()}, std::move(P)); } } void RootDb::store_block_proof_link(BlockHandle handle, td::Ref proof, td::Promise promise) { - if (handle->moved_to_storage() || handle->moved_to_archive()) { + if (handle->inited_proof_link()) { promise.set_value(td::Unit()); return; } - auto id = block_db_.get(); - - auto P = td::PromiseCreator::lambda([id, handle, promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - handle->set_proof_link(); - td::actor::send_closure(id, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); - } - }); - td::actor::send_closure(file_db_, &FileDb::store_file, FileDb::RefId{fileref::ProofLink{handle->id()}}, proof->data(), - std::move(P)); + auto P = td::PromiseCreator::lambda( + [id = archive_db_.get(), handle, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + handle->set_proof_link(); + td::actor::send_closure(id, &ArchiveManager::update_handle, std::move(handle), std::move(promise)); + } + }); + td::actor::send_closure(archive_db_, &ArchiveManager::add_file, handle, fileref::ProofLink{handle->id()}, + proof->data(), std::move(P)); } void RootDb::get_block_proof_link(BlockHandle handle, td::Promise> promise) { @@ -189,13 +171,8 @@ void RootDb::get_block_proof_link(BlockHandle handle, td::Promisemoved_to_archive()) { - td::actor::send_closure(new_archive_db_, &ArchiveManager::read, handle->unix_time(), handle->is_key_block(), - FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P)); - } else { - td::actor::send_closure(handle->moved_to_storage() ? old_archive_db_.get() : file_db_.get(), &FileDb::load_file, - FileDb::RefId{fileref::ProofLink{handle->id()}}, std::move(P)); - } + td::actor::send_closure(archive_db_, &ArchiveManager::get_file, handle, fileref::ProofLink{handle->id()}, + std::move(P)); } } @@ -204,16 +181,16 @@ void RootDb::store_block_candidate(BlockCandidate candidate, td::Promise R) mutable { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); } else { promise.set_value(td::Unit()); } }); - td::actor::send_closure(file_db_, &FileDb::store_file, - FileDb::RefId{fileref::Candidate{PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}, - candidate.id, candidate.collated_file_hash}}, + td::actor::send_closure(archive_db_, &ArchiveManager::add_temp_file_short, + fileref::Candidate{PublicKey{pubkeys::Ed25519{candidate.pubkey.as_bits256()}}, candidate.id, + candidate.collated_file_hash}, std::move(obj), std::move(P)); } @@ -234,18 +211,18 @@ void RootDb::get_block_candidate(PublicKey source, BlockIdExt id, FileHash colla std::move(val->collated_data_)}); } }); - td::actor::send_closure(file_db_, &FileDb::load_file, - FileDb::RefId{fileref::Candidate{source, id, collated_data_file_hash}}, std::move(P)); + td::actor::send_closure(archive_db_, &ArchiveManager::get_temp_file_short, + fileref::Candidate{source, id, collated_data_file_hash}, std::move(P)); } void RootDb::store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) { - if (handle->moved_to_storage() || handle->moved_to_archive()) { + if (handle->moved_to_archive()) { promise.set_value(std::move(state)); return; } if (!handle->inited_state_boc()) { - auto P = td::PromiseCreator::lambda([b = block_db_.get(), root_hash = state->root_hash(), handle, + auto P = td::PromiseCreator::lambda([b = archive_db_.get(), root_hash = state->root_hash(), handle, promise = std::move(promise)](td::Result> R) mutable { if (R.is_error()) { promise.set_error(R.move_as_error()); @@ -262,7 +239,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref state, promise.set_value(std::move(state)); }); - td::actor::send_closure(b, &BlockDb::store_block_handle, std::move(handle), std::move(P)); + td::actor::send_closure(b, &ArchiveManager::update_handle, std::move(handle), std::move(P)); } }); td::actor::send_closure(cell_db_, &CellDb::store_cell, handle->id(), state->root_cell(), std::move(P)); @@ -295,83 +272,46 @@ void RootDb::get_block_state(BlockHandle handle, td::Promise void RootDb::store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice state, td::Promise promise) { - auto id = block_db_.get(); - - auto P = td::PromiseCreator::lambda([id, promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - promise.set_value(td::Unit()); - } - }); - - td::actor::send_closure(old_archive_db_, &FileDb::store_file, - FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(state), - std::move(P)); + td::actor::send_closure(archive_db_, &ArchiveManager::add_persistent_state, block_id, masterchain_block_id, + std::move(state), std::move(promise)); } void RootDb::get_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { - td::actor::send_closure(old_archive_db_, &FileDb::load_file, - FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise)); + td::actor::send_closure(archive_db_, &ArchiveManager::get_persistent_state, block_id, masterchain_block_id, + std::move(promise)); } void RootDb::get_persistent_state_file_slice(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::int64 offset, td::int64 max_size, td::Promise promise) { - td::actor::send_closure(old_archive_db_, &FileDb::load_file_slice, - FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, offset, max_size, - std::move(promise)); + td::actor::send_closure(archive_db_, &ArchiveManager::get_persistent_state_slice, block_id, masterchain_block_id, + offset, max_size, std::move(promise)); } void RootDb::check_persistent_state_file_exists(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { - td::actor::send_closure(old_archive_db_, &FileDb::check_file, - FileDb::RefId{fileref::PersistentState{block_id, masterchain_block_id}}, std::move(promise)); + td::actor::send_closure(archive_db_, &ArchiveManager::check_persistent_state, block_id, masterchain_block_id, + std::move(promise)); } void RootDb::store_zero_state_file(BlockIdExt block_id, td::BufferSlice state, td::Promise promise) { - auto id = block_db_.get(); - - auto P = td::PromiseCreator::lambda([id, promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - promise.set_value(td::Unit()); - } - }); - - td::actor::send_closure(old_archive_db_, &FileDb::store_file, FileDb::RefId{fileref::ZeroState{block_id}}, - std::move(state), std::move(P)); + td::actor::send_closure(archive_db_, &ArchiveManager::add_zero_state, block_id, std::move(state), std::move(promise)); } void RootDb::get_zero_state_file(BlockIdExt block_id, td::Promise promise) { - td::actor::send_closure(old_archive_db_, &FileDb::load_file, FileDb::RefId{fileref::ZeroState{block_id}}, - std::move(promise)); + td::actor::send_closure(archive_db_, &ArchiveManager::get_zero_state, block_id, std::move(promise)); } void RootDb::check_zero_state_file_exists(BlockIdExt block_id, td::Promise promise) { - td::actor::send_closure(old_archive_db_, &FileDb::check_file, FileDb::RefId{fileref::ZeroState{block_id}}, - std::move(promise)); + td::actor::send_closure(archive_db_, &ArchiveManager::check_zero_state, block_id, std::move(promise)); } void RootDb::store_block_handle(BlockHandle handle, td::Promise promise) { - if (handle->moved_to_archive()) { - td::actor::send_closure(new_archive_db_, &ArchiveManager::write_handle, std::move(handle), std::move(promise)); - } else { - td::actor::send_closure(block_db_, &BlockDb::store_block_handle, std::move(handle), std::move(promise)); - } + td::actor::send_closure(archive_db_, &ArchiveManager::update_handle, std::move(handle), std::move(promise)); } void RootDb::get_block_handle(BlockIdExt id, td::Promise promise) { - auto P = td::PromiseCreator::lambda( - [db = block_db_.get(), id, promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - td::actor::send_closure(db, &BlockDb::get_block_handle, id, std::move(promise)); - } else { - promise.set_value(R.move_as_ok()); - } - }); - td::actor::send_closure(new_archive_db_, &ArchiveManager::read_handle, id, std::move(P)); + td::actor::send_closure(archive_db_, &ArchiveManager::get_handle, id, std::move(promise)); } void RootDb::try_get_static_file(FileHash file_hash, td::Promise promise) { @@ -379,24 +319,20 @@ void RootDb::try_get_static_file(FileHash file_hash, td::Promise promise) { - if (handle->id().id.seqno == 0) { - promise.set_value(td::Unit()); - } else { - td::actor::send_closure(lt_db_, &LtDb::add_new_block, handle->id(), handle->logical_time(), handle->unix_time(), - std::move(promise)); - } + td::actor::create_actor("archiver", std::move(handle), archive_db_.get(), std::move(promise)) + .release(); } -void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) { - td::actor::send_closure(lt_db_, &LtDb::get_block_by_lt, account, lt, std::move(promise)); +void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) { + td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_lt, account, lt, std::move(promise)); } -void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) { - td::actor::send_closure(lt_db_, &LtDb::get_block_by_unix_time, account, ts, std::move(promise)); +void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) { + td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_unix_time, account, ts, std::move(promise)); } -void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) { - td::actor::send_closure(lt_db_, &LtDb::get_block_by_seqno, account, seqno, std::move(promise)); +void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) { + td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_seqno, account, seqno, std::move(promise)); } void RootDb::update_init_masterchain_block(BlockIdExt block, td::Promise promise) { @@ -451,19 +387,13 @@ void RootDb::get_hardforks(td::Promise> promise) { void RootDb::start_up() { cell_db_ = td::actor::create_actor("celldb", actor_id(this), root_path_ + "/celldb/"); - block_db_ = td::actor::create_actor("blockdb", actor_id(this), root_path_ + "/blockdb/"); - file_db_ = td::actor::create_actor("filedb", actor_id(this), root_path_ + "/files/", depth_, false); - old_archive_db_ = - td::actor::create_actor("filedbarchive", actor_id(this), root_path_ + "/archive/", depth_, true); - lt_db_ = td::actor::create_actor("ltdb", actor_id(this), root_path_ + "/ltdb/"); state_db_ = td::actor::create_actor("statedb", actor_id(this), root_path_ + "/state/"); static_files_db_ = td::actor::create_actor("staticfilesdb", actor_id(this), root_path_ + "/static/"); - new_archive_db_ = td::actor::create_actor("archivemanager", root_path_ + "/archive/"); + archive_db_ = td::actor::create_actor("archive", actor_id(this), root_path_); } -void RootDb::archive(BlockIdExt block_id, td::Promise promise) { - td::actor::create_actor("archiveblock", block_id, actor_id(this), file_db_.get(), - old_archive_db_.get(), new_archive_db_.get(), std::move(promise)) +void RootDb::archive(BlockHandle handle, td::Promise promise) { + td::actor::create_actor("archiveblock", std::move(handle), archive_db_.get(), std::move(promise)) .release(); } @@ -475,57 +405,86 @@ void RootDb::allow_block_gc(BlockIdExt block_id, td::Promise promise) { td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_info_gc, block_id, std::move(promise)); } -void RootDb::allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise promise) { - ref_id.visit( - td::overloaded([&](const fileref::Empty &key) { UNREACHABLE(); }, - [&](const fileref::Block &key) { - td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_data_gc, key.block_id, - is_archive, std::move(promise)); - }, - [&](const fileref::ZeroState &key) { - td::actor::send_closure(validator_manager_, &ValidatorManager::allow_zero_state_file_gc, - key.block_id, std::move(promise)); - }, - [&](const fileref::PersistentState &key) { - CHECK(is_archive); - td::actor::send_closure(validator_manager_, &ValidatorManager::allow_persistent_state_file_gc, - key.block_id, key.masterchain_block_id, std::move(promise)); - }, - [&](const fileref::Proof &key) { - td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_proof_gc, - key.block_id, is_archive, std::move(promise)); - }, - [&](const fileref::ProofLink &key) { - td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_proof_link_gc, - key.block_id, is_archive, std::move(promise)); - }, - [&](const fileref::Signatures &key) { - CHECK(!is_archive); - td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_signatures_gc, - key.block_id, std::move(promise)); - }, - [&](const fileref::Candidate &key) { - CHECK(!is_archive); - td::actor::send_closure(validator_manager_, &ValidatorManager::allow_block_candidate_gc, - key.block_id, std::move(promise)); - }, - [&](const fileref::BlockInfo &key) { UNREACHABLE(); })); -} - void RootDb::prepare_stats(td::Promise>> promise) { auto merger = StatsMerger::create(std::move(promise)); - - td::actor::send_closure(file_db_, &FileDb::prepare_stats, merger.make_promise("filedb.")); - td::actor::send_closure(old_archive_db_, &FileDb::prepare_stats, merger.make_promise("archivedb.")); } void RootDb::truncate(td::Ref state, td::Promise promise) { td::MultiPromise mp; auto ig = mp.init_guard(); ig.add_promise(std::move(promise)); +} + +void RootDb::add_key_block_proof(td::Ref proof, td::Promise promise) { + auto i = proof->get_basic_header_info().move_as_ok(); + td::actor::send_closure(archive_db_, &ArchiveManager::add_key_block_proof, i.utime, proof->block_id().seqno(), + i.end_lt, fileref::Proof{proof->block_id()}, proof->data(), std::move(promise)); +} + +void RootDb::add_key_block_proof_link(td::Ref proof, td::Promise promise) { + auto i = proof->get_basic_header_info().move_as_ok(); + td::actor::send_closure(archive_db_, &ArchiveManager::add_key_block_proof, i.utime, proof->block_id().seqno(), + i.end_lt, fileref::ProofLink{proof->block_id()}, proof->data(), std::move(promise)); +} +void RootDb::get_key_block_proof(BlockIdExt block_id, td::Promise> promise) { + auto P = td::PromiseCreator::lambda([block_id, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + promise.set_result(create_proof(block_id, R.move_as_ok())); + } + }); + td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::Proof{block_id}, std::move(P)); +} +void RootDb::get_key_block_proof_link(BlockIdExt block_id, td::Promise> promise) { + auto P = td::PromiseCreator::lambda([block_id, promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + promise.set_result(create_proof_link(block_id, R.move_as_ok())); + } + }); + td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::Proof{block_id}, std::move(P)); +} + +void RootDb::check_key_block_proof_exists(BlockIdExt block_id, td::Promise promise) { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_result(false); + } else { + promise.set_result(true); + } + }); + td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::Proof{block_id}, std::move(P)); +} +void RootDb::check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise promise) { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_result(false); + } else { + promise.set_result(true); + } + }); + td::actor::send_closure(archive_db_, &ArchiveManager::get_key_block_proof, fileref::ProofLink{block_id}, + std::move(P)); +} + +void RootDb::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { + td::actor::send_closure(archive_db_, &ArchiveManager::get_archive_id, masterchain_seqno, std::move(promise)); +} + +void RootDb::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) { + td::actor::send_closure(archive_db_, &ArchiveManager::get_archive_slice, archive_id, offset, limit, + std::move(promise)); +} + +void RootDb::set_async_mode(bool mode, td::Promise promise) { + td::actor::send_closure(archive_db_, &ArchiveManager::set_async_mode, mode, std::move(promise)); +} - td::actor::send_closure(lt_db_, &LtDb::truncate, state, ig.get_promise()); - td::actor::send_closure(block_db_, &BlockDb::truncate, state, ig.get_promise()); +void RootDb::run_gc(UnixTime ts) { + td::actor::send_closure(archive_db_, &ArchiveManager::run_gc, ts); } } // namespace validator diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index 47cd31be18..373941e7cb 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -22,13 +22,10 @@ #include "td/db/KeyValueAsync.h" #include "ton/ton-types.h" -#include "blockdb.hpp" #include "celldb.hpp" -#include "filedb.hpp" -#include "ltdb.hpp" #include "statedb.hpp" #include "staticfilesdb.hpp" -#include "archive-db.hpp" +#include "archive-manager.hpp" namespace ton { @@ -85,9 +82,9 @@ class RootDb : public Db { void try_get_static_file(FileHash file_hash, td::Promise promise) override; void apply_block(BlockHandle handle, td::Promise promise) override; - void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; - void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) override; - void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) override; + void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) override; + void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) override; void update_init_masterchain_block(BlockIdExt block, td::Promise promise) override; void get_init_masterchain_block(td::Promise promise) override; @@ -108,16 +105,30 @@ class RootDb : public Db { void update_hardforks(std::vector blocks, td::Promise promise) override; void get_hardforks(td::Promise> promise) override; - void archive(BlockIdExt block_id, td::Promise promise) override; + void archive(BlockHandle handle, td::Promise promise) override; void allow_state_gc(BlockIdExt block_id, td::Promise promise); void allow_block_gc(BlockIdExt block_id, td::Promise promise); - void allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise promise); + //void allow_gc(FileDb::RefId ref_id, bool is_archive, td::Promise promise); void prepare_stats(td::Promise>> promise) override; void truncate(td::Ref state, td::Promise promise) override; + void add_key_block_proof(td::Ref proof, td::Promise promise) override; + void add_key_block_proof_link(td::Ref proof_link, td::Promise promise) override; + void get_key_block_proof(BlockIdExt block_id, td::Promise> promise) override; + void get_key_block_proof_link(BlockIdExt block_id, td::Promise> promise) override; + void check_key_block_proof_exists(BlockIdExt block_id, td::Promise promise) override; + void check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise promise) override; + + void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) override; + void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) override; + void set_async_mode(bool mode, td::Promise promise) override; + + void run_gc(UnixTime ts) override; + private: td::actor::ActorId validator_manager_; @@ -125,13 +136,9 @@ class RootDb : public Db { td::uint32 depth_; td::actor::ActorOwn cell_db_; - td::actor::ActorOwn block_db_; - td::actor::ActorOwn file_db_; - td::actor::ActorOwn old_archive_db_; - td::actor::ActorOwn lt_db_; td::actor::ActorOwn state_db_; td::actor::ActorOwn static_files_db_; - td::actor::ActorOwn new_archive_db_; + td::actor::ActorOwn archive_db_; }; } // namespace validator diff --git a/validator/db/statedb.cpp b/validator/db/statedb.cpp index ed78626e6d..7add44e33f 100644 --- a/validator/db/statedb.cpp +++ b/validator/db/statedb.cpp @@ -222,6 +222,22 @@ StateDb::StateDb(td::actor::ActorId root_db, std::string db_path) : root void StateDb::start_up() { kv_ = std::make_shared(td::RocksDb::open(db_path_).move_as_ok()); + + std::string value; + auto R = kv_->get(create_serialize_tl_object(), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::Ok) { + auto F = fetch_tl_object(value, true); + F.ensure(); + auto f = F.move_as_ok(); + CHECK(f->version_ == 2); + } else { + kv_->begin_transaction().ensure(); + kv_->set(create_serialize_tl_object(), + create_serialize_tl_object(2)) + .ensure(); + kv_->commit_transaction().ensure(); + } } } // namespace validator diff --git a/validator/db/statedb.hpp b/validator/db/statedb.hpp index af5fde4f4c..7da435db5f 100644 --- a/validator/db/statedb.hpp +++ b/validator/db/statedb.hpp @@ -50,6 +50,9 @@ class StateDb : public td::actor::Actor { void update_hardforks(std::vector blocks, td::Promise promise); void get_hardforks(td::Promise> promise); + void update_db_version(td::uint32 version, td::Promise promise); + void get_db_version(td::Promise promise); + StateDb(td::actor::ActorId root_db, std::string path); void start_up() override; diff --git a/validator/downloaders/download-state.cpp b/validator/downloaders/download-state.cpp index 8cbbf32765..9e690fffdb 100644 --- a/validator/downloaders/download-state.cpp +++ b/validator/downloaders/download-state.cpp @@ -194,11 +194,13 @@ void DownloadShardState::written_shard_state(td::Ref state) { handle_->set_applied(); handle_->set_split(state_->before_split()); - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result R) { + CHECK(handle->handle_moved_to_archive()); + CHECK(handle->moved_to_archive()) R.ensure(); td::actor::send_closure(SelfId, &DownloadShardState::written_block_handle); }); - handle_->flush(manager_, handle_, std::move(P)); + td::actor::send_closure(manager_, &ValidatorManager::archive, handle_, std::move(P)); } void DownloadShardState::written_block_handle() { diff --git a/validator/fabric.h b/validator/fabric.h index 54ad09a9c8..41df565be9 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -54,8 +54,9 @@ void run_fake_accept_block_query(BlockIdExt id, td::Ref data, std::ve td::Promise promise); void run_hardfork_accept_block_query(BlockIdExt id, td::Ref data, td::actor::ActorId manager, td::Promise promise); -void run_apply_block_query(BlockIdExt id, td::Ref block, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise promise); +void run_apply_block_query(BlockIdExt id, td::Ref block, BlockIdExt masterchain_block_id, + td::actor::ActorId manager, td::Timestamp timeout, + td::Promise promise); void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, td::Timestamp timeout, td::Promise promise, bool skip_check_signatures = false); void run_check_proof_query(BlockIdExt id, td::Ref proof, td::actor::ActorId manager, diff --git a/validator/full-node-master.cpp b/validator/full-node-master.cpp index 96cb25769c..a5eaefac18 100644 --- a/validator/full-node-master.cpp +++ b/validator/full-node-master.cpp @@ -309,6 +309,26 @@ void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNo promise.set_value(create_serialize_tl_object(proto_version(), proto_capabilities())); } +void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveInfo &query, + td::Promise promise) { + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(create_serialize_tl_object()); + } else { + promise.set_value(create_serialize_tl_object(R.move_as_ok())); + } + }); + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_archive_id, query.masterchain_seqno_, + std::move(P)); +} + +void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, + td::Promise promise) { + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_archive_slice, query.archive_id_, + query.offset_, query.max_size_, std::move(promise)); +} + void FullNodeMasterImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_slave_sendExtMessage &query, td::Promise promise) { td::actor::send_closure( diff --git a/validator/full-node-master.hpp b/validator/full-node-master.hpp index 0db425fc18..00e6e1f791 100644 --- a/validator/full-node-master.hpp +++ b/validator/full-node-master.hpp @@ -72,6 +72,10 @@ class FullNodeMasterImpl : public FullNodeMaster { td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_slave_sendExtMessage &query, td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveInfo &query, + td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, + td::Promise promise); // void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_prepareNextKeyBlockProof &query, // td::Promise promise); void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise); diff --git a/validator/full-node-shard.cpp b/validator/full-node-shard.cpp index 4c6caa2005..e82b0905a6 100644 --- a/validator/full-node-shard.cpp +++ b/validator/full-node-shard.cpp @@ -31,6 +31,7 @@ #include "net/download-state.hpp" #include "net/download-proof.hpp" #include "net/get-next-key-blocks.hpp" +#include "net/download-archive-slice.hpp" #include "td/utils/Random.h" @@ -146,6 +147,7 @@ void FullNodeShardImpl::got_next_block(td::Result R) { } void FullNodeShardImpl::get_next_block() { + //return; attempt_++; auto P = td::PromiseCreator::lambda([validator_manager = validator_manager_, attempt = attempt_, block_id = handle_->id(), SelfId = actor_id(this)](td::Result R) { @@ -450,6 +452,26 @@ void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNod promise.set_value(create_serialize_tl_object(proto_version(), proto_capabilities())); } +void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveInfo &query, + td::Promise promise) { + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_value(create_serialize_tl_object()); + } else { + promise.set_value(create_serialize_tl_object(R.move_as_ok())); + } + }); + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_archive_id, query.masterchain_seqno_, + std::move(P)); +} + +void FullNodeShardImpl::process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, + td::Promise promise) { + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_archive_slice, query.archive_id_, + query.offset_, query.max_size_, std::move(promise)); +} + void FullNodeShardImpl::receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise) { auto B = fetch_tl_object(std::move(query), true); @@ -637,6 +659,15 @@ void FullNodeShardImpl::get_next_key_blocks(BlockIdExt block_id, td::Timestamp t .release(); } +void FullNodeShardImpl::download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + td::Promise promise) { + auto &b = choose_neighbour(); + td::actor::create_actor( + "archive", masterchain_seqno, std::move(tmp_dir), adnl_id_, overlay_id_, adnl::AdnlNodeIdShort::zero(), timeout, + validator_manager_, rldp_, overlays_, adnl_, client_, create_neighbour_promise(b, std::move(promise))) + .release(); +} + void FullNodeShardImpl::set_handle(BlockHandle handle, td::Promise promise) { CHECK(!handle_); handle_ = std::move(handle); @@ -741,7 +772,7 @@ void FullNodeShardImpl::update_validators(std::vector public_key_ authorized_keys.emplace(key, overlay::Overlays::max_fec_broadcast_size()); } - rules_ = overlay::OverlayPrivacyRules{overlay::Overlays::max_simple_broadcast_size(), std::move(authorized_keys)}; + rules_ = overlay::OverlayPrivacyRules{1 << 14, std::move(authorized_keys)}; td::actor::send_closure(overlays_, &overlay::Overlays::set_privacy_rules, adnl_id_, overlay_id_, rules_); if (update_cert) { diff --git a/validator/full-node-shard.h b/validator/full-node-shard.h index 4527e811ed..3487a867fb 100644 --- a/validator/full-node-shard.h +++ b/validator/full-node-shard.h @@ -55,6 +55,8 @@ class FullNodeShard : public td::actor::Actor { td::Promise promise) = 0; virtual void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) = 0; + virtual void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + td::Promise promise) = 0; virtual void set_handle(BlockHandle handle, td::Promise promise) = 0; diff --git a/validator/full-node-shard.hpp b/validator/full-node-shard.hpp index 1724b9a718..875c4b53f4 100644 --- a/validator/full-node-shard.hpp +++ b/validator/full-node-shard.hpp @@ -61,7 +61,7 @@ class FullNodeShardImpl : public FullNodeShard { return 1; } static constexpr td::uint32 proto_version() { - return 1; + return 2; } static constexpr td::uint64 proto_capabilities() { return 0; @@ -120,6 +120,10 @@ class FullNodeShardImpl : public FullNodeShard { td::Promise promise); void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getCapabilities &query, td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveInfo &query, + td::Promise promise); + void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_getArchiveSlice &query, + td::Promise promise); // void process_query(adnl::AdnlNodeIdShort src, ton_api::tonNode_prepareNextKeyBlockProof &query, // td::Promise promise); void receive_query(adnl::AdnlNodeIdShort src, td::BufferSlice query, td::Promise promise); @@ -148,6 +152,8 @@ class FullNodeShardImpl : public FullNodeShard { td::Promise promise) override; void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) override; + void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + td::Promise promise) override; void set_handle(BlockHandle handle, td::Promise promise) override; diff --git a/validator/full-node.cpp b/validator/full-node.cpp index 92040469e7..b55b1008c3 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -230,6 +230,14 @@ void FullNodeImpl::get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeou td::actor::send_closure(shard, &FullNodeShard::get_next_key_blocks, block_id, timeout, std::move(promise)); } +void FullNodeImpl::download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + td::Promise promise) { + auto shard = get_shard(ShardIdFull{masterchainId}); + CHECK(!shard.empty()); + td::actor::send_closure(shard, &FullNodeShard::download_archive, masterchain_seqno, std::move(tmp_dir), timeout, + std::move(promise)); +} + td::actor::ActorId FullNodeImpl::get_shard(ShardIdFull shard) { while (shards_.count(shard) == 0) { if (shard.shard == shardIdAll) { @@ -392,6 +400,11 @@ void FullNodeImpl::start_up() { td::Promise> promise) override { td::actor::send_closure(id_, &FullNodeImpl::get_next_key_blocks, block_id, timeout, std::move(promise)); } + void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + td::Promise promise) override { + td::actor::send_closure(id_, &FullNodeImpl::download_archive, masterchain_seqno, std::move(tmp_dir), timeout, + std::move(promise)); + } void new_key_block(BlockHandle handle) override { td::actor::send_closure(id_, &FullNodeImpl::new_key_block, std::move(handle)); diff --git a/validator/full-node.hpp b/validator/full-node.hpp index 8d08f6cd05..e4eab04a67 100644 --- a/validator/full-node.hpp +++ b/validator/full-node.hpp @@ -64,6 +64,8 @@ class FullNodeImpl : public FullNode { void download_block_proof_link(BlockIdExt block_id, td::uint32 priority, td::Timestamp timeout, td::Promise promise); void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise); + void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + td::Promise promise); void got_key_block_proof(td::Ref proof); void got_zero_block_state(td::Ref state); diff --git a/validator/impl/accept-block.cpp b/validator/impl/accept-block.cpp index f7c5d2775e..18cc0472e3 100644 --- a/validator/impl/accept-block.cpp +++ b/validator/impl/accept-block.cpp @@ -41,14 +41,16 @@ using namespace std::literals::string_literals; AcceptBlockQuery::AcceptBlockQuery(BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::Ref signatures, - bool send_broadcast, td::actor::ActorId manager, - td::Promise promise) + td::Ref approve_signatures, bool send_broadcast, + td::actor::ActorId manager, td::Promise promise) : id_(id) , data_(std::move(data)) , prev_(std::move(prev)) , validator_set_(std::move(validator_set)) , signatures_(std::move(signatures)) + , approve_signatures_(std::move(approve_signatures)) , is_fake_(false) + , is_fork_(false) , send_broadcast_(send_broadcast) , manager_(manager) , promise_(std::move(promise)) { @@ -66,6 +68,7 @@ AcceptBlockQuery::AcceptBlockQuery(AcceptBlockQuery::IsFake fake, BlockIdExt id, , prev_(std::move(prev)) , validator_set_(std::move(validator_set)) , is_fake_(true) + , is_fork_(false) , send_broadcast_(false) , manager_(manager) , promise_(std::move(promise)) { @@ -75,6 +78,74 @@ AcceptBlockQuery::AcceptBlockQuery(AcceptBlockQuery::IsFake fake, BlockIdExt id, CHECK(prev_.size() > 0); } +AcceptBlockQuery::AcceptBlockQuery(ForceFork ffork, BlockIdExt id, td::Ref data, + td::actor::ActorId manager, td::Promise promise) + : id_(id) + , data_(std::move(data)) + , is_fake_(true) + , is_fork_(true) + , send_broadcast_(false) + , manager_(manager) + , promise_(std::move(promise)) { + state_keep_old_hash_.clear(); + state_old_hash_.clear(); + state_hash_.clear(); +} + +bool AcceptBlockQuery::precheck_header() { + VLOG(VALIDATOR_DEBUG) << "precheck_header()"; + // 0. sanity check + CHECK(data_.not_null()); + block_root_ = data_->root_cell(); + if (data_->block_id() != id_) { + return fatal_error("incorrect block id in block data: "s + data_->block_id().to_str() + " instead of " + + id_.to_str()); + } + // 1. root hash and file hash check + RootHash blk_rhash{block_root_->get_hash().bits()}; + if (blk_rhash != id_.root_hash) { + return fatal_error("block root hash mismatch: expected "s + id_.root_hash.to_hex() + ", found " + + blk_rhash.to_hex()); + } + if (is_fake_ || is_fork_) { + FileHash blk_fhash; + td::sha256(data_->data().as_slice(), blk_fhash.as_slice()); + if (blk_fhash != id_.file_hash) { + return fatal_error("block file hash mismatch: expected "s + id_.file_hash.to_hex() + ", computed " + + blk_fhash.to_hex()); + } + } + // 2. check header fields + std::vector prev; + ton::BlockIdExt mc_blkid; + bool after_split; + auto res = block::unpack_block_prev_blk_try(block_root_, id_, prev, mc_blkid, after_split); + if (res.is_error()) { + return fatal_error("invalid block header in AcceptBlock: "s + res.to_string()); + } + if (is_fork_) { + prev_ = prev; + } else if (prev_ != prev) { + return fatal_error("invalid previous block reference(s) in block header"); + } + // 3. unpack header and check vert_seqno fields + block::gen::Block::Record blk; + block::gen::BlockInfo::Record info; + if (!(tlb::unpack_cell(block_root_, blk) && tlb::unpack_cell(blk.info, info))) { + return fatal_error("cannot unpack block header"); + } + if (info.vert_seqno_incr && !is_fork_) { + return fatal_error("block header has vert_seqno_incr set in an ordinary AcceptBlock"); + } + if (!info.vert_seqno_incr && is_fork_) { + return fatal_error("fork block header has no vert_seqno_incr"); + } + if (is_fork_ && !info.key_block) { + return fatal_error("fork block is not a key block"); + } + return true; +} + bool AcceptBlockQuery::create_new_proof() { // 0. check block's root hash VLOG(VALIDATOR_DEBUG) << "create_new_proof() : start"; @@ -93,7 +164,7 @@ bool AcceptBlockQuery::create_new_proof() { block::CurrencyCollection fees; ShardIdFull shard; if (!(tlb::unpack_cell(usage_cell, 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) && block::gen::BlkPrevInfo{info.after_merge}.validate_ref(info.prev_ref) && tlb::unpack_cell(std::move(blk.extra), extra) && block::gen::t_ValueFlow.force_validate_ref(blk.value_flow) && (!info.not_master || tlb::unpack_cell(info.master_ref, mcref)))) { @@ -292,6 +363,26 @@ void AcceptBlockQuery::start_up() { fatal_error("no real SignatureSet passed to AcceptBlockQuery"); return; } + if (!is_fake_ && is_fork_) { + fatal_error("a non-fake AcceptBlockQuery for a forced fork block"); + return; + } + if (!is_fork_ && !prev_.size()) { + fatal_error("no previous blocks passed to AcceptBlockQuery"); + return; + } + if (is_fork_ && !is_masterchain()) { + fatal_error("cannot accept a non-masterchain fork block"); + return; + } + if (is_fork_ && data_.is_null()) { + fatal_error("cannot accept a fork block without explicit data"); + return; + } + if (data_.not_null() && !precheck_header()) { + fatal_error("invalid block header in AcceptBlock"); + return; + } td::actor::send_closure( manager_, &ValidatorManager::get_block_handle, id_, true, [SelfId = actor_id(this)](td::Result R) { @@ -330,8 +421,8 @@ void AcceptBlockQuery::written_block_data() { if (is_fake_) { signatures_ = Ref(create_signature_set(std::vector{})); } - td::actor::send_closure(manager_, &ValidatorManager::set_block_signatures, handle_, - signatures_, [SelfId = actor_id(this)](td::Result R) { + td::actor::send_closure(manager_, &ValidatorManager::set_block_signatures, handle_, signatures_, + [SelfId = actor_id(this)](td::Result R) { check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_block_signatures); }); @@ -365,8 +456,8 @@ void AcceptBlockQuery::written_block_info() { td::actor::send_closure(manager_, &ValidatorManager::wait_prev_block_state, handle_, priority(), timeout_, std::move(P)); } else { - td::actor::send_closure(manager_, &ValidatorManager::wait_block_data, handle_, priority(), - timeout_, [SelfId = actor_id(this)](td::Result> R) { + td::actor::send_closure(manager_, &ValidatorManager::wait_block_data, handle_, priority(), timeout_, + [SelfId = actor_id(this)](td::Result> R) { check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_block_data, R.move_as_ok()); @@ -382,6 +473,10 @@ void AcceptBlockQuery::got_block_data(td::Ref data) { fatal_error("block data does not contain a root cell"); return; } + if (!precheck_header()) { + fatal_error("invalid block header in AcceptBlock"); + return; + } if (handle_->received()) { written_block_data(); } else { @@ -406,8 +501,8 @@ void AcceptBlockQuery::got_prev_state(td::Ref state) { handle_->set_split(state_->before_split()); - td::actor::send_closure(manager_, &ValidatorManager::set_block_state, handle_, - state_, [SelfId = actor_id(this)](td::Result> R) { + td::actor::send_closure(manager_, &ValidatorManager::set_block_state, handle_, state_, + [SelfId = actor_id(this)](td::Result> R) { check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::written_state, R.move_as_ok()); }); @@ -479,8 +574,8 @@ void AcceptBlockQuery::got_last_mc_block(std::pair, Bl if (last_mc_id_.id.seqno < mc_blkid_.id.seqno) { VLOG(VALIDATOR_DEBUG) << "shardchain block refers to newer masterchain block " << mc_blkid_.to_str() << ", trying to obtain it"; - td::actor::send_closure_later(manager_, &ValidatorManager::wait_block_state_short, mc_blkid_, priority(), - timeout_, [SelfId = actor_id(this)](td::Result> R) { + td::actor::send_closure_later(manager_, &ValidatorManager::wait_block_state_short, mc_blkid_, priority(), timeout_, + [SelfId = actor_id(this)](td::Result> R) { check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_mc_state, R.move_as_ok()); @@ -586,7 +681,7 @@ void AcceptBlockQuery::require_proof_link(BlockIdExt id) { CHECK(ton::ShardIdFull(id) == ton::ShardIdFull(id_)); CHECK(id.id.seqno == id_.id.seqno - 1 - proof_links_.size()); td::actor::send_closure_later(manager_, &ValidatorManager::wait_block_proof_link_short, id, timeout_, - [ SelfId = actor_id(this), id ](td::Result> R) { + [SelfId = actor_id(this), id](td::Result> R) { check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::got_proof_link, id, R.move_as_ok()); @@ -785,7 +880,7 @@ void AcceptBlockQuery::written_block_info_2() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { check_send_error(SelfId, R) || td::actor::send_closure_bool(SelfId, &AcceptBlockQuery::applied); }); - run_apply_block_query(handle_->id(), data_, manager_, timeout_, std::move(P)); + run_apply_block_query(handle_->id(), data_, handle_->id(), manager_, timeout_, std::move(P)); } else { applied(); } diff --git a/validator/impl/accept-block.hpp b/validator/impl/accept-block.hpp index 799a289b94..a0c7e576ea 100644 --- a/validator/impl/accept-block.hpp +++ b/validator/impl/accept-block.hpp @@ -47,12 +47,16 @@ using td::Ref; class AcceptBlockQuery : public td::actor::Actor { public: struct IsFake {}; + struct ForceFork {}; AcceptBlockQuery(BlockIdExt id, td::Ref data, std::vector prev, - td::Ref validator_set, td::Ref signatures, bool send_broadcast, + td::Ref validator_set, td::Ref signatures, + td::Ref approve_signatures, bool send_broadcast, td::actor::ActorId manager, td::Promise promise); AcceptBlockQuery(IsFake fake, BlockIdExt id, td::Ref data, std::vector prev, td::Ref validator_set, td::actor::ActorId manager, td::Promise promise); + AcceptBlockQuery(ForceFork ffork, BlockIdExt id, td::Ref data, + td::actor::ActorId manager, td::Promise promise); private: static constexpr td::uint32 priority() { @@ -90,7 +94,9 @@ class AcceptBlockQuery : public td::actor::Actor { std::vector prev_; Ref validator_set_; Ref signatures_; + Ref approve_signatures_; bool is_fake_; + bool is_fork_; bool send_broadcast_; bool ancestors_split_{false}, is_key_block_{false}; td::Timestamp timeout_ = td::Timestamp::in(600.0); @@ -128,6 +134,7 @@ class AcceptBlockQuery : public td::actor::Actor { static bool check_send_error(td::actor::ActorId SelfId, td::Result& res) { return res.is_error() && check_send_error(std::move(SelfId), res.move_as_error()); } + bool precheck_header(); bool create_new_proof(); bool unpack_proof_link(BlockIdExt id, Ref proof); diff --git a/validator/impl/check-proof.cpp b/validator/impl/check-proof.cpp index e85055b6d0..aa6c27485b 100644 --- a/validator/impl/check-proof.cpp +++ b/validator/impl/check-proof.cpp @@ -165,7 +165,7 @@ bool CheckProof::init_parse(bool is_aux) { block::gen::ExtBlkRef::Record mcref; // _ ExtBlkRef = BlkMasterInfo; ShardIdFull shard; if (!(tlb::unpack_cell(virt_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) && block::gen::BlkPrevInfo{info.after_merge}.validate_ref(info.prev_ref) && block::gen::t_ValueFlow.force_validate_ref(blk.value_flow) && (!info.not_master || tlb::unpack_cell(info.master_ref, mcref)))) { diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index a4671a8b23..1163089e48 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -119,7 +119,8 @@ void run_accept_block_query(BlockIdExt id, td::Ref data, std::vector< td::Ref approve_signatures, bool send_broadcast, td::actor::ActorId manager, td::Promise promise) { td::actor::create_actor("accept", id, std::move(data), prev, std::move(validator_set), - std::move(signatures), send_broadcast, manager, std::move(promise)) + std::move(signatures), std::move(approve_signatures), send_broadcast, + manager, std::move(promise)) .release(); } @@ -134,13 +135,16 @@ void run_fake_accept_block_query(BlockIdExt id, td::Ref data, std::ve void run_hardfork_accept_block_query(BlockIdExt id, td::Ref data, td::actor::ActorId manager, td::Promise promise) { - promise.set_error(td::Status::Error(ErrorCode::error, "not implemented")); + td::actor::create_actor("fork/accept", AcceptBlockQuery::ForceFork(), id, std::move(data), + std::move(manager), std::move(promise)) + .release(); } -void run_apply_block_query(BlockIdExt id, td::Ref block, td::actor::ActorId manager, - td::Timestamp timeout, td::Promise promise) { - td::actor::create_actor(PSTRING() << "apply " << id, id, std::move(block), manager, timeout, - std::move(promise)) +void run_apply_block_query(BlockIdExt id, td::Ref block, BlockIdExt masterchain_block_id, + td::actor::ActorId manager, td::Timestamp timeout, + td::Promise promise) { + td::actor::create_actor(PSTRING() << "apply " << id, id, std::move(block), masterchain_block_id, manager, + timeout, std::move(promise)) .release(); } diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 23831af03a..196397a940 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -192,7 +192,7 @@ void LiteQuery::perform_getMasterchainInfo(int mode) { } td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [ Self = actor_id(this), mode ](td::Result, BlockIdExt>> res) { + [Self = actor_id(this), mode](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -230,7 +230,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -256,7 +256,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid, mode ](td::Result> res) { + [Self = actor_id(this), blkid, mode](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -371,7 +371,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { } if (blkid.id.seqno) { td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -381,7 +381,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { }); } else { td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid, - [ Self = actor_id(this), blkid ](td::Result res) { + [Self = actor_id(this), blkid](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -440,7 +440,7 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -466,7 +466,7 @@ bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_from_db_short, blkid, - [ Self = actor_id(this), blkid, mode ](td::Result> res) { + [Self = actor_id(this), blkid, mode](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : ")); @@ -488,7 +488,7 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -519,7 +519,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -541,7 +541,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -563,7 +563,7 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid, - [ Self = actor_id(this), blkid ](td::Result> res) { + [Self = actor_id(this), blkid](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : ")); @@ -588,7 +588,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_zero_state, blkid, - [ Self = actor_id(this), blkid ](td::Result res) { + [Self = actor_id(this), blkid](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : ")); @@ -632,7 +632,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, LOG(INFO) << "sending a get_top_masterchain_state_block query to manager"; td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this)](td::Result, BlockIdExt>> res)->void { + [Self = actor_id(this)](td::Result, BlockIdExt>> res) -> void { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1067,14 +1067,14 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) { << " " << trans_lt_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_by_lt_from_db, ton::extract_addr_prefix(acc_workchain_, acc_addr_), - trans_lt_, [ Self = actor_id(this), remaining, manager = manager_ ](td::Result res) { + trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{}); } else { - auto blkid = res.move_as_ok(); - LOG(DEBUG) << "requesting data for block " << blkid.to_str(); - td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self, blkid, remaining](td::Result> res) { + auto handle = res.move_as_ok(); + LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); + td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, + [Self, blkid = handle->id(), remaining](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), blkid); @@ -1141,7 +1141,7 @@ void LiteQuery::perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool e void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector param_list) { LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", ) liteserver query"; - set_continuation([ this, mode, param_list = std::move(param_list) ]() mutable { + set_continuation([this, mode, param_list = std::move(param_list)]() mutable { continue_getConfigParams(mode, std::move(param_list)); }); request_mc_block_data_state(blkid); @@ -1294,14 +1294,14 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni LOG(INFO) << "performing a lookupBlock(" << blkid.to_str() << ", " << mode << ", " << lt << ", " << utime << ") query"; auto P = td::PromiseCreator::lambda( - [ Self = actor_id(this), manager = manager_, mode = (mode >> 4) ](td::Result res) { + [Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { - auto blkid = res.move_as_ok(); - LOG(DEBUG) << "requesting data for block " << blkid.to_str(); - td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self, blkid, mode](td::Result> res) { + auto handle = res.move_as_ok(); + LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); + td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, + [Self, blkid = handle->id(), mode](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1449,7 +1449,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, if (mode & 0x1000) { BlockIdExt bblk = (from.seqno() > to.seqno()) ? from : to; td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, bblk, - [ Self = actor_id(this), from, to, bblk, mode ](td::Result> res) { + [Self = actor_id(this), from, to, bblk, mode](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1461,7 +1461,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, } else { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [ Self = actor_id(this), from, to, mode ](td::Result, BlockIdExt>> res) { + [Self = actor_id(this), from, to, mode](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1474,7 +1474,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, } else if (mode & 2) { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [ Self = actor_id(this), from, mode ](td::Result, BlockIdExt>> res) { + [Self = actor_id(this), from, mode](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1485,7 +1485,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, }); } else { td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false, - [ Self = actor_id(this), from, mode ](td::Result res) { + [Self = actor_id(this), from, mode](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { diff --git a/validator/impl/proof.cpp b/validator/impl/proof.cpp index 6270a8f9f6..2abff14936 100644 --- a/validator/impl/proof.cpp +++ b/validator/impl/proof.cpp @@ -88,6 +88,7 @@ td::Result ProofLinkQ::get_basic_header_info() const } res.cc_seqno = info.gen_catchain_seqno; res.utime = info.gen_utime; + res.end_lt = info.end_lt; res.validator_set_hash = info.gen_validator_list_hash_short; res.prev_key_mc_seqno = info.prev_key_block_seqno; return res; diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 2350d171a5..0f16a3b9bb 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -430,6 +430,7 @@ bool ValidateQuery::init_parse() { return reject_query("a non-masterchain block cannot be a key block"); } if (info.vert_seqno_incr) { + // what about non-masterchain blocks? return reject_query("new blocks cannot have vert_seqno_incr set"); } if (info.after_merge != after_merge_) { @@ -1620,12 +1621,13 @@ bool ValidateQuery::check_one_shard(const block::McShardHash& info, const block: << " has unchanged catchain seqno " << cc_seqno << ", but it must have been updated for all shards"); } - if (!cc_updated && !info.before_merge_ && old_before_merge && !workchain_created) { + bool bm_cleared = !info.before_merge_ && old_before_merge; + if (!cc_updated && bm_cleared && !workchain_created) { return reject_query(PSTRING() << "new shard configuration for shard " << shard.to_str() << " has unchanged catchain seqno " << cc_seqno << " while the before_merge bit has been cleared"); } - if (cc_updated && (!update_shard_cc_ || (!info.before_merge_ && old_before_merge))) { + if (cc_updated && !(update_shard_cc_ || bm_cleared)) { return reject_query(PSTRING() << "new shard configuration for shard " << shard.to_str() << " has increased catchain seqno " << cc_seqno << " without a good reason"); } diff --git a/validator/import-db-slice.cpp b/validator/import-db-slice.cpp new file mode 100644 index 0000000000..46fcc0aeb1 --- /dev/null +++ b/validator/import-db-slice.cpp @@ -0,0 +1,354 @@ +#include "import-db-slice.hpp" +#include "validator/db/fileref.hpp" +#include "td/utils/overloaded.h" +#include "validator/fabric.h" +#include "td/actor/MultiPromise.h" +#include "common/checksum.h" +#include "td/utils/port/path.h" + +namespace ton { + +namespace validator { + +ArchiveImporter::ArchiveImporter(std::string path, td::Ref state, BlockSeqno shard_client_seqno, + td::Ref opts, td::actor::ActorId manager, + td::Promise> promise) + : path_(std::move(path)) + , state_(std::move(state)) + , shard_client_seqno_(shard_client_seqno) + , opts_(std::move(opts)) + , manager_(manager) + , promise_(std::move(promise)) { +} + +void ArchiveImporter::start_up() { + auto R = Package::open(path_, false, false); + if (R.is_error()) { + abort_query(R.move_as_error()); + return; + } + package_ = std::make_shared(R.move_as_ok()); + + bool fail = false; + package_->iterate([&](std::string filename, td::BufferSlice data, td::uint64 offset) -> bool { + auto F = FileReference::create(filename); + if (F.is_error()) { + abort_query(F.move_as_error()); + fail = true; + return false; + } + auto f = F.move_as_ok(); + + BlockIdExt b; + bool is_proof = false; + bool ignore = true; + + f.ref().visit(td::overloaded( + [&](const fileref::Proof &p) { + b = p.block_id; + ignore = !b.is_masterchain(); + is_proof = true; + }, + [&](const fileref::ProofLink &p) { + b = p.block_id; + ignore = b.is_masterchain(); + is_proof = true; + }, + [&](const fileref::Block &p) { + b = p.block_id; + ignore = false; + is_proof = false; + }, + [&](const auto &p) { ignore = true; })); + + if (!ignore) { + blocks_[b][is_proof ? 0 : 1] = offset; + if (b.is_masterchain()) { + masterchain_blocks_[b.seqno()] = b; + } + } + return true; + }); + + if (fail) { + return; + } + + if (masterchain_blocks_.size() == 0) { + abort_query(td::Status::Error(ErrorCode::notready, "archive does not contain any masterchain blocks")); + return; + } + + auto seqno = masterchain_blocks_.begin()->first; + + check_masterchain_block(seqno); +} + +void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { + auto it = masterchain_blocks_.find(seqno); + if (it == masterchain_blocks_.end()) { + if (seqno == 0) { + abort_query(td::Status::Error(ErrorCode::notready, "no new blocks")); + return; + } + checked_all_masterchain_blocks(seqno - 1); + return; + } + if (seqno < state_->get_block_id().seqno()) { + if (!state_->check_old_mc_block_id(it->second)) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); + return; + } + check_masterchain_block(seqno + 1); + } else if (seqno == state_->get_block_id().seqno()) { + if (state_->get_block_id() != it->second) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); + return; + } + check_masterchain_block(seqno + 1); + } else { + if (seqno != state_->get_block_id().seqno() + 1) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "hole in masterchain seqno")); + return; + } + auto it2 = blocks_.find(it->second); + CHECK(it2 != blocks_.end()); + + auto R1 = package_->read(it2->second[0]); + if (R1.is_error()) { + abort_query(R1.move_as_error()); + return; + } + + auto proofR = create_proof(it->second, std::move(R1.move_as_ok().second)); + if (proofR.is_error()) { + abort_query(proofR.move_as_error()); + return; + } + + auto R2 = package_->read(it2->second[1]); + if (R2.is_error()) { + abort_query(R2.move_as_error()); + return; + } + + if (sha256_bits256(R2.ok().second.as_slice()) != it->second.file_hash) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad block file hash")); + return; + } + auto dataR = create_block(it->second, std::move(R2.move_as_ok().second)); + if (dataR.is_error()) { + abort_query(dataR.move_as_error()); + return; + } + + auto proof = proofR.move_as_ok(); + auto data = dataR.move_as_ok(); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = state_->get_block_id(), + data](td::Result R) mutable { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); + return; + } + auto handle = R.move_as_ok(); + CHECK(!handle->merge_before()); + if (handle->one_prev(true) != id) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, + td::Status::Error(ErrorCode::protoviolation, "prev block mismatch")); + return; + } + td::actor::send_closure(SelfId, &ArchiveImporter::checked_masterchain_proof, std::move(handle), std::move(data)); + }); + + run_check_proof_query(it->second, std::move(proof), manager_, td::Timestamp::in(2.0), std::move(P), state_, + opts_->is_hardfork(it->second)); + } +} + +void ArchiveImporter::checked_masterchain_proof(BlockHandle handle, td::Ref data) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveImporter::applied_masterchain_block, std::move(handle)); + }); + run_apply_block_query(handle->id(), std::move(data), handle->id(), manager_, td::Timestamp::in(10.0), std::move(P)); +} + +void ArchiveImporter::applied_masterchain_block(BlockHandle handle) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveImporter::got_new_materchain_state, + td::Ref(R.move_as_ok())); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db, handle, std::move(P)); +} + +void ArchiveImporter::got_new_materchain_state(td::Ref state) { + state_ = std::move(state); + check_masterchain_block(state_->get_block_id().seqno() + 1); +} + +void ArchiveImporter::checked_all_masterchain_blocks(BlockSeqno seqno) { + max_shard_client_seqno_ = seqno; + check_next_shard_client_seqno(shard_client_seqno_ + 1); +} + +void ArchiveImporter::check_next_shard_client_seqno(BlockSeqno seqno) { + if (seqno > max_shard_client_seqno_) { + finish_query(); + return; + } + + if (seqno == max_shard_client_seqno_) { + got_masterchain_state(state_); + } else { + BlockIdExt b; + bool f = state_->get_old_mc_block_id(seqno, b); + CHECK(f); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveImporter::got_masterchain_state, + td::Ref{R.move_as_ok()}); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db_short, b, std::move(P)); + } +} + +void ArchiveImporter::got_masterchain_state(td::Ref state) { + auto s = state->get_shards(); + + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), seqno = state->get_block_id().seqno()](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::check_next_shard_client_seqno, seqno + 1); + } + }); + + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(P)); + + for (auto &shard : s) { + apply_shard_block(shard->top_block_id(), state->get_block_id(), ig.get_promise()); + } +} + +void ArchiveImporter::apply_shard_block(BlockIdExt block_id, BlockIdExt masterchain_block_id, + td::Promise promise) { + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), masterchain_block_id, promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveImporter::apply_shard_block_cont1, R.move_as_ok(), masterchain_block_id, + std::move(promise)); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id, true, std::move(P)); +} + +void ArchiveImporter::apply_shard_block_cont1(BlockHandle handle, BlockIdExt masterchain_block_id, + td::Promise promise) { + if (handle->is_applied()) { + promise.set_value(td::Unit()); + return; + } + + auto it = blocks_.find(handle->id()); + if (it == blocks_.end()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "no proof for shard block")); + return; + } + TRY_RESULT_PROMISE(promise, data, package_->read(it->second[0])); + TRY_RESULT_PROMISE(promise, proof, create_proof_link(handle->id(), std::move(data.second))); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle, masterchain_block_id, + promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::apply_shard_block_cont2, std::move(handle), + masterchain_block_id, std::move(promise)); + } + }); + run_check_proof_link_query(handle->id(), std::move(proof), manager_, td::Timestamp::in(10.0), std::move(P)); +} + +void ArchiveImporter::apply_shard_block_cont2(BlockHandle handle, BlockIdExt masterchain_block_id, + td::Promise promise) { + if (handle->is_applied()) { + promise.set_value(td::Unit()); + return; + } + CHECK(handle->id().seqno() > 0); + + if (!handle->merge_before() && handle->one_prev(true).shard_full() == handle->id().shard_full()) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle, masterchain_block_id, + promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::apply_shard_block_cont3, std::move(handle), + masterchain_block_id, std::move(promise)); + } + }); + apply_shard_block(handle->one_prev(true), masterchain_block_id, std::move(P)); + } else { + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(promise)); + check_shard_block_applied(handle->one_prev(true), ig.get_promise()); + if (handle->merge_before()) { + check_shard_block_applied(handle->one_prev(false), ig.get_promise()); + } + } +} + +void ArchiveImporter::apply_shard_block_cont3(BlockHandle handle, BlockIdExt masterchain_block_id, + td::Promise promise) { + auto it = blocks_.find(handle->id()); + CHECK(it != blocks_.end()); + TRY_RESULT_PROMISE(promise, data, package_->read(it->second[1])); + if (sha256_bits256(data.second.as_slice()) != handle->id().file_hash) { + promise.set_error(td::Status::Error(ErrorCode::protoviolation, "bad block file hash")); + return; + } + TRY_RESULT_PROMISE(promise, block, create_block(handle->id(), std::move(data.second))); + + run_apply_block_query(handle->id(), std::move(block), masterchain_block_id, manager_, td::Timestamp::in(10.0), + std::move(promise)); +} + +void ArchiveImporter::check_shard_block_applied(BlockIdExt block_id, td::Promise promise) { + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + auto handle = R.move_as_ok(); + if (!handle->is_applied()) { + promise.set_error(td::Status::Error(ErrorCode::notready, "not applied")); + } else { + promise.set_value(td::Unit()); + } + } + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id, false, std::move(P)); +} + +void ArchiveImporter::abort_query(td::Status error) { + if (promise_) { + promise_.set_error(std::move(error)); + td::unlink(path_).ensure(); + } + stop(); +} +void ArchiveImporter::finish_query() { + if (promise_) { + promise_.set_value(std::vector(state_->get_block_id().seqno(), max_shard_client_seqno_)); + td::unlink(path_).ensure(); + } + stop(); +} + +} // namespace validator + +} // namespace ton diff --git a/validator/import-db-slice.hpp b/validator/import-db-slice.hpp new file mode 100644 index 0000000000..5b7b1f1dd9 --- /dev/null +++ b/validator/import-db-slice.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include "td/actor/actor.h" +#include "validator/interfaces/validator-manager.h" +#include "validator/db/package.hpp" + +namespace ton { + +namespace validator { + +class ArchiveImporter : public td::actor::Actor { + public: + ArchiveImporter(std::string path, td::Ref state, BlockSeqno shard_client_seqno, + td::Ref opts, td::actor::ActorId manager, + td::Promise> promise); + void start_up() override; + + void abort_query(td::Status error); + void finish_query(); + + void check_masterchain_block(BlockSeqno seqno); + void checked_masterchain_proof(BlockHandle handle, td::Ref data); + void applied_masterchain_block(BlockHandle handle); + void got_new_materchain_state(td::Ref state); + void checked_all_masterchain_blocks(BlockSeqno seqno); + + void check_next_shard_client_seqno(BlockSeqno seqno); + void got_masterchain_state(td::Ref state); + void apply_shard_block(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise); + void apply_shard_block_cont1(BlockHandle handle, BlockIdExt masterchain_block_id, td::Promise promise); + void apply_shard_block_cont2(BlockHandle handle, BlockIdExt masterchain_block_id, td::Promise promise); + void apply_shard_block_cont3(BlockHandle handle, BlockIdExt masterchain_block_id, td::Promise promise); + void check_shard_block_applied(BlockIdExt block_id, td::Promise promise); + + private: + std::string path_; + td::Ref state_; + BlockSeqno shard_client_seqno_; + BlockSeqno max_shard_client_seqno_; + + td::Ref opts_; + + std::shared_ptr package_; + + td::actor::ActorId manager_; + td::Promise> promise_; + + std::map masterchain_blocks_; + std::map> blocks_; +}; + +} // namespace validator + +} // namespace ton diff --git a/validator/interfaces/block-handle.h b/validator/interfaces/block-handle.h index b30269cf77..00ecd8abd6 100644 --- a/validator/interfaces/block-handle.h +++ b/validator/interfaces/block-handle.h @@ -32,8 +32,8 @@ struct BlockHandleInterface { public: virtual BlockIdExt id() const = 0; virtual bool received() const = 0; - virtual bool moved_to_storage() const = 0; virtual bool moved_to_archive() const = 0; + virtual bool handle_moved_to_archive() const = 0; virtual bool deleted() const = 0; virtual bool inited_next_left() const = 0; virtual bool inited_next_right() const = 0; @@ -49,6 +49,7 @@ struct BlockHandleInterface { virtual bool inited_split_after() const = 0; virtual bool inited_merge_before() const = 0; virtual bool inited_is_key_block() const = 0; + virtual bool inited_masterchain_ref_block() const = 0; virtual bool split_after() const = 0; virtual bool merge_before() const = 0; virtual bool is_key_block() const = 0; @@ -60,6 +61,7 @@ struct BlockHandleInterface { virtual bool is_zero() const = 0; virtual bool is_archived() const = 0; virtual bool is_applied() const = 0; + virtual BlockSeqno masterchain_ref_block() const = 0; virtual std::vector prev() const = 0; virtual BlockIdExt one_prev(bool left) const = 0; virtual std::vector next() const = 0; @@ -83,8 +85,8 @@ struct BlockHandleInterface { virtual void set_next(BlockIdExt next) = 0; virtual void set_prev(BlockIdExt prev) = 0; virtual void set_received() = 0; - virtual void set_moved_to_storage() = 0; virtual void set_moved_to_archive() = 0; + virtual void set_handle_moved_to_archive() = 0; virtual void set_deleted() = 0; virtual void set_split(bool value) = 0; virtual void set_merge(bool value) = 0; @@ -94,6 +96,7 @@ struct BlockHandleInterface { virtual void set_deleted_state_boc() = 0; virtual void set_archived() = 0; virtual void set_applied() = 0; + virtual void set_masterchain_ref_block(BlockSeqno seqno) = 0; virtual void unsafe_clear_applied() = 0; virtual void unsafe_clear_next() = 0; diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index 846371a19b..632b0a9cf3 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -72,9 +72,9 @@ class Db : public td::actor::Actor { virtual void get_block_handle(BlockIdExt id, td::Promise promise) = 0; virtual void apply_block(BlockHandle handle, td::Promise promise) = 0; - virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) = 0; - virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) = 0; - virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) = 0; + virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) = 0; + virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) = 0; + virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) = 0; virtual void update_init_masterchain_block(BlockIdExt block, td::Promise promise) = 0; virtual void get_init_masterchain_block(td::Promise promise) = 0; @@ -95,11 +95,25 @@ class Db : public td::actor::Actor { virtual void update_hardforks(std::vector blocks, td::Promise promise) = 0; virtual void get_hardforks(td::Promise> promise) = 0; - virtual void archive(BlockIdExt block_id, td::Promise promise) = 0; + virtual void archive(BlockHandle handle, td::Promise promise) = 0; virtual void prepare_stats(td::Promise>> promise) = 0; virtual void truncate(td::Ref state, td::Promise promise) = 0; + + virtual void add_key_block_proof(td::Ref proof, td::Promise promise) = 0; + virtual void add_key_block_proof_link(td::Ref proof_link, td::Promise promise) = 0; + virtual void get_key_block_proof(BlockIdExt block_id, td::Promise> promise) = 0; + virtual void get_key_block_proof_link(BlockIdExt block_id, td::Promise> promise) = 0; + virtual void check_key_block_proof_exists(BlockIdExt block_id, td::Promise promise) = 0; + virtual void check_key_block_proof_link_exists(BlockIdExt block_id, td::Promise promise) = 0; + + virtual void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) = 0; + virtual void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) = 0; + virtual void set_async_mode(bool mode, td::Promise promise) = 0; + + virtual void run_gc(UnixTime ts) = 0; }; } // namespace validator diff --git a/validator/interfaces/proof.h b/validator/interfaces/proof.h index 70598e684f..5830f467d0 100644 --- a/validator/interfaces/proof.h +++ b/validator/interfaces/proof.h @@ -29,6 +29,7 @@ class ProofLink : public td::CntObject { public: struct BasicHeaderInfo { UnixTime utime; + LogicalTime end_lt; CatchainSeqno cc_seqno; td::uint32 validator_set_hash; BlockSeqno prev_key_mc_seqno; diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index 4328ce4f20..71919d2155 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -147,6 +147,8 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void allow_block_candidate_gc(BlockIdExt block_id, td::Promise promise) = 0; virtual void allow_block_info_gc(BlockIdExt block_id, td::Promise promise) = 0; + virtual void archive(BlockHandle handle, td::Promise promise) = 0; + virtual void check_is_hardfork(BlockIdExt block_id, td::Promise promise) = 0; virtual void get_vertical_seqno(BlockSeqno seqno, td::Promise promise) = 0; diff --git a/validator/invariants.hpp b/validator/invariants.hpp index 01b114c65d..d10495a772 100644 --- a/validator/invariants.hpp +++ b/validator/invariants.hpp @@ -45,7 +45,7 @@ class ValidatorInvariants { CHECK(handle->inited_merge_before()); CHECK(handle->inited_split_after()); CHECK(handle->inited_prev()); - CHECK(handle->inited_signatures()); + CHECK(handle->inited_signatures() || handle->is_applied()); CHECK(handle->inited_state_root_hash()); CHECK(handle->inited_logical_time()); CHECK(handle->inited_unix_time()); diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index 9798bb17fa..7c9e2e6a4d 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -223,6 +223,42 @@ void ValidatorManagerImpl::get_block_proof(BlockHandle handle, td::Promise promise) { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + auto B = R.move_as_ok(); + promise.set_value(B->data()); + } + }); + + td::actor::send_closure(db_, &Db::get_key_block_proof, block_id, std::move(P)); +} + +void ValidatorManagerImpl::get_key_block_proof_link(BlockIdExt block_id, td::Promise promise) { + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise), block_id, db = db_.get()](td::Result> R) mutable { + if (R.is_error()) { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + auto B = R.move_as_ok(); + promise.set_value(B->data()); + } + }); + + td::actor::send_closure(db, &Db::get_key_block_proof, block_id, std::move(P)); + } else { + auto B = R.move_as_ok()->export_as_proof_link().move_as_ok(); + promise.set_value(B->data()); + } + }); + + td::actor::send_closure(db_, &Db::get_key_block_proof, block_id, std::move(P)); +} + void ValidatorManagerImpl::new_external_message(td::BufferSlice data) { auto R = create_ext_message(std::move(data)); if (R.is_ok()) { @@ -582,17 +618,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i } void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise)); } void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise)); } void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise)); } diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index 6da9110ed0..dbb85b2c3f 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -119,6 +119,8 @@ class ValidatorManagerImpl : public ValidatorManager { void get_block_proof_link(BlockHandle block_id, td::Promise promise) override { UNREACHABLE(); } + void get_key_block_proof(BlockIdExt block_id, td::Promise promise) override; + void get_key_block_proof_link(BlockIdExt block_id, td::Promise promise) override; //void get_block_description(BlockIdExt block_id, td::Promise promise) override; void new_external_message(td::BufferSlice data) override; @@ -200,11 +202,11 @@ class ValidatorManagerImpl : public ValidatorManager { void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) override; void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) override; + td::Promise promise) override; void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) override; + td::Promise promise) override; // get block handle declared in parent class void write_handle(BlockHandle handle, td::Promise promise) override; @@ -259,6 +261,14 @@ class ValidatorManagerImpl : public ValidatorManager { promise.set_error(td::Status::Error(ErrorCode::error, "download disabled")); } + void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) override { + UNREACHABLE(); + } + void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) override { + UNREACHABLE(); + } + void add_shard_block_description(td::Ref desc); void register_block_handle(BlockHandle handle, td::Promise promise); @@ -327,6 +337,9 @@ class ValidatorManagerImpl : public ValidatorManager { void allow_block_info_gc(BlockIdExt block_id, td::Promise promise) override { promise.set_result(false); } + void archive(BlockHandle handle, td::Promise promise) override { + td::actor::send_closure(db_, &Db::archive, std::move(handle), std::move(promise)); + } void update_last_known_key_block(BlockHandle handle, bool send_request) override { } void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) override { diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index d96eef778e..d2a7b861f9 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -104,16 +104,22 @@ void ValidatorManagerMasterchainReiniter::downloaded_proof_link(td::BufferSlice return; } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto proof_link = pp.move_as_ok(); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), db = db_, proof_link](td::Result R) { if (R.is_error()) { LOG(WARNING) << "downloaded proof link failed: " << R.move_as_error(); td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::download_proof_link); } else { - td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::try_download_key_blocks, false); + auto P = td::PromiseCreator::lambda([SelfId, handle = R.move_as_ok()](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainReiniter::try_download_key_blocks, false); + }); + td::actor::send_closure(db, &Db::add_key_block_proof_link, proof_link, std::move(P)); } }); - run_check_proof_link_query(handle_->id(), pp.move_as_ok(), manager_, td::Timestamp::in(60.0), std::move(P)); + run_check_proof_link_query(handle_->id(), proof_link, manager_, td::Timestamp::in(60.0), std::move(P)); } void ValidatorManagerMasterchainReiniter::downloaded_zero_state() { @@ -259,6 +265,8 @@ void ValidatorManagerMasterchainReiniter::download_masterchain_state() { void ValidatorManagerMasterchainReiniter::downloaded_masterchain_state(td::Ref state) { state_ = td::Ref{std::move(state)}; + CHECK(handle_->received_state()); + CHECK(handle_->is_applied()); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); @@ -452,9 +460,9 @@ void ValidatorManagerMasterchainStarter::got_hardforks(std::vector v return; } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); - td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_id, R.move_as_ok()); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_handle, R.move_as_ok()); }); td::actor::send_closure(db_, &Db::get_block_by_seqno, AccountIdPrefixFull{masterchainId, 0}, b.seqno() - 1, std::move(P)); diff --git a/validator/manager.cpp b/validator/manager.cpp index 2198d42a5a..4a627196b1 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -29,6 +29,7 @@ #include "ton/ton-io.hpp" #include "state-serializer.hpp" #include "get-next-key-blocks.h" +#include "import-db-slice.hpp" #include "auto/tl/lite_api.h" #include "tl-utils/lite-utils.hpp" @@ -189,6 +190,7 @@ void ValidatorManagerImpl::validate_block(ReceivedBlock block, td::Promise R) mutable { @@ -198,7 +200,7 @@ void ValidatorManagerImpl::validate_block(ReceivedBlock block, td::Promise promise) { @@ -324,6 +326,42 @@ void ValidatorManagerImpl::get_block_proof_link(BlockHandle handle, td::Promise< td::actor::send_closure(db_, &Db::get_block_proof_link, std::move(handle), std::move(P)); } +void ValidatorManagerImpl::get_key_block_proof(BlockIdExt block_id, td::Promise promise) { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + auto B = R.move_as_ok(); + promise.set_value(B->data()); + } + }); + + td::actor::send_closure(db_, &Db::get_key_block_proof, block_id, std::move(P)); +} + +void ValidatorManagerImpl::get_key_block_proof_link(BlockIdExt block_id, td::Promise promise) { + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise), block_id, db = db_.get()](td::Result> R) mutable { + if (R.is_error()) { + auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result> R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + auto B = R.move_as_ok(); + promise.set_value(B->data()); + } + }); + + td::actor::send_closure(db, &Db::get_key_block_proof, block_id, std::move(P)); + } else { + auto B = R.move_as_ok()->export_as_proof_link().move_as_ok(); + promise.set_value(B->data()); + } + }); + + td::actor::send_closure(db_, &Db::get_key_block_proof, block_id, std::move(P)); +} + void ValidatorManagerImpl::new_external_message(td::BufferSlice data) { if (!is_validator()) { return; @@ -899,17 +937,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i } void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise)); } void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise)); } void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise)); } @@ -1339,6 +1377,7 @@ void ValidatorManagerImpl::start_up() { db_ = create_db_actor(actor_id(this), db_root_, opts_->get_filedb_depth()); lite_server_cache_ = create_liteserver_cache_actor(actor_id(this), db_root_); token_manager_ = td::actor::create_actor("tokenmanager"); + td::mkdir(db_root_ + "/tmp/").ensure(); auto Q = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { @@ -1375,6 +1414,7 @@ void ValidatorManagerImpl::started(ValidatorManagerInitResult R) { gc_masterchain_state_ = std::move(R.gc_state); shard_client_ = std::move(R.clients); + td::actor::send_closure(shard_client_, &ShardClient::start); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { @@ -1741,7 +1781,7 @@ void ValidatorManagerImpl::allow_persistent_state_file_gc(BlockIdExt block_id, B } void ValidatorManagerImpl::allow_archive(BlockIdExt block_id, td::Promise promise) { - if (!gc_masterchain_handle_) { + /*if (!gc_masterchain_handle_) { promise.set_result(false); return; } @@ -1784,7 +1824,8 @@ void ValidatorManagerImpl::allow_archive(BlockIdExt block_id, td::Promise promise.set_result(true); } }); - td::actor::send_closure(db_, &Db::archive, block_id, std::move(P)); + td::actor::send_closure(db_, &Db::archive, block_id, std::move(P));*/ + promise.set_result(false); } void ValidatorManagerImpl::allow_delete(BlockIdExt block_id, td::Promise promise) { @@ -1797,10 +1838,6 @@ void ValidatorManagerImpl::allow_delete(BlockIdExt block_id, td::Promise p return; } auto handle = R.move_as_ok(); - if (!handle->moved_to_storage()) { - promise.set_result(false); - return; - } if (!handle->inited_unix_time()) { promise.set_result(true); return; @@ -1843,15 +1880,13 @@ void ValidatorManagerImpl::allow_block_state_gc(BlockIdExt block_id, td::Promise } void ValidatorManagerImpl::allow_block_info_gc(BlockIdExt block_id, td::Promise promise) { - promise.set_result(false); - return; - /*auto P = + auto P = td::PromiseCreator::lambda([db = db_.get(), promise = std::move(promise)](td::Result R) mutable { if (R.is_error()) { promise.set_result(false); } else { auto handle = R.move_as_ok(); - if (!handle->moved_to_archive()) { + if (!handle->moved_to_archive() || !handle->is_applied()) { promise.set_result(false); } else { auto P = td::PromiseCreator::lambda([promise = std::move(promise)](td::Result R) mutable { @@ -1862,7 +1897,7 @@ void ValidatorManagerImpl::allow_block_info_gc(BlockIdExt block_id, td::Promise< } } }); - get_block_handle(block_id, false, std::move(P));*/ + get_block_handle(block_id, false, std::move(P)); } void ValidatorManagerImpl::got_next_gc_masterchain_handle(BlockHandle handle) { @@ -1896,7 +1931,8 @@ void ValidatorManagerImpl::advance_gc(BlockHandle handle, td::Ref promise) { - auto seqno = handle->id().seqno(); + shard_client_handle_ = std::move(handle); + auto seqno = shard_client_handle_->id().seqno(); shard_client_update(seqno); promise.set_value(td::Unit()); } @@ -1928,15 +1964,26 @@ void ValidatorManagerImpl::state_serializer_update(BlockSeqno seqno) { void ValidatorManagerImpl::alarm() { try_advance_gc_masterchain_block(); alarm_timestamp() = td::Timestamp::in(1.0); + if (gc_masterchain_handle_) { + td::actor::send_closure(db_, &Db::run_gc, gc_masterchain_handle_->unix_time()); + } if (log_status_at_.is_in_past()) { if (last_masterchain_block_handle_) { LOG(INFO) << "STATUS: last_masterchain_block_ago=" << td::format::as_time(td::Clocks::system() - last_masterchain_block_handle_->unix_time()) << " last_known_key_block_ago=" - << td::format::as_time(td::Clocks::system() - last_known_key_block_handle_->unix_time()); + << td::format::as_time(td::Clocks::system() - last_known_key_block_handle_->unix_time()) + << " shard_client_ago=" + << td::format::as_time(td::Clocks::system() - + (shard_client_handle_ ? shard_client_handle_->unix_time() : 0)); } log_status_at_ = td::Timestamp::in(60.0); } + if (false && !downloading_archive_slice_ && shard_client_handle_ && + shard_client_handle_->unix_time() + 600 <= td::Clocks::system() && next_download_archive_slice_at_.is_in_past()) { + next_download_archive_slice_at_ = td::Timestamp::in(10.0); + try_download_archive_slice(); + } alarm_timestamp().relax(log_status_at_); if (resend_shard_blocks_at_ && resend_shard_blocks_at_.is_in_past()) { resend_shard_blocks_at_ = td::Timestamp::never(); @@ -1964,17 +2011,6 @@ void ValidatorManagerImpl::alarm() { if (check_shard_clients_.is_in_past()) { check_shard_clients_ = td::Timestamp::in(10.0); - if (!shard_client_.empty()) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { - if (R.is_error()) { - VLOG(VALIDATOR_WARNING) << "failed to get shard client status: " << R.move_as_error(); - } else { - td::actor::send_closure(SelfId, &ValidatorManagerImpl::shard_client_update, R.move_as_ok()); - } - }); - td::actor::send_closure(shard_client_, &ShardClient::get_processed_masterchain_block, std::move(P)); - } - if (!serializer_.empty()) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { @@ -2017,6 +2053,58 @@ void ValidatorManagerImpl::try_get_static_file(FileHash file_hash, td::Promise R) { + if (R.is_error()) { + LOG(INFO) << "failed to download archive slice: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::failed_to_download_archive_slice); + } else { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); + } + }); + callback_->download_archive(shard_client_handle_->id().seqno(), db_root_ + "/tmp/", td::Timestamp::in(3600.0), + std::move(P)); +} + +void ValidatorManagerImpl::failed_to_download_archive_slice() { + downloading_archive_slice_ = false; +} + +void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { + LOG(INFO) << "downloaded archive slice: " << name; + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::failed_to_download_archive_slice); + } else { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); + } + }); + + td::actor::create_actor("archiveimport", name, last_masterchain_state_, + shard_client_handle_->id().seqno(), opts_, actor_id(this), std::move(P)) + .release(); +} + +void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) { + CHECK(seqno.size() == 2); + LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; + downloading_archive_slice_ = false; + next_download_archive_slice_at_ = td::Timestamp::in(10.0); +} + +void ValidatorManagerImpl::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { + td::actor::send_closure(db_, &Db::get_archive_id, masterchain_seqno, std::move(promise)); +} + +void ValidatorManagerImpl::get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) { + td::actor::send_closure(db_, &Db::get_archive_slice, archive_id, offset, limit, std::move(promise)); +} + bool ValidatorManagerImpl::is_validator() { return temp_keys_.size() > 0 || permanent_keys_.size() > 0; } diff --git a/validator/manager.hpp b/validator/manager.hpp index 669f1c6694..2177766e9c 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -236,6 +236,7 @@ class ValidatorManagerImpl : public ValidatorManager { BlockHandle last_key_block_handle_; BlockHandle last_known_key_block_handle_; + BlockHandle shard_client_handle_; BlockHandle gc_masterchain_handle_; td::Ref gc_masterchain_state_; @@ -308,6 +309,8 @@ class ValidatorManagerImpl : public ValidatorManager { td::int64 max_length, td::Promise promise) override; void get_block_proof(BlockHandle handle, td::Promise promise) override; void get_block_proof_link(BlockHandle block_id, td::Promise promise) override; + void get_key_block_proof(BlockIdExt block_id, td::Promise promise) override; + void get_key_block_proof_link(BlockIdExt block_id, td::Promise promise) override; //void get_block_description(BlockIdExt block_id, td::Promise promise) override; void new_external_message(td::BufferSlice data) override; @@ -385,11 +388,11 @@ class ValidatorManagerImpl : public ValidatorManager { void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) override; void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) override; + td::Promise promise) override; void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) override; + td::Promise promise) override; // get block handle declared in parent class void write_handle(BlockHandle handle, td::Promise promise) override; @@ -424,6 +427,10 @@ class ValidatorManagerImpl : public ValidatorManager { void get_async_serializer_state(td::Promise promise) override; void try_get_static_file(FileHash file_hash, td::Promise promise) override; + void try_download_archive_slice(); + void downloaded_archive_slice(std::string name); + void checked_archive_slice(std::vector seqno); + void failed_to_download_archive_slice(); void get_download_token(size_t download_size, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override { @@ -431,6 +438,10 @@ class ValidatorManagerImpl : public ValidatorManager { std::move(promise)); } + void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) override; + void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) override; + void check_is_hardfork(BlockIdExt block_id, td::Promise promise) override { CHECK(block_id.is_masterchain()); promise.set_result(opts_->is_hardfork(block_id)); @@ -484,6 +495,9 @@ class ValidatorManagerImpl : public ValidatorManager { allow_block_state_gc(block_id, std::move(promise)); } void allow_block_info_gc(BlockIdExt block_id, td::Promise promise) override; + void archive(BlockHandle handle, td::Promise promise) override { + td::actor::send_closure(db_, &Db::archive, std::move(handle), std::move(promise)); + } void send_peek_key_block_request(); void got_next_key_blocks(std::vector vec); @@ -544,6 +558,9 @@ class ValidatorManagerImpl : public ValidatorManager { bool started_ = false; bool allow_validate_ = false; + bool downloading_archive_slice_ = false; + td::Timestamp next_download_archive_slice_at_ = td::Timestamp::now(); + private: double state_ttl() const { return opts_->state_ttl(); diff --git a/validator/net/download-archive-slice.cpp b/validator/net/download-archive-slice.cpp new file mode 100644 index 0000000000..8b6e718822 --- /dev/null +++ b/validator/net/download-archive-slice.cpp @@ -0,0 +1,177 @@ +#include "download-archive-slice.hpp" +#include "td/utils/port/path.h" +#include "td/utils/overloaded.h" + +namespace ton { + +namespace validator { + +namespace fullnode { + +DownloadArchiveSlice::DownloadArchiveSlice( + BlockSeqno masterchain_seqno, std::string tmp_dir, adnl::AdnlNodeIdShort local_id, + overlay::OverlayIdShort overlay_id, adnl::AdnlNodeIdShort download_from, td::Timestamp timeout, + td::actor::ActorId validator_manager, td::actor::ActorId rldp, + td::actor::ActorId overlays, td::actor::ActorId adnl, + td::actor::ActorId client, td::Promise promise) + : masterchain_seqno_(masterchain_seqno) + , tmp_dir_(std::move(tmp_dir)) + , local_id_(local_id) + , overlay_id_(overlay_id) + , download_from_(download_from) + , timeout_(timeout) + , validator_manager_(validator_manager) + , rldp_(rldp) + , overlays_(overlays) + , adnl_(adnl) + , client_(client) + , promise_(std::move(promise)) { +} + +void DownloadArchiveSlice::abort_query(td::Status reason) { + if (promise_) { + promise_.set_error(std::move(reason)); + if (!fd_.empty()) { + td::unlink(tmp_name_).ensure(); + fd_.close(); + } + } + stop(); +} + +void DownloadArchiveSlice::alarm() { + abort_query(td::Status::Error(ErrorCode::timeout, "timeout")); +} + +void DownloadArchiveSlice::finish_query() { + if (promise_) { + promise_.set_value(std::move(tmp_name_)); + fd_.close(); + } + stop(); +} + +void DownloadArchiveSlice::start_up() { + alarm_timestamp() = timeout_; + + auto R = td::mkstemp(tmp_dir_); + if (R.is_error()) { + abort_query(R.move_as_error_prefix("failed to open temp file: ")); + return; + } + auto r = R.move_as_ok(); + fd_ = std::move(r.first); + tmp_name_ = std::move(r.second); + + if (download_from_.is_zero() && client_.empty()) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &DownloadArchiveSlice::abort_query, R.move_as_error()); + } else { + auto vec = R.move_as_ok(); + if (vec.size() == 0) { + td::actor::send_closure(SelfId, &DownloadArchiveSlice::abort_query, + td::Status::Error(ErrorCode::notready, "no nodes")); + } else { + td::actor::send_closure(SelfId, &DownloadArchiveSlice::got_node_to_download, vec[0]); + } + } + }); + + td::actor::send_closure(overlays_, &overlay::Overlays::get_overlay_random_peers, local_id_, overlay_id_, 1, + std::move(P)); + } else { + got_node_to_download(download_from_); + } +} + +void DownloadArchiveSlice::got_node_to_download(adnl::AdnlNodeIdShort download_from) { + download_from_ = download_from; + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &DownloadArchiveSlice::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &DownloadArchiveSlice::got_archive_info, R.move_as_ok()); + } + }); + + auto q = create_serialize_tl_object(masterchain_seqno_); + if (client_.empty()) { + td::actor::send_closure(overlays_, &overlay::Overlays::send_query, download_from_, local_id_, overlay_id_, + "get_archive_info", std::move(P), td::Timestamp::in(3.0), std::move(q)); + } else { + td::actor::send_closure(client_, &adnl::AdnlExtClient::send_query, "get_archive_info", + create_serialize_tl_object_suffix(std::move(q)), + td::Timestamp::in(1.0), std::move(P)); + } +} + +void DownloadArchiveSlice::got_archive_info(td::BufferSlice data) { + auto F = fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error_prefix("failed to parse ArchiveInfo answer")); + return; + } + auto f = F.move_as_ok(); + + bool fail = false; + ton_api::downcast_call(*f.get(), td::overloaded( + [&](const ton_api::tonNode_archiveNotFound &obj) { + abort_query(td::Status::Error(ErrorCode::notready, "remote db not found")); + fail = true; + }, + [&](const ton_api::tonNode_archiveInfo &obj) { archive_id_ = obj.id_; })); + if (fail) { + return; + } + + get_archive_slice(); +} + +void DownloadArchiveSlice::get_archive_slice() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &DownloadArchiveSlice::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &DownloadArchiveSlice::got_archive_slice, R.move_as_ok()); + } + }); + + auto q = create_serialize_tl_object(archive_id_, offset_, slice_size()); + if (client_.empty()) { + td::actor::send_closure(overlays_, &overlay::Overlays::send_query_via, download_from_, local_id_, overlay_id_, + "get_archive_slice", std::move(P), td::Timestamp::in(3.0), std::move(q), + slice_size() + 1024, rldp_); + } else { + td::actor::send_closure(client_, &adnl::AdnlExtClient::send_query, "get_archive_slice", + create_serialize_tl_object_suffix(std::move(q)), + td::Timestamp::in(1.0), std::move(P)); + } +} + +void DownloadArchiveSlice::got_archive_slice(td::BufferSlice data) { + auto R = fd_.write(data.as_slice()); + if (R.is_error()) { + abort_query(R.move_as_error_prefix("failed to write temp file: ")); + return; + } + if (R.move_as_ok() != data.size()) { + abort_query(td::Status::Error(ErrorCode::error, "short write to temp file")); + return; + } + + offset_ += data.size(); + + if (data.size() < slice_size()) { + finish_query(); + } else { + get_archive_slice(); + } +} + +} // namespace fullnode + +} // namespace validator + +} // namespace ton diff --git a/validator/net/download-archive-slice.hpp b/validator/net/download-archive-slice.hpp new file mode 100644 index 0000000000..018650ad2a --- /dev/null +++ b/validator/net/download-archive-slice.hpp @@ -0,0 +1,83 @@ +/* + 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 . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once + +#include "overlay/overlays.h" +#include "ton/ton-types.h" +#include "validator/validator.h" +#include "rldp/rldp.h" +#include "adnl/adnl-ext-client.h" +#include "td/utils/port/FileFd.h" + +namespace ton { + +namespace validator { + +namespace fullnode { + +class DownloadArchiveSlice : public td::actor::Actor { + public: + DownloadArchiveSlice(BlockSeqno masterchain_seqno, std::string tmp_dir, adnl::AdnlNodeIdShort local_id, + overlay::OverlayIdShort overlay_id, adnl::AdnlNodeIdShort download_from, td::Timestamp timeout, + td::actor::ActorId validator_manager, + td::actor::ActorId rldp, td::actor::ActorId overlays, + td::actor::ActorId adnl, td::actor::ActorId client, + td::Promise promise); + + void abort_query(td::Status reason); + void alarm() override; + void finish_query(); + + void start_up() override; + void got_node_to_download(adnl::AdnlNodeIdShort node); + void got_archive_info(td::BufferSlice data); + void get_archive_slice(); + void got_archive_slice(td::BufferSlice data); + + static constexpr td::uint32 slice_size() { + return 1 << 17; + } + + private: + BlockSeqno masterchain_seqno_; + std::string tmp_dir_; + std::string tmp_name_; + td::FileFd fd_; + adnl::AdnlNodeIdShort local_id_; + overlay::OverlayIdShort overlay_id_; + td::uint64 offset_ = 0; + td::uint64 archive_id_; + + adnl::AdnlNodeIdShort download_from_ = adnl::AdnlNodeIdShort::zero(); + + td::Timestamp timeout_; + td::actor::ActorId validator_manager_; + td::actor::ActorId rldp_; + td::actor::ActorId overlays_; + td::actor::ActorId adnl_; + td::actor::ActorId client_; + td::Promise promise_; +}; + +} // namespace fullnode + +} // namespace validator + +} // namespace ton + diff --git a/validator/net/download-proof.cpp b/validator/net/download-proof.cpp index 3fcfd6a983..b8513c9cd3 100644 --- a/validator/net/download-proof.cpp +++ b/validator/net/download-proof.cpp @@ -79,6 +79,33 @@ void DownloadProof::finish_query() { void DownloadProof::start_up() { alarm_timestamp() = timeout_; + if (!block_id_.is_masterchain()) { + checked_db(); + return; + } + + auto P = + td::PromiseCreator::lambda([SelfId = actor_id(this), l = allow_partial_proof_](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &DownloadProof::checked_db); + } else { + if (l) { + td::actor::send_closure(SelfId, &DownloadProof::got_block_partial_proof, R.move_as_ok()); + } else { + td::actor::send_closure(SelfId, &DownloadProof::got_block_proof, R.move_as_ok()); + } + } + }); + if (allow_partial_proof_) { + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_key_block_proof_link, block_id_, + std::move(P)); + } else { + td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::get_key_block_proof, block_id_, + std::move(P)); + } +} + +void DownloadProof::checked_db() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { td::actor::send_closure(SelfId, &DownloadProof::abort_query, diff --git a/validator/net/download-proof.hpp b/validator/net/download-proof.hpp index 64ed65bd23..e72c6c2efa 100644 --- a/validator/net/download-proof.hpp +++ b/validator/net/download-proof.hpp @@ -44,6 +44,7 @@ class DownloadProof : public td::actor::Actor { void finish_query(); void start_up() override; + void checked_db(); void got_download_token(std::unique_ptr token); void got_node_to_download(adnl::AdnlNodeIdShort node); void got_block_proof_description(td::BufferSlice proof_description); diff --git a/validator/shard-client.cpp b/validator/shard-client.cpp index 2f05bdac98..a609dcf935 100644 --- a/validator/shard-client.cpp +++ b/validator/shard-client.cpp @@ -39,6 +39,15 @@ void ShardClient::start_up() { td::actor::send_closure(manager_, &ValidatorManager::get_shard_client_state, true, std::move(P)); } +void ShardClient::start() { + if (!started_ && masterchain_state_.not_null()) { + started_ = true; + apply_all_shards(); + } else { + started_ = true; + } +} + void ShardClient::got_state_from_db(BlockIdExt state) { CHECK(!init_mode_); @@ -133,7 +142,9 @@ void ShardClient::download_masterchain_state() { void ShardClient::got_masterchain_block_state(td::Ref state) { masterchain_state_ = std::move(state); build_shard_overlays(); - apply_all_shards(); + if (started_) { + apply_all_shards(); + } } void ShardClient::apply_all_shards() { @@ -170,8 +181,8 @@ void ShardClient::apply_all_shards() { } void ShardClient::downloaded_shard_state(td::Ref state, td::Promise promise) { - run_apply_block_query(state->get_block_id(), td::Ref{}, manager_, td::Timestamp::in(600), - std::move(promise)); + run_apply_block_query(state->get_block_id(), td::Ref{}, masterchain_block_handle_->id(), manager_, + td::Timestamp::in(600), std::move(promise)); } void ShardClient::new_masterchain_block_notification(BlockHandle handle, td::Ref state) { diff --git a/validator/shard-client.hpp b/validator/shard-client.hpp index 2025041fca..9ad62da2d5 100644 --- a/validator/shard-client.hpp +++ b/validator/shard-client.hpp @@ -36,6 +36,7 @@ class ShardClient : public td::actor::Actor { bool waiting_ = false; bool init_mode_ = false; + bool started_ = false; td::actor::ActorId manager_; @@ -67,6 +68,7 @@ class ShardClient : public td::actor::Actor { void start_up() override; void start_up_init_mode(); void start_up_init_mode_finished(); + void start(); void got_state_from_db(BlockIdExt masterchain_block_id); void im_download_shard_state(BlockIdExt block_id, td::Promise promise); diff --git a/validator/validate-broadcast.cpp b/validator/validate-broadcast.cpp index c20539b049..2d0fbdab31 100644 --- a/validator/validate-broadcast.cpp +++ b/validator/validate-broadcast.cpp @@ -110,12 +110,12 @@ void ValidateBroadcast::start_up() { } else if (key_block_seqno == last_masterchain_state_->get_seqno()) { got_key_block_handle(last_masterchain_block_handle_); } else { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, R.move_as_error_prefix("cannot find reference key block id: ")); } else { - td::actor::send_closure(SelfId, &ValidateBroadcast::got_key_block_id, R.move_as_ok()); + td::actor::send_closure(SelfId, &ValidateBroadcast::got_key_block_handle, R.move_as_ok()); } }); td::actor::send_closure(manager_, &ValidatorManager::get_block_by_seqno_from_db, @@ -305,7 +305,9 @@ void ValidateBroadcast::checked_proof() { } }); - td::actor::create_actor("applyblock", handle_->id(), data_, manager_, timeout_, std::move(P)).release(); + td::actor::create_actor("applyblock", handle_->id(), data_, handle_->id(), manager_, timeout_, + std::move(P)) + .release(); } else { finish_query(); } diff --git a/validator/validator.h b/validator/validator.h index 1387fc95c3..5f6a377fec 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -117,6 +117,8 @@ class ValidatorManagerInterface : public td::actor::Actor { td::Promise promise) = 0; virtual void get_next_key_blocks(BlockIdExt block_id, td::Timestamp timeout, td::Promise> promise) = 0; + virtual void download_archive(BlockSeqno masterchain_seqno, std::string tmp_dir, td::Timestamp timeout, + td::Promise promise) = 0; virtual void new_key_block(BlockHandle handle) = 0; }; @@ -157,6 +159,8 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void get_block_proof(BlockHandle handle, td::Promise promise) = 0; virtual void get_block_proof_link(BlockHandle handle, td::Promise promise) = 0; virtual void get_block_handle(BlockIdExt block_id, bool force, td::Promise promise) = 0; + virtual void get_key_block_proof(BlockIdExt block_id, td::Promise promise) = 0; + virtual void get_key_block_proof_link(BlockIdExt block_id, td::Promise promise) = 0; virtual void get_next_key_blocks(BlockIdExt block_id, td::uint32 cnt, td::Promise> promise) = 0; virtual void get_next_block(BlockIdExt block_id, td::Promise promise) = 0; @@ -184,11 +188,15 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) = 0; virtual void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) = 0; + td::Promise promise) = 0; + + virtual void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) = 0; + virtual void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, + td::Promise promise) = 0; virtual void run_ext_query(td::BufferSlice data, td::Promise promise) = 0; virtual void prepare_stats(td::Promise>> promise) = 0; From 3d51e417661a00a0b890e1dc62fce0969628eebe Mon Sep 17 00:00:00 2001 From: ton Date: Fri, 15 Nov 2019 19:05:14 +0400 Subject: [PATCH 019/667] fixed compilation error --- validator/CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/validator/CMakeLists.txt b/validator/CMakeLists.txt index 564e21efd5..a1bd1fe32d 100644 --- a/validator/CMakeLists.txt +++ b/validator/CMakeLists.txt @@ -14,8 +14,6 @@ set(VALIDATOR_DB_SOURCE db/archive-manager.hpp db/archive-slice.cpp db/archive-slice.hpp - db/blockdb.cpp - db/blockdb.hpp db/celldb.cpp db/celldb.hpp db/files-async.hpp From 0dae2c157b55b8a9640efad5947a6a3df171cd02 Mon Sep 17 00:00:00 2001 From: ton Date: Sat, 16 Nov 2019 00:16:06 +0400 Subject: [PATCH 020/667] allow db_root without "/" in the end --- validator/db/archive-manager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 72d56e56ee..1d3b8ea92d 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -11,15 +11,15 @@ namespace validator { std::string PackageId::path() const { if (temp) { - return "files/packages/"; + return "/files/packages/"; } else if (key) { char s[24]; sprintf(s, "key%03d", id / 1000000); - return PSTRING() << "archive/packages/" << s << "/"; + return PSTRING() << "/archive/packages/" << s << "/"; } else { char s[20]; sprintf(s, "arch%04d", id / 100000); - return PSTRING() << "archive/packages/" << s << "/"; + return PSTRING() << "/archive/packages/" << s << "/"; } } From 7f3a22a217c0998d9cadca972d2be6aa327711a1 Mon Sep 17 00:00:00 2001 From: ton Date: Mon, 18 Nov 2019 22:15:14 +0400 Subject: [PATCH 021/667] speed up synchronization - download old files in chunks - updated docs - fixed elector/config smartcontracts --- crypto/smartcont/config-code.fc | 12 +- crypto/smartcont/elector-code.fc | 2 +- crypto/smartcont/gen-zerostate.fif | 5 +- crypto/smartcont/stdlib.fc | 6 +- crypto/smartcont/update-config-smc.fif | 6 +- crypto/smartcont/wallet.fif | 12 +- crypto/vm/contops.cpp | 14 ++ doc/FullNode-HOWTO | 2 +- doc/Validator-HOWTO | 2 +- lite-client-docs/README | 9 +- validator/db/archive-manager.cpp | 3 +- validator/downloaders/download-state.cpp | 3 + validator/full-node.cpp | 2 +- validator/import-db-slice.cpp | 190 +++++++++++++---------- validator/import-db-slice.hpp | 2 +- validator/manager-init.cpp | 16 +- validator/manager-init.hpp | 1 + validator/manager.cpp | 149 ++++++++++++------ validator/manager.hpp | 15 +- validator/shard-client.cpp | 79 ++++++++-- validator/shard-client.hpp | 10 +- 21 files changed, 357 insertions(+), 183 deletions(-) diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index 3a897d4cd0..c7058f50c0 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -26,7 +26,7 @@ (int, int) check_validator_set(cell vset) { var cs = vset.begin_parse(); - throw_if(9, (cs~load_uint(8) - 0x11) & -2); ;; validators#11 or validators_ext#12 + throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only int utime_since = cs~load_uint(32); int utime_until = cs~load_uint(32); int total = cs~load_uint(16); @@ -103,7 +103,7 @@ .end_cell(), 0); } -() after_code_upgrade(slice param, cell old_code) impure method_id(1666) { +() after_code_upgrade(slice param, cont old_code) impure method_id(1666) { } _ perform_action(cfg_dict, public_key, action, cs) { @@ -119,7 +119,7 @@ _ perform_action(cfg_dict, public_key, action, cs) { var new_code = cs~load_ref(); set_code(new_code); var old_code = get_c3(); - set_c3(new_code); + set_c3(new_code.begin_parse().bless()); after_code_upgrade(cs, old_code); throw(0); return (cfg_dict, public_key); @@ -231,7 +231,7 @@ cell register_vote(vote_dict, action, cs, idx, weight) { if (ds.slice_bits() >= 40) { var tag = ds~load_uint(8); var since = ds.preload_uint(32); - if ((tag == 0x11) & (since >= now())) { + if ((since <= now()) & (tag == 0x12)) { ;; next validator set becomes active! var cur_vset = cfg_dict~idict_set_get_ref(kl, 34, next_vset); ;; next_vset -> cur_vset cfg_dict~idict_set_get_ref(kl, 32, cur_vset); ;; cur_vset -> prev_vset @@ -241,3 +241,7 @@ cell register_vote(vote_dict, action, cs, idx, weight) { } set_data(begin_cell().store_ref(cfg_dict).store_slice(cs).end_cell()); } + +int seqno() impure method_id { + return get_data().begin_parse().preload_uint(32); +} diff --git a/crypto/smartcont/elector-code.fc b/crypto/smartcont/elector-code.fc index 263aa463a8..37257d1bbf 100644 --- a/crypto/smartcont/elector-code.fc +++ b/crypto/smartcont/elector-code.fc @@ -338,7 +338,7 @@ int upgrade_code(s_addr, cs, query_id) { var code = cs~load_ref(); set_code(code); ifnot(cs.slice_empty?()) { - set_c3(code); + set_c3(code.begin_parse().bless()); ;; run_method3(1666, s_addr, cs, query_id); after_code_upgrade(s_addr, cs, query_id); throw(0); diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 196ca5def7..4277445fdf 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -215,10 +215,11 @@ now dup orig_vset_valid_for + 0 config.validators! * */ "auto/config-code.fif" include // code in separate source file - // data empty_cell // libraries GR$10 // balance diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 0798a0a1b5..ba9416708c 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -35,8 +35,10 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI cell get_data() asm "c4 PUSH"; () set_data(cell c) impure asm "c4 POP"; -cell get_c3() impure asm "c3 PUSH"; -() set_c3(cell c) impure asm "c3 POP"; +cont get_c3() impure asm "c3 PUSH"; +() set_c3(cont c) impure asm "c3 POP"; +cont bless(slice s) impure asm "BLESS"; + () accept_message() impure asm "ACCEPT"; () commit() impure asm "COMMIT"; diff --git a/crypto/smartcont/update-config-smc.fif b/crypto/smartcont/update-config-smc.fif index 33e5fbcebc..a954ff14e4 100755 --- a/crypto/smartcont/update-config-smc.fif +++ b/crypto/smartcont/update-config-smc.fif @@ -9,14 +9,14 @@ $# dup 2 < swap 3 > or ' usage if "config-master" constant file-base -0 constant seqno +0 constant qseqno -1 constant idx true constant bounce "auto/config-code.fif" constant config-source 100 constant interval // valid for 100 seconds $1 =: file-base -$2 parse-int =: seqno +$2 parse-int =: qseqno def? $3 { @' $3 } { "config-query" } cond constant savefile file-base +".addr" load-address @@ -30,7 +30,7 @@ config-source include dup + dup ."signing message: " [-B ] [-C ] []" cr +{ ."usage: " @' $0 type ." [-n] [-B ] [-C ] []" cr ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt } : usage "" =: comment // comment for simple transfers +true =: allow-bounce +def? $5 { @' $5 "-n" $= { false =: allow-bounce [forget] $5 + def? $6 { @' $6 =: $5 [forget] $6 } if + def? $7 { @' $7 =: $6 [forget] $7 } if + @' $# 1- =: $# + } if +} if + def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or { @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond @@ -17,7 +25,7 @@ $# dup 4 < swap 5 > or ' usage if true constant bounce $1 =: file-base -$2 bounce parse-load-address =: bounce 2=: dest_addr +$2 bounce parse-load-address allow-bounce and =: bounce 2=: dest_addr $3 parse-int =: seqno $4 $>GR =: amount def? $5 { @' $5 } { "wallet-query" } cond constant savefile diff --git a/crypto/vm/contops.cpp b/crypto/vm/contops.cpp index 0039439e6b..6fcda8de62 100644 --- a/crypto/vm/contops.cpp +++ b/crypto/vm/contops.cpp @@ -626,9 +626,23 @@ inline void throw_rangechk(bool ok) { } } // namespace +int exec_bless_pop_c3(VmState* st) { + Stack& stack = st->get_stack(); + VM_LOG(st) << "execute CTOSBLESSPOPc3"; + stack.check_underflow(1); + throw_typechk(st->set_c(3, Ref{true, vm::load_cell_slice_ref(stack.pop_cell()), st->get_cp()})); + return 0; +} + int exec_pop_ctr(VmState* st, unsigned args) { unsigned idx = args & 15; VM_LOG(st) << "execute POP c" << idx; + /* + if (idx == 3 && st->get_stack().depth() > 0 && st->get_stack().tos().is(StackEntry::t_cell)) { + // temp hack: accept cell argument for POP c3 and do auto-BLESSing + return exec_bless_pop_c3(st); + } + */ throw_typechk(st->set(idx, st->get_stack().pop_chk())); return 0; } diff --git a/doc/FullNode-HOWTO b/doc/FullNode-HOWTO index 66840c95ff..2778b02f1c 100644 --- a/doc/FullNode-HOWTO +++ b/doc/FullNode-HOWTO @@ -5,7 +5,7 @@ Note that you need a machine with a public IP address and a high-bandwidth netwo 0. Downloading and compiling ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The complete TON Blockchain Library and Validator software is downloaded and compiled similarly to the Lite Client. This process is outlined in the corresponding README file. The most important difference is that you have to download the complete sources from public GitHub repository https://github.com/ton-blockchain/ton (e.g., by running `git clone https://github.com/ton-blockchain/ton`) instead of downloading the smaller Lite Client source archive. You should also build all goals defined in CMakeLists.txt (e.g. by running `cmake ` and `make` in your build directory), not only those specifically related to the Lite Client (which is also included in the larger distribution; you don't have to download and build it separately). We strongly recommend building a "release" or a "release with debug information" version of the TON Blockchain Library and especially of the Validator/Full Node by passing `-DCMAKE_BUILD_TYPE=Release` or `-DCMAKE_BUILD_TYPE=RelWithDebInfo` as an extra argument to `cmake` during its first run (if you forgot to do this, you can later delete file `CMakeCache.txt` from your build directory and re-run `cmake` with the appropriate options). +The complete TON Blockchain Library and Validator software is downloaded and compiled similarly to the Lite Client. This process is outlined in the corresponding README file. The most important difference is that you have to download the complete sources from public GitHub repository https://github.com/ton-blockchain/ton (e.g., by running `git clone https://github.com/ton-blockchain/ton` and `git submodule update` afterwards) instead of downloading the smaller Lite Client source archive. You should also build all goals defined in CMakeLists.txt (e.g. by running `cmake ` and `make` in your build directory), not only those specifically related to the Lite Client (which is also included in the larger distribution; you don't have to download and build it separately). We strongly recommend building a "release" or a "release with debug information" version of the TON Blockchain Library and especially of the Validator/Full Node by passing `-DCMAKE_BUILD_TYPE=Release` or `-DCMAKE_BUILD_TYPE=RelWithDebInfo` as an extra argument to `cmake` during its first run (if you forgot to do this, you can later delete file `CMakeCache.txt` from your build directory and re-run `cmake` with the appropriate options). 1. Full Node binaries ~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/Validator-HOWTO b/doc/Validator-HOWTO index 689bc6b56f..9545582d8d 100644 --- a/doc/Validator-HOWTO +++ b/doc/Validator-HOWTO @@ -10,7 +10,7 @@ The basic instructions are the same as for a TON Blockchain Full Node, as explai 1. Controlling smart contract of a validator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -In order to run a Validator, you'll need a Full Node that is already up and running (and completely synchronized with the current blockchain state), and a wallet in the masterchain holding a large amount of Grams (or test Grams, if you want to run a validator in the "testnet" TON Blockchain instance). Typically you'll need at least 100,000 Grams. +In order to run a Validator, you'll need a Full Node that is already up and running (and completely synchronized with the current blockchain state), and a wallet in the masterchain holding a large amount of Grams (or test Grams, if you want to run a validator in the "testnet" TON Blockchain instance). Typically you'll need at least 100,001 Grams in the production network, and at least 10,001 test Grams in the test network. The actual value (in nanograms) can be found as the value of `min_stake` in configuration parameter #17 (available by typing `getconfig 17` into the Lite Client), plus one Gram. Each validator is identified by its (Ed25519) public key. During the validator elections, the validator (or rather its public key) is also associated with a smart contract residing in the masterchain. For simplicity, we say that the validator is "controlled" by this smart contract (e.g., a wallet smart contract). Stakes are accepted on behalf of this validator only if they arrive from its associated smart contract, and only that associated smart contract is entitled to collect the validator's stake after it is unfrozen, along with the validator's share of bonuses (e.g., block mining fees, transaction and message forwarding fees collected from the users of the TON Blockchain by the validator pool). Typically the bonuses are distributed proportionally to the (effective) stakes of the validators. On the other hand, validators with higher stakes are assigned a larger amount of work to perform (i.e., they have to create and validate blocks for more shardchains), so it is important not to stake an amount that will yield more validation work than your node is capable of handling. diff --git a/lite-client-docs/README b/lite-client-docs/README index 7379d91e51..a47456e22b 100644 --- a/lite-client-docs/README +++ b/lite-client-docs/README @@ -8,18 +8,19 @@ The software is likely to compile and work properly on most Linux systems. It sh BASIC COMPILATION AND INSTALLATION INSTRUCTIONS -1) Download and unpack the newest version of this archive, available at +1) Download the newest version of the TON blockchain sources, available at GitHub repository https://github.com/ton-blockchain/ton/ : -https://test.ton.org/download +git clone https://github.com/ton-blockchain/ton.git +git submodule update The TON Blockchain Test Network is updated quite often, so we cannot guarantee that older versions of the Lite Client will always work. Backward compatibility is not enforced at this development stage. 2) Install the newest versions of make, cmake (version 3.0.2 or later), OpenSSL (including C header files), and g++ or clang (or another C++14-compatible compiler as appropriate for your operating system). We strongly recommend installing OpenSSL version 1.1.1 or later for better performance, especially if you intend to run a Full Node or a Validator as well. -3) Suppose that you have unpacked this archive to directory ~/lite-client, where ~ is your home directory, and that you have created an empty directory ~/liteclient-build. Then run the following in a terminal on a Linux system: +3) Suppose that you have fetched the source tree to directory ~/ton, where ~ is your home directory, and that you have created an empty directory ~/liteclient-build. Then run the following in a terminal on a Linux system: cd ~/liteclient-build -cmake ~/lite-client +cmake ~/ton cmake --build . --target lite-client You might also build some extra utilities useful for smart-contract development: diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 1d3b8ea92d..7eef7bbce1 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -250,8 +250,7 @@ void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td void ArchiveManager::get_file(BlockHandle handle, FileReference ref_id, td::Promise promise) { if (handle->moved_to_archive()) { - auto f = get_file_desc(handle->id().shard_full(), PackageId{handle->masterchain_ref_block(), false, false}, 0, 0, 0, - false); + auto f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), 0, 0, 0, false); if (f) { td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(promise)); return; diff --git a/validator/downloaders/download-state.cpp b/validator/downloaders/download-state.cpp index 9e690fffdb..de5eec08e5 100644 --- a/validator/downloaders/download-state.cpp +++ b/validator/downloaders/download-state.cpp @@ -193,6 +193,9 @@ void DownloadShardState::written_shard_state(td::Ref state) { handle_->set_logical_time(state_->get_logical_time()); handle_->set_applied(); handle_->set_split(state_->before_split()); + if (!block_id_.is_masterchain()) { + handle_->set_masterchain_ref_block(masterchain_block_id_.seqno()); + } auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle = handle_](td::Result R) { CHECK(handle->handle_moved_to_archive()); diff --git a/validator/full-node.cpp b/validator/full-node.cpp index b55b1008c3..5531df2c80 100644 --- a/validator/full-node.cpp +++ b/validator/full-node.cpp @@ -99,7 +99,6 @@ void FullNodeImpl::initial_read_complete(BlockHandle top_handle) { } void FullNodeImpl::add_shard(ShardIdFull shard) { - LOG(WARNING) << "add shard " << shard; while (true) { if (shards_.count(shard) == 0) { shards_.emplace(shard, FullNodeShard::create(shard, local_id_, adnl_id_, zero_state_file_hash_, keyring_, adnl_, @@ -239,6 +238,7 @@ void FullNodeImpl::download_archive(BlockSeqno masterchain_seqno, std::string tm } td::actor::ActorId FullNodeImpl::get_shard(ShardIdFull shard) { + add_shard(ShardIdFull{shard.workchain, shardIdAll}); while (shards_.count(shard) == 0) { if (shard.shard == shardIdAll) { return td::actor::ActorId{}; diff --git a/validator/import-db-slice.cpp b/validator/import-db-slice.cpp index 46fcc0aeb1..4e404b7e38 100644 --- a/validator/import-db-slice.cpp +++ b/validator/import-db-slice.cpp @@ -5,6 +5,8 @@ #include "td/actor/MultiPromise.h" #include "common/checksum.h" #include "td/utils/port/path.h" +#include "ton/ton-io.hpp" +#include "downloaders/download-state.hpp" namespace ton { @@ -94,79 +96,85 @@ void ArchiveImporter::check_masterchain_block(BlockSeqno seqno) { checked_all_masterchain_blocks(seqno - 1); return; } - if (seqno < state_->get_block_id().seqno()) { - if (!state_->check_old_mc_block_id(it->second)) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); - return; - } - check_masterchain_block(seqno + 1); - } else if (seqno == state_->get_block_id().seqno()) { - if (state_->get_block_id() != it->second) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); - return; + while (seqno <= state_->get_block_id().seqno()) { + if (seqno < state_->get_block_id().seqno()) { + if (!state_->check_old_mc_block_id(it->second)) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); + return; + } + } else { + if (state_->get_block_id() != it->second) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad old masterchain block id")); + return; + } } - check_masterchain_block(seqno + 1); - } else { - if (seqno != state_->get_block_id().seqno() + 1) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "hole in masterchain seqno")); + seqno++; + it = masterchain_blocks_.find(seqno); + if (it == masterchain_blocks_.end()) { + checked_all_masterchain_blocks(seqno - 1); return; } - auto it2 = blocks_.find(it->second); - CHECK(it2 != blocks_.end()); + } + if (seqno != state_->get_block_id().seqno() + 1) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "hole in masterchain seqno")); + return; + } + auto it2 = blocks_.find(it->second); + CHECK(it2 != blocks_.end()); - auto R1 = package_->read(it2->second[0]); - if (R1.is_error()) { - abort_query(R1.move_as_error()); - return; - } + auto R1 = package_->read(it2->second[0]); + if (R1.is_error()) { + abort_query(R1.move_as_error()); + return; + } - auto proofR = create_proof(it->second, std::move(R1.move_as_ok().second)); - if (proofR.is_error()) { - abort_query(proofR.move_as_error()); - return; - } + auto proofR = create_proof(it->second, std::move(R1.move_as_ok().second)); + if (proofR.is_error()) { + abort_query(proofR.move_as_error()); + return; + } - auto R2 = package_->read(it2->second[1]); - if (R2.is_error()) { - abort_query(R2.move_as_error()); - return; - } + auto R2 = package_->read(it2->second[1]); + if (R2.is_error()) { + abort_query(R2.move_as_error()); + return; + } - if (sha256_bits256(R2.ok().second.as_slice()) != it->second.file_hash) { - abort_query(td::Status::Error(ErrorCode::protoviolation, "bad block file hash")); + if (sha256_bits256(R2.ok().second.as_slice()) != it->second.file_hash) { + abort_query(td::Status::Error(ErrorCode::protoviolation, "bad block file hash")); + return; + } + auto dataR = create_block(it->second, std::move(R2.move_as_ok().second)); + if (dataR.is_error()) { + abort_query(dataR.move_as_error()); + return; + } + + auto proof = proofR.move_as_ok(); + auto data = dataR.move_as_ok(); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = state_->get_block_id(), + data](td::Result R) mutable { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); return; } - auto dataR = create_block(it->second, std::move(R2.move_as_ok().second)); - if (dataR.is_error()) { - abort_query(dataR.move_as_error()); + auto handle = R.move_as_ok(); + CHECK(!handle->merge_before()); + if (handle->one_prev(true) != id) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, + td::Status::Error(ErrorCode::protoviolation, "prev block mismatch")); return; } + td::actor::send_closure(SelfId, &ArchiveImporter::checked_masterchain_proof, std::move(handle), std::move(data)); + }); - auto proof = proofR.move_as_ok(); - auto data = dataR.move_as_ok(); - - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id = state_->get_block_id(), - data](td::Result R) mutable { - if (R.is_error()) { - td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); - return; - } - auto handle = R.move_as_ok(); - CHECK(!handle->merge_before()); - if (handle->one_prev(true) != id) { - td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, - td::Status::Error(ErrorCode::protoviolation, "prev block mismatch")); - return; - } - td::actor::send_closure(SelfId, &ArchiveImporter::checked_masterchain_proof, std::move(handle), std::move(data)); - }); - - run_check_proof_query(it->second, std::move(proof), manager_, td::Timestamp::in(2.0), std::move(P), state_, - opts_->is_hardfork(it->second)); - } + run_check_proof_query(it->second, std::move(proof), manager_, td::Timestamp::in(2.0), std::move(P), state_, + opts_->is_hardfork(it->second)); } void ArchiveImporter::checked_masterchain_proof(BlockHandle handle, td::Ref data) { + CHECK(data.not_null()); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle](td::Result R) { R.ensure(); td::actor::send_closure(SelfId, &ArchiveImporter::applied_masterchain_block, std::move(handle)); @@ -189,17 +197,16 @@ void ArchiveImporter::got_new_materchain_state(td::Ref state) } void ArchiveImporter::checked_all_masterchain_blocks(BlockSeqno seqno) { - max_shard_client_seqno_ = seqno; check_next_shard_client_seqno(shard_client_seqno_ + 1); } void ArchiveImporter::check_next_shard_client_seqno(BlockSeqno seqno) { - if (seqno > max_shard_client_seqno_) { + if (seqno > state_->get_seqno()) { finish_query(); return; } - if (seqno == max_shard_client_seqno_) { + if (seqno == state_->get_seqno()) { got_masterchain_state(state_); } else { BlockIdExt b; @@ -217,14 +224,13 @@ void ArchiveImporter::check_next_shard_client_seqno(BlockSeqno seqno) { void ArchiveImporter::got_masterchain_state(td::Ref state) { auto s = state->get_shards(); - auto P = td::PromiseCreator::lambda( - [SelfId = actor_id(this), seqno = state->get_block_id().seqno()](td::Result R) { - if (R.is_error()) { - td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); - } else { - td::actor::send_closure(SelfId, &ArchiveImporter::check_next_shard_client_seqno, seqno + 1); - } - }); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), seqno = state->get_seqno()](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ArchiveImporter::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::checked_shard_client_seqno, seqno); + } + }); td::MultiPromise mp; auto ig = mp.init_guard(); @@ -235,6 +241,12 @@ void ArchiveImporter::got_masterchain_state(td::Ref state) { } } +void ArchiveImporter::checked_shard_client_seqno(BlockSeqno seqno) { + CHECK(shard_client_seqno_ + 1 == seqno); + shard_client_seqno_++; + check_next_shard_client_seqno(seqno + 1); +} + void ArchiveImporter::apply_shard_block(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise) { auto P = td::PromiseCreator::lambda( @@ -253,9 +265,18 @@ void ArchiveImporter::apply_shard_block_cont1(BlockHandle handle, BlockIdExt mas return; } + if (handle->id().seqno() == 0) { + auto P = td::PromiseCreator::lambda( + [promise = std::move(promise)](td::Result> R) mutable { promise.set_value(td::Unit()); }); + td::actor::create_actor("downloadstate", handle->id(), masterchain_block_id, 2, manager_, + td::Timestamp::in(3600), std::move(P)) + .release(); + return; + } + auto it = blocks_.find(handle->id()); if (it == blocks_.end()) { - promise.set_error(td::Status::Error(ErrorCode::notready, "no proof for shard block")); + promise.set_error(td::Status::Error(ErrorCode::notready, PSTRING() << "no proof for shard block " << handle->id())); return; } TRY_RESULT_PROMISE(promise, data, package_->read(it->second[0])); @@ -280,21 +301,21 @@ void ArchiveImporter::apply_shard_block_cont2(BlockHandle handle, BlockIdExt mas } CHECK(handle->id().seqno() > 0); + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle, masterchain_block_id, + promise = std::move(promise)](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ArchiveImporter::apply_shard_block_cont3, std::move(handle), + masterchain_block_id, std::move(promise)); + } + }); if (!handle->merge_before() && handle->one_prev(true).shard_full() == handle->id().shard_full()) { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), handle, masterchain_block_id, - promise = std::move(promise)](td::Result R) mutable { - if (R.is_error()) { - promise.set_error(R.move_as_error()); - } else { - td::actor::send_closure(SelfId, &ArchiveImporter::apply_shard_block_cont3, std::move(handle), - masterchain_block_id, std::move(promise)); - } - }); apply_shard_block(handle->one_prev(true), masterchain_block_id, std::move(P)); } else { td::MultiPromise mp; auto ig = mp.init_guard(); - ig.add_promise(std::move(promise)); + ig.add_promise(std::move(P)); check_shard_block_applied(handle->one_prev(true), ig.get_promise()); if (handle->merge_before()) { check_shard_block_applied(handle->one_prev(false), ig.get_promise()); @@ -335,15 +356,12 @@ void ArchiveImporter::check_shard_block_applied(BlockIdExt block_id, td::Promise } void ArchiveImporter::abort_query(td::Status error) { - if (promise_) { - promise_.set_error(std::move(error)); - td::unlink(path_).ensure(); - } - stop(); + LOG(INFO) << error; + finish_query(); } void ArchiveImporter::finish_query() { if (promise_) { - promise_.set_value(std::vector(state_->get_block_id().seqno(), max_shard_client_seqno_)); + promise_.set_value(std::vector{state_->get_seqno(), shard_client_seqno_}); td::unlink(path_).ensure(); } stop(); diff --git a/validator/import-db-slice.hpp b/validator/import-db-slice.hpp index 5b7b1f1dd9..b7f383d8f1 100644 --- a/validator/import-db-slice.hpp +++ b/validator/import-db-slice.hpp @@ -25,6 +25,7 @@ class ArchiveImporter : public td::actor::Actor { void checked_all_masterchain_blocks(BlockSeqno seqno); void check_next_shard_client_seqno(BlockSeqno seqno); + void checked_shard_client_seqno(BlockSeqno seqno); void got_masterchain_state(td::Ref state); void apply_shard_block(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise); void apply_shard_block_cont1(BlockHandle handle, BlockIdExt masterchain_block_id, td::Promise promise); @@ -36,7 +37,6 @@ class ArchiveImporter : public td::actor::Actor { std::string path_; td::Ref state_; BlockSeqno shard_client_seqno_; - BlockSeqno max_shard_client_seqno_; td::Ref opts_; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index d2a7b861f9..80366875c3 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -414,7 +414,8 @@ void ValidatorManagerMasterchainStarter::got_key_block_handle(BlockHandle handle void ValidatorManagerMasterchainStarter::got_shard_block_id(BlockIdExt block_id) { client_block_id_ = block_id; - finish(); + start_shard_client(); + return; auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { R.ensure(); @@ -436,7 +437,7 @@ void ValidatorManagerMasterchainStarter::got_hardforks(std::vector v return; } } - finish(); + start_shard_client(); return; } if (h.size() > vec.size() + 1) { @@ -565,13 +566,20 @@ void ValidatorManagerMasterchainStarter::truncated() { void ValidatorManagerMasterchainStarter::written_next() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { R.ensure(); - td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::finish); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::start_shard_client); }); td::actor::send_closure(db_, &Db::update_hardforks, opts_->get_hardforks(), std::move(P)); } +void ValidatorManagerMasterchainStarter::start_shard_client() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::finish); + }); + client_ = td::actor::create_actor("shardclient", opts_, manager_, std::move(P)); +} + void ValidatorManagerMasterchainStarter::finish() { - client_ = td::actor::create_actor("shardclient", opts_, manager_); promise_.set_value( ValidatorManagerInitResult{handle_, state_, std::move(client_), gc_handle_, gc_state_, last_key_block_handle_}); stop(); diff --git a/validator/manager-init.hpp b/validator/manager-init.hpp index e8c2ad4b04..49e7562c4a 100644 --- a/validator/manager-init.hpp +++ b/validator/manager-init.hpp @@ -105,6 +105,7 @@ class ValidatorManagerMasterchainStarter : public td::actor::Actor { void got_prev_key_block_handle(BlockHandle handle); void truncated(); void written_next(); + void start_shard_client(); void finish(); private: diff --git a/validator/manager.cpp b/validator/manager.cpp index 4a627196b1..17bf449aae 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1414,7 +1414,6 @@ void ValidatorManagerImpl::started(ValidatorManagerInitResult R) { gc_masterchain_state_ = std::move(R.gc_state); shard_client_ = std::move(R.clients); - td::actor::send_closure(shard_client_, &ShardClient::start); auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { @@ -1429,8 +1428,6 @@ void ValidatorManagerImpl::started(ValidatorManagerInitResult R) { }); td::actor::send_closure(db_, &Db::get_destroyed_validator_sessions, std::move(P)); - - send_peek_key_block_request(); } void ValidatorManagerImpl::read_gc_list(std::vector list) { @@ -1443,6 +1440,104 @@ void ValidatorManagerImpl::read_gc_list(std::vector list) { serializer_ = td::actor::create_actor("serializer", last_key_block_handle_->id(), opts_, actor_id(this)); + if (!out_of_sync()) { + completed_prestart_sync(); + } else { + prestart_sync(); + } +} + +bool ValidatorManagerImpl::out_of_sync() { + if (last_masterchain_block_handle_->unix_time() + 600 > td::Clocks::system()) { + return false; + } + + if (validator_groups_.size() > 0 && last_known_key_block_handle_->id().seqno() <= last_masterchain_seqno_) { + return false; + } + + return true; +} + +void ValidatorManagerImpl::prestart_sync() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + }); + td::actor::send_closure(db_, &Db::set_async_mode, false, std::move(P)); +} + +void ValidatorManagerImpl::download_next_archive() { + if (!out_of_sync()) { + finish_prestart_sync(); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + LOG(INFO) << "failed to download archive slice: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + } else { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); + } + }); + callback_->download_archive(shard_client_handle_->id().seqno() + 1, db_root_ + "/tmp/", td::Timestamp::in(3600.0), + std::move(P)); +} + +void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { + LOG(INFO) << "downloaded archive slice: " << name; + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + } else { + td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); + } + }); + + td::actor::create_actor("archiveimport", name, last_masterchain_state_, + shard_client_handle_->id().seqno(), opts_, actor_id(this), std::move(P)) + .release(); +} + +void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) { + CHECK(seqno.size() == 2); + LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; + CHECK(seqno[0] <= last_masterchain_seqno_); + + BlockIdExt b; + CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b)); + + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), db = db_.get(), client = shard_client_.get()](td::Result R) { + R.ensure(); + auto handle = R.move_as_ok(); + auto P = td::PromiseCreator::lambda([SelfId, client, handle](td::Result> R) mutable { + auto P = td::PromiseCreator::lambda([SelfId](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + }); + td::actor::send_closure(client, &ShardClient::force_update_shard_client_ex, std::move(handle), + td::Ref{R.move_as_ok()}, std::move(P)); + }); + td::actor::send_closure(db, &Db::get_block_state, std::move(handle), std::move(P)); + }); + get_block_handle(b, true, std::move(P)); +} + +void ValidatorManagerImpl::finish_prestart_sync() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ValidatorManagerImpl::completed_prestart_sync); + }); + td::actor::send_closure(db_, &Db::set_async_mode, false, std::move(P)); +} + +void ValidatorManagerImpl::completed_prestart_sync() { + td::actor::send_closure(shard_client_, &ShardClient::start); + + send_peek_key_block_request(); + LOG(WARNING) << "initial read complete: " << last_masterchain_block_handle_->id() << " " << last_masterchain_block_id_; callback_->initial_read_complete(last_masterchain_block_handle_); @@ -1979,11 +2074,6 @@ void ValidatorManagerImpl::alarm() { } log_status_at_ = td::Timestamp::in(60.0); } - if (false && !downloading_archive_slice_ && shard_client_handle_ && - shard_client_handle_->unix_time() + 600 <= td::Clocks::system() && next_download_archive_slice_at_.is_in_past()) { - next_download_archive_slice_at_ = td::Timestamp::in(10.0); - try_download_archive_slice(); - } alarm_timestamp().relax(log_status_at_); if (resend_shard_blocks_at_ && resend_shard_blocks_at_.is_in_past()) { resend_shard_blocks_at_ = td::Timestamp::never(); @@ -2053,49 +2143,6 @@ void ValidatorManagerImpl::try_get_static_file(FileHash file_hash, td::Promise R) { - if (R.is_error()) { - LOG(INFO) << "failed to download archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::failed_to_download_archive_slice); - } else { - td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); - } - }); - callback_->download_archive(shard_client_handle_->id().seqno(), db_root_ + "/tmp/", td::Timestamp::in(3600.0), - std::move(P)); -} - -void ValidatorManagerImpl::failed_to_download_archive_slice() { - downloading_archive_slice_ = false; -} - -void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { - LOG(INFO) << "downloaded archive slice: " << name; - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { - if (R.is_error()) { - LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::failed_to_download_archive_slice); - } else { - td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); - } - }); - - td::actor::create_actor("archiveimport", name, last_masterchain_state_, - shard_client_handle_->id().seqno(), opts_, actor_id(this), std::move(P)) - .release(); -} - -void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) { - CHECK(seqno.size() == 2); - LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; - downloading_archive_slice_ = false; - next_download_archive_slice_at_ = td::Timestamp::in(10.0); -} - void ValidatorManagerImpl::get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) { td::actor::send_closure(db_, &Db::get_archive_id, masterchain_seqno, std::move(promise)); } diff --git a/validator/manager.hpp b/validator/manager.hpp index 2177766e9c..b0c64c96d1 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -261,6 +261,14 @@ class ValidatorManagerImpl : public ValidatorManager { void update_gc_block_handle(BlockHandle handle, td::Promise promise) override; void update_shard_client_block_handle(BlockHandle handle, td::Promise promise) override; + bool out_of_sync(); + void prestart_sync(); + void download_next_archive(); + void downloaded_archive_slice(std::string name); + void checked_archive_slice(std::vector seqno); + void finish_prestart_sync(); + void completed_prestart_sync(); + public: void install_callback(std::unique_ptr new_callback, td::Promise promise) override { callback_ = std::move(new_callback); @@ -427,10 +435,6 @@ class ValidatorManagerImpl : public ValidatorManager { void get_async_serializer_state(td::Promise promise) override; void try_get_static_file(FileHash file_hash, td::Promise promise) override; - void try_download_archive_slice(); - void downloaded_archive_slice(std::string name); - void checked_archive_slice(std::vector seqno); - void failed_to_download_archive_slice(); void get_download_token(size_t download_size, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) override { @@ -558,9 +562,6 @@ class ValidatorManagerImpl : public ValidatorManager { bool started_ = false; bool allow_validate_ = false; - bool downloading_archive_slice_ = false; - td::Timestamp next_download_archive_slice_at_ = td::Timestamp::now(); - private: double state_ttl() const { return opts_->state_ttl(); diff --git a/validator/shard-client.cpp b/validator/shard-client.cpp index a609dcf935..662ba1721c 100644 --- a/validator/shard-client.cpp +++ b/validator/shard-client.cpp @@ -40,11 +40,9 @@ void ShardClient::start_up() { } void ShardClient::start() { - if (!started_ && masterchain_state_.not_null()) { - started_ = true; - apply_all_shards(); - } else { + if (!started_) { started_ = true; + saved_to_db(); } } @@ -52,7 +50,31 @@ void ShardClient::got_state_from_db(BlockIdExt state) { CHECK(!init_mode_); CHECK(state.is_valid()); - new_masterchain_block_id(state); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &ShardClient::got_init_handle_from_db, R.move_as_ok()); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, state, true, std::move(P)); +} + +void ShardClient::got_init_handle_from_db(BlockHandle handle) { + masterchain_block_handle_ = std::move(handle); + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + R.ensure(); + td::actor::send_closure(SelfId, &ShardClient::got_init_state_from_db, td::Ref{R.move_as_ok()}); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db, masterchain_block_handle_, + std::move(P)); +} + +void ShardClient::got_init_state_from_db(td::Ref state) { + masterchain_state_ = std::move(state); + build_shard_overlays(); + masterchain_state_.clear(); + + saved_to_db(); } void ShardClient::start_up_init_mode() { @@ -97,14 +119,19 @@ void ShardClient::applied_all_shards() { } void ShardClient::saved_to_db() { - if (init_mode_) { + CHECK(masterchain_block_handle_); + td::actor::send_closure(manager_, &ValidatorManager::update_shard_client_block_handle, masterchain_block_handle_, + [](td::Unit) {}); + if (promise_) { promise_.set_value(td::Unit()); + } + if (init_mode_) { init_mode_ = false; } - CHECK(masterchain_block_handle_); - td::actor::send_closure(manager_, &ValidatorManager::update_shard_client_block_handle, masterchain_block_handle_, - [](td::Unit) {}); + if (!started_) { + return; + } if (masterchain_block_handle_->inited_next_left()) { new_masterchain_block_id(masterchain_block_handle_->one_next(true)); } else { @@ -239,6 +266,40 @@ void ShardClient::build_shard_overlays() { } } +void ShardClient::force_update_shard_client(BlockHandle handle, td::Promise promise) { + CHECK(!init_mode_); + CHECK(!started_); + + if (masterchain_block_handle_->id().seqno() >= handle->id().seqno()) { + promise.set_value(td::Unit()); + return; + } + + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), handle, promise = std::move(promise)](td::Result> R) mutable { + R.ensure(); + td::actor::send_closure(SelfId, &ShardClient::force_update_shard_client_ex, std::move(handle), + td::Ref{R.move_as_ok()}, std::move(promise)); + }); + td::actor::send_closure(manager_, &ValidatorManager::get_shard_state_from_db, std::move(handle), std::move(P)); +} + +void ShardClient::force_update_shard_client_ex(BlockHandle handle, td::Ref state, + td::Promise promise) { + CHECK(!init_mode_); + CHECK(!started_); + + if (masterchain_block_handle_->id().seqno() >= handle->id().seqno()) { + promise.set_value(td::Unit()); + return; + } + masterchain_block_handle_ = std::move(handle); + masterchain_state_ = std::move(state); + promise_ = std::move(promise); + build_shard_overlays(); + applied_all_shards(); +} + } // namespace validator } // namespace ton diff --git a/validator/shard-client.hpp b/validator/shard-client.hpp index 9ad62da2d5..960f2514f3 100644 --- a/validator/shard-client.hpp +++ b/validator/shard-client.hpp @@ -55,8 +55,9 @@ class ShardClient : public td::actor::Actor { , promise_(std::move(promise)) { init_mode_ = true; } - ShardClient(td::Ref opts, td::actor::ActorId manager) - : opts_(std::move(opts)), manager_(manager) { + ShardClient(td::Ref opts, td::actor::ActorId manager, + td::Promise promise) + : opts_(std::move(opts)), manager_(manager), promise_(std::move(promise)) { } static constexpr td::uint32 shard_client_priority() { @@ -70,6 +71,8 @@ class ShardClient : public td::actor::Actor { void start_up_init_mode_finished(); void start(); void got_state_from_db(BlockIdExt masterchain_block_id); + void got_init_handle_from_db(BlockHandle handle); + void got_init_state_from_db(td::Ref state); void im_download_shard_state(BlockIdExt block_id, td::Promise promise); void im_downloaded_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise promise); @@ -91,6 +94,9 @@ class ShardClient : public td::actor::Actor { void get_processed_masterchain_block(td::Promise promise); void get_processed_masterchain_block_id(td::Promise promise); + + void force_update_shard_client(BlockHandle handle, td::Promise promise); + void force_update_shard_client_ex(BlockHandle handle, td::Ref state, td::Promise promise); }; } // namespace validator From 090e0c16eb86184eaa3fda0e5d1c9838cf2ff88e Mon Sep 17 00:00:00 2001 From: ton Date: Thu, 28 Nov 2019 18:44:14 +0400 Subject: [PATCH 022/667] slightly changed block format - small change in block format - added config in blockchain explorer - bugfixes --- CMakeLists.txt | 8 +- .../blockchain-explorer-http.cpp | 50 +- .../blockchain-explorer-http.hpp | 26 + .../blockchain-explorer-query.cpp | 449 ++++++++++++++++++ .../blockchain-explorer-query.hpp | 71 +++ blockchain-explorer/blockchain-explorer.cpp | 108 ++++- blockchain-explorer/blockchain-explorer.hpp | 2 + crypto/CMakeLists.txt | 2 + crypto/block/block.cpp | 17 +- crypto/block/block.h | 11 +- crypto/block/block.tlb | 5 + crypto/block/mc-config.cpp | 24 + crypto/block/mc-config.h | 4 +- crypto/block/transaction.cpp | 53 +++ crypto/block/transaction.h | 5 +- crypto/fift/lib/Asm.fif | 2 + crypto/fift/lib/Fift.fif | 1 + crypto/fift/lib/GetOpt.fif | 92 ++++ crypto/fift/utils.cpp | 7 + crypto/fift/words.cpp | 84 +++- crypto/smartcont/config-code.fc | 5 +- crypto/smartcont/elector-code.fc | 4 +- crypto/smartcont/gen-zerostate.fif | 8 +- crypto/smartcont/multisig-code.fc | 109 +++-- crypto/smartcont/show-addr.fif | 7 +- crypto/smartcont/stdlib.fc | 1 + crypto/smartcont/wallet.fif | 31 +- crypto/smc-envelope/MultisigWallet.cpp | 26 +- crypto/smc-envelope/MultisigWallet.h | 7 +- crypto/smc-envelope/SmartContract.cpp | 4 - crypto/smc-envelope/SmartContractCode.cpp | 6 +- crypto/test/test-smartcont.cpp | 19 +- crypto/vm/continuation.cpp | 3 + crypto/vm/dict.h | 5 + crypto/vm/stack.hpp | 14 +- crypto/vm/tonops.cpp | 39 +- crypto/vm/utils.cpp | 134 ++++++ crypto/vm/utils.h | 11 + doc/fiftbase.tex | 1 + doc/tvm.tex | 4 +- lite-client/lite-client.cpp | 209 ++++---- lite-client/lite-client.h | 6 + tddb/td/db/MemoryKeyValue.h | 6 +- test/test-adnl.cpp | 1 + test/test-catchain.cpp | 1 + test/test-dht.cpp | 1 + test/test-rldp.cpp | 1 + test/test-validator-session-state.cpp | 4 +- tl/generate/scheme/lite_api.tl | 2 + tl/generate/scheme/lite_api.tlo | Bin 11088 -> 11676 bytes ton/ton-types.h | 10 +- tonlib/test/online.cpp | 4 +- tonlib/tonlib/KeyValue.cpp | 6 +- validator-engine/validator-engine.cpp | 73 ++- validator/db/archive-manager.cpp | 88 +++- validator/db/archive-manager.hpp | 25 +- validator/db/archive-slice.cpp | 37 +- validator/db/archive-slice.hpp | 9 +- validator/db/package.cpp | 4 + validator/db/package.hpp | 2 + validator/db/rootdb.cpp | 16 +- validator/db/rootdb.hpp | 16 +- validator/fabric.h | 1 + validator/impl/config.cpp | 8 + validator/impl/config.hpp | 1 + validator/impl/fabric.cpp | 4 + validator/impl/liteserver.cpp | 116 ++++- validator/impl/liteserver.hpp | 2 + validator/impl/shard.hpp | 2 +- validator/impl/validate-query.cpp | 2 +- validator/interfaces/block-handle.h | 1 + validator/interfaces/config.h | 1 + validator/interfaces/db.h | 18 +- validator/interfaces/shard.h | 2 +- validator/manager-disk.cpp | 15 +- validator/manager-disk.hpp | 15 +- validator/manager-init.cpp | 4 +- validator/manager.cpp | 30 +- validator/manager.hpp | 15 +- validator/validate-broadcast.cpp | 6 +- validator/validate-broadcast.hpp | 2 +- validator/validator.h | 14 +- 82 files changed, 1850 insertions(+), 389 deletions(-) create mode 100644 crypto/fift/lib/GetOpt.fif create mode 100644 crypto/vm/utils.cpp create mode 100644 crypto/vm/utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 8146bef970..4d59081b9e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -441,7 +441,7 @@ if (USE_LIBRAPTORQ) endif() add_executable(test-hello-world test/test-hello-world.cpp ) -target_link_libraries(test-hello-world tl_api crypto ton_crypto) +target_link_libraries(test-hello-world tl_api ton_crypto) add_executable(test-adnl test/test-adnl.cpp) target_link_libraries(test-adnl adnl adnltest dht tl_api) @@ -510,6 +510,12 @@ add_test(test-tonlib-offline test-tonlib-offline) #BEGIN internal if (NOT TON_ONLY_TONLIB) +add_test(test-adnl test-adnl) +add_test(test-dht test-dht) +add_test(test-rldp test-rldp) +add_test(test-validator-session-state test-validator-session-state) +add_test(test-catchain test-catchain) + add_test(test-fec test-fec) add_test(test-tddb test-tddb ${TEST_OPTIONS}) add_test(test-db test-db ${TEST_OPTIONS}) diff --git a/blockchain-explorer/blockchain-explorer-http.cpp b/blockchain-explorer/blockchain-explorer-http.cpp index 43d63d91cd..d5d41a15dd 100644 --- a/blockchain-explorer/blockchain-explorer-http.cpp +++ b/blockchain-explorer/blockchain-explorer-http.cpp @@ -379,6 +379,23 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) { return *this; } + *this << "
" + << "
" + << "

Run get method

" + << "

" + << "" + << "
\n" + << "
" + << "
" + << "" + << "" + << "" + << "" + << "" + << "" + << "
" + << "
\n"; + *this << "
\n" << "
block" - << block_id.id.to_str() << "
block" << block_id.id.to_str() + << "
workchain" << acc_c.addr.workchain << "
account hex" << acc_c.addr.addr.to_hex() << "
account" << acc_c.addr.rserialize(true) << "
roothash" << block_id.root_hash.to_hex() << "
filehash" << block_id.file_hash.to_hex() << "
time" << info.gen_utime << "
lt" << info.start_lt << " .. " << info.end_lt - << "
lt" << info.start_lt << " .. " << info.end_lt << "
global_id" << blk.global_id << "
version" << info.version << "
flags" << info.flags << "
key_block" << info.key_block << "
not_master" << info.not_master << "
after_merge" << info.after_merge << "
after_split" << info.after_split << "
before_split" << info.before_split << "
want_merge" << info.want_merge << "
want_split" << info.want_split << "
validator_list_hash_short" - << info.gen_validator_list_hash_short << "
validator_list_hash_short" << info.gen_validator_list_hash_short << "
catchain_seqno" << info.gen_catchain_seqno << "
min_ref_mc_seqno" << info.min_ref_mc_seqno - << "
min_ref_mc_seqno" << info.min_ref_mc_seqno << "
vert_seqno" << info.vert_seq_no << "
vert_seqno_incr" << info.vert_seqno_incr << "
prev_key_block_seqno" + << ton::BlockId{ton::masterchainId, ton::shardIdAll, info.prev_key_block_seqno} << "
prev block" << id << "
\n"; *this << "
block" << block_id.id.to_str() @@ -480,10 +497,13 @@ HttpAnswer& HttpAnswer::operator<<(BlockHeaderCell head_c) { return *this; } - return *this << "

download block" - << "view block\n" - << "

"; + *this << "

download block" + << "view block\n"; + if (block_id.is_masterchain()) { + *this << "view config\n"; + } + return *this << "

"; } HttpAnswer& HttpAnswer::operator<<(BlockShardsCell shards_c) { @@ -571,6 +591,12 @@ HttpAnswer& HttpAnswer::operator<<(BlockViewLink block) { return *this; } +HttpAnswer& HttpAnswer::operator<<(ConfigViewLink block) { + *this << prefix_ << "config?"; + block_id_link(block.block_id); + return *this; +} + HttpAnswer& HttpAnswer::operator<<(BlockDownloadLink block) { *this << prefix_ << "download?"; block_id_link(block.block_id); @@ -606,10 +632,26 @@ HttpAnswer& HttpAnswer::operator<<(TransactionList trans) { return *this << "
"; } +HttpAnswer& HttpAnswer::operator<<(ConfigParam conf) { + std::ostringstream os; + *this << "

param " << conf.idx << "

"; + if (conf.idx >= 0) { + *this << RawData{conf.root, conf.idx}; + } else { + *this << RawData{conf.root}; + } + *this << "
\n"; + return *this; +} + HttpAnswer& HttpAnswer::operator<<(Error error) { return *this << "
" << error.error.to_string() << "
"; } +HttpAnswer& HttpAnswer::operator<<(Notification n) { + return *this << "
" << n.text << "
"; +} + void HttpAnswer::block_id_link(ton::BlockIdExt block_id) { *this << "workchain=" << block_id.id.workchain << "&shard=" << ton::shard_to_str(block_id.id.shard) << "&seqno=" << block_id.id.seqno << "&roothash=" << block_id.root_hash << "&filehash=" << block_id.file_hash; diff --git a/blockchain-explorer/blockchain-explorer-http.hpp b/blockchain-explorer/blockchain-explorer-http.hpp index dadd7e171c..4fbb42c923 100644 --- a/blockchain-explorer/blockchain-explorer-http.hpp +++ b/blockchain-explorer/blockchain-explorer-http.hpp @@ -84,6 +84,9 @@ class HttpAnswer { struct BlockViewLink { ton::BlockIdExt block_id; }; + struct ConfigViewLink { + ton::BlockIdExt block_id; + }; struct BlockDownloadLink { ton::BlockIdExt block_id; }; @@ -116,9 +119,16 @@ class HttpAnswer { struct CodeBlock { std::string data; }; + struct ConfigParam { + td::int32 idx; + td::Ref root; + }; struct Error { td::Status error; }; + struct Notification { + std::string text; + }; template struct RawData { td::Ref root; @@ -189,14 +199,17 @@ class HttpAnswer { HttpAnswer &operator<<(TransactionLinkShort trans); HttpAnswer &operator<<(BlockLink block); HttpAnswer &operator<<(BlockViewLink block); + HttpAnswer &operator<<(ConfigViewLink block); HttpAnswer &operator<<(BlockDownloadLink block); HttpAnswer &operator<<(Error error); + HttpAnswer &operator<<(Notification notification); HttpAnswer &operator<<(TransactionList trans); HttpAnswer &operator<<(CodeBlock block) { return *this << "
" << block.data << "
"; } + HttpAnswer &operator<<(ConfigParam conf); template HttpAnswer &operator<<(RawData data) { @@ -220,3 +233,16 @@ class HttpAnswer { std::unique_ptr sb_; td::BufferSlice buf_; }; + +template <> +struct HttpAnswer::RawData { + td::Ref root; + RawData(td::Ref root) : root(std::move(root)) { + } +}; +template <> +inline HttpAnswer &HttpAnswer::operator<<(RawData data) { + std::ostringstream outp; + vm::load_cell_slice(data.root).print_rec(outp); + return *this << CodeBlock{outp.str()}; +} diff --git a/blockchain-explorer/blockchain-explorer-query.cpp b/blockchain-explorer/blockchain-explorer-query.cpp index 061aace6f9..db731f1416 100644 --- a/blockchain-explorer/blockchain-explorer-query.cpp +++ b/blockchain-explorer/blockchain-explorer-query.cpp @@ -28,6 +28,8 @@ */ #include "blockchain-explorer-query.hpp" #include "blockchain-explorer-http.hpp" +#include "block/mc-config.h" +#include "crypto/block/check-proof.h" #include "auto/tl/lite_api.h" @@ -39,6 +41,40 @@ #include "common/errorcode.h" #include "block/block-auto.h" +#include "crypto/vm/utils.h" +#include "td/utils/crypto.h" + +#include "vm/boc.h" +#include "vm/cellops.h" +#include "vm/cells/MerkleProof.h" +#include "vm/continuation.h" +#include "vm/cp0.h" + +namespace { + +td::Ref prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref my_addr, + const block::CurrencyCollection &balance) { + td::BitArray<256> rand_seed; + td::RefInt256 rand_seed_int{true}; + td::Random::secure_bytes(rand_seed.as_slice()); + if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) { + return {}; + } + auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea + td::make_refint(0), // actions:Integer + td::make_refint(0), // msgs_sent:Integer + td::make_refint(now), // unixtime:Integer + td::make_refint(lt), // block_lt:Integer + td::make_refint(lt), // trans_lt:Integer + std::move(rand_seed_int), // rand_seed:Integer + balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] + my_addr, // myself:MsgAddressInt + vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo; + LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); + return vm::make_tuple_ref(std::move(tuple)); +} + +} // namespace td::Result parse_block_id(std::map &opts, bool allow_empty) { if (allow_empty) { @@ -990,6 +1026,419 @@ void HttpQueryViewLastBlock::finish_query() { stop(); } +HttpQueryConfig::HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector params, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)), block_id_(block_id), params_(std::move(params)) { +} + +HttpQueryConfig::HttpQueryConfig(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { + auto R = parse_block_id(opts, true); + if (R.is_error()) { + error_ = R.move_as_error(); + return; + } + block_id_ = R.move_as_ok(); + + auto it = opts.find("param"); + if (it != opts.end()) { + auto R2 = td::to_integer_safe(it->second); + if (R2.is_error()) { + error_ = R2.move_as_error(); + return; + } + params_.push_back(R2.move_as_ok()); + } +} + +void HttpQueryConfig::start_up() { + if (error_.is_error()) { + abort_query(std::move(error_)); + return; + } + if (block_id_.is_valid()) { + send_main_query(); + } else { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &HttpQueryConfig::got_block, R.move_as_ok()); + } + }); + + auto query = ton::serialize_tl_object(ton::create_tl_object(), true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); + } +} + +void HttpQueryConfig::got_block(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + return; + } + auto f = F.move_as_ok(); + block_id_ = ton::create_block_id(f->last_); + + send_main_query(); +} + +void HttpQueryConfig::send_main_query() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQueryConfig::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &HttpQueryConfig::got_result, R.move_as_ok()); + } + }); + auto query = + params_.size() > 0 + ? ton::serialize_tl_object(ton::create_tl_object( + 0, ton::create_tl_lite_block_id(block_id_), std::vector(params_)), + true) + : ton::serialize_tl_object(ton::create_tl_object( + 0, ton::create_tl_lite_block_id(block_id_)), + true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); +} + +void HttpQueryConfig::got_result(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + return; + } + auto f = F.move_as_ok(); + + state_proof_ = std::move(f->state_proof_); + config_proof_ = std::move(f->config_proof_); + + finish_query(); +} + +void HttpQueryConfig::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"config", prefix_}; + A.set_block_id(block_id_); + auto R = block::check_extract_state_proof(block_id_, state_proof_.as_slice(), config_proof_.as_slice()); + if (R.is_error()) { + A.abort(PSTRING() << "masterchain state proof for " << block_id_.to_str() + << " is invalid : " << R.move_as_error()); + return A.finish(); + } + try { + auto res = block::Config::extract_from_state(R.move_as_ok(), 0); + if (res.is_error()) { + A.abort(PSTRING() << "cannot unpack configuration: " << res.move_as_error()); + return A.finish(); + } + auto config = res.move_as_ok(); + if (params_.size() > 0) { + A << "
"; + for (int i : params_) { + auto value = config->get_config_param(i); + if (value.not_null()) { + A << HttpAnswer::ConfigParam{i, value}; + } else { + A << HttpAnswer::Error{td::Status::Error(404, PSTRING() << "empty param " << i)}; + } + } + } else { + A << "

params: "; + config->foreach_config_param([&](int i, td::Ref value) { + if (value.not_null()) { + A << "" << i << " "; + } + return true; + }); + A << "

"; + config->foreach_config_param([&](int i, td::Ref value) { + if (value.not_null()) { + A << HttpAnswer::ConfigParam{i, value}; + } + return true; + }); + } + } catch (vm::VmError &err) { + A.abort(PSTRING() << "error while traversing configuration: " << err.get_msg()); + } catch (vm::VmVirtError &err) { + A.abort(PSTRING() << "virtualization error while traversing configuration: " << err.get_msg()); + } + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} + +HttpQuerySendForm::HttpQuerySendForm(std::string prefix, td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { +} + +HttpQuerySendForm::HttpQuerySendForm(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { +} + +void HttpQuerySendForm::start_up() { + finish_query(); +} + +void HttpQuerySendForm::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"send", prefix_}; + A << "
" + << "" + << "" + << "" + << "
"; + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} + +HttpQuerySend::HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)), data_(std::move(data)) { +} + +HttpQuerySend::HttpQuerySend(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(prefix, std::move(promise)) { + auto it = opts.find("filedata"); + if (it != opts.end()) { + data_ = td::BufferSlice{it->second}; + } else { + error_ = td::Status::Error("no file data"); + return; + } +} + +void HttpQuerySend::start_up() { + if (error_.is_error()) { + abort_query(std::move(error_)); + return; + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQuerySend::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &HttpQuerySend::got_result, R.move_as_ok()); + } + }); + auto query = + ton::serialize_tl_object(ton::create_tl_object(std::move(data_)), true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); +} + +void HttpQuerySend::got_result(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + } else { + status_ = F.move_as_ok()->status_; + } + finish_query(); +} + +void HttpQuerySend::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"send", prefix_}; + if (status_ >= 0) { + A << HttpAnswer::Notification{"success"}; + } else { + A << HttpAnswer::Error{td::Status::Error(status_, "failed")}; + } + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} + +HttpQueryRunMethod::HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name, + std::vector params, std::string prefix, + td::Promise promise) + : HttpQueryCommon(std::move(prefix), std::move(promise)) + , block_id_(block_id) + , addr_(addr) + , method_name_(std::move(method_name)) + , params_(std::move(params)) { +} + +HttpQueryRunMethod::HttpQueryRunMethod(std::map opts, std::string prefix, + td::Promise promise) + : HttpQueryCommon(std::move(prefix), std::move(promise)) { + auto R = parse_block_id(opts, true); + if (R.is_ok()) { + block_id_ = R.move_as_ok(); + if (!block_id_.is_valid()) { + block_id_.id.workchain = ton::masterchainId; + block_id_.id.shard = ton::shardIdAll; + block_id_.id.seqno = static_cast(0xffffffff); + block_id_.root_hash.set_zero(); + block_id_.file_hash.set_zero(); + } + } else { + error_ = R.move_as_error(); + return; + } + auto R2 = parse_account_addr(opts); + if (R2.is_ok()) { + addr_ = R2.move_as_ok(); + } else { + error_ = R2.move_as_error(); + return; + } + auto it = opts.find("method"); + if (it == opts.end()) { + error_ = td::Status::Error("no method"); + return; + } else { + method_name_ = it->second; + } + it = opts.find("params"); + if (it != opts.end()) { + auto R3 = vm::parse_stack_entries(it->second); + if (R3.is_error()) { + error_ = R3.move_as_error(); + return; + } + params_ = R3.move_as_ok(); + } +} + +void HttpQueryRunMethod::start_up_query() { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &HttpQueryRunMethod::abort_query, R.move_as_error_prefix("litequery failed: ")); + } else { + td::actor::send_closure(SelfId, &HttpQueryRunMethod::got_account, R.move_as_ok()); + } + }); + auto a = ton::create_tl_object(addr_.workchain, addr_.addr); + auto query = ton::serialize_tl_object(ton::create_tl_object( + ton::create_tl_lite_block_id(block_id_), std::move(a)), + true); + td::actor::send_closure(CoreActorInterface::instance_actor_id(), &CoreActorInterface::send_lite_query, + std::move(query), std::move(P)); +} + +void HttpQueryRunMethod::got_account(td::BufferSlice data) { + auto F = ton::fetch_tl_object(std::move(data), true); + if (F.is_error()) { + abort_query(F.move_as_error()); + return; + } + + auto f = F.move_as_ok(); + data_ = std::move(f->state_); + proof_ = std::move(f->proof_); + shard_proof_ = std::move(f->shard_proof_); + block_id_ = ton::create_block_id(f->id_); + res_block_id_ = ton::create_block_id(f->shardblk_); + + finish_query(); +} + +void HttpQueryRunMethod::finish_query() { + if (promise_) { + auto page = [&]() -> std::string { + HttpAnswer A{"account", prefix_}; + A.set_account_id(addr_); + A.set_block_id(res_block_id_); + + block::AccountState account_state; + account_state.blk = block_id_; + account_state.shard_blk = res_block_id_; + account_state.shard_proof = std::move(shard_proof_); + account_state.proof = std::move(proof_); + account_state.state = std::move(data_); + auto r_info = account_state.validate(block_id_, addr_); + if (r_info.is_error()) { + A.abort(r_info.move_as_error()); + return A.finish(); + } + auto info = r_info.move_as_ok(); + if (info.root.is_null()) { + A.abort(PSTRING() << "account state of " << addr_ << " is empty (cannot run method `" << method_name_ << "`)"); + return A.finish(); + } + block::gen::Account::Record_account acc; + block::gen::AccountStorage::Record store; + block::CurrencyCollection balance; + if (!(tlb::unpack_cell(info.root, acc) && tlb::csr_unpack(acc.storage, store) && + balance.validate_unpack(store.balance))) { + A.abort("error unpacking account state"); + return A.finish(); + } + int tag = block::gen::t_AccountState.get_tag(*store.state); + switch (tag) { + case block::gen::AccountState::account_uninit: + A.abort(PSTRING() << "account " << addr_ << " not initialized yet (cannot run any methods)"); + return A.finish(); + case block::gen::AccountState::account_frozen: + A.abort(PSTRING() << "account " << addr_ << " frozen (cannot run any methods)"); + return A.finish(); + } + + CHECK(store.state.write().fetch_ulong(1) == 1); // account_init$1 _:StateInit = AccountState; + block::gen::StateInit::Record state_init; + CHECK(tlb::csr_unpack(store.state, state_init)); + auto code = state_init.code->prefetch_ref(); + auto data = state_init.data->prefetch_ref(); + auto stack = td::make_ref(std::move(params_)); + td::int64 method_id = (td::crc16(td::Slice{method_name_}) & 0xffff) | 0x10000; + stack.write().push_smallint(method_id); + long long gas_limit = vm::GasLimits::infty; + // OstreamLogger ostream_logger(ctx.error_stream); + // auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); + vm::GasLimits gas{gas_limit}; + LOG(DEBUG) << "creating VM"; + vm::VmState vm{code, std::move(stack), gas, 1, data, vm::VmLog()}; + vm.set_c7(prepare_vm_c7(info.gen_utime, info.gen_lt, acc.addr, balance)); // tuple with SmartContractInfo + // vm.incr_stack_trace(1); // enable stack dump after each step + int exit_code = ~vm.run(); + if (exit_code != 0) { + A.abort(PSTRING() << "VM terminated with error code " << exit_code); + return A.finish(); + } + stack = vm.get_stack_ref(); + { + std::ostringstream os; + os << "result: "; + stack->dump(os, 3); + + A << HttpAnswer::CodeBlock{os.str()}; + } + + return A.finish(); + }(); + promise_.set_value( + MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + } + stop(); +} HttpQueryStatus::HttpQueryStatus(std::string prefix, td::Promise promise) : HttpQueryCommon(std::move(prefix), std::move(promise)) { } diff --git a/blockchain-explorer/blockchain-explorer-query.hpp b/blockchain-explorer/blockchain-explorer-query.hpp index 2acf9efbe1..ce7a713f96 100644 --- a/blockchain-explorer/blockchain-explorer-query.hpp +++ b/blockchain-explorer/blockchain-explorer-query.hpp @@ -258,6 +258,77 @@ class HttpQueryViewLastBlock : public HttpQueryCommon { ton::BlockIdExt res_block_id_; }; +class HttpQueryConfig : public HttpQueryCommon { + public: + HttpQueryConfig(std::string prefix, ton::BlockIdExt block_id, std::vector params, + td::Promise promise); + HttpQueryConfig(std::map opts, std::string prefix, td::Promise promise); + + void finish_query(); + + void start_up() override; + void got_block(td::BufferSlice result); + void send_main_query(); + void got_result(td::BufferSlice result); + + private: + ton::BlockIdExt block_id_; + std::vector params_; + + td::BufferSlice state_proof_; + td::BufferSlice config_proof_; +}; + +class HttpQuerySendForm : public HttpQueryCommon { + public: + HttpQuerySendForm(std::string prefix, td::Promise promise); + HttpQuerySendForm(std::map opts, std::string prefix, td::Promise promise); + + void start_up() override; + void finish_query(); + + private: +}; + +class HttpQuerySend : public HttpQueryCommon { + public: + HttpQuerySend(std::string prefix, td::BufferSlice data, td::Promise promise); + HttpQuerySend(std::map opts, std::string prefix, td::Promise promise); + + void finish_query(); + + void start_up() override; + void got_result(td::BufferSlice result); + + private: + td::BufferSlice data_; + td::int32 status_; +}; + +class HttpQueryRunMethod : public HttpQueryCommon { + public: + HttpQueryRunMethod(ton::BlockIdExt block_id, block::StdAddress addr, std::string method_name, + std::vector params, std::string prefix, td::Promise promise); + HttpQueryRunMethod(std::map opts, std::string prefix, td::Promise promise); + + void finish_query(); + + void start_up_query() override; + void got_account(td::BufferSlice result); + + private: + ton::BlockIdExt block_id_; + block::StdAddress addr_; + + std::string method_name_; + std::vector params_; + + td::BufferSlice data_; + td::BufferSlice proof_; + td::BufferSlice shard_proof_; + ton::BlockIdExt res_block_id_; +}; + class HttpQueryStatus : public HttpQueryCommon { public: HttpQueryStatus(std::string prefix, td::Promise promise); diff --git a/blockchain-explorer/blockchain-explorer.cpp b/blockchain-explorer/blockchain-explorer.cpp index 2fa1bf75c3..b3776e3e52 100644 --- a/blockchain-explorer/blockchain-explorer.cpp +++ b/blockchain-explorer/blockchain-explorer.cpp @@ -49,6 +49,12 @@ #include "blockchain-explorer-http.hpp" #include "blockchain-explorer-query.hpp" +#include "vm/boc.h" +#include "vm/cellops.h" +#include "vm/cells/MerkleProof.h" +#include "vm/continuation.h" +#include "vm/cp0.h" + #include "auto/tl/lite_api.h" #include "ton/lite-tl.hpp" #include "tl-utils/lite-utils.hpp" @@ -263,22 +269,80 @@ class CoreActor : public CoreActorInterface { return MHD_YES; } + struct HttpRequestExtra { + HttpRequestExtra(MHD_Connection* connection, bool is_post) { + if (is_post) { + postprocessor = MHD_create_post_processor(connection, 1 << 14, iterate_post, static_cast(this)); + } + } + ~HttpRequestExtra() { + MHD_destroy_post_processor(postprocessor); + } + static int iterate_post(void* coninfo_cls, enum MHD_ValueKind kind, const char* key, const char* filename, + const char* content_type, const char* transfer_encoding, const char* data, uint64_t off, + size_t size) { + auto ptr = static_cast(coninfo_cls); + ptr->total_size += strlen(key) + size; + if (ptr->total_size > MAX_POST_SIZE) { + return MHD_NO; + } + std::string k = key; + if (ptr->opts[k].size() < off + size) { + ptr->opts[k].resize(off + size); + } + td::MutableSlice(ptr->opts[k]).remove_prefix(off).copy_from(td::Slice(data, size)); + return MHD_YES; + } + MHD_PostProcessor* postprocessor; + std::map opts; + td::uint64 total_size = 0; + }; + + static void request_completed(void* cls, struct MHD_Connection* connection, void** ptr, + enum MHD_RequestTerminationCode toe) { + auto e = static_cast(*ptr); + if (e) { + delete e; + } + } + static int process_http_request(void* cls, struct MHD_Connection* connection, const char* url, const char* method, const char* version, const char* upload_data, size_t* upload_data_size, void** ptr) { - static int dummy; struct MHD_Response* response = nullptr; int ret; - if (0 != std::strcmp(method, "GET")) + bool is_post = false; + if (std::strcmp(method, "GET") == 0) { + is_post = false; + } else if (std::strcmp(method, "POST") == 0) { + is_post = true; + } else { return MHD_NO; /* unexpected method */ - if (&dummy != *ptr) { - /* The first time only the headers are valid, - do not respond in the first round... */ - *ptr = &dummy; - return MHD_YES; } - if (0 != *upload_data_size) - return MHD_NO; /* upload data in a GET!? */ + std::map opts; + if (!is_post) { + if (!*ptr) { + *ptr = static_cast(new HttpRequestExtra{connection, false}); + return MHD_YES; + } + if (0 != *upload_data_size) + return MHD_NO; /* upload data in a GET!? */ + } else { + if (!*ptr) { + *ptr = static_cast(new HttpRequestExtra{connection, true}); + return MHD_YES; + } + auto e = static_cast(*ptr); + if (0 != *upload_data_size) { + CHECK(e->postprocessor); + MHD_post_process(e->postprocessor, upload_data, *upload_data_size); + *upload_data_size = 0; + return MHD_YES; + } + for (auto& o : e->opts) { + opts[o.first] = std::move(o.second); + } + } std::string url_s = url; @@ -295,7 +359,6 @@ class CoreActor : public CoreActorInterface { command = url_s.substr(pos + 1); } - std::map opts; MHD_get_connection_values(connection, MHD_GET_ARGUMENT_KIND, get_arg_iterate, static_cast(&opts)); if (command == "status") { @@ -352,6 +415,26 @@ class CoreActor : public CoreActorInterface { .release(); }}; response = g.wait(); + } else if (command == "config") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("getconfig", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); + } else if (command == "send") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("send", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); + } else if (command == "sendform") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("sendform", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); + } else if (command == "runmethod") { + HttpQueryRunner g{[&](td::Promise promise) { + td::actor::create_actor("runmethod", opts, prefix, std::move(promise)).release(); + }}; + response = g.wait(); } else { ret = MHD_NO; } @@ -394,7 +477,8 @@ class CoreActor : public CoreActorInterface { remote_addr_, make_callback(0))); } daemon_ = MHD_start_daemon(MHD_USE_SELECT_INTERNALLY, static_cast(http_port_), nullptr, nullptr, - &process_http_request, nullptr, MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END); + &process_http_request, nullptr, MHD_OPTION_NOTIFY_COMPLETED, request_completed, nullptr, + MHD_OPTION_THREAD_POOL_SIZE, 16, MHD_OPTION_END); CHECK(daemon_ != nullptr); } }; @@ -567,6 +651,8 @@ int main(int argc, char* argv[]) { }); #endif + vm::init_op_cp0(); + td::actor::Scheduler scheduler({2}); scheduler_ptr = &scheduler; scheduler.run_in_context([&] { x = td::actor::create_actor("testnode"); }); diff --git a/blockchain-explorer/blockchain-explorer.hpp b/blockchain-explorer/blockchain-explorer.hpp index 84b8665858..59e42cce2c 100644 --- a/blockchain-explorer/blockchain-explorer.hpp +++ b/blockchain-explorer/blockchain-explorer.hpp @@ -32,6 +32,8 @@ #include "ton/ton-types.h" #include "td/utils/port/IPAddress.h" +#define MAX_POST_SIZE (64 << 10) + class CoreActorInterface : public td::actor::Actor { public: struct RemoteNodeStatus { diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 38f65500c4..ff7d922791 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -35,6 +35,7 @@ set(TON_CRYPTO_SOURCE vm/debugops.cpp vm/tonops.cpp vm/boc.cpp + vm/utils.cpp tl/tlblib.cpp Ed25519.h @@ -80,6 +81,7 @@ set(TON_CRYPTO_SOURCE vm/tupleops.h vm/tonops.h vm/vmstate.h + vm/utils.h vm/cells.h vm/cellslice.h diff --git a/crypto/block/block.cpp b/crypto/block/block.cpp index bdbf08a609..b2cad14286 100644 --- a/crypto/block/block.cpp +++ b/crypto/block/block.cpp @@ -789,10 +789,11 @@ td::Status ShardState::unpack_state(ton::BlockIdExt blkid, Ref prev_st return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid global_balance"); } if (extra.r1.flags & 1) { - if (extra.r1.block_create_stats->prefetch_ulong(8) != 0x17) { + if (extra.r1.block_create_stats->prefetch_ulong(8) == 0x17) { + block_create_stats_ = std::make_unique(extra.r1.block_create_stats->prefetch_ref(), 256); + } else { return td::Status::Error(-666, "ShardState of "s + id_.to_str() + " does not contain a valid BlockCreateStats"); } - block_create_stats_ = std::make_unique(extra.r1.block_create_stats->prefetch_ref(), 256); } else { block_create_stats_ = std::make_unique(256); } @@ -1846,6 +1847,18 @@ td::Status check_block_header(Ref block_root, const ton::BlockIdExt& i return td::Status::OK(); } +std::unique_ptr get_block_create_stats_dict(Ref state_root) { + block::gen::ShardStateUnsplit::Record info; + block::gen::McStateExtra::Record extra; + block::gen::BlockCreateStats::Record_block_create_stats cstats; + if (!(::tlb::unpack_cell(std::move(state_root), info) && info.custom->size_refs() && + ::tlb::unpack_cell(info.custom->prefetch_ref(), extra) && (extra.r1.flags & 1) && + ::tlb::csr_unpack(std::move(extra.r1.block_create_stats), cstats))) { + return {}; + } + return std::make_unique(std::move(cstats.counters), 256); +} + std::unique_ptr get_prev_blocks_dict(Ref state_root) { block::gen::ShardStateUnsplit::Record info; block::gen::McStateExtra::Record extra_info; diff --git a/crypto/block/block.h b/crypto/block/block.h index fd9a724f26..0cfbe24a36 100644 --- a/crypto/block/block.h +++ b/crypto/block/block.h @@ -163,12 +163,12 @@ struct MsgProcessedUpto { MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash) : shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) { } - bool operator<(const MsgProcessedUpto& other) const& { + bool operator<(const MsgProcessedUpto& other) const & { return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno); } - bool contains(const MsgProcessedUpto& other) const&; + bool contains(const MsgProcessedUpto& other) const &; bool contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash, - ton::BlockSeqno other_mc_seqno) const&; + ton::BlockSeqno other_mc_seqno) const &; // NB: this is for checking whether we have already imported an internal message bool already_processed(const EnqueuedMsgDescr& msg) const; }; @@ -514,6 +514,9 @@ struct DiscountedCounter { return last_updated == other.last_updated && total == other.total && cnt2048 <= other.cnt2048 + 1 && other.cnt2048 <= cnt2048 + 1 && cnt65536 <= other.cnt65536 + 1 && other.cnt65536 <= cnt65536 + 1; } + bool modified_since(ton::UnixTime utime) const { + return last_updated >= utime; + } bool validate(); bool increase_by(unsigned count, ton::UnixTime now); bool fetch(vm::CellSlice& cs); @@ -629,6 +632,8 @@ td::Status unpack_block_prev_blk_try(Ref block_root, const ton::BlockI td::Status check_block_header(Ref block_root, const ton::BlockIdExt& id, ton::Bits256* store_shard_hash_to = nullptr); +std::unique_ptr get_block_create_stats_dict(Ref state_root); + std::unique_ptr get_prev_blocks_dict(Ref state_root); bool get_old_mc_block_id(vm::AugmentedDictionary* prev_blocks_dict, ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr); diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index ec91ef5755..074a5d4bc6 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -366,6 +366,10 @@ action_send_msg#0ec3c86d mode:(## 8) action_set_code#ad4de08e new_code:^Cell = OutAction; action_reserve_currency#36e6b809 mode:(## 8) currency:CurrencyCollection = OutAction; +libref_hash$0 lib_hash:bits256 = LibRef; +libref_ref$1 library:^Cell = LibRef; +action_change_library#26fa1dd4 mode:(## 7) { mode <= 2 } + libref:LibRef = OutAction; out_list_node$_ prev:^Cell action:OutAction = OutListNode; // @@ -505,6 +509,7 @@ _ (HashmapAugE 32 KeyExtBlkRef KeyMaxLt) = OldMcBlocksInfo; counters#_ last_updated:uint32 total:uint64 cnt2048:uint64 cnt65536:uint64 = Counters; creator_info#4 mc_blocks:Counters shard_blocks:Counters = CreatorStats; block_create_stats#17 counters:(HashmapE 256 CreatorStats) = BlockCreateStats; +block_create_stats_ext#34 counters:(HashmapAugE 256 CreatorStats uint32) = BlockCreateStats; masterchain_state_extra#cc26 shard_hashes:ShardHashes diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index b5959f7557..c686e20065 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -1710,6 +1710,30 @@ std::vector Config::compute_total_validator_set(int next) c return res.move_as_ok()->export_validator_set(); } +td::Result> Config::unpack_validator_set_start_stop(Ref vset_root) { + if (vset_root.is_null()) { + return td::Status::Error("validator set absent"); + } + gen::ValidatorSet::Record_validators_ext rec; + if (tlb::unpack_cell(vset_root, rec)) { + return std::pair(rec.utime_since, rec.utime_until); + } + gen::ValidatorSet::Record_validators rec0; + if (tlb::unpack_cell(std::move(vset_root), rec0)) { + return std::pair(rec0.utime_since, rec0.utime_until); + } + return td::Status::Error("validator set is invalid"); +} + +std::pair Config::get_validator_set_start_stop(int next) const { + auto res = unpack_validator_set_start_stop(get_config_param(next < 0 ? 32 : (next ? 36 : 34))); + if (res.is_error()) { + return {0, 0}; + } else { + return res.move_as_ok(); + } +} + bool WorkchainInfo::unpack(ton::WorkchainId wc, vm::CellSlice& cs) { workchain = ton::workchainInvalid; if (wc == ton::workchainInvalid) { diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 241ae88c55..55bf1122f0 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -50,7 +50,7 @@ struct ValidatorDescr { : pubkey(_pubkey), weight(_weight), cum_weight(_cum_weight) { adnl_addr.set_zero(); } - bool operator<(td::uint64 wt_pos) const& { + bool operator<(td::uint64 wt_pos) const & { return cum_weight < wt_pos; } }; @@ -558,6 +558,7 @@ class Config { const ValidatorSet* get_cur_validator_set() const { return cur_validators_.get(); } + std::pair get_validator_set_start_stop(int next = 0) const; ton::ValidatorSessionConfig get_consensus_config() const; bool foreach_config_param(std::function)> scan_func) const; Ref get_workchain_info(ton::WorkchainId workchain_id) const; @@ -577,6 +578,7 @@ class Config { static td::Result> unpack_config(Ref config_csr, int mode = 0); static td::Result> extract_from_state(Ref mc_state_root, int mode = 0); static td::Result> extract_from_key_block(Ref key_block_root, int mode = 0); + static td::Result> unpack_validator_set_start_stop(Ref root); protected: Config(int _mode) : mode(_mode) { diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index 6667cf8774..df4cf883c2 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1098,6 +1098,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) { case block::gen::OutAction::action_reserve_currency: err_code = try_action_reserve_currency(cs, ap, cfg); break; + case block::gen::OutAction::action_change_library: + err_code = try_action_change_library(cs, ap, cfg); + break; } if (err_code) { ap.result_code = (err_code == -1 ? 34 : err_code); @@ -1148,6 +1151,56 @@ int Transaction::try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const A return 0; } +int Transaction::try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) { + block::gen::OutAction::Record_action_change_library rec; + if (!tlb::unpack_exact(cs, rec)) { + return -1; + } + // mode: +0 = remove library, +1 = add private library, +2 = add public library + Ref lib_ref = rec.libref->prefetch_ref(); + ton::Bits256 hash; + if (lib_ref.not_null()) { + hash = lib_ref->get_hash().bits(); + } else { + CHECK(rec.libref.write().fetch_ulong(1) == 0 && rec.libref.write().fetch_bits_to(hash)); + } + try { + vm::Dictionary dict{new_library, 256}; + if (!rec.mode) { + // remove library + dict.lookup_delete(hash); + LOG(DEBUG) << "removed " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex(); + } else { + auto val = dict.lookup(hash); + if (val.not_null()) { + bool is_public = val->prefetch_ulong(1); + auto ref = val->prefetch_ref(); + if (hash == ref->get_hash().bits()) { + lib_ref = ref; + if (is_public == (rec.mode >> 1)) { + // library already in required state + ap.spec_actions++; + return 0; + } + } + } + if (lib_ref.is_null()) { + // library code not found + return 41; + } + vm::CellBuilder cb; + CHECK(cb.store_bool_bool(rec.mode >> 1) && cb.store_ref_bool(std::move(lib_ref))); + CHECK(dict.set_builder(hash, cb)); + LOG(DEBUG) << "added " << ((rec.mode >> 1) ? "public" : "private") << " library with hash " << hash.to_hex(); + } + new_library = std::move(dict).extract_root_cell(); + } catch (vm::VmError& vme) { + return 42; + } + ap.spec_actions++; + return 0; +} + // msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms // ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms // bits in the root cell of a message are not included in msg.bits (lump_price pays for them) diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 25f8059560..10150f1c31 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -65,10 +65,10 @@ struct NewOutMsg { NewOutMsg(ton::LogicalTime _lt, Ref _msg, Ref _trans) : lt(_lt), msg(std::move(_msg)), trans(std::move(_trans)) { } - bool operator<(const NewOutMsg& other) const& { + bool operator<(const NewOutMsg& other) const & { return lt < other.lt || (lt == other.lt && msg->get_hash() < other.msg->get_hash()); } - bool operator>(const NewOutMsg& other) const& { + bool operator>(const NewOutMsg& other) const & { return lt > other.lt || (lt == other.lt && other.msg->get_hash() < msg->get_hash()); } }; @@ -371,6 +371,7 @@ struct Transaction { Ref prepare_vm_c7(const ComputePhaseConfig& cfg) const; bool prepare_rand_seed(td::BitArray<256>& rand_seed, const ComputePhaseConfig& cfg) const; int try_action_set_code(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); + int try_action_change_library(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); int try_action_send_msg(const vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg, int redoing = 0); int try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg); bool check_replace_src_addr(Ref& src_addr) const; diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index eb9034ec1e..730ef78a06 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1013,6 +1013,8 @@ x{FB00} @Defop SENDRAWMSG x{FB02} @Defop RAWRESERVE x{FB03} @Defop RAWRESERVEX x{FB04} @Defop SETCODE +x{FB06} @Defop SETLIBCODE +x{FB07} @Defop CHANGELIB // // debug primitives diff --git a/crypto/fift/lib/Fift.fif b/crypto/fift/lib/Fift.fif index 5430c359e7..724ef81281 100644 --- a/crypto/fift/lib/Fift.fif +++ b/crypto/fift/lib/Fift.fif @@ -111,3 +111,4 @@ variable base { true (atom) drop } : atom { bl word atom 1 'nop } ::_ ` { hole dup 1 { @ execute } does create } : recursive +{ 0 { 1+ dup 1 ' $() does over (.) "$" swap $+ 0 (create) } rot times drop } : :$1..n diff --git a/crypto/fift/lib/GetOpt.fif b/crypto/fift/lib/GetOpt.fif new file mode 100644 index 0000000000..ee8a59afca --- /dev/null +++ b/crypto/fift/lib/GetOpt.fif @@ -0,0 +1,92 @@ +library GetOpt // Simple command-line options parser +"Lists.fif" include + +// May be used as follows: +// begin-options +// "h" { ."Help Message" 0 halt } short-option +// "v" { parse-int =: verbosity } short-option-arg +// "i" "--interactive" { true =: interactive } short-long-option +// parse-options + +// ( l -- l') computes tail of list l if non-empty; else () +{ dup null? ' cdr ifnot } : safe-cdr +// ( l c -- l') deletes first c elements from list l +{ ' safe-cdr swap times } : list-delete-first +// ( l n c -- l' ) deletes c elements starting from n-th in list l +recursive list-delete-range { + dup 0<= { 2drop } { + over 0<= { nip list-delete-first } { + swap 1- swap rot uncons 2swap list-delete-range cons + } cond } cond +} swap ! +// ( n c -- ) deletes $n .. $(n+c-1) from the argument list $* +{ swap 1- $* @ swap rot list-delete-range $* ! } : $*del.. +// ( s s' -- ? ) checks whether s' is a prefix of s +{ tuck $len over $len over >= { $| drop $= } { 2drop drop false } cond +} : $pfx? +// ( s -- ? ) checks whether s is an option (a string beginning with '-') +{ dup $len 1 > { "-" $pfx? } { drop false } cond } : is-opt? +// ( l -- s i or 0 ) finds first string in l beginning with '-' +{ 0 { 1+ over null? { 2drop 0 true } { + swap uncons over is-opt? { drop swap true } { nip swap false } cond + } cond } until +} : list-find-opt +// ( -- s i or 0 ) finds first option in cmdline args +{ $* @ list-find-opt } : first-opt +// ( s t -- ? ) checks whether short/long option s matches description t +{ third $= } : short-option-matches +' second : get-opt-flags +' first : get-opt-exec +{ dup get-opt-flags 4 and 0= 3 + [] $= +} : long-option-matches +// ( s l -- t -1 or 0 ) finds short/long option s in list l +{ swap 1 { swap short-option-matches } does assoc-gen +} : lookup-short-option +{ swap 1 { swap long-option-matches } does assoc-gen +} : lookup-long-option +// ( s -- s' null or s' s'' ) Splits long option --opt=arg at '=' +{ dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond +} : split-longopt +// ( l -- i or 0 ) +// parses command line arguments according to option description list l +// and returns index i of first incorrect option +{ { first-opt dup 0= { true } { + swap dup "--" $pfx? { // l i s + dup $len 2 = { drop dup 1 $*del.. 0 true } { + split-longopt swap 3 pick + lookup-long-option not { drop true } { // l i s' t f + dup get-opt-exec swap get-opt-flags 3 and // l i s' e f' + 2 pick null? { dup 1 = } { dup 0= negate } cond // l i s' e f' f'' + dup 1 = { 2drop 2drop true } { + { drop nip over 1+ $() swap execute 2 $*del.. false } { + ' nip ifnot execute 1 $*del.. false + } cond } cond } cond } cond } { // l i s + 1 $| nip { + dup $len 0= { drop 1 $*del.. false true } { + 1 $| swap 3 pick // l i s' s l + lookup-short-option not { drop true true } { // l i s' t + dup get-opt-exec swap get-opt-flags 3 and // l i s' e f' + ?dup 0= { execute false } { + 2 pick $len { drop execute "" false } { + 2 = { nip null swap execute "" false } { // l i e + nip over 1+ $() swap execute 2 $*del.. false true + } cond } cond } cond } cond } cond } until + } cond + } cond } until nip +} : getopt +// ( l -- ) Parses options and throws an error on failure +{ getopt ?dup { $() "cannot parse command line options near `" swap $+ +"`" abort } if +} : run-getopt + +anon constant opt-list-marker +' opt-list-marker : begin-options +{ opt-list-marker list-until-marker } : end-options +{ end-options run-getopt } : parse-options +// ( s e -- o ) Creates short/long option s with execution token e +{ 0 rot triple } dup : short-option : long-option +// ( s s' e -- o ) Creates a combined short option s and long option s' with execution token e +{ 4 2swap 4 tuple } : short-long-option +{ 1 rot triple } dup : short-option-arg : long-option-arg +{ 2 rot triple } dup : short-option-?arg : long-option-?arg +{ 5 2swap 4 tuple } : short-long-option-arg +{ 6 2swap 4 tuple } : short-long-option-?arg diff --git a/crypto/fift/utils.cpp b/crypto/fift/utils.cpp index fdbd0c7eef..3ed11c31b6 100644 --- a/crypto/fift/utils.cpp +++ b/crypto/fift/utils.cpp @@ -46,6 +46,9 @@ td::Result load_Lists_fif(std::string dir = "") { td::Result load_Lisp_fif(std::string dir = "") { return load_source("Lisp.fif", dir); } +td::Result load_GetOpt_fif(std::string dir = "") { + return load_source("GetOpt.fif", dir); +} class MemoryFileLoader : public fift::FileLoader { public: @@ -115,6 +118,10 @@ td::Result create_source_lookup(std::string main, bool need_ TRY_RESULT(f, load_TonUtil_fif(dir)); loader->add_file("/TonUtil.fif", std::move(f)); } + { + TRY_RESULT(f, load_GetOpt_fif(dir)); + loader->add_file("/GetOpt.fif", std::move(f)); + } } if (need_lisp) { TRY_RESULT(f, load_Lisp_fif(dir)); diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index ed703b1252..7459656bb4 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -604,6 +604,12 @@ void interpret_str_split(vm::Stack& stack) { stack.push_string(std::string{str, sz}); } +void interpret_str_pos(vm::Stack& stack) { + auto s2 = stack.pop_string(), s1 = stack.pop_string(); + auto pos = s1.find(s2); + stack.push_smallint(pos == std::string::npos ? -1 : pos); +} + void interpret_str_reverse(vm::Stack& stack) { std::string s = stack.pop_string(); auto it = s.begin(); @@ -659,6 +665,20 @@ void interpret_utf8_str_split(vm::Stack& stack) { } } +void interpret_utf8_str_pos(vm::Stack& stack) { + auto s2 = stack.pop_string(), s1 = stack.pop_string(); + auto pos = s1.find(s2); + if (pos == std::string::npos) { + stack.push_smallint(-1); + return; + } + int cnt = 0; + for (std::size_t i = 0; i < pos; i++) { + cnt += ((s1[i] & 0xc0) != 0x80); + } + stack.push_smallint(cnt); +} + void interpret_str_remove_trailing_int(vm::Stack& stack, int arg) { char x = (char)(arg ? arg : stack.pop_long_range(127)); std::string s = stack.pop_string(); @@ -2336,12 +2356,38 @@ void interpret_db_run_vm_parallel(IntCtx& ctx) { do_interpret_db_run_vm_parallel(ctx.error_stream, ctx.stack, ctx.ton_db, threads_n, tasks_n); } +Ref cmdline_args{true}; + +void interpret_get_fixed_cmdline_arg(vm::Stack& stack, int n) { + if (!n) { + return; + } + auto v = cmdline_args->get(); + while (true) { + if (v.empty()) { + stack.push(vm::StackEntry{}); + return; + } + auto t = v.as_tuple_range(2, 2); + if (t.is_null()) { + throw IntError{"invalid cmdline arg list"}; + } + if (!--n) { + stack.push(t->at(0)); + return; + } + v = t->at(1); + } +} + // n -- executes $n void interpret_get_cmdline_arg(IntCtx& ctx) { int n = ctx.stack.pop_smallint_range(999999); - char buffer[14]; - sprintf(buffer, "$%d ", n); - auto entry = ctx.dictionary->lookup(std::string{buffer}); + if (n) { + interpret_get_fixed_cmdline_arg(ctx.stack, n); + return; + } + auto entry = ctx.dictionary->lookup("$0 "); if (!entry) { throw IntError{"-?"}; } else { @@ -2349,6 +2395,19 @@ void interpret_get_cmdline_arg(IntCtx& ctx) { } } +void interpret_get_cmdline_arg_count(vm::Stack& stack) { + auto v = cmdline_args->get(); + int cnt; + for (cnt = 0; !v.empty(); cnt++) { + auto t = v.as_tuple_range(2, 2); + if (t.is_null()) { + throw IntError{"invalid cmdline arg list"}; + } + v = t->at(1); + } + stack.push_smallint(cnt); +} + void interpret_getenv(vm::Stack& stack) { auto str = stack.pop_string(); auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr; @@ -2568,6 +2627,7 @@ void init_words_common(Dictionary& d) { d.def_stack_word("$= ", interpret_str_equal); d.def_stack_word("$cmp ", interpret_str_cmp); d.def_stack_word("$reverse ", interpret_str_reverse); + d.def_stack_word("$pos ", interpret_str_pos); d.def_stack_word("(-trailing) ", std::bind(interpret_str_remove_trailing_int, _1, 0)); d.def_stack_word("-trailing ", std::bind(interpret_str_remove_trailing_int, _1, ' ')); d.def_stack_word("-trailing0 ", std::bind(interpret_str_remove_trailing_int, _1, '0')); @@ -2575,6 +2635,7 @@ void init_words_common(Dictionary& d) { d.def_stack_word("Blen ", interpret_bytes_len); d.def_stack_word("$Len ", interpret_utf8_str_len); d.def_stack_word("$Split ", interpret_utf8_str_split); + d.def_stack_word("$Pos ", interpret_utf8_str_pos); d.def_ctx_word("Bx. ", std::bind(interpret_bytes_hex_print_raw, _1, true)); d.def_stack_word("B>X ", std::bind(interpret_bytes_to_hex, _1, true)); d.def_stack_word("B>x ", std::bind(interpret_bytes_to_hex, _1, false)); @@ -2766,6 +2827,10 @@ void init_words_common(Dictionary& d) { d.def_ctx_word("quit ", interpret_quit); d.def_ctx_word("bye ", interpret_bye); d.def_stack_word("halt ", interpret_halt); + // cmdline args + d.def_stack_word("$* ", std::bind(interpret_literal, _1, vm::StackEntry{cmdline_args})); + d.def_stack_word("$# ", interpret_get_cmdline_arg_count); + d.def_ctx_word("$() ", interpret_get_cmdline_arg); } void init_words_ton(Dictionary& d) { @@ -2799,13 +2864,16 @@ void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* con using namespace std::placeholders; LOG(DEBUG) << "import_cmdlist_args(" << arg0 << "," << n << ")"; d.def_stack_word("$0 ", std::bind(interpret_literal, _1, vm::StackEntry{arg0})); - for (int i = 0; i < n; i++) { + vm::StackEntry list; + for (int i = n - 1; i >= 0; i--) { + list = vm::StackEntry::cons(vm::StackEntry{argv[i]}, list); + } + cmdline_args->set(std::move(list)); + for (int i = 1; i <= n; i++) { char buffer[14]; - sprintf(buffer, "$%d ", i + 1); - d.def_stack_word(buffer, std::bind(interpret_literal, _1, vm::StackEntry{argv[i]})); + sprintf(buffer, "$%d ", i); + d.def_stack_word(buffer, std::bind(interpret_get_fixed_cmdline_arg, _1, i)); } - d.def_stack_word("$# ", std::bind(interpret_const, _1, n)); - d.def_ctx_word("$() ", interpret_get_cmdline_arg); } std::pair numeric_value_ext(std::string s, bool allow_frac = true) { diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index c7058f50c0..8c453cde48 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -9,10 +9,9 @@ (cell, int, int, cell) load_data() inline { var cs = get_data().begin_parse(); - var (cfg_dict, stored_seqno, public_key) = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256)); - var vote_dict = cs.slice_empty?() ? new_dict() : cs~load_dict(); + var res = (cs~load_ref(), cs~load_uint(32), cs~load_uint(256), cs~load_dict()); cs.end_parse(); - return (cfg_dict, stored_seqno, public_key, vote_dict); + return res; } () store_data(cfg_dict, stored_seqno, public_key, vote_dict) impure inline { diff --git a/crypto/smartcont/elector-code.fc b/crypto/smartcont/elector-code.fc index 37257d1bbf..563aa7bc35 100644 --- a/crypto/smartcont/elector-code.fc +++ b/crypto/smartcont/elector-code.fc @@ -796,7 +796,7 @@ _ participant_list() method_id { _ participant_list_extended() method_id { var elect = get_data().begin_parse().preload_dict(); if (elect.null?()) { - return nil; + return (0, 0, 0, 0, nil, 0, 0); } var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect(); var l = nil; @@ -809,7 +809,7 @@ _ participant_list_extended() method_id { l = cons(pair(id, tuple4(stake, max_factor, addr, adnl_addr)), l); } } until (~ f); - return l; + return (elect_at, elect_close, min_stake, total_stake, l, failed, finished); } ;; computes the return stake diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 4277445fdf..5c4de5647d 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -160,7 +160,7 @@ Masterchain swap // 9 4 1 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! +GR$10000 GR$10000000 GR$500000 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 @@ -219,7 +219,7 @@ now dup orig_vset_valid_for + 0 config.validators! 0 32 u, // seqno "config-master" +suffix +".pk" load-generate-keypair drop B, - newdict dict, // vote dict + dictnew dict, // vote dict b> // data empty_cell // libraries GR$10 // balance @@ -237,8 +237,8 @@ Masterchain swap */ // pubkey amount `create-wallet1` or pubkey amount `create-wallet2` -PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$100 create-wallet1 -PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$170 create-wallet0 +PK'PuZPPXK5Rff9SvtoS7Y9lUuEixvy-J6aishYFj3Qn6P0pJMb GR$1000000000 create-wallet1 +PK'PuYiB1zAWzr4p8j6I681+sGUrRGcn6Ylf7vXl0xaUl/w6Xfg GR$1700000000 create-wallet0 /* * diff --git a/crypto/smartcont/multisig-code.fc b/crypto/smartcont/multisig-code.fc index cbe2d30feb..36e21a7b7f 100644 --- a/crypto/smartcont/multisig-code.fc +++ b/crypto/smartcont/multisig-code.fc @@ -2,21 +2,32 @@ _ unpack_state() inline_ref { var ds = begin_parse(get_data()); - var res = (ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict()); + var res = (ds~load_uint(32), ds~load_uint(8), ds~load_uint(8), ds~load_uint(64), ds~load_dict(), ds~load_dict()); ds.end_parse(); return res; } -_ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, int n) inline_ref { +_ pack_state(cell pending_queries, cell owner_infos, int last_cleaned, int k, int n, int wallet_id) inline_ref { return begin_cell() + .store_uint(wallet_id, 32) .store_uint(n, 8) .store_uint(k, 8) .store_uint(last_cleaned, 64) - .store_dict(public_keys) + .store_dict(owner_infos) .store_dict(pending_queries) .end_cell(); } +_ pack_owner_info(int public_key, int flood) inline_ref { + return begin_cell() + .store_uint(public_key, 256) + .store_uint(flood, 8); +} + +_ unpack_owner_info(slice cs) inline_ref { + return (cs~load_uint(256), cs~load_uint(8)); +} + (int, int) check_signatures(cell public_keys, cell signatures, int hash, int cnt_bits) inline_ref { int cnt = 0; @@ -41,44 +52,60 @@ _ pack_state(cell pending_queries, cell public_keys, int last_cleaned, int k, in return (cnt, cnt_bits); } - () recv_internal(slice in_msg) impure { ;; do nothing for internal messages } -(int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?) inline_ref { +(int, int, int, slice) unpack_query_data(slice in_msg, int n, slice query, var found?, int root_i) inline_ref { if (found?) { throw_unless(35, query~load_int(1)); - (int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(n), query); + (int creator_i, int cnt, int cnt_bits, slice msg) = (query~load_uint(8), query~load_uint(8), query~load_uint(n), query); throw_unless(36, slice_hash(msg) == slice_hash(in_msg)); - return (cnt, cnt_bits, msg); + return (creator_i, cnt, cnt_bits, msg); } - return (0, 0, in_msg); + return (root_i, 0, 0, in_msg); } () try_init() impure inline_ref { ;; first query without signatures is always accepted - (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state(); + (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state(); throw_if(37, last_cleaned); accept_message(); - set_data(pack_state(pending_queries, public_keys, 1, k, n)); + set_data(pack_state(pending_queries, owner_infos, 1, k, n, wallet_id)); } -cell update_pending_queries(cell pending_queries, slice msg, int query_id, int cnt, int cnt_bits, int n, int k) impure inline_ref { +(cell, cell) update_pending_queries(cell pending_queries, cell owner_infos, slice msg, int query_id, int creator_i, int cnt, int cnt_bits, int n, int k) impure inline_ref { if (cnt >= k) { + accept_message(); while (msg.slice_refs()) { var mode = msg~load_uint(8); send_raw_message(msg~load_ref(), mode); } pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1)); + + (slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i); + (int public_key, int flood) = unpack_owner_info(owner_info); + owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1)); } else { pending_queries~udict_set_builder(64, query_id, begin_cell() .store_uint(1, 1) + .store_uint(creator_i, 8) .store_uint(cnt, 8) .store_uint(cnt_bits, n) .store_slice(msg)); } - return pending_queries; + return (pending_queries, owner_infos); +} + +(int, int) calc_boc_size(int cells, int bits, slice root) { + cells += 1; + bits += root.slice_bits(); + + while (root.slice_refs()) { + (cells, bits) = calc_boc_size(cells, bits, root~load_ref().begin_parse()); + } + + return (cells, bits); } () recv_external(slice in_msg) impure { @@ -92,44 +119,62 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c int root_hash = slice_hash(in_msg); int root_i = in_msg~load_uint(8); - (int n, int k, int last_cleaned, cell public_keys, cell pending_queries) = unpack_state(); + (int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state(); last_cleaned -= last_cleaned == 0; - (slice public_key, var found?) = public_keys.udict_get?(8, root_i); + (slice owner_info, var found?) = owner_infos.udict_get?(8, root_i); + (int public_key, int flood) = unpack_owner_info(owner_info); throw_unless(31, found?); - throw_unless(32, check_signature(root_hash, root_signature, public_key.preload_uint(256))); + throw_unless(32, check_signature(root_hash, root_signature, public_key)); cell signatures = in_msg~load_dict(); var hash = slice_hash(in_msg); + int query_wallet_id = in_msg~load_uint(32); + throw_unless(42, query_wallet_id == wallet_id); + int query_id = in_msg~load_uint(64); + (int cnt, int bits) = calc_boc_size(0, 0, in_msg); + throw_if(40, (cnt > 8) | (bits > 2048)); + + (slice query, var found?) = pending_queries.udict_get?(64, query_id); + + ifnot (found?) { + flood += 1; + throw_if(39, flood > 10); + } + var bound = (now() << 32); throw_if(33, query_id < bound); - (slice query, var found?) = pending_queries.udict_get?(64, query_id); - (int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?); + (int creator_i, int cnt, int cnt_bits, slice msg) = unpack_query_data(in_msg, n, query, found?, root_i); int mask = 1 << root_i; throw_if(34, cnt_bits & mask); cnt_bits |= mask; cnt += 1; - ;; TODO: reserve some gas or FAIL - accept_message(); + set_gas_limit(100000); - pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k); - set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n)); + ifnot (found?) { + owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood)); + throw_if(41, (cnt < k) & (bound + ((60 * 60) << 32) > query_id)); + } + + (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k); + set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id)); commit(); int need_save = 0; ifnot (cell_null?(signatures) | (cnt >= k)) { - (int new_cnt, cnt_bits) = check_signatures(public_keys, signatures, hash, cnt_bits); + (int new_cnt, cnt_bits) = check_signatures(owner_infos, signatures, hash, cnt_bits); cnt += new_cnt; - pending_queries = update_pending_queries(pending_queries, msg, query_id, cnt, cnt_bits, n, k); + (pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k); need_save = -1; } + accept_message(); bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago int old_last_cleaned = last_cleaned; do { @@ -146,18 +191,18 @@ cell update_pending_queries(cell pending_queries, slice msg, int query_id, int c } until (~ f); if (need_save) { - set_data(pack_state(pending_queries, public_keys, last_cleaned, k, n)); + set_data(pack_state(pending_queries, owner_infos, last_cleaned, k, n, wallet_id)); } } ;; Get methods ;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten) (int, int) get_query_state(int query_id) method_id { - (int n, _, int last_cleaned, _, cell pending_queries) = unpack_state(); + (_, int n, _, int last_cleaned, _, cell pending_queries) = unpack_state(); (slice cs, var found) = pending_queries.udict_get?(64, query_id); if (found) { if (cs~load_int(1)) { - cs~load_uint(8); + cs~load_uint(8 + 8); return (0, cs~load_uint(n)); } else { return (-1, 0); @@ -172,8 +217,8 @@ int processed?(int query_id) method_id { return x; } -cell create_init_state(int n, int k, cell public_keys) method_id { - return pack_state(new_dict(), public_keys, 0, k, n); +cell create_init_state(int wallet_id, int n, int k, cell owners_info) method_id { + return pack_state(new_dict(), owners_info, 0, k, n, wallet_id); } cell merge_list(cell a, cell b) { @@ -196,7 +241,7 @@ cell merge_list(cell a, cell b) { } cell get_public_keys() method_id { - (_, _, _, cell public_keys, _) = unpack_state(); + (_, _, _, _, cell public_keys, _) = unpack_state(); return public_keys; } @@ -222,14 +267,14 @@ cell get_public_keys() method_id { } cell messages_by_mask(int mask) method_id { - (int n, _, _, _, cell pending_queries) = unpack_state(); + (_, int n, _, _, _, cell pending_queries) = unpack_state(); int i = -1; cell a = new_dict(); do { (i, var cs, var f) = pending_queries.udict_get_next?(64, i); if (f) { if (cs~load_int(1)) { - int cnt_bits = cs.skip_bits(8).preload_uint(n); + int cnt_bits = cs.skip_bits(8 + 8).preload_uint(n); if (cnt_bits & mask) { a~udict_set_builder(64, i, begin_cell().store_slice(cs)); } @@ -248,7 +293,7 @@ cell get_messages_unsigned() method_id { } (int, int) get_n_k() method_id { - (int n, int k, _, _, _) = unpack_state(); + (_, int n, int k, _, _, _) = unpack_state(); return (n, k); } diff --git a/crypto/smartcont/show-addr.fif b/crypto/smartcont/show-addr.fif index 5d3094644f..51b2df8cbf 100755 --- a/crypto/smartcont/show-addr.fif +++ b/crypto/smartcont/show-addr.fif @@ -1,12 +1,13 @@ #!/usr/bin/fift -s "TonUtil.fif" include -{ ."usage: " @' $0 type ." " cr +{ ."usage: " $0 type ." " cr ."Shows the address of a simple wallet created by new-wallet.fif, with address in .addr " ."and private key in file .pk" cr 1 halt } : usage -def? $# { @' $# 1 > ' usage if } if -def? $1 { @' $1 } { "new-wallet" } cond constant file-base +$# 1 > ' usage if +1 :$1..n +$1 dup null? { drop "new-wallet" } if =: file-base file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B| dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index ba9416708c..12889a13bf 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -40,6 +40,7 @@ cont get_c3() impure asm "c3 PUSH"; cont bless(slice s) impure asm "BLESS"; () accept_message() impure asm "ACCEPT"; +() set_gas_limit(int limit) impure asm "SETGASLIMIT"; () commit() impure asm "COMMIT"; int min(int x, int y) asm "MIN"; diff --git a/crypto/smartcont/wallet.fif b/crypto/smartcont/wallet.fif index bdd0339993..e5cf6ad41a 100755 --- a/crypto/smartcont/wallet.fif +++ b/crypto/smartcont/wallet.fif @@ -1,35 +1,32 @@ #!/usr/bin/fift -s "TonUtil.fif" include +"GetOpt.fif" include { ."usage: " @' $0 type ." [-n] [-B ] [-C ] []" cr ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt } : usage + "" =: comment // comment for simple transfers true =: allow-bounce -def? $5 { @' $5 "-n" $= { false =: allow-bounce [forget] $5 - def? $6 { @' $6 =: $5 [forget] $6 } if - def? $7 { @' $7 =: $6 [forget] $7 } if - @' $# 1- =: $# - } if -} if - -def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or - { @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6 - def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond - @' $# 2- =: $# - } if -} if -$# dup 4 < swap 5 > or ' usage if +3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors -true constant bounce +begin-options + "n" "--no-bounce" { false =: allow-bounce } short-long-option + "B" "--body" { =: body-boc-file } short-long-option-arg + "C" "--comment" { =: comment } short-long-option-arg + "m" "--mode" { parse-int =: send-mode } short-long-option-arg + "h" "--help" { usage } short-long-option +parse-options +$# dup 4 < swap 5 > or ' usage if +5 :$1..n +true =: bounce $1 =: file-base $2 bounce parse-load-address allow-bounce and =: bounce 2=: dest_addr $3 parse-int =: seqno $4 $>GR =: amount -def? $5 { @' $5 } { "wallet-query" } cond constant savefile -3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +$5 dup null? { drop "wallet-query" } if =: savefile // "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment file-base +".addr" load-address diff --git a/crypto/smc-envelope/MultisigWallet.cpp b/crypto/smc-envelope/MultisigWallet.cpp index 36830179c5..0c8b56d799 100644 --- a/crypto/smc-envelope/MultisigWallet.cpp +++ b/crypto/smc-envelope/MultisigWallet.cpp @@ -8,8 +8,13 @@ namespace ton { -MultisigWallet::QueryBuilder::QueryBuilder(td::int64 query_id, td::Ref msg, int mode) { - msg_ = vm::CellBuilder().store_long(query_id, 64).store_long(mode, 8).store_ref(std::move(msg)).finalize(); +MultisigWallet::QueryBuilder::QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref msg, int mode) { + msg_ = vm::CellBuilder() + .store_long(wallet_id, 32) + .store_long(query_id, 64) + .store_long(mode, 8) + .store_ref(std::move(msg)) + .finalize(); } void MultisigWallet::QueryBuilder::sign(td::int32 id, td::Ed25519::PrivateKey& pk) { CHECK(id < td::narrow_cast(mask_.size())); @@ -87,26 +92,29 @@ std::vector MultisigWallet::get_public_keys() const { return res; } -td::Ref MultisigWallet::create_init_data(std::vector public_keys, int k) const { +td::Ref MultisigWallet::create_init_data(td::uint32 wallet_id, std::vector public_keys, + int k) const { vm::Dictionary pk(8); for (size_t i = 0; i < public_keys.size(); i++) { auto key = pk.integer_key(td::make_refint(i), 8, false); - pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice())); + pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8)); } - auto res = run_get_method("create_init_state", - {td::make_refint(public_keys.size()), td::make_refint(k), pk.get_root_cell()}); + auto res = run_get_method("create_init_state", {td::make_refint(wallet_id), td::make_refint(public_keys.size()), + td::make_refint(k), pk.get_root_cell()}); CHECK(res.code == 0); return res.stack.write().pop_cell(); } -td::Ref MultisigWallet::create_init_data_fast(std::vector public_keys, int k) { +td::Ref MultisigWallet::create_init_data_fast(td::uint32 wallet_id, std::vector public_keys, + int k) { vm::Dictionary pk(8); for (size_t i = 0; i < public_keys.size(); i++) { auto key = pk.integer_key(td::make_refint(i), 8, false); - pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice())); + pk.set_builder(key.bits(), 8, vm::CellBuilder().store_bytes(public_keys[i].as_slice()).store_long(0, 8)); } vm::CellBuilder cb; + cb.store_long(wallet_id, 32); cb.store_long(public_keys.size(), 8).store_long(k, 8).store_long(0, 64); cb.ensure_throw(cb.store_maybe_ref(pk.get_root_cell())); cb.ensure_throw(cb.store_maybe_ref({})); @@ -156,7 +164,7 @@ std::vector MultisigWallet::get_unsigned_messaged(int i vm::Dictionary dict(std::move(cell), 64); std::vector res; dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) { - cs.write().skip_first(8); + cs.write().skip_first(8 + 8); Message message; td::BigInt256 query_id; query_id.import_bits(ptr, ptr_bits, false); diff --git a/crypto/smc-envelope/MultisigWallet.h b/crypto/smc-envelope/MultisigWallet.h index 10c6d07604..17395e472f 100644 --- a/crypto/smc-envelope/MultisigWallet.h +++ b/crypto/smc-envelope/MultisigWallet.h @@ -20,7 +20,7 @@ class MultisigWallet : public ton::SmartContract { class QueryBuilder { public: - QueryBuilder(td::int64 query_id, td::Ref msg, int mode = 3); + QueryBuilder(td::uint32 wallet_id, td::int64 query_id, td::Ref msg, int mode = 3); void sign(td::int32 id, td::Ed25519::PrivateKey& pk); td::Ref create_inner() const; @@ -42,8 +42,9 @@ class MultisigWallet : public ton::SmartContract { // creation static td::Ref create(td::Ref data = {}); - td::Ref create_init_data(std::vector public_keys, int k) const; - static td::Ref create_init_data_fast(std::vector public_keys, int k); + td::Ref create_init_data(td::uint32 wallet_id, std::vector public_keys, int k) const; + static td::Ref create_init_data_fast(td::uint32 wallet_id, std::vector public_keys, + int k); // get methods int processed(td::uint64 query_id) const; diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index aec7bed469..02964a7c87 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -30,10 +30,6 @@ namespace ton { namespace { -td::int32 get_method_id(td::Slice method_name) { - unsigned crc = td::crc16(method_name); - return (crc & 0xffff) | 0x10000; -} td::Ref prepare_vm_stack(td::Ref body) { td::Ref stack_ref{true}; td::RefInt256 acc_addr{true}; diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp index 4e17139be2..e61cab2422 100644 --- a/crypto/smc-envelope/SmartContractCode.cpp +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -27,11 +27,7 @@ namespace ton { namespace { const auto& get_map() { static auto map = [] { - class Cmp : public std::less<> { - public: - using is_transparent = void; - }; - std::map, Cmp> map; + std::map, std::less<>> map; auto with_tvm_code = [&](auto name, td::Slice code_str) { map[name] = vm::std_boc_deserialize(td::base64_decode(code_str).move_as_ok()).move_as_ok(); }; diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp index 750ed94b73..0ec52f5819 100644 --- a/crypto/test/test-smartcont.cpp +++ b/crypto/test/test-smartcont.cpp @@ -455,16 +455,17 @@ TEST(Smartcon, Multisig) { int n = 100; int k = 99; + td::uint32 wallet_id = std::numeric_limits::max() - 3; std::vector keys; for (int i = 0; i < n; i++) { keys.push_back(td::Ed25519::generate_private_key().move_as_ok()); } auto init_state = ms_lib->create_init_data( - td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k); + wallet_id, td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k); auto ms = ton::MultisigWallet::create(init_state); - td::uint64 query_id = 123; - ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize()); + td::uint64 query_id = 123 | ((100 * 60ull) << 32); + ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize()); // first empty query (init) CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0); // first empty query @@ -491,7 +492,7 @@ TEST(Smartcon, Multisig) { ASSERT_EQ(0, ms->processed(query_id)); { - ton::MultisigWallet::QueryBuilder qb(query_id, vm::CellBuilder().finalize()); + ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize()); for (int i = 50; i + 1 < 100; i++) { qb.sign(i, keys[i]); } @@ -507,6 +508,7 @@ TEST(Smartcon, Multisig) { TEST(Smartcont, MultisigStress) { int n = 10; int k = 5; + td::uint32 wallet_id = std::numeric_limits::max() - 3; std::vector keys; for (int i = 0; i < n; i++) { @@ -515,13 +517,14 @@ TEST(Smartcont, MultisigStress) { auto public_keys = td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }); auto ms_lib = ton::MultisigWallet::create(); auto init_state_old = - ms_lib->create_init_data_fast(td::transform(public_keys, [](auto& key) { return key.copy(); }), k); - auto init_state = ms_lib->create_init_data(td::transform(public_keys, [](auto& key) { return key.copy(); }), k); + ms_lib->create_init_data_fast(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k); + auto init_state = + ms_lib->create_init_data(wallet_id, td::transform(public_keys, [](auto& key) { return key.copy(); }), k); CHECK(init_state_old->get_hash() == init_state->get_hash()); auto ms = ton::MultisigWallet::create(init_state); CHECK(ms->get_public_keys() == public_keys); - td::int32 now = 0; + td::int32 now = 100 * 60; td::int32 qid = 1; using Mask = std::bitset<128>; struct Query { @@ -566,7 +569,7 @@ TEST(Smartcont, MultisigStress) { }; auto sign_query = [&](Query& query, Mask mask) { - auto qb = ton::MultisigWallet::QueryBuilder(query.id, query.message); + auto qb = ton::MultisigWallet::QueryBuilder(wallet_id, query.id, query.message); int first_i = -1; for (int i = 0; i < (int)mask.size(); i++) { if (mask.test(i)) { diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 5273e15972..83ba6e1052 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -708,6 +708,9 @@ int VmState::step() { } int VmState::run() { + if (code.is_null()) { + throw VmError{Excno::fatal, "cannot run an uninitialized VM"}; + } int res; Guard guard(this); do { diff --git a/crypto/vm/dict.h b/crypto/vm/dict.h index 11234277dd..9bb11be3a7 100644 --- a/crypto/vm/dict.h +++ b/crypto/vm/dict.h @@ -237,6 +237,11 @@ class DictionaryFixed : public DictionaryBase { Ref get_minmax_key(T& key_buffer, bool fetch_max = false, bool invert_first = false) { return get_minmax_key(key_buffer.bits(), key_buffer.size(), fetch_max, invert_first); } + template + Ref lookup_nearest_key(T& key_buffer, bool fetch_next = false, bool allow_eq = false, + bool invert_first = false) { + return lookup_nearest_key(key_buffer.bits(), key_buffer.size(), fetch_next, allow_eq, invert_first); + } protected: virtual int label_mode() const { diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index 1c7fb55c62..507f98df09 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -57,6 +57,11 @@ class Atom; using Tuple = td::Cnt>; +template +Ref make_tuple_ref(Args&&... args) { + return td::make_cnt_ref>(std::vector{std::forward(args)...}); +} + struct from_object_t {}; constexpr from_object_t from_object{}; @@ -192,6 +197,10 @@ class StackEntry { public: static StackEntry make_list(std::vector&& elems); static StackEntry make_list(const std::vector& elems); + template + static StackEntry cons(T1&& x, T2&& y) { + return StackEntry{make_tuple_ref(std::forward(x), std::forward(y))}; + } template static StackEntry maybe(Ref ref) { if (ref.is_null()) { @@ -268,11 +277,6 @@ inline void swap(StackEntry& se1, StackEntry& se2) { se1.swap(se2); } -template -Ref make_tuple_ref(Args&&... args) { - return td::make_cnt_ref>(std::vector{std::forward(args)...}); -} - const StackEntry& tuple_index(const Tuple& tup, unsigned idx); StackEntry tuple_extend_index(const Ref& tup, unsigned idx); unsigned tuple_extend_set_index(Ref& tup, unsigned idx, StackEntry&& value, bool force = false); diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index e2142f072f..8697ddccaf 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -662,12 +662,49 @@ int exec_set_code(VmState* st) { return install_output_action(st, cb.finalize()); } +int exec_set_lib_code(VmState* st) { + VM_LOG(st) << "execute SETLIBCODE"; + Stack& stack = st->get_stack(); + stack.check_underflow(2); + int mode = stack.pop_smallint_range(2); + auto code = stack.pop_cell(); + CellBuilder cb; + if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n) + && cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4 + && cb.store_long_bool(mode * 2 + 1, 8) // mode:(## 7) { mode <= 2 } + && cb.store_ref_bool(std::move(code)))) { // libref:LibRef = OutAction; + throw VmError{Excno::cell_ov, "cannot serialize new library code into an output action cell"}; + } + return install_output_action(st, cb.finalize()); +} + +int exec_change_lib(VmState* st) { + VM_LOG(st) << "execute CHANGELIB"; + Stack& stack = st->get_stack(); + stack.check_underflow(2); + int mode = stack.pop_smallint_range(2); + auto hash = stack.pop_int_finite(); + if (!hash->unsigned_fits_bits(256)) { + throw VmError{Excno::range_chk, "library hash must be non-negative"}; + } + CellBuilder cb; + if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n) + && cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4 + && cb.store_long_bool(mode * 2, 8) // mode:(## 7) { mode <= 2 } + && cb.store_int256_bool(hash, 256, false))) { // libref:LibRef = OutAction; + throw VmError{Excno::cell_ov, "cannot serialize library hash into an output action cell"}; + } + return install_output_action(st, cb.finalize()); +} + void register_ton_message_ops(OpcodeTable& cp0) { using namespace std::placeholders; cp0.insert(OpcodeInstr::mksimple(0xfb00, 16, "SENDRAWMSG", exec_send_raw_message)) .insert(OpcodeInstr::mksimple(0xfb02, 16, "RESERVERAW", std::bind(exec_reserve_raw, _1, 0))) .insert(OpcodeInstr::mksimple(0xfb03, 16, "RESERVERAWX", std::bind(exec_reserve_raw, _1, 1))) - .insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code)); + .insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code)) + .insert(OpcodeInstr::mksimple(0xfb06, 16, "SETLIBCODE", exec_set_lib_code)) + .insert(OpcodeInstr::mksimple(0xfb07, 16, "CHANGELIB", exec_change_lib)); } void register_ton_ops(OpcodeTable& cp0) { diff --git a/crypto/vm/utils.cpp b/crypto/vm/utils.cpp new file mode 100644 index 0000000000..25e01f21aa --- /dev/null +++ b/crypto/vm/utils.cpp @@ -0,0 +1,134 @@ +#include "utils.h" + +namespace vm { + +td::Result convert_stack_entry(td::Slice word); +td::Result> parse_stack_entries_in(td::Slice& str, bool prefix_only = false); +td::Result parse_stack_entry_in(td::Slice& str, bool prefix_only = false); + +namespace { + +td::Slice& skip_spaces(td::Slice& str, const char* delims) { + while (str.size() > 0 && strchr(delims, str[0])) { + str.remove_prefix(1); + } + return str; +} + +td::Slice get_word(td::Slice& str, const char* delims, const char* specials) { + skip_spaces(str, delims); + + size_t p = 0; + while (p < str.size() && !strchr(delims, str[p])) { + if (specials && strchr(specials, str[p])) { + if (!p) { + p++; + } + break; + } + p++; + } + + td::Slice ret = str.copy().truncate(p); + str.remove_prefix(p); + return ret; +} + +} // namespace + +td::Result parse_stack_entry_in(td::Slice& str, bool prefix_only) { + auto word = get_word(str, " \t", "[()]"); + if (word.empty()) { + return td::Status::Error("stack value expected instead of end-of-line"); + } + if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) { + int expected = (word[0] == '(' ? ')' : ']'); + TRY_RESULT(values, parse_stack_entries_in(str, true)); + word = get_word(str, " \t", "[()]"); + if (word.size() != 1 || word[0] != expected) { + return td::Status::Error("closing bracket expected"); + } + vm::StackEntry value; + if (expected == ']') { + value = vm::StackEntry{std::move(values)}; + } else { + value = vm::StackEntry::make_list(std::move(values)); + } + if (prefix_only || (skip_spaces(str, " \t").size() == 0)) { + return value; + } else { + return td::Status::Error("extra data at the end"); + } + } else { + return convert_stack_entry(word); + } +} + +td::Result convert_stack_entry(td::Slice str) { + if (str.empty() || str.size() > 65535) { + return td::Status::Error("too long string"); + } + int l = (int)str.size(); + if (str[0] == '"') { + vm::CellBuilder cb; + if (l == 1 || str.back() != '"' || l >= 127 + 2 || !cb.store_bytes_bool(str.data() + 1, l - 2)) { + return td::Status::Error("incomplete (or too long) string"); + } + return vm::StackEntry{vm::load_cell_slice_ref(cb.finalize())}; + } + if (l >= 3 && (str[0] == 'x' || str[0] == 'b') && str[1] == '{' && str.back() == '}') { + unsigned char buff[128]; + int bits = + (str[0] == 'x') + ? (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1) + : (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1); + if (bits < 0) { + return td::Status::Error("failed to parse raw b{...}/x{...} number"); + } + return vm::StackEntry{ + Ref{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}}; + } + auto num = td::RefInt256{true}; + auto& x = num.unique_write(); + if (l >= 3 && str[0] == '0' && str[1] == 'x') { + if (x.parse_hex(str.data() + 2, l - 2) != l - 2) { + return td::Status::Error("failed to parse 0x... hex number"); + } + } else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') { + if (x.parse_hex(str.data() + 3, l - 3) != l - 3) { + return td::Status::Error("failed to parse -0x... hex number"); + } + x.negate().normalize(); + } else if (!l || x.parse_dec(str.data(), l) != l) { + return td::Status::Error("failed to parse dec number"); + } + return vm::StackEntry{std::move(num)}; +} + +td::Result> parse_stack_entries_in(td::Slice& str, bool prefix_only) { + std::vector ret; + while (!skip_spaces(str, " \t").empty()) { + auto c = str.copy(); + auto word = get_word(c, " \t", "[()]"); + if (word == "]" || word == ")") { + if (prefix_only) { + return ret; + } else { + return td::Status::Error("not paired closing bracket"); + } + } + TRY_RESULT(value, parse_stack_entry_in(str, true)); + ret.push_back(std::move(value)); + } + return ret; +} + +td::Result> parse_stack_entries(td::Slice str, bool prefix_only) { + return parse_stack_entries_in(str, prefix_only); +} + +td::Result parse_stack_entry(td::Slice str, bool prefix_only) { + return parse_stack_entry_in(str, prefix_only); +} + +} // namespace vm diff --git a/crypto/vm/utils.h b/crypto/vm/utils.h new file mode 100644 index 0000000000..e1afe60c18 --- /dev/null +++ b/crypto/vm/utils.h @@ -0,0 +1,11 @@ +#pragma once +#include "stack.hpp" + +#include + +namespace vm { + +td::Result> parse_stack_entries(td::Slice str, bool prefix_only = false); +td::Result parse_stack_entry(td::Slice str, bool prefix_only = false); + +} // namespace vm diff --git a/doc/fiftbase.tex b/doc/fiftbase.tex index a175f3a48c..d39bf3fc8f 100644 --- a/doc/fiftbase.tex +++ b/doc/fiftbase.tex @@ -1848,6 +1848,7 @@ \section*{Introduction} \item {\tt \$@?+} ($s$ $x$ -- $S$ $s'$ $-1$ or $s$ $0$), similar to {\tt \$@+}, but uses a flag to indicate failure instead of throwing an exception, cf.~\ptref{p:slice.ops}. \item {\tt \$cmp} ($S$ $S'$ -- $x$), returns $0$ if strings $S$ and $S'$ are equal, $-1$ if $S$ is lexicographically less than $S'$, and $1$ if $S$ is lexicographically greater than $S'$, cf.~\ptref{p:string.cmp.ops}. \item {\tt \$len} ($S$ -- $x$), computes the byte length (not the UTF-8 character length!) of a string, cf.~\ptref{p:string.ops}. +\item {\tt \$pos} ($S$ $S'$ -- $x$ or $-1$), returns the position (byte offset)~$x$ of the first occurence of substring $S'$ in string~$S$ or $-1$. \item {\tt \$reverse} ($S$ -- $S'$), reverses the order of UTF-8 characters in {\em String\/}~$S$. If $S$ is not a valid UTF-8 string, the return value is undefined and may be also invalid. \item {\tt \%1<{<}} ($x$ $y$ -- $z$), computes $z:=x\bmod 2^y=x\&(2^y-1)$ for two {\em Integer\/}s $x$ and $0\leq y\leq 256$. \item {\tt \underline{'} $\langle\textit{word-name}\rangle$} ( -- $e$), returns the execution token equal to the current (compile-time) definition of $\langle\textit{word-name}\rangle$, cf.~\ptref{p:blocks}. If the specified word is not found, throws an exception. diff --git a/doc/tvm.tex b/doc/tvm.tex index 26f7df2474..73a2145be2 100644 --- a/doc/tvm.tex +++ b/doc/tvm.tex @@ -2267,7 +2267,9 @@ \section*{Introduction} \item {\tt FB02} --- {\tt RAWRESERVE} ($x$ $y$ -- ), creates an output action which would reserve exactly $x$ nanograms (if $y=0$), at most $x$ nanograms (if $y=2$), or all but $x$ nanograms (if $y=1$ or $y=3$), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying $x$ nanograms (or $b-x$ nanograms, where $b$ is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit $+2$ in $y$ means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Currently $x$ must be a non-negative integer, and $y$ must be in the range $0\ldots 3$. \item {\tt FB03} --- {\tt RAWRESERVEX} ($s$ $y$ -- ), similar to {\tt RAWRESERVE}, but accepts a {\em Slice $s$} with a {\em CurrencyCollection\/} as an argument. In this way currencies other than Grams can be reserved. \item {\tt FB04} --- {\tt SETCODE} ($c$ -- ), creates an output action that would change this smart contract code to that given by {\em Cell\/}~$c$. Notice that this change will take effect only after the successful termination of the current run of the smart contract. -\item {\tt FB05}--{\tt FB3F} --- Reserved for output action primitives. +\item {\tt FB06} --- {\tt SETLIBCODE} ($c$ $x$ -- ), creates an output action that would modify the collection of this smart contract libraries by adding or removing library with code given in {\em Cell\/}~$c$. If $x=0$, the library is actually removed if it was previously present in the collection (if not, this action does nothing). If $x=1$, the library is added as a private library, and if $x=2$, the library is added as a public library (and becomes available to all smart contracts if the current smart contract resides in the masterchain); if the library was present in the collection before, its public/private status is changed according to $x$. Values of $x$ other than $0\ldots 2$ are invalid. +\item {\tt FB07} --- {\tt CHANGELIB} ($h$ $x$ -- ), creates an output action similarly to {\tt SETLIBCODE}, but instead of the library code accepts its hash as an unsigned 256-bit integer $h$. If $x\neq0$ and the library with hash $h$ is absent from the library collection of this smart contract, this output action will fail. +\item {\tt FB08}--{\tt FB3F} --- Reserved for output action primitives. \end{itemize} \mysubsection{Debug primitives}\label{p:prim.debug} diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index a4d3af516b..d304119340 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -59,6 +59,7 @@ #include "vm/cp0.h" #include "ton/ton-shard.h" #include "openssl/rand.hpp" +#include "crypto/vm/utils.h" #if TD_DARWIN || TD_LINUX #include @@ -756,91 +757,6 @@ bool TestNode::parse_shard_id(ton::ShardIdFull& shard) { return convert_shard_id(get_word(), shard) || set_error("cannot parse full shard identifier or prefix"); } -bool TestNode::parse_stack_value(td::Slice str, vm::StackEntry& value) { - if (str.empty() || str.size() > 65535) { - return false; - } - int l = (int)str.size(); - if (str[0] == '"') { - vm::CellBuilder cb; - if (l == 1 || str.back() != '"' || l >= 127 + 2 || !cb.store_bytes_bool(str.data() + 1, l - 2)) { - return false; - } - value = vm::StackEntry{vm::load_cell_slice_ref(cb.finalize())}; - return true; - } - if (l >= 3 && (str[0] == 'x' || str[0] == 'b') && str[1] == '{' && str.back() == '}') { - unsigned char buff[128]; - int bits = - (str[0] == 'x') - ? (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1) - : (int)td::bitstring::parse_bitstring_binary_literal(buff, sizeof(buff), str.begin() + 2, str.end() - 1); - if (bits < 0) { - return false; - } - value = - vm::StackEntry{Ref{true, vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize()}}; - return true; - } - auto num = td::RefInt256{true}; - auto& x = num.unique_write(); - if (l >= 3 && str[0] == '0' && str[1] == 'x') { - if (x.parse_hex(str.data() + 2, l - 2) != l - 2) { - return false; - } - } else if (l >= 4 && str[0] == '-' && str[1] == '0' && str[2] == 'x') { - if (x.parse_hex(str.data() + 3, l - 3) != l - 3) { - return false; - } - x.negate().normalize(); - } else if (!l || x.parse_dec(str.data(), l) != l) { - return false; - } - value = vm::StackEntry{std::move(num)}; - return true; -} - -bool TestNode::parse_stack_value(vm::StackEntry& value) { - auto word = get_word_ext(" \t", "[()]"); - if (word.empty()) { - return set_error("stack value expected instead of end-of-line"); - } - if (word.size() == 1 && (word[0] == '[' || word[0] == '(')) { - int expected = (word[0] == '(' ? ')' : ']'); - std::vector values; - if (!parse_stack_values(values)) { - return false; - } - word = get_word_ext(" \t", "[()]"); - if (word.size() != 1 || word[0] != expected) { - return set_error("closing bracket expected"); - } - if (expected == ']') { - value = vm::StackEntry{std::move(values)}; - } else { - value = vm::StackEntry::make_list(std::move(values)); - } - return true; - } else { - return parse_stack_value(word, value) || set_error("invalid vm stack value"); - } -} - -bool TestNode::parse_stack_values(std::vector& values) { - values.clear(); - while (!seekeoln()) { - if (cur() == ']' || cur() == ')') { - break; - } - values.emplace_back(); - if (!parse_stack_value(values.back())) { - values.pop_back(); - return false; - } - } - return true; -} - bool TestNode::set_error(std::string err_msg) { return set_error(td::Status::Error(-1, err_msg)); } @@ -912,6 +828,11 @@ bool TestNode::show_help(std::string command) { "header\n" "byutime \tLooks up a block by workchain, shard and creation time, and " "shows its header\n" + "creatorstats [ []]\tLists block creator statistics by validator public " + "key\n" + "recentcreatorstats [ []]\tLists block creator statistics " + "updated after by validator public " + "key\n" "known\tShows the list of all known block ids\n" "privkey \tLoads a private key from file\n" "help []\tThis help\n" @@ -999,6 +920,12 @@ bool TestNode::do_parse_line() { return parse_shard_id(shard) && parse_uint32(utime) && seekeoln() && lookup_block(shard, 4, utime); } else if (word == "bylt") { return parse_shard_id(shard) && parse_lt(lt) && seekeoln() && lookup_block(shard, 2, lt); + } else if (word == "creatorstats" || word == "recentcreatorstats") { + count = 1000; + int mode = (word == "recentcreatorstats" ? 4 : 0); + return parse_block_id_ext(blkid) && (!mode || parse_uint32(utime)) && (seekeoln() || parse_uint32(count)) && + (seekeoln() || (parse_hash(hash) && (mode |= 1))) && seekeoln() && + get_creator_stats(blkid, mode, count, hash, utime); } else if (word == "known") { return eoln() && show_new_blkids(true); } else if (word == "quit" && eoln()) { @@ -1090,13 +1017,12 @@ bool TestNode::get_account_state(ton::WorkchainId workchain, ton::StdSmcAddress bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress addr, ton::BlockIdExt ref_blkid, std::string method_name) { - std::vector params; - if (!parse_stack_values(params)) { - return set_error("cannot parse list of TVM stack values"); - } - if (!seekeoln()) { - return set_error("extra characters after a list of TVM stack values"); + auto R = vm::parse_stack_entries(td::Slice(parse_ptr_, parse_end_)); + if (R.is_error()) { + return set_error(R.move_as_error().to_string()); } + parse_ptr_ = parse_end_; + auto params = R.move_as_ok(); if (!ref_blkid.is_valid()) { return set_error("must obtain last block information before making other queries"); } @@ -2236,6 +2162,107 @@ void TestNode::got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mod show_new_blkids(); } +bool TestNode::get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after, + ton::UnixTime min_utime) { + if (!(ready_ && !client_.empty())) { + return set_error("server connection not ready"); + } + if (!blkid.is_masterchain_ext()) { + return set_error("only masterchain blocks contain block creator statistics"); + } + if (!(mode & 1)) { + start_after.set_zero(); + } + auto b = ton::serialize_tl_object(ton::create_tl_object( + mode, ton::create_tl_lite_block_id(blkid), req_count, start_after, min_utime), + true); + LOG(INFO) << "requesting up to " << req_count << " block creator stats records with respect to masterchain block " + << blkid.to_str() << " starting from validator public key " << start_after.to_hex() << " created after " + << min_utime << " (mode=" << mode << ")"; + return envelope_send_query(std::move(b), [ Self = actor_id(this), mode, blkid, req_count, start_after, + min_utime ](td::Result R) { + if (R.is_error()) { + return; + } + auto F = ton::fetch_tl_object(R.move_as_ok(), true); + if (F.is_error()) { + LOG(ERROR) << "cannot parse answer to liteServer.getValidatorStats"; + } else { + auto f = F.move_as_ok(); + td::actor::send_closure_later(Self, &TestNode::got_creator_stats, blkid, ton::create_block_id(f->id_), mode, + f->mode_, start_after, min_utime, std::move(f->state_proof_), + std::move(f->data_proof_), f->count_, req_count, f->complete_); + } + }); +} + +void TestNode::got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode, + td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof, + td::BufferSlice data_proof, int count, int req_count, bool complete) { + LOG(INFO) << "got answer to getValidatorStats query: " << count << " records out of " << req_count << ", " + << (complete ? "complete" : "incomplete"); + if (!blkid.is_masterchain_ext()) { + LOG(ERROR) << "reference block " << blkid.to_str() + << " for block creator statistics is not a valid masterchain block"; + return; + } + if (count > req_count) { + LOG(ERROR) << "obtained " << count << " answers to getValidatorStats query, but only " << req_count + << " were requested"; + return; + } + if (blkid != req_blkid) { + LOG(ERROR) << "answer to getValidatorStats refers to masterchain block " << blkid.to_str() + << " different from requested " << req_blkid.to_str(); + return; + } + auto R = block::check_extract_state_proof(blkid, state_proof.as_slice(), data_proof.as_slice()); + if (R.is_error()) { + LOG(ERROR) << "masterchain state proof for " << blkid.to_str() << " is invalid : " << R.move_as_error().to_string(); + return; + } + bool allow_eq = (mode & 3) != 1; + ton::Bits256 key{start_after}; + std::ostringstream os; + try { + auto dict = block::get_block_create_stats_dict(R.move_as_ok()); + if (!dict) { + LOG(ERROR) << "cannot extract BlockCreateStats from mc state"; + return; + } + for (int i = 0; i < count + (int)complete; i++) { + auto v = dict->lookup_nearest_key(key, true, allow_eq); + if (v.is_null()) { + if (i != count) { + LOG(ERROR) << "could fetch only " << i << " CreatorStats entries out of " << count + << " declared in answer to getValidatorStats"; + return; + } + break; + } + block::DiscountedCounter mc_cnt, shard_cnt; + if (!block::unpack_CreatorStats(std::move(v), mc_cnt, shard_cnt)) { + LOG(ERROR) << "invalid CreatorStats record with key " << key.to_hex(); + return; + } + if (mc_cnt.modified_since(min_utime) || shard_cnt.modified_since(min_utime)) { + os << key.to_hex() << " mc_cnt:" << mc_cnt << " shard_cnt:" << shard_cnt << std::endl; + } + allow_eq = false; + } + if (complete) { + os << "(complete)" << std::endl; + } else { + os << "(incomplete, repeat query from " << key.to_hex() << " )" << std::endl; + } + td::TerminalIO::out() << os.str(); + } catch (vm::VmError& err) { + LOG(ERROR) << "error while traversing block creator stats: " << err.get_msg(); + } catch (vm::VmVirtError& err) { + LOG(ERROR) << "virtualization error while traversing block creator stats: " << err.get_msg(); + } +} + int main(int argc, char* argv[]) { SET_VERBOSITY_LEVEL(verbosity_INFO); td::set_default_failure_signal_handler(); diff --git a/lite-client/lite-client.h b/lite-client/lite-client.h index bb4f31dfc7..f016c96eef 100644 --- a/lite-client/lite-client.h +++ b/lite-client/lite-client.h @@ -153,6 +153,12 @@ class TestNode : public td::actor::Actor { std::vector trans, td::BufferSlice proof); bool get_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode); void got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, td::BufferSlice res); + bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after, + ton::UnixTime min_utime); + void got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode, + td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof, + td::BufferSlice data_proof, int count, int req_count, bool complete); + // parser bool do_parse_line(); bool show_help(std::string command); td::Slice get_word(char delim = ' '); diff --git a/tddb/td/db/MemoryKeyValue.h b/tddb/td/db/MemoryKeyValue.h index f2c261fe67..f2c047138d 100644 --- a/tddb/td/db/MemoryKeyValue.h +++ b/tddb/td/db/MemoryKeyValue.h @@ -38,11 +38,7 @@ class MemoryKeyValue : public KeyValue { std::string stats() const override; private: - class Cmp : public std::less<> { - public: - using is_transparent = void; - }; - std::map map_; + std::map> map_; int64 get_count_{0}; }; } // namespace td diff --git a/test/test-adnl.cpp b/test/test-adnl.cpp index 88758fc7b9..1b4156446f 100644 --- a/test/test-adnl.cpp +++ b/test/test-adnl.cpp @@ -42,6 +42,7 @@ int main() { SET_VERBOSITY_LEVEL(verbosity_INFO); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-catchain.cpp b/test/test-catchain.cpp index 49a1c196b6..0e9bd20745 100644 --- a/test/test-catchain.cpp +++ b/test/test-catchain.cpp @@ -217,6 +217,7 @@ int main(int argc, char *argv[]) { td::set_default_failure_signal_handler().ensure(); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-dht.cpp b/test/test-dht.cpp index 8bbb8d18d5..61a49fe625 100644 --- a/test/test-dht.cpp +++ b/test/test-dht.cpp @@ -42,6 +42,7 @@ int main() { SET_VERBOSITY_LEVEL(verbosity_INFO); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-rldp.cpp b/test/test-rldp.cpp index 90d7ba0469..0d1730d340 100644 --- a/test/test-rldp.cpp +++ b/test/test-rldp.cpp @@ -41,6 +41,7 @@ int main() { SET_VERBOSITY_LEVEL(verbosity_INFO); std::string db_root_ = "tmp-ee"; + td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); td::set_default_failure_signal_handler().ensure(); diff --git a/test/test-validator-session-state.cpp b/test/test-validator-session-state.cpp index 877347bb0a..5c28380e9b 100644 --- a/test/test-validator-session-state.cpp +++ b/test/test-validator-session-state.cpp @@ -318,8 +318,8 @@ int main() { CHECK(!found); auto vec = s->choose_blocks_to_approve(desc, i); CHECK(vec.size() == 1); - CHECK(vec[1] == nullptr); - CHECK(ton::validatorsession::SentBlock::get_block_id(vec[1]) == ton::validatorsession::skip_round_candidate_id()); + CHECK(vec[0] == nullptr); + CHECK(ton::validatorsession::SentBlock::get_block_id(vec[0]) == ton::validatorsession::skip_round_candidate_id()); } for (td::uint32 i = 0; i < total_nodes; i++) { diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index 9604624a74..0dd3fa22f8 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -48,6 +48,7 @@ liteServer.blockLinkBack to_key_block:Bool from:tonNode.blockIdExt to:tonNode.bl liteServer.blockLinkForward to_key_block:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt dest_proof:bytes config_proof:bytes signatures:liteServer.SignatureSet = liteServer.BlockLink; liteServer.partialBlockProof complete:Bool from:tonNode.blockIdExt to:tonNode.blockIdExt steps:(vector liteServer.BlockLink) = liteServer.PartialBlockProof; liteServer.configInfo mode:# id:tonNode.blockIdExt state_proof:bytes config_proof:bytes = liteServer.ConfigInfo; +liteServer.validatorStats mode:# id:tonNode.blockIdExt count:int complete:Bool state_proof:bytes data_proof:bytes = liteServer.ValidatorStats; liteServer.debug.verbosity value:int = liteServer.debug.Verbosity; @@ -71,6 +72,7 @@ liteServer.listBlockTransactions id:tonNode.blockIdExt mode:# count:# after:mode liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; liteServer.getConfigAll mode:# id:tonNode.blockIdExt = liteServer.ConfigInfo; liteServer.getConfigParams mode:# id:tonNode.blockIdExt param_list:(vector int) = liteServer.ConfigInfo; +liteServer.getValidatorStats#091a58bc mode:# id:tonNode.blockIdExt limit:int start_after:mode.0?int256 modified_after:mode.2?int = liteServer.ValidatorStats; liteServer.queryPrefix = Object; liteServer.query data:bytes = Object; diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index 9b01c0d881f3ff2d6c37ae801295789e59e6ed3f..02b507f40130729f588a1251a733640b2f7fdc44 100644 GIT binary patch delta 301 zcmcZ*HYb|*(QJJy1}M + namespace ton { using WorkchainId = td::int32; @@ -141,7 +143,7 @@ struct AccountIdPrefixFull { std::string to_str() const { char buffer[64]; return std::string{ - buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx)", workchain, (unsigned long long)account_id_prefix)}; + buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx)", workchain, static_cast(account_id_prefix))}; } }; @@ -204,8 +206,8 @@ struct BlockId { } std::string to_str() const { char buffer[64]; - return std::string{buffer, - (unsigned)snprintf(buffer, 63, "(%d,%016llx,%u)", workchain, (unsigned long long)shard, seqno)}; + return std::string{buffer, (unsigned)snprintf(buffer, 63, "(%d,%016llx,%u)", workchain, + static_cast(shard), seqno)}; } }; @@ -282,7 +284,7 @@ struct BlockIdExt { BlockIdExt v; char rh[65]; char fh[65]; - auto r = sscanf(s.begin(), "(%d,%lx,%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh); + auto r = sscanf(s.begin(), "(%d,%" SCNu64 ",%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh); if (r < 5) { return td::Status::Error("failed to parse block id"); } diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 65c6f6f8fe..0e25dd6647 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -451,6 +451,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { int n = 16; int k = 10; + td::uint32 wallet_id = 7; std::vector private_keys; for (int i = 0; i < n; i++) { private_keys.push_back(td::Ed25519::generate_private_key().move_as_ok()); @@ -458,6 +459,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { auto ms = ton::MultisigWallet::create(); auto init_data = ms->create_init_data( + wallet_id, td::transform(private_keys, [](const auto& pk) { return pk.get_public_key().move_as_ok().as_octet_string(); }), k); ms = ton::MultisigWallet::create(init_data); @@ -472,7 +474,7 @@ void test_multisig(Client& client, const Wallet& giver_wallet) { ton::GenericAccount::store_int_message(icb, block::StdAddress::parse(giver_wallet.address).move_as_ok(), 1); icb.store_bytes("\0\0\0\0", 4); vm::CellString::store(icb, "Greatings from multisig", 35 * 8).ensure(); - ton::MultisigWallet::QueryBuilder qb(-1 - i, icb.finalize()); + ton::MultisigWallet::QueryBuilder qb(wallet_id, -1 - i, icb.finalize()); for (int i = 0; i < k - 1; i++) { qb.sign(i, private_keys[i]); } diff --git a/tonlib/tonlib/KeyValue.cpp b/tonlib/tonlib/KeyValue.cpp index ccf9a38372..5943ea4722 100644 --- a/tonlib/tonlib/KeyValue.cpp +++ b/tonlib/tonlib/KeyValue.cpp @@ -106,11 +106,7 @@ class KeyValueInmemory : public KeyValue { } private: - class Cmp : public std::less<> { - public: - using is_transparent = void; - }; - std::map map_; + std::map> map_; }; } // namespace detail diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index 542ecf000f..c110abbe22 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -648,12 +648,13 @@ td::Result Config::config_del_gc(ton::PublicKeyHash key) { class ValidatorElectionBidCreator : public td::actor::Actor { public: ValidatorElectionBidCreator(td::uint32 date, std::string addr, std::string wallet, std::string dir, - td::actor::ActorId engine, + std::vector old_keys, td::actor::ActorId engine, td::actor::ActorId keyring, td::Promise promise) : date_(date) , addr_(addr) , wallet_(wallet) , dir_(dir) + , old_keys_(std::move(old_keys)) , engine_(engine) , keyring_(keyring) , promise_(std::move(promise)) { @@ -661,6 +662,22 @@ class ValidatorElectionBidCreator : public td::actor::Actor { } void start_up() override { + if (old_keys_.size() > 0) { + CHECK(old_keys_.size() == 3); + + adnl_addr_ = ton::adnl::AdnlNodeIdShort{old_keys_[2]}; + perm_key_ = old_keys_[0]; + + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::abort_query, R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &ValidatorElectionBidCreator::got_perm_public_key, R.move_as_ok()); + } + }); + td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, perm_key_, std::move(P)); + return; + } auto pk1 = ton::PrivateKey{ton::privkeys::Ed25519::random()}; perm_key_full_ = pk1.compute_public_key(); perm_key_ = perm_key_full_.compute_short_id(); @@ -712,6 +729,11 @@ class ValidatorElectionBidCreator : public td::actor::Actor { ttl_, ig.get_promise()); } + void got_perm_public_key(ton::PublicKey pub) { + perm_key_full_ = pub; + updated_config(); + } + void updated_config() { auto codeR = td::read_file_str(dir_ + "/validator-elect-req.fif"); if (codeR.is_error()) { @@ -792,6 +814,7 @@ class ValidatorElectionBidCreator : public td::actor::Actor { std::string addr_; std::string wallet_; std::string dir_; + std::vector old_keys_; td::actor::ActorId engine_; td::actor::ActorId keyring_; @@ -899,28 +922,24 @@ void ValidatorEngine::alarm() { if (state_.not_null()) { bool need_write = false; + auto configR = state_->get_config_holder(); + configR.ensure(); + auto config = configR.move_as_ok(); + auto cur_t = config->get_validator_set_start_stop(0); + CHECK(cur_t.first > 0); + LOG(ERROR) << "curt: " << cur_t.first << " " << cur_t.second; + auto val_set = state_->get_total_validator_set(0); auto e = val_set->export_vector(); - std::set adnl_ids; - for (auto &el : e) { - adnl_ids.insert(ton::PublicKeyHash{el.addr}); - } std::set to_del; for (auto &val : config_.validators) { - if (val.second.expire_at < state_->get_unix_time() && - !val_set->is_validator(ton::NodeIdShort{val.first.bits256_value()})) { + bool is_validator = false; + if (val_set->is_validator(ton::NodeIdShort{val.first.bits256_value()})) { + is_validator = true; + } + if (!is_validator && val.second.election_date < cur_t.first && cur_t.first + 600 < state_->get_unix_time()) { to_del.insert(val.first); - } else { - std::set to_del_2; - for (auto &x : val.second.adnl_ids) { - if (x.second < state_->get_unix_time() && !adnl_ids.count(x.first)) { - to_del_2.insert(x.first); - } - } - for (auto &x : to_del_2) { - config_.config_del_validator_temp_key(val.first, x); - need_write = true; - } + continue; } } for (auto &x : to_del) { @@ -2699,9 +2718,23 @@ void ValidatorEngine::run_control_query(ton::ton_api::engine_validator_createEle return; } + std::vector v; + for (auto &x : config_.validators) { + if (x.second.election_date == static_cast(query.election_date_)) { + if (x.second.temp_keys.size() == 0 || x.second.adnl_ids.size() == 0) { + promise.set_value( + create_control_query_error(td::Status::Error(ton::ErrorCode::notready, "prev bid is partial"))); + return; + } + v.push_back(x.first); + v.push_back(x.second.temp_keys.begin()->first); + v.push_back(x.second.adnl_ids.begin()->first); + } + } + td::actor::create_actor("bidcreate", query.election_date_, query.election_addr_, - query.wallet_, fift_dir_, actor_id(this), keyring_.get(), - std::move(promise)) + query.wallet_, fift_dir_, std::move(v), actor_id(this), + keyring_.get(), std::move(promise)) .release(); } diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 7eef7bbce1..fe6663137e 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -45,9 +45,11 @@ void ArchiveManager::add_handle(BlockHandle handle, td::Promise promis update_handle(std::move(handle), std::move(promise)); return; } - auto p = get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(), - handle->unix_time(), handle->logical_time(), - handle->inited_is_key_block() && handle->is_key_block()); + auto p = handle->id().is_masterchain() + ? get_package_id_force(handle->masterchain_ref_block(), handle->id().shard_full(), handle->id().seqno(), + handle->unix_time(), handle->logical_time(), + handle->inited_is_key_block() && handle->is_key_block()) + : get_package_id(handle->masterchain_ref_block()); auto f = get_file_desc(handle->id().shard_full(), p, handle->id().seqno(), handle->unix_time(), handle->logical_time(), true); td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::add_handle, std::move(handle), std::move(promise)); @@ -248,7 +250,7 @@ void ArchiveManager::get_file_short_cont(FileReference ref_id, PackageId idx, td td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(ref_id), std::move(P)); } -void ArchiveManager::get_file(BlockHandle handle, FileReference ref_id, td::Promise promise) { +void ArchiveManager::get_file(ConstBlockHandle handle, FileReference ref_id, td::Promise promise) { if (handle->moved_to_archive()) { auto f = get_file_desc(handle->id().shard_full(), get_package_id(handle->masterchain_ref_block()), 0, 0, 0, false); if (f) { @@ -368,27 +370,53 @@ void ArchiveManager::check_persistent_state(BlockIdExt block_id, BlockIdExt mast } void ArchiveManager::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, - td::Promise promise) { + td::Promise promise) { auto f = get_file_desc_by_unix_time(account_id, ts, false); if (f) { - td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts, - std::move(promise)); + auto n = get_next_file_desc(f); + td::actor::ActorId aid; + if (n) { + aid = n->file_actor_id(); + } + auto P = td::PromiseCreator::lambda( + [aid, account_id, ts, promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok() || R.error().code() != ErrorCode::notready || aid.empty()) { + promise.set_result(std::move(R)); + } else { + td::actor::send_closure(aid, &ArchiveSlice::get_block_by_unix_time, account_id, ts, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_unix_time, account_id, ts, std::move(P)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "ts not in db")); } } -void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise) { +void ArchiveManager::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, + td::Promise promise) { auto f = get_file_desc_by_lt(account_id, lt, false); if (f) { - td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise)); + auto n = get_next_file_desc(f); + td::actor::ActorId aid; + if (n) { + aid = n->file_actor_id(); + } + auto P = td::PromiseCreator::lambda( + [aid, account_id, lt, promise = std::move(promise)](td::Result R) mutable { + if (R.is_ok() || R.error().code() != ErrorCode::notready || aid.empty()) { + promise.set_result(std::move(R)); + } else { + td::actor::send_closure(aid, &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_lt, account_id, lt, std::move(P)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "lt not in db")); } } void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { auto f = get_file_desc_by_seqno(account_id, seqno, false); if (f) { td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_block_by_seqno, account_id, seqno, @@ -398,7 +426,7 @@ void ArchiveManager::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeq } } -void ArchiveManager::delete_package(PackageId id) { +void ArchiveManager::delete_package(PackageId id, td::Promise promise) { auto key = create_serialize_tl_object(id.id, id.key, id.temp); std::string value; @@ -411,24 +439,27 @@ void ArchiveManager::delete_package(PackageId id) { auto x = R.move_as_ok(); if (x->deleted_) { + promise.set_value(td::Unit()); return; } auto &m = get_file_map(id); auto it = m.find(id); if (it == m.end() || it->second.deleted) { + promise.set_value(td::Unit()); return; } it->second.deleted = true; - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), id](td::Result R) { - R.ensure(); - td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id); - }); + auto P = td::PromiseCreator::lambda( + [SelfId = actor_id(this), id, promise = std::move(promise)](td::Result R) mutable { + R.ensure(); + td::actor::send_closure(SelfId, &ArchiveManager::deleted_package, id, std::move(promise)); + }); td::actor::send_closure(it->second.file_actor_id(), &ArchiveSlice::destroy, std::move(P)); } -void ArchiveManager::deleted_package(PackageId id) { +void ArchiveManager::deleted_package(PackageId id, td::Promise promise) { auto key = create_serialize_tl_object(id.id, id.key, id.temp); std::string value; @@ -441,6 +472,7 @@ void ArchiveManager::deleted_package(PackageId id) { auto x = R.move_as_ok(); if (x->deleted_) { + promise.set_value(td::Unit()); return; } x->deleted_ = true; @@ -453,6 +485,7 @@ void ArchiveManager::deleted_package(PackageId id) { CHECK(it != m.end()); CHECK(it->second.deleted); it->second.clear_actor_id(); + promise.set_value(td::Unit()); } void ArchiveManager::load_package(PackageId id) { @@ -690,6 +723,18 @@ ArchiveManager::FileDescription *ArchiveManager::get_file_desc_by_lt(AccountIdPr return nullptr; } +ArchiveManager::FileDescription *ArchiveManager::get_next_file_desc(FileDescription *f) { + auto &m = get_file_map(f->id); + auto it = m.find(f->id); + CHECK(it != m.end()); + it++; + if (it == m.end()) { + return nullptr; + } else { + return &it->second; + } +} + ArchiveManager::FileDescription *ArchiveManager::get_temp_file_desc_by_idx(PackageId idx) { auto it = temp_files_.find(idx); if (it != temp_files_.end()) { @@ -797,7 +842,7 @@ void ArchiveManager::run_gc(UnixTime ts) { vec.resize(vec.size() - 1, PackageId::empty(false, true)); for (auto &x : vec) { - delete_package(x); + delete_package(x, [](td::Unit) {}); } } @@ -841,7 +886,7 @@ void ArchiveManager::persistent_state_gc(FileHash last) { return; } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), hash](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ArchiveManager::got_gc_masterchain_handle, nullptr, hash); } else { @@ -852,7 +897,7 @@ void ArchiveManager::persistent_state_gc(FileHash last) { get_block_by_seqno(AccountIdPrefixFull{masterchainId, 0}, seqno, std::move(P)); } -void ArchiveManager::got_gc_masterchain_handle(BlockHandle handle, FileHash hash) { +void ArchiveManager::got_gc_masterchain_handle(ConstBlockHandle handle, FileHash hash) { bool to_del = false; if (!handle || !handle->inited_unix_time() || !handle->unix_time()) { to_del = true; @@ -881,7 +926,7 @@ PackageId ArchiveManager::get_temp_package_id_by_unixtime(UnixTime ts) const { } PackageId ArchiveManager::get_key_package_id(BlockSeqno seqno) const { - return PackageId{seqno - seqno % 200000, true, false}; + return PackageId{seqno - seqno % key_archive_size(), true, false}; } PackageId ArchiveManager::get_package_id(BlockSeqno seqno) const { @@ -896,7 +941,7 @@ PackageId ArchiveManager::get_package_id_force(BlockSeqno masterchain_seqno, Sha PackageId p = PackageId::empty(false, false); if (!is_key) { auto it = files_.upper_bound(PackageId{masterchain_seqno, false, false}); - p = PackageId{masterchain_seqno - (masterchain_seqno % 20000), false, false}; + p = PackageId{masterchain_seqno - (masterchain_seqno % archive_size()), false, false}; if (it != files_.begin()) { it--; if (p < it->first) { @@ -980,6 +1025,7 @@ void ArchiveManager::set_async_mode(bool mode, td::Promise promise) { } } } + } // namespace validator } // namespace ton diff --git a/validator/db/archive-manager.hpp b/validator/db/archive-manager.hpp index fc073b6b90..2f6e475211 100644 --- a/validator/db/archive-manager.hpp +++ b/validator/db/archive-manager.hpp @@ -48,7 +48,7 @@ class ArchiveManager : public td::actor::Actor { void get_key_block_proof(FileReference ref_id, td::Promise promise); void get_temp_file_short(FileReference ref_id, td::Promise promise); void get_file_short(FileReference ref_id, td::Promise promise); - void get_file(BlockHandle handle, FileReference ref_id, td::Promise promise); + void get_file(ConstBlockHandle handle, FileReference ref_id, td::Promise promise); void add_zero_state(BlockIdExt block_id, td::BufferSlice data, td::Promise promise); void add_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice data, @@ -60,12 +60,15 @@ class ArchiveManager : public td::actor::Actor { void check_persistent_state(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::Promise promise); void check_zero_state(BlockIdExt block_id, td::Promise promise); + //void truncate(BlockSeqno masterchain_seqno, td::Promise promise); + //void truncate_continue(BlockSeqno masterchain_seqno, td::Promise promise); + void run_gc(UnixTime ts); /* from LTDB */ - void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); - void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); - void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); + void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); + void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise); void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, @@ -77,6 +80,13 @@ class ArchiveManager : public td::actor::Actor { void commit_transaction(); void set_async_mode(bool mode, td::Promise promise); + static constexpr td::uint32 archive_size() { + return 20000; + } + static constexpr td::uint32 key_archive_size() { + return 200000; + } + private: struct FileDescription { struct Desc { @@ -113,8 +123,8 @@ class ArchiveManager : public td::actor::Actor { std::map perm_states_; void load_package(PackageId seqno); - void delete_package(PackageId seqno); - void deleted_package(PackageId seqno); + void delete_package(PackageId seqno, td::Promise promise); + void deleted_package(PackageId seqno, td::Promise promise); void get_handle_cont(BlockIdExt block_id, PackageId id, td::Promise promise); void get_handle_finish(BlockHandle handle, td::Promise promise); void get_file_short_cont(FileReference ref_id, PackageId idx, td::Promise promise); @@ -129,6 +139,7 @@ class ArchiveManager : public td::actor::Actor { FileDescription *get_file_desc_by_seqno(AccountIdPrefixFull shard, BlockSeqno seqno, bool key_block); FileDescription *get_file_desc_by_lt(AccountIdPrefixFull shard, LogicalTime lt, bool key_block); FileDescription *get_file_desc_by_unix_time(AccountIdPrefixFull shard, UnixTime ts, bool key_block); + FileDescription *get_next_file_desc(FileDescription *f); FileDescription *get_temp_file_desc_by_idx(PackageId idx); PackageId get_max_temp_file_desc_idx(); PackageId get_prev_temp_file_desc_idx(PackageId id); @@ -136,7 +147,7 @@ class ArchiveManager : public td::actor::Actor { void written_perm_state(FileReferenceShort id); void persistent_state_gc(FileHash last); - void got_gc_masterchain_handle(BlockHandle handle, FileHash hash); + void got_gc_masterchain_handle(ConstBlockHandle handle, FileHash hash); std::string db_root_; diff --git a/validator/db/archive-slice.cpp b/validator/db/archive-slice.cpp index b623415a21..ceccfc4ce1 100644 --- a/validator/db/archive-slice.cpp +++ b/validator/db/archive-slice.cpp @@ -204,6 +204,28 @@ void ArchiveSlice::get_handle(BlockIdExt block_id, td::Promise prom promise.set_value(std::move(handle)); } +void ArchiveSlice::get_temp_handle(BlockIdExt block_id, td::Promise promise) { + if (destroyed_) { + promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); + return; + } + CHECK(!key_blocks_only_); + std::string value; + auto R = kv_->get(get_db_key_block_info(block_id), value); + R.ensure(); + if (R.move_as_ok() == td::KeyValue::GetStatus::NotFound) { + promise.set_error(td::Status::Error(ErrorCode::notready, "handle not in archive slice")); + return; + } + auto E = create_block_handle(td::BufferSlice{value}); + E.ensure(); + auto handle = E.move_as_ok(); + if (!temp_) { + handle->set_handle_moved_to_archive(); + } + promise.set_value(std::move(handle)); +} + void ArchiveSlice::get_file(FileReference ref_id, td::Promise promise) { if (destroyed_) { promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); @@ -231,7 +253,7 @@ void ArchiveSlice::get_file(FileReference ref_id, td::Promise p void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, std::function compare_desc, std::function compare, bool exact, - td::Promise promise) { + td::Promise promise) { if (destroyed_) { promise.set_error(td::Status::Error(ErrorCode::notready, "package already gc'd")); return; @@ -281,7 +303,7 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, lseq = create_block_id(e->id_); l = x; } else { - get_handle(create_block_id(e->id_), std::move(promise)); + get_temp_handle(create_block_id(e->id_), std::move(promise)); return; } } @@ -299,7 +321,7 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, } if (block_id.is_valid() && ls + 1 == block_id.id.seqno) { if (!exact) { - get_handle(block_id, std::move(promise)); + get_temp_handle(block_id, std::move(promise)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); } @@ -307,13 +329,14 @@ void ArchiveSlice::get_block_common(AccountIdPrefixFull account_id, } } if (!exact && block_id.is_valid()) { - get_handle(block_id, std::move(promise)); + get_temp_handle(block_id, std::move(promise)); } else { promise.set_error(td::Status::Error(ErrorCode::notready, "ltdb: block not found")); } } -void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise) { +void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, + td::Promise promise) { return get_block_common( account_id, [lt](ton_api::db_lt_desc_value &w) { @@ -326,7 +349,7 @@ void ArchiveSlice::get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime l } void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { return get_block_common( account_id, [seqno](ton_api::db_lt_desc_value &w) { @@ -343,7 +366,7 @@ void ArchiveSlice::get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno } void ArchiveSlice::get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, - td::Promise promise) { + td::Promise promise) { return get_block_common( account_id, [ts](ton_api::db_lt_desc_value &w) { diff --git a/validator/db/archive-slice.hpp b/validator/db/archive-slice.hpp index b4e03e01db..45a40a12d1 100644 --- a/validator/db/archive-slice.hpp +++ b/validator/db/archive-slice.hpp @@ -35,16 +35,17 @@ class ArchiveSlice : public td::actor::Actor { void update_handle(BlockHandle handle, td::Promise promise); void add_file(FileReference ref_id, td::BufferSlice data, td::Promise promise); void get_handle(BlockIdExt block_id, td::Promise promise); + void get_temp_handle(BlockIdExt block_id, td::Promise promise); void get_file(FileReference ref_id, td::Promise promise); /* from LTDB */ - void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); - void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); - void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); + void get_block_by_unix_time(AccountIdPrefixFull account_id, UnixTime ts, td::Promise promise); + void get_block_by_lt(AccountIdPrefixFull account_id, LogicalTime lt, td::Promise promise); + void get_block_by_seqno(AccountIdPrefixFull account_id, BlockSeqno seqno, td::Promise promise); void get_block_common(AccountIdPrefixFull account_id, std::function compare_desc, std::function compare, bool exact, - td::Promise promise); + td::Promise promise); void get_slice(td::uint64 offset, td::uint32 limit, td::Promise promise); diff --git a/validator/db/package.cpp b/validator/db/package.cpp index e15767dfc6..e41c36883a 100644 --- a/validator/db/package.cpp +++ b/validator/db/package.cpp @@ -170,4 +170,8 @@ void Package::iterate(std::function open(std::string path, bool read_only = false, bool create = false); Package(td::FileFd fd); + Package(Package &&p) = default; + ~Package(); td::Status truncate(td::uint64 size); diff --git a/validator/db/rootdb.cpp b/validator/db/rootdb.cpp index 21cc196d01..6dfb971e1d 100644 --- a/validator/db/rootdb.cpp +++ b/validator/db/rootdb.cpp @@ -51,7 +51,7 @@ void RootDb::store_block_data(BlockHandle handle, td::Ref block, td:: std::move(P)); } -void RootDb::get_block_data(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_data(ConstBlockHandle handle, td::Promise> promise) { if (!handle->received()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -88,7 +88,7 @@ void RootDb::store_block_signatures(BlockHandle handle, td::Refserialize(), std::move(P)); } -void RootDb::get_block_signatures(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_signatures(ConstBlockHandle handle, td::Promise> promise) { if (!handle->inited_signatures() || handle->moved_to_archive()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -124,7 +124,7 @@ void RootDb::store_block_proof(BlockHandle handle, td::Ref proof, td::Pro std::move(P)); } -void RootDb::get_block_proof(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_proof(ConstBlockHandle handle, td::Promise> promise) { if (!handle->inited_proof()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -159,7 +159,7 @@ void RootDb::store_block_proof_link(BlockHandle handle, td::Ref proof proof->data(), std::move(P)); } -void RootDb::get_block_proof_link(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_proof_link(ConstBlockHandle handle, td::Promise> promise) { if (!handle->inited_proof_link()) { promise.set_error(td::Status::Error(ErrorCode::notready, "not in db")); } else { @@ -248,7 +248,7 @@ void RootDb::store_block_state(BlockHandle handle, td::Ref state, } } -void RootDb::get_block_state(BlockHandle handle, td::Promise> promise) { +void RootDb::get_block_state(ConstBlockHandle handle, td::Promise> promise) { if (handle->inited_state_boc()) { if (handle->deleted_state_boc()) { promise.set_error(td::Status::Error(ErrorCode::error, "state already gc'd")); @@ -323,15 +323,15 @@ void RootDb::apply_block(BlockHandle handle, td::Promise promise) { .release(); } -void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) { +void RootDb::get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_lt, account, lt, std::move(promise)); } -void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) { +void RootDb::get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_unix_time, account, ts, std::move(promise)); } -void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) { +void RootDb::get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) { td::actor::send_closure(archive_db_, &ArchiveManager::get_block_by_seqno, account, seqno, std::move(promise)); } diff --git a/validator/db/rootdb.hpp b/validator/db/rootdb.hpp index 373941e7cb..7c4b7fa976 100644 --- a/validator/db/rootdb.hpp +++ b/validator/db/rootdb.hpp @@ -41,17 +41,17 @@ class RootDb : public Db { void start_up() override; void store_block_data(BlockHandle handle, td::Ref block, td::Promise promise) override; - void get_block_data(BlockHandle handle, td::Promise> promise) override; + void get_block_data(ConstBlockHandle handle, td::Promise> promise) override; void store_block_signatures(BlockHandle handle, td::Ref data, td::Promise promise) override; - void get_block_signatures(BlockHandle handle, td::Promise> promise) override; + void get_block_signatures(ConstBlockHandle handle, td::Promise> promise) override; void store_block_proof(BlockHandle handle, td::Ref proof, td::Promise promise) override; - void get_block_proof(BlockHandle handle, td::Promise> promise) override; + void get_block_proof(ConstBlockHandle handle, td::Promise> promise) override; void store_block_proof_link(BlockHandle handle, td::Ref proof, td::Promise promise) override; - void get_block_proof_link(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_link(ConstBlockHandle handle, td::Promise> promise) override; void store_block_candidate(BlockCandidate candidate, td::Promise promise) override; void get_block_candidate(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, @@ -59,7 +59,7 @@ class RootDb : public Db { void store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) override; - void get_block_state(BlockHandle handle, td::Promise> promise) override; + void get_block_state(ConstBlockHandle handle, td::Promise> promise) override; void store_block_handle(BlockHandle handle, td::Promise promise) override; void get_block_handle(BlockIdExt id, td::Promise promise) override; @@ -82,9 +82,9 @@ class RootDb : public Db { void try_get_static_file(FileHash file_hash, td::Promise promise) override; void apply_block(BlockHandle handle, td::Promise promise) override; - void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; - void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) override; - void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) override; + void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) override; + void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) override; void update_init_masterchain_block(BlockIdExt block, td::Promise promise) override; void get_init_masterchain_block(td::Promise promise) override; diff --git a/validator/fabric.h b/validator/fabric.h index 41df565be9..55f4bfd3ed 100644 --- a/validator/fabric.h +++ b/validator/fabric.h @@ -38,6 +38,7 @@ td::Result> create_signature_set(td::BufferSlice sig_ td::Result> create_shard_state(BlockIdExt block_id, td::BufferSlice data); td::Result> create_shard_state(BlockIdExt block_id, td::Ref root_cell); td::Result create_block_handle(td::BufferSlice data); +td::Result create_temp_block_handle(td::BufferSlice data); BlockHandle create_empty_block_handle(BlockIdExt id); td::Result> create_ext_message(td::BufferSlice data); td::Result> create_ihr_message(td::BufferSlice data); diff --git a/validator/impl/config.cpp b/validator/impl/config.cpp index e8f1c83c53..8f496caa7b 100644 --- a/validator/impl/config.cpp +++ b/validator/impl/config.cpp @@ -53,6 +53,14 @@ td::Ref ConfigHolderQ::get_validator_set(ShardIdFull shard, UnixTi return Ref{true, cc_seqno, shard, std::move(nodes)}; } +std::pair ConfigHolderQ::get_validator_set_start_stop(int next) const { + if (!config_) { + LOG(ERROR) << "MasterchainStateQ::get_validator_set_start_stop() : no config"; + return {}; + } + return config_->get_validator_set_start_stop(next); +} + } // namespace validator } // namespace ton diff --git a/validator/impl/config.hpp b/validator/impl/config.hpp index 4fe82ee3fc..68982ed9f4 100644 --- a/validator/impl/config.hpp +++ b/validator/impl/config.hpp @@ -43,6 +43,7 @@ class ConfigHolderQ : public ConfigHolder { // if necessary, add more public methods providing interface to config_->...() td::Ref get_total_validator_set(int next) const override; // next = -1 -> prev, next = 0 -> cur td::Ref get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const override; + std::pair get_validator_set_start_stop(int next) const override; }; } // namespace validator diff --git a/validator/impl/fabric.cpp b/validator/impl/fabric.cpp index 1163089e48..4273748050 100644 --- a/validator/impl/fabric.cpp +++ b/validator/impl/fabric.cpp @@ -96,6 +96,10 @@ td::Result create_block_handle(td::BufferSlice data) { return ton::validator::BlockHandleImpl::create(std::move(data)); } +td::Result create_temp_block_handle(td::BufferSlice data) { + return ton::validator::BlockHandleImpl::create(std::move(data)); +} + BlockHandle create_empty_block_handle(BlockIdExt id) { return ton::validator::BlockHandleImpl::create_empty(id); } diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index 196397a940..b9f89f0cc6 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -167,6 +167,11 @@ void LiteQuery::start_up() { q.mode_ & 1 ? ton::create_block_id(q.target_block_) : ton::BlockIdExt{}, q.mode_); }, + [&](lite_api::liteServer_getValidatorStats& q) { + this->perform_getValidatorStats(ton::create_block_id(q.id_), q.mode_, q.limit_, + q.mode_ & 1 ? q.start_after_ : td::Bits256::zero(), + q.mode_ & 4 ? q.modified_after_ : 0); + }, [&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); })); } @@ -192,7 +197,7 @@ void LiteQuery::perform_getMasterchainInfo(int mode) { } td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -230,7 +235,7 @@ void LiteQuery::perform_getBlock(BlockIdExt blkid) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -256,7 +261,7 @@ void LiteQuery::perform_getBlockHeader(BlockIdExt blkid, int mode) { return; } td::actor::send_closure_later(manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid, mode](td::Result> res) { + [ Self = actor_id(this), blkid, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -371,7 +376,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { } if (blkid.id.seqno) { td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -381,7 +386,7 @@ void LiteQuery::perform_getState(BlockIdExt blkid) { }); } else { td::actor::send_closure_later(manager_, &ValidatorManager::get_zero_state, blkid, - [Self = actor_id(this), blkid](td::Result res) { + [ Self = actor_id(this), blkid ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -440,7 +445,7 @@ bool LiteQuery::request_mc_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -466,7 +471,7 @@ bool LiteQuery::request_mc_proof(BlockIdExt blkid, int mode) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_from_db_short, blkid, - [Self = actor_id(this), blkid, mode](td::Result> res) { + [ Self = actor_id(this), blkid, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof for "s + blkid.to_str() + " : ")); @@ -488,7 +493,7 @@ bool LiteQuery::request_mc_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -519,7 +524,7 @@ bool LiteQuery::request_block_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_shard_state_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load state for "s + blkid.to_str() + " : ")); @@ -541,7 +546,7 @@ bool LiteQuery::request_block_data(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_data_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load block "s + blkid.to_str() + " : ")); @@ -563,7 +568,7 @@ bool LiteQuery::request_proof_link(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_proof_link_from_db_short, blkid, - [Self = actor_id(this), blkid](td::Result> res) { + [ Self = actor_id(this), blkid ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load proof link for "s + blkid.to_str() + " : ")); @@ -588,7 +593,7 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) { ++pending_; td::actor::send_closure_later( manager_, &ValidatorManager::get_zero_state, blkid, - [Self = actor_id(this), blkid](td::Result res) { + [ Self = actor_id(this), blkid ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error_prefix("cannot load zerostate of "s + blkid.to_str() + " : ")); @@ -632,7 +637,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, LOG(INFO) << "sending a get_top_masterchain_state_block query to manager"; td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this)](td::Result, BlockIdExt>> res) -> void { + [Self = actor_id(this)](td::Result, BlockIdExt>> res)->void { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1067,14 +1072,14 @@ void LiteQuery::continue_getTransactions(unsigned remaining, bool exact) { << " " << trans_lt_; td::actor::send_closure_later( manager_, &ValidatorManager::get_block_by_lt_from_db, ton::extract_addr_prefix(acc_workchain_, acc_addr_), - trans_lt_, [Self = actor_id(this), remaining, manager = manager_](td::Result res) { + trans_lt_, [ Self = actor_id(this), remaining, manager = manager_ ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), ton::BlockIdExt{}); } else { auto handle = res.move_as_ok(); LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, - [Self, blkid = handle->id(), remaining](td::Result> res) { + [ Self, blkid = handle->id(), remaining ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_getTransactions, res.move_as_error(), blkid); @@ -1141,7 +1146,7 @@ void LiteQuery::perform_getShardInfo(BlockIdExt blkid, ShardIdFull shard, bool e void LiteQuery::perform_getConfigParams(BlockIdExt blkid, int mode, std::vector param_list) { LOG(INFO) << "started a getConfigParams(" << blkid.to_str() << ", " << mode << ", ) liteserver query"; - set_continuation([this, mode, param_list = std::move(param_list)]() mutable { + set_continuation([ this, mode, param_list = std::move(param_list) ]() mutable { continue_getConfigParams(mode, std::move(param_list)); }); request_mc_block_data_state(blkid); @@ -1294,14 +1299,14 @@ void LiteQuery::perform_lookupBlock(BlockId blkid, int mode, LogicalTime lt, Uni LOG(INFO) << "performing a lookupBlock(" << blkid.to_str() << ", " << mode << ", " << lt << ", " << utime << ") query"; auto P = td::PromiseCreator::lambda( - [Self = actor_id(this), manager = manager_, mode = (mode >> 4)](td::Result res) { + [ Self = actor_id(this), manager = manager_, mode = (mode >> 4) ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { auto handle = res.move_as_ok(); LOG(DEBUG) << "requesting data for block " << handle->id().to_str(); td::actor::send_closure_later(manager, &ValidatorManager::get_block_data_from_db, handle, - [Self, blkid = handle->id(), mode](td::Result> res) { + [ Self, blkid = handle->id(), mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1449,7 +1454,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, if (mode & 0x1000) { BlockIdExt bblk = (from.seqno() > to.seqno()) ? from : to; td::actor::send_closure_later(manager_, &ValidatorManager::get_shard_state_from_db_short, bblk, - [Self = actor_id(this), from, to, bblk, mode](td::Result> res) { + [ Self = actor_id(this), from, to, bblk, mode ](td::Result> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1461,7 +1466,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, } else { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), from, to, mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), from, to, mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1474,7 +1479,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, } else if (mode & 2) { td::actor::send_closure_later( manager_, &ton::validator::ValidatorManager::get_top_masterchain_state_block, - [Self = actor_id(this), from, mode](td::Result, BlockIdExt>> res) { + [ Self = actor_id(this), from, mode ](td::Result, BlockIdExt>> res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1485,7 +1490,7 @@ void LiteQuery::perform_getBlockProof(ton::BlockIdExt from, ton::BlockIdExt to, }); } else { td::actor::send_closure_later(manager_, &ton::validator::ValidatorManager::get_shard_client_state, false, - [Self = actor_id(this), from, mode](td::Result res) { + [ Self = actor_id(this), from, mode ](td::Result res) { if (res.is_error()) { td::actor::send_closure(Self, &LiteQuery::abort_query, res.move_as_error()); } else { @@ -1873,5 +1878,72 @@ bool LiteQuery::finish_proof_chain(ton::BlockIdExt id) { } } +void LiteQuery::perform_getValidatorStats(BlockIdExt blkid, int mode, int count, Bits256 start_after, + UnixTime min_utime) { + LOG(INFO) << "started a getValidatorStats(" << blkid.to_str() << ", " << mode << ", " << count << ", " + << start_after.to_hex() << ", " << min_utime << ") liteserver query"; + if (count <= 0) { + fatal_error("requested entry count limit must be positive"); + return; + } + if ((mode & ~7) != 0) { + fatal_error("unknown flags set in mode"); + return; + } + set_continuation([this, mode, count, min_utime, start_after]() { + continue_getValidatorStats(mode, count, start_after, min_utime); + }); + request_mc_block_data_state(blkid); +} + +void LiteQuery::continue_getValidatorStats(int mode, int limit, Bits256 start_after, UnixTime min_utime) { + LOG(INFO) << "completing getValidatorStats(" << base_blk_id_.to_str() << ", " << mode << ", " << limit << ", " + << start_after.to_hex() << ", " << min_utime << ") liteserver query"; + Ref proof1; + if (!make_mc_state_root_proof(proof1)) { + return; + } + vm::MerkleProofBuilder mpb{mc_state_->root_cell()}; + int count; + bool complete = false, allow_eq = (mode & 3) != 1; + limit = std::min(limit, 1000); + try { + auto dict = block::get_block_create_stats_dict(mpb.root()); + if (!dict) { + fatal_error("cannot extract block create stats from mc state"); + return; + } + for (count = 0; count < limit; count++) { + auto v = dict->lookup_nearest_key(start_after, true, allow_eq); + if (v.is_null()) { + complete = true; + break; + } + if (!block::gen::t_CreatorStats.validate_csr(std::move(v))) { + fatal_error("invalid CreatorStats record with key "s + start_after.to_hex()); + return; + } + allow_eq = false; + } + } catch (vm::VmError& err) { + fatal_error("error while traversing required block create stats records: "s + err.get_msg()); + return; + } + auto res1 = vm::std_boc_serialize(std::move(proof1)); + if (res1.is_error()) { + fatal_error("cannot serialize Merkle proof : "s + res1.move_as_error().to_string()); + return; + } + auto res2 = mpb.extract_proof_boc(); + if (res2.is_error()) { + fatal_error("cannot serialize Merkle proof : "s + res2.move_as_error().to_string()); + return; + } + LOG(INFO) << "getValidatorStats() query completed"; + auto b = ton::create_serialize_tl_object( + mode & 0xff, ton::create_tl_lite_block_id(base_blk_id_), count, complete, res1.move_as_ok(), res2.move_as_ok()); + finish_query(std::move(b)); +} + } // namespace validator } // namespace ton diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 9118ec0a8c..36fc9a8092 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -114,6 +114,8 @@ class LiteQuery : public td::actor::Actor { void perform_getBlockProof(BlockIdExt from, BlockIdExt to, int mode); void continue_getBlockProof(BlockIdExt from, BlockIdExt to, int mode, BlockIdExt baseblk, Ref state); + void perform_getValidatorStats(BlockIdExt blkid, int mode, int count, Bits256 start_after, UnixTime min_utime); + void continue_getValidatorStats(int mode, int limit, Bits256 start_after, UnixTime min_utime); bool construct_proof_chain(BlockIdExt id); bool construct_proof_link_forward(ton::BlockIdExt cur, ton::BlockIdExt next); bool construct_proof_link_forward_cont(ton::BlockIdExt cur, ton::BlockIdExt next); diff --git a/validator/impl/shard.hpp b/validator/impl/shard.hpp index b067da14ce..2e4b368a43 100644 --- a/validator/impl/shard.hpp +++ b/validator/impl/shard.hpp @@ -139,7 +139,7 @@ class MasterchainStateQ : public MasterchainState, public ShardStateQ { std::shared_ptr get_config() const { return config_; } - td::Result> get_key_block_config() const override { + td::Result> get_config_holder() const override { if (!config_) { return td::Status::Error(ErrorCode::notready, "config not found"); } else { diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp index 0f16a3b9bb..b8737873e4 100644 --- a/validator/impl/validate-query.cpp +++ b/validator/impl/validate-query.cpp @@ -5086,7 +5086,7 @@ bool ValidateQuery::check_mc_state_extra() { } // block_create_stats:(flags . 0)?BlockCreateStats if (new_extra.r1.flags & 1) { - block::gen::BlockCreateStats::Record rec; + block::gen::BlockCreateStats::Record_block_create_stats rec; if (!tlb::csr_unpack(new_extra.r1.block_create_stats, rec)) { return reject_query("cannot unpack BlockCreateStats in the new masterchain state"); } diff --git a/validator/interfaces/block-handle.h b/validator/interfaces/block-handle.h index 00ecd8abd6..a0ed5f2ae3 100644 --- a/validator/interfaces/block-handle.h +++ b/validator/interfaces/block-handle.h @@ -107,6 +107,7 @@ struct BlockHandleInterface { }; using BlockHandle = std::shared_ptr; +using ConstBlockHandle = std::shared_ptr; } // namespace validator diff --git a/validator/interfaces/config.h b/validator/interfaces/config.h index fba2024999..06368e4b55 100644 --- a/validator/interfaces/config.h +++ b/validator/interfaces/config.h @@ -35,6 +35,7 @@ class ConfigHolder : public td::CntObject { virtual td::Ref get_total_validator_set(int next) const = 0; // next = -1 -> prev, next = 0 -> cur virtual td::Ref get_validator_set(ShardIdFull shard, UnixTime utime, CatchainSeqno seqno) const = 0; + virtual std::pair get_validator_set_start_stop(int next) const = 0; }; } // namespace validator diff --git a/validator/interfaces/db.h b/validator/interfaces/db.h index 632b0a9cf3..704272e895 100644 --- a/validator/interfaces/db.h +++ b/validator/interfaces/db.h @@ -31,17 +31,17 @@ class Db : public td::actor::Actor { virtual ~Db() = default; virtual void store_block_data(BlockHandle handle, td::Ref data, td::Promise promise) = 0; - virtual void get_block_data(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_data(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_signatures(BlockHandle handle, td::Ref data, td::Promise promise) = 0; - virtual void get_block_signatures(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_signatures(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_proof(BlockHandle handle, td::Ref proof, td::Promise promise) = 0; - virtual void get_block_proof(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_proof_link(BlockHandle handle, td::Ref proof, td::Promise promise) = 0; - virtual void get_block_proof_link(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof_link(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_block_candidate(BlockCandidate candidate, td::Promise promise) = 0; virtual void get_block_candidate(ton::PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, @@ -49,7 +49,7 @@ class Db : public td::actor::Actor { virtual void store_block_state(BlockHandle handle, td::Ref state, td::Promise> promise) = 0; - virtual void get_block_state(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_state(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void store_persistent_state_file(BlockIdExt block_id, BlockIdExt masterchain_block_id, td::BufferSlice state, td::Promise promise) = 0; @@ -72,9 +72,11 @@ class Db : public td::actor::Actor { virtual void get_block_handle(BlockIdExt id, td::Promise promise) = 0; virtual void apply_block(BlockHandle handle, td::Promise promise) = 0; - virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) = 0; - virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, td::Promise promise) = 0; - virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, td::Promise promise) = 0; + virtual void get_block_by_lt(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) = 0; + virtual void get_block_by_unix_time(AccountIdPrefixFull account, UnixTime ts, + td::Promise promise) = 0; + virtual void get_block_by_seqno(AccountIdPrefixFull account, BlockSeqno seqno, + td::Promise promise) = 0; virtual void update_init_masterchain_block(BlockIdExt block, td::Promise promise) = 0; virtual void get_init_masterchain_block(td::Promise promise) = 0; diff --git a/validator/interfaces/shard.h b/validator/interfaces/shard.h index 33b98c1e61..eab3639976 100644 --- a/validator/interfaces/shard.h +++ b/validator/interfaces/shard.h @@ -79,7 +79,7 @@ class MasterchainState : virtual public ShardState { virtual bool get_old_mc_block_id(ton::BlockSeqno seqno, ton::BlockIdExt& blkid, ton::LogicalTime* end_lt = nullptr) const = 0; virtual bool check_old_mc_block_id(const ton::BlockIdExt& blkid, bool strict = false) const = 0; - virtual td::Result> get_key_block_config() const = 0; + virtual td::Result> get_config_holder() const = 0; virtual td::Status prepare() { return td::Status::OK(); } diff --git a/validator/manager-disk.cpp b/validator/manager-disk.cpp index 7c9e2e6a4d..564dc6b82f 100644 --- a/validator/manager-disk.cpp +++ b/validator/manager-disk.cpp @@ -541,7 +541,7 @@ void ValidatorManagerImpl::complete_ihr_messages(std::vector t std::vector to_delete) { } -void ValidatorManagerImpl::get_block_data_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_data, handle, std::move(promise)); } @@ -558,7 +558,7 @@ void ValidatorManagerImpl::get_block_data_from_db_short(BlockIdExt block_id, td: get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_shard_state_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_state, handle, std::move(promise)); } @@ -582,7 +582,7 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise)); } -void ValidatorManagerImpl::get_block_proof_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise)); } @@ -599,7 +599,8 @@ void ValidatorManagerImpl::get_block_proof_from_db_short(BlockIdExt block_id, td get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_link_from_db(ConstBlockHandle handle, + td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof_link, std::move(handle), std::move(promise)); } @@ -618,17 +619,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i } void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise)); } void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise)); } void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise)); } diff --git a/validator/manager-disk.hpp b/validator/manager-disk.hpp index dbb85b2c3f..f65617f4e5 100644 --- a/validator/manager-disk.hpp +++ b/validator/manager-disk.hpp @@ -191,22 +191,23 @@ class ValidatorManagerImpl : public ValidatorManager { //void set_first_block(ZeroStateIdExt state, BlockIdExt block, td::Promise promise) override; void set_next_block(BlockIdExt prev, BlockIdExt next, td::Promise promise) override; - void get_block_data_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_data_from_db_short(BlockIdExt block_id, td::Promise> promise) override; - void get_shard_state_from_db(BlockHandle handle, td::Promise> promise) override; + void get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) override; void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; - void get_block_proof_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, + td::Promise promise) override; void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) override; + td::Promise promise) override; void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) override; + td::Promise promise) override; // get block handle declared in parent class void write_handle(BlockHandle handle, td::Promise promise) override; diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index 80366875c3..fce822656e 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -461,9 +461,9 @@ void ValidatorManagerMasterchainStarter::got_hardforks(std::vector v return; } - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), db = db_](td::Result R) { R.ensure(); - td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_handle, R.move_as_ok()); + td::actor::send_closure(SelfId, &ValidatorManagerMasterchainStarter::got_truncate_block_id, R.move_as_ok()->id()); }); td::actor::send_closure(db_, &Db::get_block_by_seqno, AccountIdPrefixFull{masterchainId, 0}, b.seqno() - 1, std::move(P)); diff --git a/validator/manager.cpp b/validator/manager.cpp index 17bf449aae..286baca49c 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -846,7 +846,7 @@ void ValidatorManagerImpl::complete_ihr_messages(std::vector t } } -void ValidatorManagerImpl::get_block_data_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_data, std::move(handle), std::move(promise)); } @@ -863,7 +863,7 @@ void ValidatorManagerImpl::get_block_data_from_db_short(BlockIdExt block_id, td: get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_shard_state_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_state, handle, std::move(promise)); } @@ -887,7 +887,7 @@ void ValidatorManagerImpl::get_block_candidate_from_db(PublicKey source, BlockId td::actor::send_closure(db_, &Db::get_block_candidate, source, id, collated_data_file_hash, std::move(promise)); } -void ValidatorManagerImpl::get_block_proof_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) { td::actor::send_closure(db_, &Db::get_block_proof, std::move(handle), std::move(promise)); } @@ -904,7 +904,8 @@ void ValidatorManagerImpl::get_block_proof_from_db_short(BlockIdExt block_id, td get_block_handle(block_id, false, std::move(P)); } -void ValidatorManagerImpl::get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) { +void ValidatorManagerImpl::get_block_proof_link_from_db(ConstBlockHandle handle, + td::Promise> promise) { if (handle->inited_proof_link()) { td::actor::send_closure(db_, &Db::get_block_proof_link, std::move(handle), std::move(promise)); } else if (handle->inited_proof()) { @@ -937,17 +938,17 @@ void ValidatorManagerImpl::get_block_proof_link_from_db_short(BlockIdExt block_i } void ValidatorManagerImpl::get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_lt, account, lt, std::move(promise)); } void ValidatorManagerImpl::get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_unix_time, account, ts, std::move(promise)); } void ValidatorManagerImpl::get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) { + td::Promise promise) { td::actor::send_closure(db_, &Db::get_block_by_seqno, account, seqno, std::move(promise)); } @@ -1455,6 +1456,8 @@ bool ValidatorManagerImpl::out_of_sync() { if (validator_groups_.size() > 0 && last_known_key_block_handle_->id().seqno() <= last_masterchain_seqno_) { return false; } + LOG(INFO) << "groups=" << validator_groups_.size() << " seqno=" << last_known_key_block_handle_->id().seqno() + << " our_seqno=" << last_masterchain_seqno_; return true; } @@ -1475,7 +1478,8 @@ void ValidatorManagerImpl::download_next_archive() { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { LOG(INFO) << "failed to download archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); }, + td::Timestamp::in(2.0)); } else { td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); } @@ -1489,7 +1493,8 @@ void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { LOG(INFO) << "failed to check downloaded archive slice: " << R.error(); - td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); + delay_action([SelfId]() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::download_next_archive); }, + td::Timestamp::in(2.0)); } else { td::actor::send_closure(SelfId, &ValidatorManagerImpl::checked_archive_slice, R.move_as_ok()); } @@ -1504,9 +1509,14 @@ void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) CHECK(seqno.size() == 2); LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; CHECK(seqno[0] <= last_masterchain_seqno_); + CHECK(seqno[1] <= seqno[0]); BlockIdExt b; - CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b)); + if (seqno[1] < last_masterchain_seqno_) { + CHECK(last_masterchain_state_->get_old_mc_block_id(seqno[1], b)); + } else { + b = last_masterchain_block_id_; + } auto P = td::PromiseCreator::lambda( [SelfId = actor_id(this), db = db_.get(), client = shard_client_.get()](td::Result R) { diff --git a/validator/manager.hpp b/validator/manager.hpp index b0c64c96d1..e085240b4a 100644 --- a/validator/manager.hpp +++ b/validator/manager.hpp @@ -385,22 +385,23 @@ class ValidatorManagerImpl : public ValidatorManager { void set_next_block(BlockIdExt prev, BlockIdExt next, td::Promise promise) override; - void get_block_data_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_data_from_db_short(BlockIdExt block_id, td::Promise> promise) override; - void get_shard_state_from_db(BlockHandle handle, td::Promise> promise) override; + void get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) override; void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) override; - void get_block_proof_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) override; + void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) override; void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) override; - void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, td::Promise promise) override; + void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, + td::Promise promise) override; void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) override; + td::Promise promise) override; void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) override; + td::Promise promise) override; // get block handle declared in parent class void write_handle(BlockHandle handle, td::Promise promise) override; diff --git a/validator/validate-broadcast.cpp b/validator/validate-broadcast.cpp index 2d0fbdab31..1aa025e3a8 100644 --- a/validator/validate-broadcast.cpp +++ b/validator/validate-broadcast.cpp @@ -110,7 +110,7 @@ void ValidateBroadcast::start_up() { } else if (key_block_seqno == last_masterchain_state_->get_seqno()) { got_key_block_handle(last_masterchain_block_handle_); } else { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { if (R.is_error()) { td::actor::send_closure(SelfId, &ValidateBroadcast::abort_query, R.move_as_error_prefix("cannot find reference key block id: ")); @@ -138,7 +138,7 @@ void ValidateBroadcast::got_key_block_id(BlockIdExt block_id) { td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, block_id, false, std::move(P)); } -void ValidateBroadcast::got_key_block_handle(BlockHandle handle) { +void ValidateBroadcast::got_key_block_handle(ConstBlockHandle handle) { if (handle->id().seqno() == 0) { auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { if (R.is_error()) { @@ -183,7 +183,7 @@ void ValidateBroadcast::got_key_block_proof_link(td::Ref key_proof_li void ValidateBroadcast::got_zero_state(td::Ref state) { zero_state_ = state; - auto confR = state->get_key_block_config(); + auto confR = state->get_config_holder(); if (confR.is_error()) { abort_query(confR.move_as_error_prefix("failed to extract config from zero state: ")); return; diff --git a/validator/validate-broadcast.hpp b/validator/validate-broadcast.hpp index 4cb8ba4913..ff42f63546 100644 --- a/validator/validate-broadcast.hpp +++ b/validator/validate-broadcast.hpp @@ -65,7 +65,7 @@ class ValidateBroadcast : public td::actor::Actor { void start_up() override; void got_key_block_id(BlockIdExt block_id); - void got_key_block_handle(BlockHandle block_handle); + void got_key_block_handle(ConstBlockHandle block_handle); void got_key_block_proof_link(td::Ref proof_link); void got_zero_state(td::Ref state); void check_signatures_common(td::Ref conf); diff --git a/validator/validator.h b/validator/validator.h index 5f6a377fec..f9f3761137 100644 --- a/validator/validator.h +++ b/validator/validator.h @@ -176,23 +176,23 @@ class ValidatorManagerInterface : public td::actor::Actor { virtual void get_download_token(size_t download_size, td::uint32 priority, td::Timestamp timeout, td::Promise> promise) = 0; - virtual void get_block_data_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_data_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_block_data_from_db_short(BlockIdExt block_id, td::Promise> promise) = 0; virtual void get_block_candidate_from_db(PublicKey source, BlockIdExt id, FileHash collated_data_file_hash, td::Promise promise) = 0; - virtual void get_shard_state_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_shard_state_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_shard_state_from_db_short(BlockIdExt block_id, td::Promise> promise) = 0; - virtual void get_block_proof_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_block_proof_from_db_short(BlockIdExt id, td::Promise> promise) = 0; - virtual void get_block_proof_link_from_db(BlockHandle handle, td::Promise> promise) = 0; + virtual void get_block_proof_link_from_db(ConstBlockHandle handle, td::Promise> promise) = 0; virtual void get_block_proof_link_from_db_short(BlockIdExt id, td::Promise> promise) = 0; virtual void get_block_by_lt_from_db(AccountIdPrefixFull account, LogicalTime lt, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_block_by_unix_time_from_db(AccountIdPrefixFull account, UnixTime ts, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_block_by_seqno_from_db(AccountIdPrefixFull account, BlockSeqno seqno, - td::Promise promise) = 0; + td::Promise promise) = 0; virtual void get_archive_id(BlockSeqno masterchain_seqno, td::Promise promise) = 0; virtual void get_archive_slice(td::uint64 archive_id, td::uint64 offset, td::uint32 limit, From ceaed40ac4871ffaeb5b493586ab6f0899fd490d Mon Sep 17 00:00:00 2001 From: ton Date: Thu, 5 Dec 2019 16:51:51 +0400 Subject: [PATCH 023/667] updated fift + bugfixes --- crypto/block/block.tlb | 44 ++++++++++++ crypto/fift/IntCtx.cpp | 5 ++ crypto/fift/IntCtx.h | 5 ++ crypto/fift/lib/Fift.fif | 4 ++ crypto/fift/lib/GetOpt.fif | 60 +++++++++++----- crypto/fift/lib/TonUtil.fif | 2 + crypto/smartcont/config-code.fc | 61 ++++++++++++----- crypto/smartcont/highload-wallet-v2.fif | 39 ++++++++--- crypto/smartcont/highload-wallet.fif | 39 ++++++++--- crypto/smartcont/new-wallet-v3.fif | 5 +- crypto/smartcont/show-addr.fif | 2 +- crypto/smartcont/update-config-smc.fif | 5 +- crypto/smartcont/wallet-v2.fif | 48 +++++++++---- crypto/smartcont/wallet-v3.fif | 47 +++++++++---- crypto/smartcont/wallet.fif | 23 +++++-- crypto/test/test-smartcont.cpp | 4 +- crypto/tl/tlbc-gen-cpp.cpp | 6 +- crypto/tl/tlblib.hpp | 3 + crypto/vm/cells/CellBuilder.h | 4 ++ crypto/vm/cells/CellSlice.cpp | 11 +++ crypto/vm/cells/CellSlice.h | 1 + crypto/vm/continuation.cpp | 84 +++++++++++++++++++++++ crypto/vm/continuation.h | 37 +++++++--- crypto/vm/stack.cpp | 87 ++++++++++++++++++++++++ crypto/vm/stack.hpp | 3 + ton/ton-types.h | 2 +- validator-engine/validator-engine.cpp | 1 - validator/interfaces/validator-manager.h | 6 +- 28 files changed, 530 insertions(+), 108 deletions(-) diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 074a5d4bc6..65537a1abe 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -683,3 +683,47 @@ top_block_descr#d5 proof_for:BlockIdExt signatures:(Maybe ^BlockSignatures) // COLLATED DATA // top_block_descr_set#4ac789f3 collection:(HashmapE 96 ^TopBlockDescr) = TopBlockDescrSet; + +// +// TVM REFLECTION +// +vm_stk_null#00 = VmStackValue; +vm_stk_tinyint#01 value:int64 = VmStackValue; +vm_stk_int#0201_ value:int257 = VmStackValue; +vm_stk_nan#02ff = VmStackValue; +vm_stk_cell#03 cell:^Cell = VmStackValue; +_ cell:^Cell st_bits:(## 10) end_bits:(## 10) { st_bits <= end_bits } + st_ref:(#<= 4) end_ref:(#<= 4) { st_ref <= end_ref } = VmCellSlice; +vm_stk_slice#04 _:VmCellSlice = VmStackValue; +vm_stk_builder#05 cell:^Cell = VmStackValue; +vm_stk_cont#06 cont:VmCont = VmStackValue; +vm_tupref_nil$_ = VmTupleRef 0; +vm_tupref_single$_ entry:^VmStackValue = VmTupleRef 1; +vm_tupref_any$_ {n:#} ref:^(VmTuple (n + 2)) = VmTupleRef (n + 2); +vm_tuple_nil$_ = VmTuple 0; +vm_tuple_tcons$_ {n:#} head:(VmTupleRef n) tail:^VmStackValue = VmTuple (n + 1); +vm_stk_tuple#07 len:(## 16) data:(VmTuple len) = VmStackValue; + +vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack; +vm_stk_cons#_ {n:#} rest:^(VmStackList n) tos:VmStackValue = VmStackList (n + 1); +vm_stk_nil#_ = VmStackList 0; + +_ cregs:(HashmapE 4 VmStackValue) = VmSaveList; +gas_limits#_ remaining:int64 _:^[ max_limit:int64 cur_limit:int64 credit:int64 ] + = VmGasLimits; +_ libraries:(HashmapE 256 ^Cell) = VmLibraries; + +vm_ctl_data$_ nargs:int14 stack:(Maybe VmStack) save:VmSaveList +cp:int16 = VmControlData; +vmc_std$00 cdata:VmControlData code:VmCellSlice = VmCont; +vmc_envelope$01 cdata:VmControlData next:^VmCont = VmCont; +vmc_quit$1000 exit_code:int32 = VmCont; +vmc_quit_exc$1001 = VmCont; +vmc_repeat$10100 count:uint63 body:^VmCont after:^VmCont = VmCont; +vmc_until$110000 body:^VmCont after:^VmCont = VmCont; +vmc_again$110001 body:^VmCont = VmCont; +vmc_while_cond$110010 cond:^VmCont body:^VmCont +after:^VmCont = VmCont; +vmc_while_body$110011 cond:^VmCont body:^VmCont +after:^VmCont = VmCont; +vmc_pushint$1111 value:int32 next:^VmCont = VmCont; diff --git a/crypto/fift/IntCtx.cpp b/crypto/fift/IntCtx.cpp index f2da340973..a091c04955 100644 --- a/crypto/fift/IntCtx.cpp +++ b/crypto/fift/IntCtx.cpp @@ -71,6 +71,7 @@ IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string std::istream* new_input_stream) : ctx(_ctx) , old_line_no(_ctx.line_no) + , old_need_line(_ctx.need_line) , old_filename(_ctx.filename) , old_current_dir(_ctx.currentd_dir) , old_input_stream(_ctx.input_stream) @@ -87,6 +88,7 @@ IntCtx::Savepoint::Savepoint(IntCtx& _ctx, std::string new_filename, std::string IntCtx::Savepoint::~Savepoint() { ctx.line_no = old_line_no; + ctx.need_line = old_need_line; ctx.filename = old_filename; ctx.currentd_dir = old_current_dir; ctx.input_stream = old_input_stream; @@ -99,6 +101,7 @@ bool IntCtx::load_next_line() { if (!std::getline(*input_stream, str)) { return false; } + need_line = false; if (!str.empty() && str.back() == '\r') { str.pop_back(); } @@ -111,6 +114,7 @@ bool IntCtx::is_sb() const { } td::Slice IntCtx::scan_word_to(char delim, bool err_endl) { + load_next_line_ifreq(); auto ptr = input_ptr; while (*ptr && *ptr != delim) { ptr++; @@ -121,6 +125,7 @@ td::Slice IntCtx::scan_word_to(char delim, bool err_endl) { } else if (err_endl && delim) { throw IntError{std::string{"end delimiter `"} + delim + "` not found"}; } else { + need_line = true; std::swap(ptr, input_ptr); return td::Slice{ptr, input_ptr}; } diff --git a/crypto/fift/IntCtx.h b/crypto/fift/IntCtx.h index 1adb8f325a..b2725cdae8 100644 --- a/crypto/fift/IntCtx.h +++ b/crypto/fift/IntCtx.h @@ -68,6 +68,7 @@ struct IntCtx { int state{0}; int include_depth{0}; int line_no{0}; + bool need_line{true}; std::string filename; std::string currentd_dir; std::istream* input_stream{nullptr}; @@ -116,6 +117,9 @@ struct IntCtx { } bool load_next_line(); + bool load_next_line_ifreq() { + return need_line && load_next_line(); + } bool is_sb() const; @@ -126,6 +130,7 @@ struct IntCtx { class Savepoint { IntCtx& ctx; int old_line_no; + bool old_need_line; std::string old_filename; std::string old_current_dir; std::istream* old_input_stream; diff --git a/crypto/fift/lib/Fift.fif b/crypto/fift/lib/Fift.fif index 724ef81281..4a16aca661 100644 --- a/crypto/fift/lib/Fift.fif +++ b/crypto/fift/lib/Fift.fif @@ -112,3 +112,7 @@ variable base { bl word atom 1 'nop } ::_ ` { hole dup 1 { @ execute } does create } : recursive { 0 { 1+ dup 1 ' $() does over (.) "$" swap $+ 0 (create) } rot times drop } : :$1..n +{ 10 hold } : +cr +{ 9 hold } : +tab +{ "" swap { 0 word 2dup $cmp } { rot swap $+ +cr swap } while 2drop } : scan-until-word +{ 0 word -trailing scan-until-word 1 'nop } ::_ $<< diff --git a/crypto/fift/lib/GetOpt.fif b/crypto/fift/lib/GetOpt.fif index ee8a59afca..d354dd3f18 100644 --- a/crypto/fift/lib/GetOpt.fif +++ b/crypto/fift/lib/GetOpt.fif @@ -33,12 +33,16 @@ recursive list-delete-range { } : list-find-opt // ( -- s i or 0 ) finds first option in cmdline args { $* @ list-find-opt } : first-opt -// ( s t -- ? ) checks whether short/long option s matches description t -{ third $= } : short-option-matches ' second : get-opt-flags ' first : get-opt-exec +// ( s t -- ? ) checks whether short/long option s matches description t +{ third $= } : short-option-matches { dup get-opt-flags 4 and 0= 3 + [] $= } : long-option-matches +// ( t -- s -1 or 0 ) extracts help message from description +{ dup get-opt-flags 4 and 0= 4 + over count over > + { [] true } { 2drop false } cond +} : get-opt-help // ( s l -- t -1 or 0 ) finds short/long option s in list l { swap 1 { swap short-option-matches } does assoc-gen } : lookup-short-option @@ -47,37 +51,57 @@ recursive list-delete-range { // ( s -- s' null or s' s'' ) Splits long option --opt=arg at '=' { dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond } : split-longopt +variable options-list // ( l -- i or 0 ) // parses command line arguments according to option description list l // and returns index i of first incorrect option -{ { first-opt dup 0= { true } { - swap dup "--" $pfx? { // l i s +{ options-list ! + { first-opt dup 0= { true } { + swap dup "--" $pfx? { // i s dup $len 2 = { drop dup 1 $*del.. 0 true } { - split-longopt swap 3 pick - lookup-long-option not { drop true } { // l i s' t f - dup get-opt-exec swap get-opt-flags 3 and // l i s' e f' - 2 pick null? { dup 1 = } { dup 0= negate } cond // l i s' e f' f'' + split-longopt swap options-list @ + lookup-long-option not { drop true } { // i s' t f + dup get-opt-exec swap get-opt-flags 3 and // i s' e f' + 2 pick null? { dup 1 = } { dup 0= negate } cond // i s' e f' f'' dup 1 = { 2drop 2drop true } { { drop nip over 1+ $() swap execute 2 $*del.. false } { ' nip ifnot execute 1 $*del.. false - } cond } cond } cond } cond } { // l i s + } cond } cond } cond } cond } { // i s 1 $| nip { dup $len 0= { drop 1 $*del.. false true } { - 1 $| swap 3 pick // l i s' s l - lookup-short-option not { drop true true } { // l i s' t - dup get-opt-exec swap get-opt-flags 3 and // l i s' e f' + 1 $| swap options-list @ // i s' s l + lookup-short-option not { drop true true } { // i s' t + dup get-opt-exec swap get-opt-flags 3 and // i s' e f' ?dup 0= { execute false } { 2 pick $len { drop execute "" false } { - 2 = { nip null swap execute "" false } { // l i e + 2 = { nip null swap execute "" false } { // i e nip over 1+ $() swap execute 2 $*del.. false true } cond } cond } cond } cond } cond } until } cond - } cond } until nip + } cond } until } : getopt +// ( t -- ) Displays help message for one option +{ dup get-opt-flags dup 4 and 2 pick third swap { + ."-" type ."/" over 3 [] type } { + dup $len { dup "--" $pfx? { ."-" } ifnot type } { + drop ."usage: " $0 type + } cond } cond + dup 3 and ?dup { + 2 = { ."[=]" } { ."=" } cond + } if + 8 and { 9 emit } ifnot + get-opt-help { type } { ."No help available" } cond cr +} : show-opt-help +// ( -- ) Displays options help message according to options-list +{ options-list @ { dup null? not } { + uncons swap show-opt-help + } while drop +} : show-options-help // ( l -- ) Parses options and throws an error on failure -{ getopt ?dup { $() "cannot parse command line options near `" swap $+ +"`" abort } if +{ getopt ?dup { + $() "cannot parse command line options near `" swap $+ +"`" + show-options-help abort } if } : run-getopt - anon constant opt-list-marker ' opt-list-marker : begin-options { opt-list-marker list-until-marker } : end-options @@ -90,3 +114,7 @@ anon constant opt-list-marker { 2 rot triple } dup : short-option-?arg : long-option-?arg { 5 2swap 4 tuple } : short-long-option-arg { 6 2swap 4 tuple } : short-long-option-?arg +// ( o s -- s' ) Adds help message to option +' , : option-help +// ( s -- o ) Creates a generic help message +{ 'nop 8 "" 3 roll 4 tuple } : generic-help diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif index 7f1a376bb5..716247987e 100644 --- a/crypto/fift/lib/TonUtil.fif +++ b/crypto/fift/lib/TonUtil.fif @@ -12,6 +12,8 @@ library TonUtil // TON Blockchain Fift Library { (number) 1- abort"integer expected" } : parse-int +{ over null? ' swap if drop } : replace-if-null + // Private key load/generate // ( fname -- pubkey privkey ) { dup ."Loading private key from file " type cr diff --git a/crypto/smartcont/config-code.fc b/crypto/smartcont/config-code.fc index 8c453cde48..15c87b3833 100644 --- a/crypto/smartcont/config-code.fc +++ b/crypto/smartcont/config-code.fc @@ -137,16 +137,21 @@ _ perform_action(cfg_dict, public_key, action, cs) { } } -slice get_validator_descr(int idx) inline_ref { +(slice, int) get_validator_descr(int idx) inline_ref { var vset = config_param(34); if (vset.null?()) { - return null(); + return (null(), 0); } var cs = begin_parse(vset); - cs~skip_bits(8 + 32 + 32 + 16 + 16); + ;; validators_ext#12 utime_since:uint32 utime_until:uint32 + ;; total:(## 16) main:(## 16) { main <= total } { main >= 1 } + ;; total_weight:uint64 + throw_unless(40, cs~load_uint(8) == 0x12); + cs~skip_bits(32 + 32 + 16 + 16); + int total_weight = cs~load_uint(64); var dict = begin_cell().store_slice(cs).end_cell(); var (value, _) = dict.udict_get?(16, idx); - return value; + return (value, total_weight); } (int, int) unpack_validator_descr(slice cs) inline { @@ -158,33 +163,51 @@ slice get_validator_descr(int idx) inline_ref { return (cs~load_uint(256), cs~load_uint(64)); } -slice create_new_entry(cs) inline { - return begin_cell().store_int(false, 1).store_uint(0, 64).store_uint(0, 256).store_slice(cs).end_cell().begin_parse(); +(cell, int, int, slice) new_proposal(cs) inline { + return (null(), 0, 0, cs); } -(cell, int, int, slice) unpack_suggestion(slice cs) inline { +(cell, int, int, slice) unpack_proposal(slice cs) inline { return (cs~load_dict(), cs~load_uint(64), cs~load_uint(256), cs); } -builder pack_suggestion(cell voters, int sum_weight, int vset_id, slice body) inline { +builder pack_proposal(cell voters, int sum_weight, int vset_id, slice body) inline { return begin_cell().store_dict(voters).store_uint(sum_weight, 64).store_uint(vset_id, 256).store_slice(body); } -cell register_vote(vote_dict, action, cs, idx, weight) { +(cell, slice) register_vote(vote_dict, action, cs, idx, weight, total_weight, cur_vset_id) { int hash = 0; + int found? = 0; var entry = null(); if (action & 1) { hash = slice_hash(cs); - (entry, var found?) = vote_dict.udict_get?(256, hash); - ifnot (found?) { - entry = create_new_entry(cs); - } + (entry, found?) = vote_dict.udict_get?(256, hash); } else { hash = cs.preload_uint(256); - (entry, var found?) = vote_dict.udict_get?(256, hash); + (entry, found?) = vote_dict.udict_get?(256, hash); throw_unless(42, found?); } - return vote_dict; + var (voters, sum_weight, vset_id, body) = found? ? unpack_proposal(entry) : (null(), 0, cur_vset_id, cs); + if (vset_id != cur_vset_id) { + voters = null(); + sum_weight = 0; + vset_id = cur_vset_id; + } + var (_, found?) = voters.udict_get?(16, idx); + ifnot (found?) { + voters~udict_set_builder(16, idx, begin_cell().store_uint(32, now())); + sum_weight += weight; + if (sum_weight * 3 > total_weight * 2) { + ;; proposal accepted + vote_dict~udict_delete?(256, hash); + return (vote_dict, body); + } else { + vote_dict~udict_set_builder(256, hash, pack_proposal(voters, sum_weight, cur_vset_id, body)); + return (vote_dict, null()); + } + } else { + return (vote_dict, null()); + } } () recv_external(slice in_msg) impure { @@ -198,15 +221,19 @@ cell register_vote(vote_dict, action, cs, idx, weight) { throw_unless(33, msg_seqno == stored_seqno); ifnot ((action - 0x566f7465) & -2) { var idx = cs~load_uint(16); - var vdescr = get_validator_descr(idx); + var (vdescr, total_weight) = get_validator_descr(idx); var (val_pubkey, weight) = unpack_validator_descr(vdescr); throw_unless(34, check_signature(slice_hash(in_msg), signature, val_pubkey)); accept_message(); stored_seqno += 1; store_data(cfg_dict, stored_seqno, public_key, vote_dict); commit(); - vote_dict = register_vote(vote_dict, action, cs, idx, weight); + (vote_dict, var accepted) = register_vote(vote_dict, action, cs, idx, weight, total_weight, config_param(34).cell_hash()); store_data(cfg_dict, stored_seqno, public_key, vote_dict); + ifnot (accepted.null?()) { + (cfg_dict, public_key) = perform_action(cfg_dict, public_key, accepted~load_uint(32), accepted); + store_data(cfg_dict, stored_seqno, public_key, vote_dict); + } return (); } throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); diff --git a/crypto/smartcont/highload-wallet-v2.fif b/crypto/smartcont/highload-wallet-v2.fif index b8f0be72e9..9651aa5395 100755 --- a/crypto/smartcont/highload-wallet-v2.fif +++ b/crypto/smartcont/highload-wallet-v2.fif @@ -1,20 +1,41 @@ #!/usr/bin/fift -s "TonUtil.fif" include +"GetOpt.fif" include + +{ show-options-help 1 halt } : usage + +true =: allow-bounce +false =: force-bounce +3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +60 =: timeout // external message expires in 60 seconds + +begin-options + " [-n|-b] [-t] []" +cr +tab + +"Creates a request with up to 254 orders loaded from to high-load (sub)wallet created by new-highload-v2-wallet.fif, with private key loaded from file .pk " + +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" +cr + +" is a text file with lines `SEND `" + generic-help + "n" "--no-bounce" { false =: allow-bounce } short-long-option + "Clears bounce flag" option-help + "b" "--force-bounce" { true =: force-bounce } short-long-option + "Forces bounce flag" option-help + "t" "--timeout" { parse-int =: timeout } short-long-option-arg + "Sets expiration timeout in seconds (" timeout (.) $+ +" by default)" option-help + "m" "--mode" { parse-int =: send-mode } short-long-option-arg + "Sets transfer mode (0..255) for SENDRAWMSG (" send-mode (.) $+ +" by default)" + option-help + "h" "--help" { usage } short-long-option + "Shows a help message" option-help +parse-options -{ ."usage: " @' $0 type ." []" cr - ."Creates a request with up to 254 orders loaded from to high-load v2 (sub)wallet created by new-highload-v2-wallet.fif, with private key loaded from file .pk " - ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr - ." is a text file with lines `SEND `" cr 1 halt -} : usage $# dup 3 < swap 4 > or ' usage if +4 :$1..n $1 =: file-base $2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id { subwallet-id (.) $+ } : +subwallet $3 =: order-file -def? $4 { @' $4 } { "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 +$4 "wallet-query" replace-if-null =: savefile file-base +subwallet +".addr" load-address 2dup 2constant wallet_addr @@ -41,7 +62,7 @@ variable order# order# 0! -rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr } : .transfer // addr$ ng -- c -{ swap parse-smc-addr // ng wc addr bnc +{ swap parse-smc-addr force-bounce or allow-bounce and // ng wc addr bnc 2over 2over .transfer create-int-msg } : create-simple-transfer diff --git a/crypto/smartcont/highload-wallet.fif b/crypto/smartcont/highload-wallet.fif index e851016cc9..f0c0c411ba 100755 --- a/crypto/smartcont/highload-wallet.fif +++ b/crypto/smartcont/highload-wallet.fif @@ -1,21 +1,42 @@ #!/usr/bin/fift -s "TonUtil.fif" include +"GetOpt.fif" include + +{ show-options-help 1 halt } : usage + +true =: allow-bounce +false =: force-bounce +3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +60 =: timeout // external message expires in 60 seconds + +begin-options + " [-n|-b] [-t] []" +cr +tab + +"Creates a request with up to 254 orders loaded from to high-load (sub)wallet created by new-highload-wallet.fif, with private key loaded from file .pk " + +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" +cr + +" is a text file with lines `SEND `" + generic-help + "n" "--no-bounce" { false =: allow-bounce } short-long-option + "Clears bounce flag" option-help + "b" "--force-bounce" { true =: force-bounce } short-long-option + "Forces bounce flag" option-help + "t" "--timeout" { parse-int =: timeout } short-long-option-arg + "Sets expiration timeout in seconds (" timeout (.) $+ +" by default)" option-help + "m" "--mode" { parse-int =: send-mode } short-long-option-arg + "Sets transfer mode (0..255) for SENDRAWMSG (" send-mode (.) $+ +" by default)" + option-help + "h" "--help" { usage } short-long-option + "Shows a help message" option-help +parse-options -{ ."usage: " @' $0 type ." []" cr - ."Creates a request with up to 254 orders loaded from to high-load (sub)wallet created by new-highload-wallet.fif, with private key loaded from file .pk " - ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr - ." is a text file with lines `SEND `" cr 1 halt -} : usage $# dup 4 < swap 5 > or ' usage if +5 :$1..n $1 =: file-base $2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id { subwallet-id (.) $+ } : +subwallet $3 parse-int =: seqno $4 =: order-file -def? $5 { @' $5 } { "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 +$5 "wallet-query" replace-if-null =: savefile file-base +subwallet +".addr" load-address 2dup 2constant wallet_addr @@ -42,7 +63,7 @@ variable order# order# 0! -rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr } : .transfer // addr$ ng -- c -{ swap parse-smc-addr // ng wc addr bnc +{ swap parse-smc-addr force-bounce or allow-bounce and // ng wc addr bnc 2over 2over .transfer create-int-msg } : create-simple-transfer diff --git a/crypto/smartcont/new-wallet-v3.fif b/crypto/smartcont/new-wallet-v3.fif index 187519c9cd..658df598cd 100644 --- a/crypto/smartcont/new-wallet-v3.fif +++ b/crypto/smartcont/new-wallet-v3.fif @@ -2,15 +2,16 @@ "TonUtil.fif" include "Asm.fif" include -{ ."usage: " @' $0 type ." []" cr +{ ."usage: " $0 type ." []" cr ."Creates a new advanced wallet with unique 32-bit identifier in specified workchain, with private key saved to or loaded from .pk" cr ."('new-wallet.pk' by default)" cr 1 halt } : usage $# 2- -2 and ' usage if +3 :$1..n $1 parse-workchain-id =: wc // set workchain id from command line argument $2 parse-int =: subwallet-id -def? $3 { @' $3 } { "new-wallet" } cond constant file-base +$3 "new-wallet" replace-if-null =: file-base ."Creating new advanced v3 wallet in workchain " wc . cr ."with unique wallet id " subwallet-id . cr diff --git a/crypto/smartcont/show-addr.fif b/crypto/smartcont/show-addr.fif index 51b2df8cbf..506aa49275 100755 --- a/crypto/smartcont/show-addr.fif +++ b/crypto/smartcont/show-addr.fif @@ -7,7 +7,7 @@ } : usage $# 1 > ' usage if 1 :$1..n -$1 dup null? { drop "new-wallet" } if =: file-base +$1 "new-wallet" replace-if-null =: file-base file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B| dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc diff --git a/crypto/smartcont/update-config-smc.fif b/crypto/smartcont/update-config-smc.fif index a954ff14e4..3924363223 100755 --- a/crypto/smartcont/update-config-smc.fif +++ b/crypto/smartcont/update-config-smc.fif @@ -1,7 +1,7 @@ #!/usr/bin/fift -s "TonUtil.fif" include -{ ."usage: " @' $0 type ." []" cr +{ ."usage: " $0 type ." []" cr ."Creates a request to simple configuration smart contract requesting to change configuration smart contract code to the one currently stored in auto/config-code.fif, " ."with private key loaded from file .pk, " ."and saves it into .boc ('config-query.boc' by default)" cr 1 halt @@ -15,9 +15,10 @@ true constant bounce "auto/config-code.fif" constant config-source 100 constant interval // valid for 100 seconds +3 :$1..n $1 =: file-base $2 parse-int =: qseqno -def? $3 { @' $3 } { "config-query" } cond constant savefile +$3 "config-query" replace-if-null constant savefile file-base +".addr" load-address 2dup 2constant config_addr diff --git a/crypto/smartcont/wallet-v2.fif b/crypto/smartcont/wallet-v2.fif index 3d36643b45..a131d8c52f 100755 --- a/crypto/smartcont/wallet-v2.fif +++ b/crypto/smartcont/wallet-v2.fif @@ -1,30 +1,52 @@ #!/usr/bin/fift -s "TonUtil.fif" include +"GetOpt.fif" include -{ ."usage: " @' $0 type ." [-B ] []" cr - ."Creates a request to advanced wallet created by new-wallet-v2.fif, with private key loaded from file .pk " - ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt -} : usage -def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond - @' $# 2- =: $# } if } if -$# dup 4 < swap 5 > or ' usage if +{ show-options-help 1 halt } : usage -true constant bounce +"" =: comment // comment for simple transfers +true =: allow-bounce +false =: force-bounce +3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +60 =: timeout // external message expires in 60 seconds + +begin-options + " [-n|-b] [-t] [-B ] [-C ] []" +cr +tab + +"Creates a request to advanced wallet created by new-wallet-v2.fif, with private key loaded from file .pk " + +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" + generic-help + "n" "--no-bounce" { false =: allow-bounce } short-long-option + "Clears bounce flag" option-help + "b" "--force-bounce" { true =: force-bounce } short-long-option + "Forces bounce flag" option-help + "t" "--timeout" { parse-int =: timeout } short-long-option-arg + "Sets expiration timeout in seconds (" timeout (.) $+ +" by default)" option-help + "B" "--body" { =: body-boc-file } short-long-option-arg + "Sets the payload of the transfer message" option-help + "C" "--comment" { =: comment } short-long-option-arg + "Sets the comment to be sent in the transfer message" option-help + "m" "--mode" { parse-int =: send-mode } short-long-option-arg + "Sets transfer mode (0..255) for SENDRAWMSG (" send-mode (.) $+ +" by default)" + option-help + "h" "--help" { usage } short-long-option + "Shows a help message" option-help +parse-options +$# dup 4 < swap 5 > or ' usage if +5 :$1..n +true constant bounce $1 =: file-base -$2 bounce parse-load-address =: bounce 2=: dest_addr +$2 bounce parse-load-address force-bounce or allow-bounce and =: bounce 2=: dest_addr $3 parse-int =: seqno $4 $>GR =: amount -def? $5 { @' $5 } { "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 +$5 "wallet-query" replace-if-null =: savefile 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 } { } cond +def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond constant body-cell ."Transferring " amount .GR ."to account " diff --git a/crypto/smartcont/wallet-v3.fif b/crypto/smartcont/wallet-v3.fif index b23f941adf..1568e198ad 100644 --- a/crypto/smartcont/wallet-v3.fif +++ b/crypto/smartcont/wallet-v3.fif @@ -1,31 +1,54 @@ #!/usr/bin/fift -s "TonUtil.fif" include +"GetOpt.fif" include + +{ show-options-help 1 halt } : usage + +"" =: comment // comment for simple transfers +true =: allow-bounce +false =: force-bounce +3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +60 =: timeout // external message expires in 60 seconds + +begin-options + " [-n|-b] [-t] [-B ] [-C ] []" +cr +tab + +"Creates a request to advanced wallet created by new-wallet-v3.fif, with private key loaded from file .pk " + +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" + generic-help + "n" "--no-bounce" { false =: allow-bounce } short-long-option + "Clears bounce flag" option-help + "b" "--force-bounce" { true =: force-bounce } short-long-option + "Forces bounce flag" option-help + "t" "--timeout" { parse-int =: timeout } short-long-option-arg + "Sets expiration timeout in seconds (" timeout (.) $+ +" by default)" option-help + "B" "--body" { =: body-boc-file } short-long-option-arg + "Sets the payload of the transfer message" option-help + "C" "--comment" { =: comment } short-long-option-arg + "Sets the comment to be sent in the transfer message" option-help + "m" "--mode" { parse-int =: send-mode } short-long-option-arg + "Sets transfer mode (0..255) for SENDRAWMSG (" send-mode (.) $+ +" by default)" + option-help + "h" "--help" { usage } short-long-option + "Shows a help message" option-help +parse-options -{ ."usage: " @' $0 type ." [-B ] []" cr - ."Creates a request to advanced wallet created by new-wallet-v3.fif, with private key loaded from file .pk " - ."and address from .addr, and saves it into .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 +6 :$1..n true constant bounce - $1 =: file-base -$2 bounce parse-load-address =: bounce 2=: dest_addr +$2 bounce parse-load-address force-bounce or allow-bounce and =: 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 +$6 "wallet-query" replace-if-null =: savefile 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 } { } cond +def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond constant body-cell ."Transferring " amount .GR ."to account " diff --git a/crypto/smartcont/wallet.fif b/crypto/smartcont/wallet.fif index e5cf6ad41a..c3d628d4cc 100755 --- a/crypto/smartcont/wallet.fif +++ b/crypto/smartcont/wallet.fif @@ -2,31 +2,42 @@ "TonUtil.fif" include "GetOpt.fif" include -{ ."usage: " @' $0 type ." [-n] [-B ] [-C ] []" cr - ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " - ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt -} : usage +{ show-options-help 1 halt } : usage "" =: comment // comment for simple transfers true =: allow-bounce +false =: force-bounce 3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors begin-options + " [-n|-b] [-B ] [-C ] []" +cr +tab + +"Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " + +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" + generic-help "n" "--no-bounce" { false =: allow-bounce } short-long-option + "Clears bounce flag" option-help + "b" "--force-bounce" { true =: force-bounce } short-long-option + "Forces bounce flag" option-help "B" "--body" { =: body-boc-file } short-long-option-arg + "Sets the payload of the transfer message" option-help "C" "--comment" { =: comment } short-long-option-arg + "Sets the comment to be sent in the transfer message" option-help "m" "--mode" { parse-int =: send-mode } short-long-option-arg + "Sets transfer mode (0..255) for SENDRAWMSG (" send-mode (.) $+ +" by default)" + option-help "h" "--help" { usage } short-long-option + "Shows a help message" option-help parse-options $# dup 4 < swap 5 > or ' usage if 5 :$1..n true =: bounce $1 =: file-base -$2 bounce parse-load-address allow-bounce and =: bounce 2=: dest_addr +$2 bounce parse-load-address allow-bounce and force-bounce or =: bounce 2=: dest_addr $3 parse-int =: seqno $4 $>GR =: amount -$5 dup null? { drop "wallet-query" } if =: savefile +$5 "wallet-query" replace-if-null =: savefile +allow-bounce not force-bounce and abort"cannot have bounce flag both set and cleared" // "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment file-base +".addr" load-address diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp index 0ec52f5819..f129e0b35a 100644 --- a/crypto/test/test-smartcont.cpp +++ b/crypto/test/test-smartcont.cpp @@ -226,7 +226,7 @@ TEST(Tonlib, Wallet) { 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", "123", "321"}) + {"aba", "new-wallet", "-C", "TESTv2", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "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( @@ -275,7 +275,7 @@ TEST(Tonlib, WalletV3) { 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"}) + {"aba", "new-wallet", "-C", "TESTv3", "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( diff --git a/crypto/tl/tlbc-gen-cpp.cpp b/crypto/tl/tlbc-gen-cpp.cpp index bbf3adf79c..8dfbbb1f29 100644 --- a/crypto/tl/tlbc-gen-cpp.cpp +++ b/crypto/tl/tlbc-gen-cpp.cpp @@ -922,7 +922,7 @@ void CppTypeCode::generate_get_tag_param1(std::ostream& os, std::string nl, cons match_param_pattern(os, nl, A, 8, "# > 1 && (# & 1)", param_names[0])) { return; } - os << nl << "static inline size_t nat_abs(int x) { return (x > 1) * 2 + (x & 1); }"; + os << nl << "// static inline size_t nat_abs(int x) { return (x > 1) * 2 + (x & 1); }"; os << nl << "static signed char ctab[4] = { "; for (int i = 0; i < 4; i++) { if (i > 0) { @@ -941,7 +941,7 @@ void CppTypeCode::generate_get_tag_param2(std::ostream& os, std::string nl, cons os << ' ' << (int)A[i][j]; } } - os << nl << "static inline size_t nat_abs(int x) { return (x > 1) * 2 + (x & 1); }"; + os << nl << "// static inline size_t nat_abs(int x) { return (x > 1) * 2 + (x & 1); }"; os << nl << "static signed char ctab[4][4] = { "; for (int i = 0; i < 16; i++) { if (i > 0) { @@ -964,7 +964,7 @@ void CppTypeCode::generate_get_tag_param3(std::ostream& os, std::string nl, cons } } } - os << nl << "static inline size_t nat_abs(int x) { return (x > 1) * 2 + (x & 1); }"; + os << nl << "// static inline size_t nat_abs(int x) { return (x > 1) * 2 + (x & 1); }"; os << nl << "static signed char ctab[4][4][4] = { "; for (int i = 0; i < 64; i++) { if (i > 0) { diff --git a/crypto/tl/tlblib.hpp b/crypto/tl/tlblib.hpp index b5d51c3154..ba9c783f66 100644 --- a/crypto/tl/tlblib.hpp +++ b/crypto/tl/tlblib.hpp @@ -218,6 +218,9 @@ class TLB { return cs_ref.not_null() ? as_string(*cs_ref, indent) : ""; } std::string as_string_ref(Ref cell_ref, int indent = 0) const; + static inline size_t nat_abs(int x) { + return (x > 1) * 2 + (x & 1); + } protected: bool validate_ref_internal(Ref cell_ref, bool weak = false) const; diff --git a/crypto/vm/cells/CellBuilder.h b/crypto/vm/cells/CellBuilder.h index 0095018e44..e709725c0e 100644 --- a/crypto/vm/cells/CellBuilder.h +++ b/crypto/vm/cells/CellBuilder.h @@ -85,6 +85,10 @@ class CellBuilder : public td::CntObject { return idx < refs_cnt ? refs[idx] : Ref{}; } void reset(); + bool reset_bool() { + reset(); + return true; + } CellBuilder& operator=(const CellBuilder&); CellBuilder& operator=(CellBuilder&&); CellBuilder& store_bytes(const char* str, std::size_t len); diff --git a/crypto/vm/cells/CellSlice.cpp b/crypto/vm/cells/CellSlice.cpp index bb550a6969..74a78970e3 100644 --- a/crypto/vm/cells/CellSlice.cpp +++ b/crypto/vm/cells/CellSlice.cpp @@ -218,6 +218,17 @@ unsigned CellSlice::get_level() const { return l; } +Ref CellSlice::get_base_cell() const { + if (cell.is_null()) { + return {}; + } + auto res = cell->virtualize(virt); + if (!tree_node.empty()) { + res = UsageCell::create(std::move(res), tree_node); + } + return res; +} + bool CellSlice::advance(unsigned bits) { if (have(bits)) { bits_st += bits; diff --git a/crypto/vm/cells/CellSlice.h b/crypto/vm/cells/CellSlice.h index 433344f982..da0814691b 100644 --- a/crypto/vm/cells/CellSlice.h +++ b/crypto/vm/cells/CellSlice.h @@ -137,6 +137,7 @@ class CellSlice : public td::CntObject { } unsigned get_cell_level() const; unsigned get_level() const; + Ref get_base_cell() const; // be careful with this one! int fetch_octet(); int prefetch_octet() const; unsigned long long prefetch_ulong_top(unsigned& bits) const; diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 83ba6e1052..3946f10d30 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -107,6 +107,47 @@ ControlRegs& ControlRegs::operator&=(const ControlRegs& save) { return *this; } +bool ControlRegs::serialize(CellBuilder& cb) const { + Dictionary dict{4}; + CellBuilder cb2; + for (int i = 0; i < creg_num; i++) { + if (c[i].not_null() && + !(StackEntry{c[i]}.serialize(cb2) && dict.set_builder(td::BitArray<4>(i), cb2) && cb2.reset_bool())) { + return false; + } + } + for (int i = 0; i < dreg_num; i++) { + if (d[i].not_null() && !(StackEntry{d[i]}.serialize(cb2) && dict.set_builder(td::BitArray<4>(dreg_idx + i), cb2) && + cb2.reset_bool())) { + return false; + } + } + return (c7.is_null() || (StackEntry{c7}.serialize(cb2) && dict.set_builder(td::BitArray<4>(7), cb2))) && + std::move(dict).append_dict_to_bool(cb); +} + +bool ControlData::serialize(CellBuilder& cb) const { + // vm_ctl_data$_ nargs:(Maybe int13) stack:(Maybe VmStack) save:VmSaveList + // cp:(Maybe int16) = VmControlData; + return cb.store_bool_bool(nargs >= 0) // vm_ctl_data$_ nargs:(Maybe ... + && (nargs < 0 || cb.store_long_bool(nargs, 13)) // ... int13) + && cb.store_bool_bool(stack.not_null()) // stack:(Maybe ... + && (stack.is_null() || stack->serialize(cb)) // ... VmStack) + && save.serialize(cb) // save:VmSaveList + && cb.store_bool_bool(cp != -1) // cp:(Maybe ... + && (cp == -1 || cb.store_long_bool(cp, 16)); // ... int16) +} + +bool Continuation::serialize_ref(CellBuilder& cb) const { + vm::CellBuilder cb2; + return serialize(cb2) && cb.store_ref_bool(cb2.finalize()); +} + +bool QuitCont::serialize(CellBuilder& cb) const { + // vmc_quit$1000 exit_code:int32 = VmCont; + return cb.store_long_bool(8, 4) && cb.store_long_bool(exit_code, 32); +} + int ExcQuitCont::jump(VmState* st) const & { int n = 0; try { @@ -118,6 +159,11 @@ int ExcQuitCont::jump(VmState* st) const & { return ~n; } +bool ExcQuitCont::serialize(CellBuilder& cb) const { + // vmc_quit_exc$1001 = VmCont; + return cb.store_long_bool(9, 4); +} + int PushIntCont::jump(VmState* st) const & { VM_LOG(st) << "execute implicit PUSH " << push_val << " (slow)"; st->get_stack().push_smallint(push_val); @@ -130,6 +176,11 @@ int PushIntCont::jump_w(VmState* st) & { return st->jump(std::move(next)); } +bool PushIntCont::serialize(CellBuilder& cb) const { + // vmc_pushint$1111 value:int32 next:^VmCont = VmCont; + return cb.store_long_bool(15, 4) && cb.store_long_bool(push_val, 32) && next->serialize_ref(cb); +} + int ArgContExt::jump(VmState* st) const & { st->adjust_cr(data.save); if (data.cp != -1) { @@ -146,6 +197,11 @@ int ArgContExt::jump_w(VmState* st) & { return st->jump_to(std::move(ext)); } +bool ArgContExt::serialize(CellBuilder& cb) const { + // vmc_envelope$01 cdata:VmControlData next:^VmCont = VmCont; + return cb.store_long_bool(1, 2) && data.serialize(cb) && ext->serialize_ref(cb); +} + int RepeatCont::jump(VmState* st) const & { VM_LOG(st) << "repeat " << count << " more times (slow)\n"; if (count <= 0) { @@ -174,6 +230,12 @@ int RepeatCont::jump_w(VmState* st) & { return st->jump(body); } +bool RepeatCont::serialize(CellBuilder& cb) const { + // vmc_repeat$10100 count:uint63 body:^VmCont after:^VmCont = VmCont; + return cb.store_long_bool(0x14, 5) && cb.store_long_bool(count, 63) && body->serialize_ref(cb) && + after->serialize_ref(cb); +} + int VmState::repeat(Ref body, Ref after, long long count) { if (count <= 0) { body.clear(); @@ -201,6 +263,11 @@ int AgainCont::jump_w(VmState* st) & { } } +bool AgainCont::serialize(CellBuilder& cb) const { + // vmc_again$110001 body:^VmCont = VmCont; + return cb.store_long_bool(0x31, 6) && body->serialize_ref(cb); +} + int VmState::again(Ref body) { return jump(Ref{true, std::move(body)}); } @@ -233,6 +300,11 @@ int UntilCont::jump_w(VmState* st) & { } } +bool UntilCont::serialize(CellBuilder& cb) const { + // vmc_until$110000 body:^VmCont after:^VmCont = VmCont; + return cb.store_long_bool(0x30, 6) && body->serialize_ref(cb) && after->serialize_ref(cb); +} + int VmState::until(Ref body, Ref after) { if (!body->has_c0()) { set_c0(Ref{true, body, std::move(after)}); @@ -292,6 +364,13 @@ int WhileCont::jump_w(VmState* st) & { } } +bool WhileCont::serialize(CellBuilder& cb) const { + // vmc_while_cond$110010 cond:^VmCont body:^VmCont after:^VmCont = VmCont; + // vmc_while_body$110011 cond:^VmCont body:^VmCont after:^VmCont = VmCont; + return cb.store_long_bool(0x19, 5) && cb.store_bool_bool(!chkcond) && cond->serialize_ref(cb) && + body->serialize_ref(cb) && after->serialize_ref(cb); +} + int VmState::loop_while(Ref cond, Ref body, Ref after) { if (!cond->has_c0()) { set_c0(Ref{true, cond, std::move(body), std::move(after), true}); @@ -311,6 +390,11 @@ int OrdCont::jump_w(VmState* st) & { return 0; } +bool OrdCont::serialize(CellBuilder& cb) const { + // vmc_std$00 cdata:VmControlData code:VmCellSlice = VmCont; + return cb.store_long_bool(1, 2) && data.serialize(cb) && StackEntry{code}.serialize(cb); +} + void VmState::init_cregs(bool same_c3, bool push_0) { cr.set_c0(quit0); cr.set_c1(quit1); diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index c6c9d320ed..a5ff599b0d 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -135,6 +135,7 @@ struct ControlRegs { ControlRegs& operator&=(const ControlRegs& save); // clears all c[i]'s which are present in save ControlRegs& operator^=(const ControlRegs& save); // sets c[i]=save.c[i] for all save.c[i] != 0 ControlRegs& operator^=(ControlRegs&& save); + bool serialize(CellBuilder& cb) const; }; struct ControlData { @@ -150,11 +151,12 @@ struct ControlData { } ControlData(int _cp, Ref _stack, int _nargs = -1) : stack(std::move(_stack)), nargs(_nargs), cp(_cp) { } + bool serialize(CellBuilder& cb) const; }; class Continuation : public td::CntObject { public: - virtual int jump(VmState* st) const& = 0; + virtual int jump(VmState* st) const & = 0; virtual int jump_w(VmState* st) &; virtual ControlData* get_cdata() { return 0; @@ -162,6 +164,10 @@ class Continuation : public td::CntObject { virtual const ControlData* get_cdata() const { return 0; } + virtual bool serialize(CellBuilder& cb) const { + return false; + } + bool serialize_ref(CellBuilder& cb) const; bool has_c0() const; Continuation() { } @@ -184,16 +190,18 @@ class QuitCont : public Continuation { QuitCont(int _code = 0) : exit_code(_code) { } ~QuitCont() override = default; - int jump(VmState* st) const& override { + int jump(VmState* st) const & override { return ~exit_code; } + bool serialize(CellBuilder& cb) const override; }; class ExcQuitCont : public Continuation { public: ExcQuitCont() = default; ~ExcQuitCont() override = default; - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; + bool serialize(CellBuilder& cb) const override; }; class PushIntCont : public Continuation { @@ -204,8 +212,9 @@ class PushIntCont : public Continuation { PushIntCont(int val, Ref _next) : push_val(val), next(_next) { } ~PushIntCont() override = default; - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; int jump_w(VmState* st) & override; + bool serialize(CellBuilder& cb) const override; }; class RepeatCont : public Continuation { @@ -217,8 +226,9 @@ class RepeatCont : public Continuation { : body(std::move(_body)), after(std::move(_after)), count(_count) { } ~RepeatCont() override = default; - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; int jump_w(VmState* st) & override; + bool serialize(CellBuilder& cb) const override; }; class AgainCont : public Continuation { @@ -228,8 +238,9 @@ class AgainCont : public Continuation { AgainCont(Ref _body) : body(std::move(_body)) { } ~AgainCont() override = default; - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; int jump_w(VmState* st) & override; + bool serialize(CellBuilder& cb) const override; }; class UntilCont : public Continuation { @@ -239,8 +250,9 @@ class UntilCont : public Continuation { UntilCont(Ref _body, Ref _after) : body(std::move(_body)), after(std::move(_after)) { } ~UntilCont() override = default; - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; int jump_w(VmState* st) & override; + bool serialize(CellBuilder& cb) const override; }; class WhileCont : public Continuation { @@ -252,8 +264,9 @@ class WhileCont : public Continuation { : cond(std::move(_cond)), body(std::move(_body)), after(std::move(_after)), chkcond(_chk) { } ~WhileCont() override = default; - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; int jump_w(VmState* st) & override; + bool serialize(CellBuilder& cb) const override; }; class ArgContExt : public Continuation { @@ -268,7 +281,7 @@ class ArgContExt : public Continuation { ArgContExt(const ArgContExt&) = default; ArgContExt(ArgContExt&&) = default; ~ArgContExt() override = default; - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; int jump_w(VmState* st) & override; ControlData* get_cdata() override { return &data; @@ -279,6 +292,7 @@ class ArgContExt : public Continuation { td::CntObject* make_copy() const override { return new ArgContExt{*this}; } + bool serialize(CellBuilder& cb) const override; }; class OrdCont : public Continuation { @@ -303,7 +317,7 @@ class OrdCont : public Continuation { td::CntObject* make_copy() const override { return new OrdCont{*this}; } - int jump(VmState* st) const& override; + int jump(VmState* st) const & override; int jump_w(VmState* st) & override; ControlData* get_cdata() override { @@ -321,12 +335,13 @@ class OrdCont : public Continuation { Ref get_stack_ref() const { return data.stack; } - Ref copy_ord() const& { + Ref copy_ord() const & { return Ref{true, *this}; } Ref copy_ord() && { return Ref{true, *this}; } + bool serialize(CellBuilder& cb) const override; }; struct GasLimits { diff --git a/crypto/vm/stack.cpp b/crypto/vm/stack.cpp index b2ba081fa8..f3c8050459 100644 --- a/crypto/vm/stack.cpp +++ b/crypto/vm/stack.cpp @@ -671,4 +671,91 @@ void Stack::push_maybe_cellslice(Ref cs) { push_maybe(std::move(cs)); } +/* + * + * SERIALIZE/DESERIALIZE STACK VALUES + * + */ + +bool StackEntry::serialize(vm::CellBuilder& cb, int mode) const { + switch (tp) { + case t_null: + return cb.store_long_bool(0, 8); // vm_stk_null#00 = VmStackValue; + case t_int: { + auto val = as_int(); + if (!val->is_valid()) { + // vm_stk_nan#02ff = VmStackValue; + return cb.store_long_bool(0x02ff, 16); + } else if (!(mode & 1) && val->signed_fits_bits(64)) { + // vm_stk_tinyint#01 value:int64 = VmStackValue; + return cb.store_long_bool(1, 8) && cb.store_int256_bool(std::move(val), 256); + } else { + // vm_stk_int#0201_ value:int257 = VmStackValue; + return cb.store_long_bool(0x0200 / 2, 15) && cb.store_int256_bool(std::move(val), 257); + } + } + case t_cell: + // vm_stk_cell#03 cell:^Cell = VmStackValue; + return cb.store_long_bool(3, 8) && cb.store_ref_bool(as_cell()); + case t_slice: { + // _ cell:^Cell st_bits:(## 10) end_bits:(## 10) { st_bits <= end_bits } + // st_ref:(#<= 4) end_ref:(#<= 4) { st_ref <= end_ref } = VmCellSlice; + const auto& cs = *static_cast>(ref); + return cb.store_long_bool(4, 8) // vm_stk_slice#04 _:VmCellSlice = VmStackValue; + && cb.store_ref_bool(cs.get_base_cell()) // _ cell:^Cell + && cb.store_long_bool(cs.cur_pos(), 10) // st_bits:(## 10) + && cb.store_long_bool(cs.cur_pos() + cs.size(), 10) // end_bits:(## 10) + && cb.store_long_bool(cs.cur_ref(), 3) // st_ref:(#<= 4) + && cb.store_long_bool(cs.cur_ref() + cs.size_refs(), 3); // end_ref:(#<= 4) + } + case t_builder: + // vm_stk_builder#05 cell:^Cell = VmStackValue; + return cb.store_long_bool(5, 8) && cb.store_ref_bool(as_builder()->finalize_copy()); + case t_vmcont: + // vm_stk_cont#06 cont:VmCont = VmStackValue; + return !(mode & 2) && cb.store_long_bool(6, 8) && as_cont()->serialize(cb); + case t_tuple: { + const auto& tuple = *static_cast>(ref); + auto n = tuple.size(); + // vm_stk_tuple#07 len:(## 16) data:(VmTuple len) = VmStackValue; + Ref head, tail; + vm::CellBuilder cb2; + for (std::size_t i = 0; i < n; i++) { + std::swap(head, tail); + if (i > 1 && + !(cb2.store_ref_bool(std::move(tail)) && cb2.store_ref_bool(std::move(head)) && cb2.finalize_to(head))) { + return false; + } + if (!(tuple[i].serialize(cb2, mode) && cb2.finalize_to(tail))) { + return false; + } + } + return cb.store_long_bool(7, 8) && cb.store_long_bool(n, 16) && (head.is_null() || cb.store_ref_bool(head)) && + (tail.is_null() || cb.store_ref_bool(tail)); + } + default: + return false; + } +} + +bool Stack::serialize(vm::CellBuilder& cb, int mode) const { + // vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack; + unsigned n = depth(); + if (!cb.store_ulong_rchk_bool(n, 24)) { // vm_stack#_ depth:(## 24) + return false; + } + if (!n) { + return true; + } + vm::CellBuilder cb2; + Ref rest = cb2.finalize(); // vm_stk_nil#_ = VmStackList 0; + for (unsigned i = 0; i < n - 1; i++) { + // vm_stk_cons#_ {n:#} rest:^(VmStackList n) tos:VmStackValue = VmStackList (n + 1); + if (!(cb2.store_ref_bool(std::move(rest)) && stack[i].serialize(cb2, mode) && cb2.finalize_to(rest))) { + return false; + } + } + return cb.store_ref_bool(std::move(rest)) && stack[n - 1].serialize(cb, mode); +} + } // namespace vm diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index 507f98df09..1fce43f25d 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -166,6 +166,8 @@ class StackEntry { Type type() const { return tp; } + // mode: +1 = disable short ints, +2 = disable continuations + bool serialize(vm::CellBuilder& cb, int mode = 0) const; private: static bool is_list(const StackEntry* se); @@ -508,6 +510,7 @@ class Stack : public td::CntObject { } // mode: +1 = add eoln, +2 = Lisp-style lists void dump(std::ostream& os, int mode = 1) const; + bool serialize(vm::CellBuilder& cb, int mode = 0) const; }; } // namespace vm diff --git a/ton/ton-types.h b/ton/ton-types.h index 4743253aa0..a259c505d5 100644 --- a/ton/ton-types.h +++ b/ton/ton-types.h @@ -284,7 +284,7 @@ struct BlockIdExt { BlockIdExt v; char rh[65]; char fh[65]; - auto r = sscanf(s.begin(), "(%d,%" SCNu64 ",%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh); + auto r = sscanf(s.begin(), "(%d,%" SCNx64 ",%u):%64s:%64s", &v.id.workchain, &v.id.shard, &v.id.seqno, rh, fh); if (r < 5) { return td::Status::Error("failed to parse block id"); } diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp index c110abbe22..ccb0b703df 100644 --- a/validator-engine/validator-engine.cpp +++ b/validator-engine/validator-engine.cpp @@ -927,7 +927,6 @@ void ValidatorEngine::alarm() { auto config = configR.move_as_ok(); auto cur_t = config->get_validator_set_start_stop(0); CHECK(cur_t.first > 0); - LOG(ERROR) << "curt: " << cur_t.first << " " << cur_t.second; auto val_set = state_->get_total_validator_set(0); auto e = val_set->export_vector(); diff --git a/validator/interfaces/validator-manager.h b/validator/interfaces/validator-manager.h index 71919d2155..405efdfbcf 100644 --- a/validator/interfaces/validator-manager.h +++ b/validator/interfaces/validator-manager.h @@ -162,13 +162,13 @@ class ValidatorManager : public ValidatorManagerInterface { virtual void wait_shard_client_state(BlockSeqno seqno, td::Timestamp timeout, td::Promise promise) = 0; static bool is_persistent_state(UnixTime ts, UnixTime prev_ts) { - return ts / 1024 != prev_ts / 1024; + return ts / (1 << 17) != prev_ts / (1 << 17); } static UnixTime persistent_state_ttl(UnixTime ts) { - auto x = ts / 1024; + auto x = ts / (1 << 17); CHECK(x > 0); auto b = td::count_trailing_zeroes32(x); - return ts + (2048 << b); + return ts + ((1 << 18) << b); } }; From 6143b2051f3a1a213ede2f2a3e8dd2c80d6ede11 Mon Sep 17 00:00:00 2001 From: ton Date: Sun, 8 Dec 2019 16:22:48 +0400 Subject: [PATCH 024/667] fixed download of an serialized state --- validator/import-db-slice.cpp | 7 +++++++ validator/manager.cpp | 13 ++++++++----- validator/state-serializer.cpp | 4 +++- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/validator/import-db-slice.cpp b/validator/import-db-slice.cpp index 4e404b7e38..24618ff5c8 100644 --- a/validator/import-db-slice.cpp +++ b/validator/import-db-slice.cpp @@ -82,6 +82,10 @@ void ArchiveImporter::start_up() { } auto seqno = masterchain_blocks_.begin()->first; + if (seqno > state_->get_seqno() + 1) { + abort_query(td::Status::Error(ErrorCode::notready, "too big first masterchain seqno")); + return; + } check_masterchain_block(seqno); } @@ -197,6 +201,9 @@ void ArchiveImporter::got_new_materchain_state(td::Ref state) } void ArchiveImporter::checked_all_masterchain_blocks(BlockSeqno seqno) { + if (shard_client_seqno_ > seqno) { + shard_client_seqno_ = seqno; + } check_next_shard_client_seqno(shard_client_seqno_ + 1); } diff --git a/validator/manager.cpp b/validator/manager.cpp index 286baca49c..0e7101a466 100644 --- a/validator/manager.cpp +++ b/validator/manager.cpp @@ -1484,8 +1484,9 @@ void ValidatorManagerImpl::download_next_archive() { td::actor::send_closure(SelfId, &ValidatorManagerImpl::downloaded_archive_slice, R.move_as_ok()); } }); - callback_->download_archive(shard_client_handle_->id().seqno() + 1, db_root_ + "/tmp/", td::Timestamp::in(3600.0), - std::move(P)); + + auto seqno = std::min(last_masterchain_seqno_, shard_client_handle_->id().seqno()); + callback_->download_archive(seqno + 1, db_root_ + "/tmp/", td::Timestamp::in(3600.0), std::move(P)); } void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { @@ -1500,8 +1501,10 @@ void ValidatorManagerImpl::downloaded_archive_slice(std::string name) { } }); - td::actor::create_actor("archiveimport", name, last_masterchain_state_, - shard_client_handle_->id().seqno(), opts_, actor_id(this), std::move(P)) + auto seqno = std::min(last_masterchain_seqno_, shard_client_handle_->id().seqno()); + + td::actor::create_actor("archiveimport", name, last_masterchain_state_, seqno, opts_, actor_id(this), + std::move(P)) .release(); } @@ -1509,7 +1512,7 @@ void ValidatorManagerImpl::checked_archive_slice(std::vector seqno) CHECK(seqno.size() == 2); LOG(INFO) << "checked downloaded archive slice: mc_top_seqno=" << seqno[0] << " shard_top_seqno_=" << seqno[1]; CHECK(seqno[0] <= last_masterchain_seqno_); - CHECK(seqno[1] <= seqno[0]); + CHECK(seqno[1] <= last_masterchain_seqno_); BlockIdExt b; if (seqno[1] < last_masterchain_seqno_) { diff --git a/validator/state-serializer.cpp b/validator/state-serializer.cpp index a63f92eb77..cc40626fd3 100644 --- a/validator/state-serializer.cpp +++ b/validator/state-serializer.cpp @@ -94,7 +94,8 @@ void AsyncStateSerializer::next_iteration() { td::actor::send_closure(manager_, &ValidatorManager::get_block_handle, last_block_id_, true, std::move(P)); return; } - if (!masterchain_handle_->inited_unix_time() || !masterchain_handle_->inited_is_key_block()) { + if (!masterchain_handle_->inited_unix_time() || !masterchain_handle_->inited_is_key_block() || + !masterchain_handle_->is_applied()) { return; } CHECK(masterchain_handle_->id() == last_block_id_); @@ -213,6 +214,7 @@ void AsyncStateSerializer::got_shard_state(BlockHandle handle, td::Refid(), masterchain_handle_->id(), std::move(B), std::move(P)); + LOG(INFO) << "storing persistent state for " << masterchain_handle_->id().seqno() << ":" << handle->id().id.shard; next_idx_++; } From b6f6788532138f6c35d6fbbca5c73667124df3fb Mon Sep 17 00:00:00 2001 From: ton Date: Sun, 8 Dec 2019 19:01:57 +0400 Subject: [PATCH 025/667] validator: fixed initial state download --- validator/manager-init.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/manager-init.cpp b/validator/manager-init.cpp index fce822656e..0e77ef948b 100644 --- a/validator/manager-init.cpp +++ b/validator/manager-init.cpp @@ -228,7 +228,7 @@ void ValidatorManagerMasterchainReiniter::choose_masterchain_state() { if (!p || ValidatorManager::is_persistent_state(h->unix_time(), p->unix_time())) { auto ttl = ValidatorManager::persistent_state_ttl(h->unix_time()); td::Clocks::Duration time_to_download = 3600; - if (ttl < td::Clocks::system() + time_to_download) { + if (ttl > td::Clocks::system() + time_to_download) { handle = h; break; } else { From d41ce55305718d207867f0dac60319ec4b6846f2 Mon Sep 17 00:00:00 2001 From: ton Date: Thu, 12 Dec 2019 19:16:25 +0400 Subject: [PATCH 026/667] updated fift/func --- crypto/block/block.tlb | 4 +- crypto/block/dump-block.cpp | 14 +- crypto/fift/lib/Asm.fif | 17 +- crypto/fift/lib/GetOpt.fif | 19 +- crypto/fift/words.cpp | 21 +++ crypto/func/analyzer.cpp | 17 +- crypto/func/test/a10.fc | 9 + crypto/func/test/a6_4.fc | 20 ++ crypto/func/test/w8.fc | 22 +++ crypto/func/test/w9.fc | 8 + crypto/smartcont/highload-wallet-v2.fif | 2 +- crypto/smartcont/highload-wallet.fif | 2 +- crypto/smartcont/simple-wallet-code.fc | 2 +- crypto/smartcont/simple-wallet-ext-code.fc | 6 +- crypto/smartcont/stdlib.fc | 10 +- crypto/smartcont/wallet-v2.fif | 2 +- crypto/smartcont/wallet-v3.fif | 2 +- crypto/smartcont/wallet.fif | 2 +- crypto/tl/tlbc.cpp | 9 +- crypto/tl/tlblib.cpp | 6 + crypto/tl/tlblib.hpp | 1 + crypto/vm/continuation.cpp | 208 ++++++++++++++++++++- crypto/vm/continuation.h | 39 +++- crypto/vm/stack.cpp | 149 ++++++++++++++- crypto/vm/stack.hpp | 12 ++ crypto/vm/tonops.cpp | 120 +++++++++++- doc/tvm.tex | 20 +- tddb/td/db/RocksDb.cpp | 12 ++ tddb/td/db/RocksDb.h | 4 + tddb/test/binlog.cpp | 2 +- tddb/test/key_value.cpp | 22 ++- 31 files changed, 717 insertions(+), 66 deletions(-) create mode 100644 crypto/func/test/a10.fc create mode 100644 crypto/func/test/a6_4.fc create mode 100644 crypto/func/test/w8.fc create mode 100644 crypto/func/test/w9.fc diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 65537a1abe..882e0d7b28 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -713,8 +713,8 @@ gas_limits#_ remaining:int64 _:^[ max_limit:int64 cur_limit:int64 credit:int64 ] = VmGasLimits; _ libraries:(HashmapE 256 ^Cell) = VmLibraries; -vm_ctl_data$_ nargs:int14 stack:(Maybe VmStack) save:VmSaveList -cp:int16 = VmControlData; +vm_ctl_data$_ nargs:(Maybe uint13) stack:(Maybe VmStack) save:VmSaveList +cp:(Maybe int16) = VmControlData; vmc_std$00 cdata:VmControlData code:VmCellSlice = VmCont; vmc_envelope$01 cdata:VmControlData next:^VmCont = VmCont; vmc_quit$1000 exit_code:int32 = VmCont; diff --git a/crypto/block/dump-block.cpp b/crypto/block/dump-block.cpp index a29d5c0aa6..86465bf64f 100644 --- a/crypto/block/dump-block.cpp +++ b/crypto/block/dump-block.cpp @@ -199,10 +199,13 @@ void usage() { int main(int argc, char* const argv[]) { int i; int new_verbosity_level = VERBOSITY_NAME(INFO); - bool dump_state = false; + bool dump_state = false, dump_vmcont = false; auto zerostate = std::make_unique(); - while ((i = getopt(argc, argv, "Shv:")) != -1) { + while ((i = getopt(argc, argv, "CShv:")) != -1) { switch (i) { + case 'C': + dump_vmcont = true; + break; case 'S': dump_state = true; break; @@ -230,12 +233,13 @@ int main(int argc, char* const argv[]) { vm::CellSlice cs{vm::NoVm(), boc}; cs.print_rec(std::cout); std::cout << std::endl; - auto& type = dump_state ? (const tlb::TLB&)block::gen::t_ShardStateUnsplit : block::gen::t_Block; - std::string type_name = dump_state ? "ShardState" : "Block"; + auto& type = !dump_vmcont + ? (dump_state ? (const tlb::TLB&)block::gen::t_ShardStateUnsplit : block::gen::t_Block) + : block::gen::t_VmCont; type.print_ref(std::cout, boc); std::cout << std::endl; bool ok = type.validate_ref(boc); - std::cout << "(" << (ok ? "" : "in") << "valid " << type_name << ")" << std::endl; + std::cout << "(" << (ok ? "" : "in") << "valid " << type << ")" << std::endl; } } if (!done) { diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 730ef78a06..997e06a335 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -310,14 +310,14 @@ x{A938} @Defop(8u+1) MODPOW2# x{A984} @Defop MULDIV x{A985} @Defop MULDIVR x{A98C} @Defop MULDIVMOD +x{A9A4} @Defop MULRSHIFT +x{A9A5} @Defop MULRSHIFTR +x{A9B4} @Defop(8u+1) MULRSHIFT# +x{A9B5} @Defop(8u+1) MULRSHIFTR# x{A9C4} @Defop LSHIFTDIV x{A9C5} @Defop LSHIFTDIVR x{A9D4} @Defop(8u+1) LSHIFT#DIV x{A9D5} @Defop(8u+1) LSHIFT#DIVR -x{A9E4} @Defop MULRSHIFT -x{A9E5} @Defop MULRSHIFTR -x{A9F4} @Defop(8u+1) MULRSHIFT# -x{A9F5} @Defop(8u+1) MULRSHIFTR# x{AA} @Defop(8u+1) LSHIFT# x{AB} @Defop(8u+1) RSHIFT# x{AC} @Defop LSHIFT @@ -972,12 +972,17 @@ x{F800} @Defop ACCEPT x{F801} @Defop SETGASLIMIT x{F80F} @Defop COMMIT +x{F810} @Defop RANDU256 +x{F811} @Defop RAND +x{F814} @Defop SETRAND +x{F815} dup @Defop ADDRAND @Defop RANDOMIZE + x{F82} @Defop(4u) GETPARAM x{F823} @Defop NOW x{F824} @Defop BLOCKLT x{F825} @Defop LTIME -x{F826} @Defop BALANCE -x{F827} @Defop RANDSEED +x{F826} @Defop RANDSEED +x{F827} @Defop BALANCE x{F828} @Defop MYADDR x{F829} @Defop CONFIGROOT x{F830} @Defop CONFIGDICT diff --git a/crypto/fift/lib/GetOpt.fif b/crypto/fift/lib/GetOpt.fif index d354dd3f18..442552b636 100644 --- a/crypto/fift/lib/GetOpt.fif +++ b/crypto/fift/lib/GetOpt.fif @@ -26,9 +26,14 @@ recursive list-delete-range { } : $pfx? // ( s -- ? ) checks whether s is an option (a string beginning with '-') { dup $len 1 > { "-" $pfx? } { drop false } cond } : is-opt? +// ( s -- ? ) checks whether s is a digit option +{ 2 $| drop 1 $| nip $>B 8 B>u@ dup 57 <= swap 48 >= and } : is-digit-opt? +0 box constant disable-digit-opts // ( l -- s i or 0 ) finds first string in l beginning with '-' { 0 { 1+ over null? { 2drop 0 true } { - swap uncons over is-opt? { drop swap true } { nip swap false } cond + swap uncons over is-opt? + { disable-digit-opts @ { over is-digit-opt? not } { true } cond } { false } cond + { drop swap true } { nip swap false } cond } cond } until } : list-find-opt // ( -- s i or 0 ) finds first option in cmdline args @@ -51,11 +56,15 @@ recursive list-delete-range { // ( s -- s' null or s' s'' ) Splits long option --opt=arg at '=' { dup "=" $pos 1+ ?dup { tuck $| swap rot 1- $| drop swap } { null } cond } : split-longopt +// ( l -- f or 0 ) Extracts global option flags from first entry of l +{ dup null? { drop 0 } { car get-opt-flags -256 and } cond +} : get-global-option-flags variable options-list // ( l -- i or 0 ) // parses command line arguments according to option description list l // and returns index i of first incorrect option -{ options-list ! +{ dup options-list ! get-global-option-flags + 256 and disable-digit-opts ! { first-opt dup 0= { true } { swap dup "--" $pfx? { // i s dup $len 2 = { drop dup 1 $*del.. 0 true } { @@ -116,5 +125,7 @@ anon constant opt-list-marker { 6 2swap 4 tuple } : short-long-option-?arg // ( o s -- s' ) Adds help message to option ' , : option-help -// ( s -- o ) Creates a generic help message -{ 'nop 8 "" 3 roll 4 tuple } : generic-help +// ( s f -- o ) Creates a generic help message +{ swap 'nop rot "" 3 roll 4 tuple } : generic-help-setopt +{ 0 generic-help-setopt } : generic-help +256 constant disable-digit-options diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 7459656bb4..043fc17f1c 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -2356,6 +2356,25 @@ void interpret_db_run_vm_parallel(IntCtx& ctx) { do_interpret_db_run_vm_parallel(ctx.error_stream, ctx.stack, ctx.ton_db, threads_n, tasks_n); } +void interpret_store_vm_cont(vm::Stack& stack) { + auto vmcont = stack.pop_cont(); + auto cb = stack.pop_builder(); + if (!vmcont->serialize(cb.write())) { + throw IntError{"cannot serialize vm continuation"}; + } + stack.push_builder(std::move(cb)); +} + +void interpret_fetch_vm_cont(vm::Stack& stack) { + auto cs = stack.pop_cellslice(); + auto vmcont = vm::Continuation::deserialize(cs.write()); + if (vmcont.is_null()) { + throw IntError{"cannot deserialize vm continuation"}; + } + stack.push_cellslice(std::move(cs)); + stack.push_cont(std::move(vmcont)); +} + Ref cmdline_args{true}; void interpret_get_fixed_cmdline_arg(vm::Stack& stack, int n) { @@ -2858,6 +2877,8 @@ void init_words_vm(Dictionary& d) { d.def_ctx_word("gasrunvmctx ", std::bind(interpret_run_vm_c7, _1, true)); d.def_ctx_word("dbrunvm ", interpret_db_run_vm); d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel); + d.def_stack_word("vmcont, ", interpret_store_vm_cont); + d.def_stack_word("vmcont@ ", interpret_fetch_vm_cont); } void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]) { diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index a80bc74e23..354486cf03 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -431,26 +431,25 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { } case _While: { // while (block0 || left) block1; - // ... { block0 left block1 } block0 left next - VarDescrList after_cond_first{next_var_info}; - after_cond_first += left; - code.compute_used_code_vars(block0, after_cond_first, false); - VarDescrList new_var_info{block0->var_info}; + // ... block0 left { block1 block0 left } next + VarDescrList new_var_info{next_var_info}; bool changes = false; do { - code.compute_used_code_vars(block1, block0->var_info, changes); - VarDescrList after_cond{block1->var_info}; + VarDescrList after_cond{new_var_info}; after_cond += left; code.compute_used_code_vars(block0, after_cond, changes); + code.compute_used_code_vars(block1, block0->var_info, changes); std::size_t n = new_var_info.size(); - new_var_info += block0->var_info; + new_var_info += block1->var_info; new_var_info.clear_last(); if (changes) { break; } changes = (new_var_info.size() == n); } while (changes <= edit); - return set_var_info(std::move(new_var_info)); + new_var_info += left; + code.compute_used_code_vars(block0, new_var_info, edit); + return set_var_info(block0->var_info); } case _Until: { // until (block0 || left); diff --git a/crypto/func/test/a10.fc b/crypto/func/test/a10.fc new file mode 100644 index 0000000000..70c7871d70 --- /dev/null +++ b/crypto/func/test/a10.fc @@ -0,0 +1,9 @@ +_ f(int a, int x) { + int y = 0; + int z = 0; + while ((y = x * x) > a) { + x -= 1; + z = 1; + } + return (y, z); +} diff --git a/crypto/func/test/a6_4.fc b/crypto/func/test/a6_4.fc new file mode 100644 index 0000000000..d7e6596a09 --- /dev/null +++ b/crypto/func/test/a6_4.fc @@ -0,0 +1,20 @@ +var calc_root(m) { + int base = 1; + repeat(70) { base *= 10; } + var (a, b, c) = (1, 0, - m); + var (p1, q1, p2, q2) = (1, 0, 0, 1); + do { + int k = -1; + var (a1, b1, c1) = (0, 0, 0); + do { + k += 1; + (a1, b1, c1) = (a, b, c); + c += b; + c += b += a; + } until (c > 0); + (a, b, c) = (- c1, - b1, - a1); + (p1, q1) = (k * p1 + q1, p1); + (p2, q2) = (k * p2 + q2, p2); + } until (p1 > base); + return (p1, q1, p2, q2); +} diff --git a/crypto/func/test/w8.fc b/crypto/func/test/w8.fc new file mode 100644 index 0000000000..bd0696e736 --- /dev/null +++ b/crypto/func/test/w8.fc @@ -0,0 +1,22 @@ +int check_signatures(msg_hash, signatures, signers, bitmask_size) impure { + var bitmask = 0; + var id = -1; + do { + (id, var signature, var f) = signatures.udict_get_next?(32, id); + if (f){ + var sig = signature.preload_bits(512); + var public_key = -1; + do { + (public_key, var cs, var _found) = signers.udict_get_next?(256, public_key); + if (_found){ + if (check_signature(msg_hash, sig, public_key)){ + var signer_index = cs~load_uint(bitmask_size); + bitmask = bitmask | (1 << (signer_index - 1)); + } + } + } until (~ _found); + ;; signature~touch(); + } + } until (~ f); + return bitmask; +} diff --git a/crypto/func/test/w9.fc b/crypto/func/test/w9.fc new file mode 100644 index 0000000000..d943554210 --- /dev/null +++ b/crypto/func/test/w9.fc @@ -0,0 +1,8 @@ +_ g(s) { + var (z, t) = (17, s); + while (z > 0) { + t = s; + z -= 1; + } + return ~ t; +} diff --git a/crypto/smartcont/highload-wallet-v2.fif b/crypto/smartcont/highload-wallet-v2.fif index 9651aa5395..ba1fc801d9 100755 --- a/crypto/smartcont/highload-wallet-v2.fif +++ b/crypto/smartcont/highload-wallet-v2.fif @@ -14,7 +14,7 @@ begin-options +"Creates a request with up to 254 orders loaded from to high-load (sub)wallet created by new-highload-v2-wallet.fif, with private key loaded from file .pk " +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" +cr +" is a text file with lines `SEND `" - generic-help + disable-digit-options generic-help-setopt "n" "--no-bounce" { false =: allow-bounce } short-long-option "Clears bounce flag" option-help "b" "--force-bounce" { true =: force-bounce } short-long-option diff --git a/crypto/smartcont/highload-wallet.fif b/crypto/smartcont/highload-wallet.fif index f0c0c411ba..2ab8521eaf 100755 --- a/crypto/smartcont/highload-wallet.fif +++ b/crypto/smartcont/highload-wallet.fif @@ -14,7 +14,7 @@ begin-options +"Creates a request with up to 254 orders loaded from to high-load (sub)wallet created by new-highload-wallet.fif, with private key loaded from file .pk " +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" +cr +" is a text file with lines `SEND `" - generic-help + disable-digit-options generic-help-setopt "n" "--no-bounce" { false =: allow-bounce } short-long-option "Clears bounce flag" option-help "b" "--force-bounce" { true =: force-bounce } short-long-option diff --git a/crypto/smartcont/simple-wallet-code.fc b/crypto/smartcont/simple-wallet-code.fc index 266bc9ac1f..a43b8b9299 100644 --- a/crypto/smartcont/simple-wallet-code.fc +++ b/crypto/smartcont/simple-wallet-code.fc @@ -15,7 +15,7 @@ throw_unless(33, msg_seqno == stored_seqno); throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key)); accept_message(); - cs~touch_slice(); + cs~touch(); if (cs.slice_refs()) { var mode = cs~load_uint(8); send_raw_message(cs~load_ref(), mode); diff --git a/crypto/smartcont/simple-wallet-ext-code.fc b/crypto/smartcont/simple-wallet-ext-code.fc index a20b96a861..52c4619f71 100644 --- a/crypto/smartcont/simple-wallet-ext-code.fc +++ b/crypto/smartcont/simple-wallet-ext-code.fc @@ -30,7 +30,7 @@ slice do_verify_message(slice in_msg, int seqno, int public_key) { (int stored_seqno, int public_key) = load_state(); var cs = do_verify_message(in_msg, stored_seqno, public_key); accept_message(); - cs~touch_slice(); + cs~touch(); if (cs.slice_refs()) { var mode = cs~load_uint(8); send_raw_message(cs~load_ref(), mode); @@ -57,11 +57,7 @@ cell prepare_send_message(int mode, cell msg) method_id { return prepare_send_message_with_seqno(mode, msg, seqno()); } - slice verify_message(slice msg) method_id { var (stored_seqno, public_key) = load_state(); return do_verify_message(msg, stored_seqno, public_key); } - - - diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 12889a13bf..9041bafe26 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -21,6 +21,8 @@ forall X -> X null() asm "PUSHNULL"; int now() asm "NOW"; slice my_address() asm "MYADDR"; tuple get_balance() asm "BALANCE"; +int cur_lt() asm "LTIME"; +int block_lt() asm "BLOCKLT"; int cell_hash(cell c) asm "HASHCU"; int slice_hash(slice s) asm "HASHSU"; @@ -154,5 +156,9 @@ int cell_null?(cell c) asm "ISNULL"; () send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG"; () set_code(cell new_code) impure asm "SETCODE"; -slice touch_slice(slice s) asm "NOP"; -(slice,()) ~touch_slice(slice s) asm "NOP"; +int random() impure asm "RANDU256"; +int rand(int range) impure asm "RAND"; +int get_seed() impure asm "RANDSEED"; +int set_seed() impure asm "SETRAND"; +() randomize(int x) impure asm "ADDRAND"; +() randomize_lt() impure asm "LTIME" "ADDRAND"; diff --git a/crypto/smartcont/wallet-v2.fif b/crypto/smartcont/wallet-v2.fif index a131d8c52f..6bf12103a3 100755 --- a/crypto/smartcont/wallet-v2.fif +++ b/crypto/smartcont/wallet-v2.fif @@ -14,7 +14,7 @@ begin-options " [-n|-b] [-t] [-B ] [-C ] []" +cr +tab +"Creates a request to advanced wallet created by new-wallet-v2.fif, with private key loaded from file .pk " +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" - generic-help + disable-digit-options generic-help-setopt "n" "--no-bounce" { false =: allow-bounce } short-long-option "Clears bounce flag" option-help "b" "--force-bounce" { true =: force-bounce } short-long-option diff --git a/crypto/smartcont/wallet-v3.fif b/crypto/smartcont/wallet-v3.fif index 1568e198ad..8804acbbd1 100644 --- a/crypto/smartcont/wallet-v3.fif +++ b/crypto/smartcont/wallet-v3.fif @@ -14,7 +14,7 @@ begin-options " [-n|-b] [-t] [-B ] [-C ] []" +cr +tab +"Creates a request to advanced wallet created by new-wallet-v3.fif, with private key loaded from file .pk " +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" - generic-help + disable-digit-options generic-help-setopt "n" "--no-bounce" { false =: allow-bounce } short-long-option "Clears bounce flag" option-help "b" "--force-bounce" { true =: force-bounce } short-long-option diff --git a/crypto/smartcont/wallet.fif b/crypto/smartcont/wallet.fif index c3d628d4cc..e382b64503 100755 --- a/crypto/smartcont/wallet.fif +++ b/crypto/smartcont/wallet.fif @@ -13,7 +13,7 @@ begin-options " [-n|-b] [-B ] [-C ] []" +cr +tab +"Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " +"and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" - generic-help + disable-digit-options generic-help-setopt "n" "--no-bounce" { false =: allow-bounce } short-long-option "Clears bounce flag" option-help "b" "--force-bounce" { true =: force-bounce } short-long-option diff --git a/crypto/tl/tlbc.cpp b/crypto/tl/tlbc.cpp index ff2e19e60e..0364e5dbf2 100644 --- a/crypto/tl/tlbc.cpp +++ b/crypto/tl/tlbc.cpp @@ -102,13 +102,15 @@ unsigned long long get_special_value(std::string str) { unsigned long long val = 0; int bits = 0; if (str[0] == '#') { - while (i < n) { - int c = str[i++]; + for (; i < n; i++) { + int c = str[i]; if (c == '_') { break; } if (c >= '0' && c <= '9') { c -= '0'; + } else if (c >= 'A' && c <= 'F') { + c -= 'A' - 10; } else if (c >= 'a' && c <= 'f') { c -= 'a' - 10; } else { @@ -146,6 +148,9 @@ unsigned long long get_special_value(std::string str) { while (bits && !((val >> (64 - bits)) & 1)) { --bits; } + if (bits) { + --bits; + } } if (bits == 64) { return 0; diff --git a/crypto/tl/tlblib.cpp b/crypto/tl/tlblib.cpp index 40637c57b4..ae759de525 100644 --- a/crypto/tl/tlblib.cpp +++ b/crypto/tl/tlblib.cpp @@ -33,6 +33,12 @@ const NatWidth t_Nat{32}; const Anything t_Anything; const RefAnything t_RefCell; +std::string TLB::get_type_name() const { + std::ostringstream os; + print_type(os); + return os.str(); +} + bool Bool::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const { int t = get_tag(cs); return cs.advance(1) && pp.out(t ? "bool_true" : "bool_false"); diff --git a/crypto/tl/tlblib.hpp b/crypto/tl/tlblib.hpp index ba9c783f66..d90bfcc419 100644 --- a/crypto/tl/tlblib.hpp +++ b/crypto/tl/tlblib.hpp @@ -196,6 +196,7 @@ class TLB { virtual std::ostream& print_type(std::ostream& os) const { return os << ""; } + std::string get_type_name() const; virtual bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const; virtual bool print(PrettyPrinter& pp, const vm::CellSlice& cs) const { vm::CellSlice cs_copy{cs}; diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 3946f10d30..09d2be8946 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -32,6 +32,17 @@ bool Continuation::has_c0() const { return cont_data && cont_data->save.c[0].not_null(); } +bool ControlRegs::clear() { + for (unsigned i = 0; i < creg_num; i++) { + c[i].clear(); + } + for (unsigned i = 0; i < dreg_num; i++) { + d[i].clear(); + } + c7.clear(); + return true; +} + StackEntry ControlRegs::get(unsigned idx) const { if (idx < creg_num) { return get_c(idx); @@ -108,6 +119,7 @@ ControlRegs& ControlRegs::operator&=(const ControlRegs& save) { } bool ControlRegs::serialize(CellBuilder& cb) const { + // _ cregs:(HashmapE 4 VmStackValue) = VmSaveList; Dictionary dict{4}; CellBuilder cb2; for (int i = 0; i < creg_num; i++) { @@ -126,8 +138,34 @@ bool ControlRegs::serialize(CellBuilder& cb) const { std::move(dict).append_dict_to_bool(cb); } +bool ControlRegs::deserialize(CellSlice& cs, int mode) { + // _ cregs:(HashmapE 4 VmStackValue) = VmSaveList; + Ref root; + return cs.fetch_maybe_ref(root) && deserialize(std::move(root), mode); +} + +bool ControlRegs::deserialize(Ref root, int mode) { + try { + clear(); + Dictionary dict{std::move(root), 4}; + return dict.check_for_each([this, mode](Ref val, td::ConstBitPtr key, int n) { + StackEntry value; + return value.deserialize(val.write(), mode) && val->empty_ext() && set((int)key.get_uint(4), std::move(value)); + }); + } catch (VmError&) { + return false; + } +} + +bool ControlData::clear() { + stack.clear(); + save.clear(); + nargs = cp = -1; + return true; +} + bool ControlData::serialize(CellBuilder& cb) const { - // vm_ctl_data$_ nargs:(Maybe int13) stack:(Maybe VmStack) save:VmSaveList + // vm_ctl_data$_ nargs:(Maybe uint13) stack:(Maybe VmStack) save:VmSaveList // cp:(Maybe int16) = VmControlData; return cb.store_bool_bool(nargs >= 0) // vm_ctl_data$_ nargs:(Maybe ... && (nargs < 0 || cb.store_long_bool(nargs, 13)) // ... int13) @@ -138,16 +176,88 @@ bool ControlData::serialize(CellBuilder& cb) const { && (cp == -1 || cb.store_long_bool(cp, 16)); // ... int16) } +bool ControlData::deserialize(CellSlice& cs, int mode) { + // vm_ctl_data$_ nargs:(Maybe uint13) stack:(Maybe VmStack) save:VmSaveList + // cp:(Maybe int16) = VmControlData; + nargs = cp = -1; + stack.clear(); + bool f; + return cs.fetch_bool_to(f) && (!f || cs.fetch_uint_to(13, nargs)) // nargs:(Maybe uint13) + && cs.fetch_bool_to(f) && (!f || Stack::deserialize_to(cs, stack, mode)) // stack:(Maybe VmStack) + && save.deserialize(cs, mode) // save:VmSaveList + && cs.fetch_bool_to(f) && (!f || (cs.fetch_int_to(16, cp) && cp != -1)); // cp:(Maybe int16) +} + bool Continuation::serialize_ref(CellBuilder& cb) const { vm::CellBuilder cb2; return serialize(cb2) && cb.store_ref_bool(cb2.finalize()); } +Ref Continuation::deserialize(CellSlice& cs, int mode) { + if (mode & 0x1002) { + return {}; + } + mode |= 0x1000; + switch (cs.bselect_ext(6, 0x100f011100010001ULL)) { + case 0: + // vmc_std$00 cdata:VmControlData code:VmCellSlice = VmCont; + return OrdCont::deserialize(cs, mode); + case 1: + // vmc_envelope$01 cdata:VmControlData next:^VmCont = VmCont; + return ArgContExt::deserialize(cs, mode); + case 2: + // vmc_quit$1000 exit_code:int32 = VmCont; + return QuitCont::deserialize(cs, mode); + case 3: + // vmc_quit_exc$1001 = VmCont; + return ExcQuitCont::deserialize(cs, mode); + case 4: + // vmc_repeat$10100 count:uint63 body:^VmCont after:^VmCont = VmCont; + return RepeatCont::deserialize(cs, mode); + case 5: + // vmc_until$110000 body:^VmCont after:^VmCont = VmCont; + return UntilCont::deserialize(cs, mode); + case 6: + // vmc_again$110001 body:^VmCont = VmCont; + return AgainCont::deserialize(cs, mode); + case 7: + // vmc_while_cond$110010 cond:^VmCont body:^VmCont after:^VmCont = VmCont; + return WhileCont::deserialize(cs, mode | 0x2000); + case 8: + // vmc_while_body$110011 cond:^VmCont body:^VmCont after:^VmCont = VmCont; + return WhileCont::deserialize(cs, mode & ~0x2000); + case 9: + // vmc_pushint$1111 value:int32 next:^VmCont = VmCont; + return PushIntCont::deserialize(cs, mode); + default: + return {}; + } +} + +bool Continuation::deserialize_to(Ref cell, Ref& cont, int mode) { + if (cell.is_null()) { + cont.clear(); + return false; + } + CellSlice cs = load_cell_slice(std::move(cell)); + return deserialize_to(cs, cont, mode & ~0x1000) && cs.empty_ext(); +} + bool QuitCont::serialize(CellBuilder& cb) const { // vmc_quit$1000 exit_code:int32 = VmCont; return cb.store_long_bool(8, 4) && cb.store_long_bool(exit_code, 32); } +Ref QuitCont::deserialize(CellSlice& cs, int mode) { + // vmc_quit$1000 exit_code:int32 = VmCont; + int exit_code; + if (cs.fetch_ulong(4) == 8 && cs.fetch_int_to(32, exit_code)) { + return Ref{true, exit_code}; + } else { + return {}; + } +} + int ExcQuitCont::jump(VmState* st) const & { int n = 0; try { @@ -164,6 +274,11 @@ bool ExcQuitCont::serialize(CellBuilder& cb) const { return cb.store_long_bool(9, 4); } +Ref ExcQuitCont::deserialize(CellSlice& cs, int mode) { + // vmc_quit_exc$1001 = VmCont; + return cs.fetch_ulong(4) == 9 ? Ref{true} : Ref{}; +} + int PushIntCont::jump(VmState* st) const & { VM_LOG(st) << "execute implicit PUSH " << push_val << " (slow)"; st->get_stack().push_smallint(push_val); @@ -181,6 +296,19 @@ bool PushIntCont::serialize(CellBuilder& cb) const { return cb.store_long_bool(15, 4) && cb.store_long_bool(push_val, 32) && next->serialize_ref(cb); } +Ref PushIntCont::deserialize(CellSlice& cs, int mode) { + // vmc_pushint$1111 value:int32 next:^VmCont = VmCont; + int value; + Ref ref; + Ref next; + if (cs.fetch_ulong(4) == 15 && cs.fetch_int_to(32, value) && cs.fetch_ref_to(ref) && + deserialize_to(std::move(ref), next, mode)) { + return Ref{true, value, std::move(next)}; + } else { + return {}; + } +} + int ArgContExt::jump(VmState* st) const & { st->adjust_cr(data.save); if (data.cp != -1) { @@ -202,6 +330,18 @@ bool ArgContExt::serialize(CellBuilder& cb) const { return cb.store_long_bool(1, 2) && data.serialize(cb) && ext->serialize_ref(cb); } +Ref ArgContExt::deserialize(CellSlice& cs, int mode) { + // vmc_envelope$01 cdata:VmControlData next:^VmCont = VmCont; + ControlData cdata; + Ref ref; + Ref next; + mode &= ~0x1000; + return cs.fetch_ulong(2) == 1 && cdata.deserialize(cs, mode) && cs.fetch_ref_to(ref) && + deserialize_to(std::move(ref), next, mode) + ? Ref{true, std::move(next), std::move(cdata)} + : Ref{}; +} + int RepeatCont::jump(VmState* st) const & { VM_LOG(st) << "repeat " << count << " more times (slow)\n"; if (count <= 0) { @@ -236,6 +376,20 @@ bool RepeatCont::serialize(CellBuilder& cb) const { after->serialize_ref(cb); } +Ref RepeatCont::deserialize(CellSlice& cs, int mode) { + // vmc_repeat$10100 count:uint63 body:^VmCont after:^VmCont = VmCont; + long long count; + Ref ref; + Ref body, after; + if (cs.fetch_ulong(5) == 0x14 && cs.fetch_uint_to(63, count) && cs.fetch_ref_to(ref) && + deserialize_to(std::move(ref), body, mode) && cs.fetch_ref_to(ref) && + deserialize_to(std::move(ref), after, mode)) { + return Ref{true, std::move(body), std::move(after), count}; + } else { + return {}; + } +} + int VmState::repeat(Ref body, Ref after, long long count) { if (count <= 0) { body.clear(); @@ -268,6 +422,17 @@ bool AgainCont::serialize(CellBuilder& cb) const { return cb.store_long_bool(0x31, 6) && body->serialize_ref(cb); } +Ref AgainCont::deserialize(CellSlice& cs, int mode) { + // vmc_again$110001 body:^VmCont = VmCont; + Ref ref; + Ref body; + if (cs.fetch_ulong(6) == 0x31 && cs.fetch_ref_to(ref) && deserialize_to(std::move(ref), body, mode)) { + return Ref{true, std::move(body)}; + } else { + return {}; + } +} + int VmState::again(Ref body) { return jump(Ref{true, std::move(body)}); } @@ -305,6 +470,18 @@ bool UntilCont::serialize(CellBuilder& cb) const { return cb.store_long_bool(0x30, 6) && body->serialize_ref(cb) && after->serialize_ref(cb); } +Ref UntilCont::deserialize(CellSlice& cs, int mode) { + // vmc_until$110000 body:^VmCont after:^VmCont = VmCont; + Ref ref; + Ref body, after; + if (cs.fetch_ulong(6) == 0x30 && cs.fetch_ref_to(ref) && deserialize_to(std::move(ref), body, mode) && + cs.fetch_ref_to(ref) && deserialize_to(std::move(ref), after, mode)) { + return Ref{true, std::move(body), std::move(after)}; + } else { + return {}; + } +} + int VmState::until(Ref body, Ref after) { if (!body->has_c0()) { set_c0(Ref{true, body, std::move(after)}); @@ -371,6 +548,22 @@ bool WhileCont::serialize(CellBuilder& cb) const { body->serialize_ref(cb) && after->serialize_ref(cb); } +Ref WhileCont::deserialize(CellSlice& cs, int mode) { + // vmc_while_cond$110010 cond:^VmCont body:^VmCont after:^VmCont = VmCont; + // vmc_while_body$110011 cond:^VmCont body:^VmCont after:^VmCont = VmCont; + bool at_body; + Ref ref; + Ref cond, body, after; + if (cs.fetch_ulong(5) == 0x19 && cs.fetch_bool_to(at_body) && cs.fetch_ref_to(ref) && + deserialize_to(std::move(ref), cond, mode) && cs.fetch_ref_to(ref) && + deserialize_to(std::move(ref), body, mode) && cs.fetch_ref_to(ref) && + deserialize_to(std::move(ref), after, mode)) { + return Ref{true, std::move(cond), std::move(body), std::move(after), !at_body}; + } else { + return {}; + } +} + int VmState::loop_while(Ref cond, Ref body, Ref after) { if (!cond->has_c0()) { set_c0(Ref{true, cond, std::move(body), std::move(after), true}); @@ -392,7 +585,18 @@ int OrdCont::jump_w(VmState* st) & { bool OrdCont::serialize(CellBuilder& cb) const { // vmc_std$00 cdata:VmControlData code:VmCellSlice = VmCont; - return cb.store_long_bool(1, 2) && data.serialize(cb) && StackEntry{code}.serialize(cb); + return cb.store_long_bool(0, 2) && data.serialize(cb) && StackEntry{code}.serialize(cb, 0x1000); +} + +Ref OrdCont::deserialize(CellSlice& cs, int mode) { + // vmc_std$00 cdata:VmControlData code:VmCellSlice = VmCont; + ControlData cdata; + StackEntry val; + mode &= ~0x1000; + return cs.fetch_ulong(2) == 0 && cdata.deserialize(cs, mode) && val.deserialize(cs, 0x4000) && + val.is(StackEntry::t_slice) + ? Ref{true, std::move(val).as_slice(), std::move(cdata)} + : Ref{}; } void VmState::init_cregs(bool same_c3, bool push_0) { diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index a5ff599b0d..61cbc2d630 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -37,6 +37,7 @@ struct ControlRegs { Ref c[creg_num]; // c0..c3 Ref d[dreg_num]; // c4..c5 Ref c7; // c7 + bool clear(); Ref get_c(unsigned idx) const { return idx < creg_num ? c[idx] : Ref{}; } @@ -136,6 +137,8 @@ struct ControlRegs { ControlRegs& operator^=(const ControlRegs& save); // sets c[i]=save.c[i] for all save.c[i] != 0 ControlRegs& operator^=(ControlRegs&& save); bool serialize(CellBuilder& cb) const; + bool deserialize(CellSlice& cs, int mode = 0); + bool deserialize(Ref root, int mode = 0); }; struct ControlData { @@ -151,7 +154,9 @@ struct ControlData { } ControlData(int _cp, Ref _stack, int _nargs = -1) : stack(std::move(_stack)), nargs(_nargs), cp(_cp) { } + bool clear(); bool serialize(CellBuilder& cb) const; + bool deserialize(CellSlice& cs, int mode = 0); }; class Continuation : public td::CntObject { @@ -164,10 +169,6 @@ class Continuation : public td::CntObject { virtual const ControlData* get_cdata() const { return 0; } - virtual bool serialize(CellBuilder& cb) const { - return false; - } - bool serialize_ref(CellBuilder& cb) const; bool has_c0() const; Continuation() { } @@ -181,6 +182,15 @@ class Continuation : public td::CntObject { return *this; } ~Continuation() override = default; + virtual bool serialize(CellBuilder& cb) const { + return false; + } + bool serialize_ref(CellBuilder& cb) const; + static Ref deserialize(CellSlice& cs, int mode = 0); + static bool deserialize_to(CellSlice& cs, Ref& cont, int mode = 0) { + return (cont = deserialize(cs, mode)).not_null(); + } + static bool deserialize_to(Ref cell, Ref& cont, int mode = 0); }; class QuitCont : public Continuation { @@ -194,6 +204,7 @@ class QuitCont : public Continuation { return ~exit_code; } bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class ExcQuitCont : public Continuation { @@ -202,6 +213,7 @@ class ExcQuitCont : public Continuation { ~ExcQuitCont() override = default; int jump(VmState* st) const & override; bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class PushIntCont : public Continuation { @@ -215,6 +227,7 @@ class PushIntCont : public Continuation { int jump(VmState* st) const & override; int jump_w(VmState* st) & override; bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class RepeatCont : public Continuation { @@ -229,6 +242,7 @@ class RepeatCont : public Continuation { int jump(VmState* st) const & override; int jump_w(VmState* st) & override; bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class AgainCont : public Continuation { @@ -241,6 +255,7 @@ class AgainCont : public Continuation { int jump(VmState* st) const & override; int jump_w(VmState* st) & override; bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class UntilCont : public Continuation { @@ -253,6 +268,7 @@ class UntilCont : public Continuation { int jump(VmState* st) const & override; int jump_w(VmState* st) & override; bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class WhileCont : public Continuation { @@ -267,6 +283,7 @@ class WhileCont : public Continuation { int jump(VmState* st) const & override; int jump_w(VmState* st) & override; bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class ArgContExt : public Continuation { @@ -274,9 +291,13 @@ class ArgContExt : public Continuation { Ref ext; public: - ArgContExt(Ref _ext) : data(), ext(_ext) { + ArgContExt(Ref _ext) : data(), ext(std::move(_ext)) { + } + ArgContExt(Ref _ext, Ref _stack) : data(std::move(_stack)), ext(std::move(_ext)) { } - ArgContExt(Ref _ext, Ref _stack) : data(_stack), ext(_ext) { + ArgContExt(Ref _ext, const ControlData& _cdata) : data(_cdata), ext(std::move(_ext)) { + } + ArgContExt(Ref _ext, ControlData&& _cdata) : data(std::move(_cdata)), ext(std::move(_ext)) { } ArgContExt(const ArgContExt&) = default; ArgContExt(ArgContExt&&) = default; @@ -293,6 +314,7 @@ class ArgContExt : public Continuation { return new ArgContExt{*this}; } bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; class OrdCont : public Continuation { @@ -310,6 +332,10 @@ class OrdCont : public Continuation { OrdCont(Ref _code, int _cp, Ref _stack, int nargs = -1) : data(_cp, std::move(_stack), nargs), code(std::move(_code)) { } + OrdCont(Ref _code, const ControlData& _cdata) : data(_cdata), code(std::move(_code)) { + } + OrdCont(Ref _code, ControlData&& _cdata) : data(std::move(_cdata)), code(std::move(_code)) { + } OrdCont(const OrdCont&) = default; OrdCont(OrdCont&&) = default; ~OrdCont() override = default; @@ -342,6 +368,7 @@ class OrdCont : public Continuation { return Ref{true, *this}; } bool serialize(CellBuilder& cb) const override; + static Ref deserialize(CellSlice& cs, int mode = 0); }; struct GasLimits { diff --git a/crypto/vm/stack.cpp b/crypto/vm/stack.cpp index f3c8050459..7327df60c8 100644 --- a/crypto/vm/stack.cpp +++ b/crypto/vm/stack.cpp @@ -688,7 +688,7 @@ bool StackEntry::serialize(vm::CellBuilder& cb, int mode) const { return cb.store_long_bool(0x02ff, 16); } else if (!(mode & 1) && val->signed_fits_bits(64)) { // vm_stk_tinyint#01 value:int64 = VmStackValue; - return cb.store_long_bool(1, 8) && cb.store_int256_bool(std::move(val), 256); + return cb.store_long_bool(1, 8) && cb.store_int256_bool(std::move(val), 64); } else { // vm_stk_int#0201_ value:int257 = VmStackValue; return cb.store_long_bool(0x0200 / 2, 15) && cb.store_int256_bool(std::move(val), 257); @@ -701,7 +701,7 @@ bool StackEntry::serialize(vm::CellBuilder& cb, int mode) const { // _ cell:^Cell st_bits:(## 10) end_bits:(## 10) { st_bits <= end_bits } // st_ref:(#<= 4) end_ref:(#<= 4) { st_ref <= end_ref } = VmCellSlice; const auto& cs = *static_cast>(ref); - return cb.store_long_bool(4, 8) // vm_stk_slice#04 _:VmCellSlice = VmStackValue; + return ((mode & 0x1000) || cb.store_long_bool(4, 8)) // vm_stk_slice#04 _:VmCellSlice = VmStackValue; && cb.store_ref_bool(cs.get_base_cell()) // _ cell:^Cell && cb.store_long_bool(cs.cur_pos(), 10) // st_bits:(## 10) && cb.store_long_bool(cs.cur_pos() + cs.size(), 10) // end_bits:(## 10) @@ -738,6 +738,110 @@ bool StackEntry::serialize(vm::CellBuilder& cb, int mode) const { } } +bool StackEntry::deserialize(CellSlice& cs, int mode) { + clear(); + int t = (mode & 0xf000) ? ((mode >> 12) & 15) : (int)cs.prefetch_ulong(8); + switch (t) { + case 0: + // vm_stk_null#00 = VmStackValue; + return cs.advance(8); + case 1: { + // vm_stk_tinyint#01 value:int64 = VmStackValue; + td::RefInt256 val; + return !(mode & 1) && cs.advance(8) && cs.fetch_int256_to(64, val) && set_int(std::move(val)); + } + case 2: { + t = (int)cs.prefetch_ulong(16) & 0x1ff; + if (t == 0xff) { + // vm_stk_nan#02ff = VmStackValue; + return cs.advance(16) && set_int(td::RefInt256{true}); + } else { + // vm_stk_int#0201_ value:int257 = VmStackValue; + td::RefInt256 val; + return cs.fetch_ulong(15) == 0x0200 / 2 && cs.fetch_int256_to(257, val) && set_int(std::move(val)); + } + } + case 3: { + // vm_stk_cell#03 cell:^Cell = VmStackValue; + return cs.have_refs() && cs.advance(8) && set(t_cell, cs.fetch_ref()); + } + case 4: { + // _ cell:^Cell st_bits:(## 10) end_bits:(## 10) { st_bits <= end_bits } + // st_ref:(#<= 4) end_ref:(#<= 4) { st_ref <= end_ref } = VmCellSlice; + // vm_stk_slice#04 _:VmCellSlice = VmStackValue; + unsigned st_bits, end_bits, st_ref, end_ref; + Ref cell; + Ref csr; + return ((mode & 0xf000) || cs.advance(8)) // vm_stk_slice#04 + && cs.fetch_ref_to(cell) // cell:^Cell + && cs.fetch_uint_to(10, st_bits) // st_bits:(## 10) + && cs.fetch_uint_to(10, end_bits) // end_bits:(## 10) + && st_bits <= end_bits // { st_bits <= end_bits } + && cs.fetch_uint_to(3, st_ref) // st_ref:(#<= 4) + && cs.fetch_uint_to(3, end_ref) // end_ref:(#<= 4) + && st_ref <= end_ref && end_ref <= 4 // { st_ref <= end_ref } + && (csr = load_cell_slice_ref(std::move(cell))).not_null() // load cell slice + && csr->have(end_bits, end_ref) && + csr.write().skip_last(csr->size() - end_bits, csr->size_refs() - end_ref) && + csr.write().skip_first(st_bits, st_ref) && set(t_slice, std::move(csr)); + } + case 5: { + // vm_stk_builder#05 cell:^Cell = VmStackValue; + Ref cell; + Ref csr; + Ref cb{true}; + return cs.advance(8) && cs.fetch_ref_to(cell) && (csr = load_cell_slice_ref(std::move(cell))).not_null() && + cb.write().append_cellslice_bool(std::move(csr)) && set(t_builder, std::move(cb)); + } + case 6: { + // vm_stk_cont#06 cont:VmCont = VmStackValue; + Ref cont; + return !(mode & 2) && cs.advance(8) && Continuation::deserialize_to(cs, cont, mode) && + set(t_vmcont, std::move(cont)); + } + case 7: { + // vm_stk_tuple#07 len:(## 16) data:(VmTuple len) = VmStackValue; + int n; + if (!(cs.advance(8) && cs.fetch_uint_to(16, n))) { + return false; + } + Ref tuple{true, n}; + auto& t = tuple.write(); + if (n > 1) { + Ref head, tail; + n--; + if (!(cs.fetch_ref_to(head) && cs.fetch_ref_to(tail) && t[n].deserialize(std::move(tail), mode))) { + return false; + } + vm::CellSlice cs2; + while (--n > 0) { + if (!(cs2.load(std::move(head)) && cs2.fetch_ref_to(head) && cs2.fetch_ref_to(tail) && cs2.empty_ext() && + t[n].deserialize(std::move(tail), mode))) { + return false; + } + } + if (!t[0].deserialize(std::move(head), mode)) { + return false; + } + } else if (n == 1) { + return cs.have_refs() && t[0].deserialize(cs.fetch_ref(), mode); + } + return set(t_tuple, std::move(tuple)); + } + default: + return false; + } +} + +bool StackEntry::deserialize(Ref cell, int mode) { + if (cell.is_null()) { + clear(); + return false; + } + CellSlice cs = load_cell_slice(std::move(cell)); + return deserialize(cs, mode) && cs.empty_ext(); +} + bool Stack::serialize(vm::CellBuilder& cb, int mode) const { // vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack; unsigned n = depth(); @@ -758,4 +862,45 @@ bool Stack::serialize(vm::CellBuilder& cb, int mode) const { return cb.store_ref_bool(std::move(rest)) && stack[n - 1].serialize(cb, mode); } +bool Stack::deserialize(vm::CellSlice& cs, int mode) { + clear(); + // vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack; + int n; + if (!cs.fetch_uint_to(24, n)) { + return false; + } + if (!n) { + return true; + } + stack.resize(n); + Ref rest; + if (!(cs.fetch_ref_to(rest) && stack[n - 1].deserialize(cs, mode))) { + clear(); + return false; + } + for (int i = n - 2; i >= 0; --i) { + // vm_stk_cons#_ {n:#} rest:^(VmStackList n) tos:VmStackValue = VmStackList (n + 1); + vm::CellSlice cs2 = load_cell_slice(std::move(rest)); + if (!(cs2.fetch_ref_to(rest) && stack[i].deserialize(cs2, mode) && cs2.empty_ext())) { + clear(); + return false; + } + } + if (!load_cell_slice(std::move(rest)).empty_ext()) { + clear(); + return false; + } + return true; +} + +bool Stack::deserialize_to(vm::CellSlice& cs, Ref& stack, int mode) { + stack = Ref{true}; + if (stack.unique_write().deserialize(cs, mode)) { + return true; + } else { + stack.clear(); + return false; + } +} + } // namespace vm diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index 1fce43f25d..830f434019 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -135,6 +135,9 @@ class StackEntry { tp = t_null; return *this; } + bool set_int(td::RefInt256 value) { + return set(t_int, std::move(value)); + } bool empty() const { return tp == t_null; } @@ -168,6 +171,8 @@ class StackEntry { } // mode: +1 = disable short ints, +2 = disable continuations bool serialize(vm::CellBuilder& cb, int mode = 0) const; + bool deserialize(vm::CellSlice& cs, int mode = 0); + bool deserialize(Ref cell, int mode = 0); private: static bool is_list(const StackEntry* se); @@ -195,6 +200,11 @@ class StackEntry { Ref move_as() & { return tp == tag ? Ref{td::static_cast_ref(), std::move(ref)} : td::Ref{}; } + bool set(Type _tp, RefAny _ref) { + tp = _tp; + ref = std::move(_ref); + return ref.not_null() || tp == t_null; + } public: static StackEntry make_list(std::vector&& elems); @@ -511,6 +521,8 @@ class Stack : public td::CntObject { // mode: +1 = add eoln, +2 = Lisp-style lists void dump(std::ostream& os, int mode = 1) const; bool serialize(vm::CellBuilder& cb, int mode = 0) const; + bool deserialize(vm::CellSlice& cs, int mode = 0); + static bool deserialize_to(vm::CellSlice& cs, Ref& stack, int mode = 0); }; } // namespace vm diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index 8697ddccaf..5dcd0d54ad 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -165,6 +165,8 @@ int exec_set_global_common(VmState* st, unsigned idx) { if (idx >= 255) { throw VmError{Excno::range_chk, "tuple index out of range"}; } + static auto empty_tuple = Ref{true}; + st->set_c7(empty_tuple); // optimization; use only if no exception can be thrown until true set_c7() auto tpay = tuple_extend_set_index(tuple, idx, std::move(x)); if (tpay > 0) { st->consume_tuple_gas(tpay); @@ -193,7 +195,8 @@ void register_ton_config_ops(OpcodeTable& cp0) { .insert(OpcodeInstr::mksimple(0xf823, 16, "NOW", std::bind(exec_get_param, _1, 3, "NOW"))) .insert(OpcodeInstr::mksimple(0xf824, 16, "BLOCKLT", std::bind(exec_get_param, _1, 4, "BLOCKLT"))) .insert(OpcodeInstr::mksimple(0xf825, 16, "LTIME", std::bind(exec_get_param, _1, 5, "LTIME"))) - .insert(OpcodeInstr::mkfixedrange(0xf826, 0xf828, 16, 4, instr::dump_1c("GETPARAM "), exec_get_var_param)) + .insert(OpcodeInstr::mksimple(0xf826, 16, "RANDSEED", std::bind(exec_get_param, _1, 6, "RANDSEED"))) + .insert(OpcodeInstr::mksimple(0xf827, 16, "BALANCE", std::bind(exec_get_param, _1, 7, "BALANCE"))) .insert(OpcodeInstr::mksimple(0xf828, 16, "MYADDR", std::bind(exec_get_param, _1, 8, "MYADDR"))) .insert(OpcodeInstr::mksimple(0xf829, 16, "CONFIGROOT", std::bind(exec_get_param, _1, 9, "CONFIGROOT"))) .insert(OpcodeInstr::mkfixedrange(0xf82a, 0xf830, 16, 4, instr::dump_1c("GETPARAM "), exec_get_var_param)) @@ -206,6 +209,112 @@ void register_ton_config_ops(OpcodeTable& cp0) { .insert(OpcodeInstr::mkfixedrange(0xf861, 0xf880, 16, 5, instr::dump_1c_and(31, "SETGLOB "), exec_set_global)); } +static constexpr int randseed_idx = 6; + +td::RefInt256 generate_randu256(VmState* st) { + auto tuple = st->get_c7(); + auto t1 = tuple_index(*tuple, 0).as_tuple_range(255); + if (t1.is_null()) { + throw VmError{Excno::type_chk, "intermediate value is not a tuple"}; + } + auto seedv = tuple_index(*t1, randseed_idx).as_int(); + if (seedv.is_null()) { + throw VmError{Excno::type_chk, "random seed is not an integer"}; + } + unsigned char seed[32]; + if (!seedv->export_bytes(seed, 32, false)) { + throw VmError{Excno::range_chk, "random seed out of range"}; + } + unsigned char hash[64]; + digest::hash_str(hash, seed, 32); + if (!seedv.write().import_bytes(hash, 32, false)) { + throw VmError{Excno::range_chk, "cannot store new random seed"}; + } + td::RefInt256 res{true}; + if (!res.write().import_bytes(hash + 32, 32, false)) { + throw VmError{Excno::range_chk, "cannot store new random number"}; + } + static auto empty_tuple = Ref{true}; + st->set_c7(empty_tuple); // optimization; use only if no exception can be thrown until true set_c7() + tuple.write()[0].clear(); + t1.write().at(randseed_idx) = std::move(seedv); + st->consume_tuple_gas(t1); + tuple.write().at(0) = std::move(t1); + st->consume_tuple_gas(tuple); + st->set_c7(std::move(tuple)); + return res; +} + +int exec_randu256(VmState* st) { + VM_LOG(st) << "execute RANDU256"; + st->get_stack().push_int(generate_randu256(st)); + return 0; +} + +int exec_rand_int(VmState* st) { + VM_LOG(st) << "execute RAND"; + auto& stack = st->get_stack(); + stack.check_underflow(1); + auto x = stack.pop_int_finite(); + auto y = generate_randu256(st); + typename td::BigInt256::DoubleInt tmp{0}; + tmp.add_mul(*x, *y); + tmp.rshift(256, -1).normalize(); + stack.push_int(td::RefInt256{true, tmp}); + return 0; +} + +int exec_set_rand(VmState* st, bool mix) { + VM_LOG(st) << "execute " << (mix ? "ADDRAND" : "SETRAND"); + auto& stack = st->get_stack(); + stack.check_underflow(1); + auto x = stack.pop_int_finite(); + if (!x->unsigned_fits_bits(256)) { + throw VmError{Excno::range_chk, "new random seed out of range"}; + } + auto tuple = st->get_c7(); + auto t1 = tuple_index(*tuple, 0).as_tuple_range(255); + if (t1.is_null()) { + throw VmError{Excno::type_chk, "intermediate value is not a tuple"}; + } + if (mix) { + auto seedv = tuple_index(*t1, randseed_idx).as_int(); + if (seedv.is_null()) { + throw VmError{Excno::type_chk, "random seed is not an integer"}; + } + unsigned char buffer[64], hash[32]; + if (!std::move(seedv)->export_bytes(buffer, 32, false)) { + throw VmError{Excno::range_chk, "random seed out of range"}; + } + if (!x->export_bytes(buffer + 32, 32, false)) { + throw VmError{Excno::range_chk, "mixed seed value out of range"}; + } + digest::hash_str(hash, buffer, 64); + if (!x.write().import_bytes(hash, 32, false)) { + throw VmError{Excno::range_chk, "new random seed value out of range"}; + } + } + static auto empty_tuple = Ref{true}; + st->set_c7(empty_tuple); // optimization; use only if no exception can be thrown until true set_c7() + tuple.write()[0].clear(); + auto tpay = tuple_extend_set_index(t1, randseed_idx, std::move(x)); + if (tpay > 0) { + st->consume_tuple_gas(tpay); + } + tuple.unique_write()[0] = std::move(t1); + st->consume_tuple_gas(tuple); + st->set_c7(std::move(tuple)); + return 0; +} + +void register_prng_ops(OpcodeTable& cp0) { + using namespace std::placeholders; + cp0.insert(OpcodeInstr::mksimple(0xf810, 16, "RANDU256", exec_randu256)) + .insert(OpcodeInstr::mksimple(0xf811, 16, "RAND", exec_rand_int)) + .insert(OpcodeInstr::mksimple(0xf814, 16, "SETRAND", std::bind(exec_set_rand, _1, false))) + .insert(OpcodeInstr::mksimple(0xf815, 16, "ADDRAND", std::bind(exec_set_rand, _1, true))); +} + int exec_compute_hash(VmState* st, int mode) { VM_LOG(st) << "execute HASH" << (mode & 1 ? 'S' : 'C') << 'U'; Stack& stack = st->get_stack(); @@ -677,7 +786,7 @@ int exec_set_lib_code(VmState* st) { } return install_output_action(st, cb.finalize()); } - + int exec_change_lib(VmState* st) { VM_LOG(st) << "execute CHANGELIB"; Stack& stack = st->get_stack(); @@ -688,9 +797,9 @@ int exec_change_lib(VmState* st) { throw VmError{Excno::range_chk, "library hash must be non-negative"}; } CellBuilder cb; - if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n) - && cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4 - && cb.store_long_bool(mode * 2, 8) // mode:(## 7) { mode <= 2 } + if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n) + && cb.store_long_bool(0x26fa1dd4, 32) // action_change_library#26fa1dd4 + && cb.store_long_bool(mode * 2, 8) // mode:(## 7) { mode <= 2 } && cb.store_int256_bool(hash, 256, false))) { // libref:LibRef = OutAction; throw VmError{Excno::cell_ov, "cannot serialize library hash into an output action cell"}; } @@ -710,6 +819,7 @@ void register_ton_message_ops(OpcodeTable& cp0) { void register_ton_ops(OpcodeTable& cp0) { register_basic_gas_ops(cp0); register_ton_gas_ops(cp0); + register_prng_ops(cp0); register_ton_config_ops(cp0); register_ton_crypto_ops(cp0); register_ton_currency_address_ops(cp0); diff --git a/doc/tvm.tex b/doc/tvm.tex index 73a2145be2..cf6027e83a 100644 --- a/doc/tvm.tex +++ b/doc/tvm.tex @@ -1505,7 +1505,7 @@ \section*{Introduction} \begin{itemize} \item $0\leq m\leq1$ --- Indicates whether there is pre-multiplication ({\tt MULDIV} operation and its variants), possibly replaced by a left shift. \item $0\leq s\leq2$ --- Indicates whether either the multiplication or the division have been replaced by shifts: $s=0$---no replacement, $s=1$---division replaced by a right shift, $s=2$---multiplication replaced by a left shift (possible only for $m=1$). -\item $0\leq c\leq1$ --- Indicates whether there is a constant one-byte argument $tt$ for the shift operator (if $s\neq0$). For $s=0$, $c=0$. If $c=1$, then $0\leq tt\leq 255$, and the shift is performed by $tt+1$ bits. +\item $0\leq c\leq1$ --- Indicates whether there is a constant one-byte argument $tt$ for the shift operator (if $s\neq0$). For $s=0$, $c=0$. If $c=1$, then $0\leq tt\leq 255$, and the shift is performed by $tt+1$ bits. If $s\neq0$ and $c=0$, then the shift amount is provided to the instruction as a top-of-stack {\em Integer\/} in range $0\ldots256$. \item $1\leq d\leq3$ --- Indicates which results of division are required: $1$---only the quotient, $2$---only the remainder, $3$---both. \item $0\leq f\leq2$ --- Rounding mode: $0$---floor, $1$---nearest integer, $2$---ceiling (cf.~\ptref{sp:div.round}). \end{itemize} @@ -1524,6 +1524,14 @@ \section*{Introduction} \item {\tt A938$tt$} --- {\tt MODPOW2 $tt+1$}: ($x$ -- $x\bmod 2^{tt+1}$). \item {\tt A985} --- {\tt MULDIVR} ($x$ $y$ $z$ -- $q'$), where $q'=\lfloor xy/z+1/2\rfloor$. \item {\tt A98C} --- {\tt MULDIVMOD} ($x$ $y$ $z$ -- $q$ $r$), where $q:=\lfloor x\cdot y/z\rfloor$, $r:=x\cdot y\bmod z$ (same as {\tt */MOD} in Forth). +\item {\tt A9A4} --- {\tt MULRSHIFT} ($x$ $y$ $z$ -- $\lfloor xy\cdot2^{-z}\rfloor$) for $0\leq z\leq 256$. +\item {\tt A9A5} --- {\tt MULRSHIFTR} ($x$ $y$ $z$ -- $\lfloor xy\cdot2^{-z}+1/2\rfloor$) for $0\leq z\leq 256$. +\item {\tt A9B4$tt$} --- {\tt MULRSHIFT $tt+1$} ($x$ $y$ -- $\lfloor xy\cdot 2^{-tt-1}\rfloor$). +\item {\tt A9B5$tt$} --- {\tt MULRSHIFTR $tt+1$} ($x$ $y$ -- $\lfloor xy\cdot 2^{-tt-1}+1/2\rfloor$). +\item {\tt A9C4} --- {\tt LSHIFTDIV} ($x$ $y$ $z$ -- $\lfloor 2^zx/y\rfloor$) for $0\leq z\leq 256$. +\item {\tt A9C5} --- {\tt LSHIFTDIVR} ($x$ $y$ $z$ -- $\lfloor 2^zx/y+1/2\rfloor$) for $0\leq z\leq 256$. +\item {\tt A9D4$tt$} --- {\tt LSHIFTDIV $tt+1$} ($x$ $y$ -- $\lfloor 2^{tt+1}x/y\rfloor$). +\item {\tt A9D5$tt$} --- {\tt LSHIFTDIVR $tt+1$} ($x$ $y$ -- $\lfloor 2^{tt+1}x/y+1/2\rfloor$). \end{itemize} The most useful of these operations are {\tt DIV}, {\tt DIVMOD}, {\tt MOD}, {\tt DIVR}, {\tt DIVC}, {\tt MODPOW2 $t$}, and {\tt RSHIFTR $t$} (for integer arithmetic); and {\tt MULDIVMOD}, {\tt MULDIV}, {\tt MULDIVR}, {\tt LSHIFTDIVR $t$}, and {\tt MULRSHIFTR $t$} (for fixed-point arithmetic). @@ -2165,8 +2173,12 @@ \section*{Introduction} \end{itemize} \nxsubpoint\emb{Pseudo-random number generator primitives} -The pseudo-random number generator uses the random seed and (sometimes) other data kept in {\tt c7}. +The pseudo-random number generator uses the random seed (parameter \#6, cf.~\ptref{sp:prim.conf.param}), an unsigned 256-bit {\em Integer}, and (sometimes) other data kept in {\tt c7}. The initial value of the random seed before a smart contract is executed in TON Blockchain is a hash of the smart contract address and the global block random seed. If there are several runs of the same smart contract inside a block, then all of these runs will have the same random seed. This can be fixed, for example, by running {\tt LTIME; ADDRAND} before using the pseudo-random number generator for the first time. \begin{itemize} +\item {\tt F810} --- {\tt RANDU256} ( -- $x$), generates a new pseudo-random unsigned 256-bit {\em Integer}~$x$. The algorithm is as follows: if $r$ is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its $\opsc{sha512}(r)$ is computed; the first 32 bytes of this hash are stored as the new value $r'$ of the random seed, and the remaining 32 bytes are returned as the next random value~$x$. +\item {\tt F811} --- {\tt RAND} ($y$ -- $z$), generates a new pseudo-random integer $z$ in the range $0\ldots y-1$ (or $y\ldots-1$, if $y<0$). More precisely, an unsigned random value $x$ is generated as in {\tt RAND256U}; then $z:=\lfloor xy/2^{256}\rfloor$ is computed. Equivalent to {\tt RANDU256; MULRSHIFT 256}. +\item {\tt F814} --- {\tt SETRAND} ($x$ -- ), sets the random seed to unsigned 256-bit {\em Integer\/}~$x$. +\item {\tt F815} --- {\tt ADDRAND} ($x$ -- ), mixes unsigned 256-bit {\em Integer\/}~$x$ into the random seed $r$ by setting the random seed to $\Sha$ of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed $r$, and the second with the big-endian representation of $x$. \item {\tt F810}--{\tt F81F} --- Reserved for pseudo-random number generator primitives. \end{itemize} @@ -2177,8 +2189,8 @@ \section*{Introduction} \item {\tt F823} --- {\tt NOW} ( -- $x$), returns the current Unix time as an {\em Integer}. If it is impossible to recover the requested value starting from {\tt c7}, throws a type checking or range checking exception as appropriate. Equivalent to {\tt GETPARAM 3}. \item {\tt F824} --- {\tt BLOCKLT} ( -- $x$), returns the starting logical time of the current block. Equivalent to {\tt GETPARAM 4}. \item {\tt F825} --- {\tt LTIME} ( -- $x$), returns the logical time of the current transaction. Equivalent to {\tt GETPARAM 5}. -\item {\tt F826} --- {\tt BALANCE} ( -- $t$), returns the remaining balance of the smart contract as a {\em Tuple\/} consisting of an {\em Integer} (the remaining Gram balance in nanograms) and a {\em Maybe Cell} (a dictionary with 32-bit keys representing the balance of ``extra currencies''). Equivalent to {\tt GETPARAM 6}. Note that {\tt RAW} primitives such as {\tt SENDRAWMSG} do not update this field. -\item {\tt F827} --- {\tt RANDSEED} ( -- $x$), returns the current random seed as an unsigned 256-bit {\em Integer}. Equivalent to {\tt GETPARAM 7}. +\item {\tt F826} --- {\tt RANDSEED} ( -- $x$), returns the current random seed as an unsigned 256-bit {\em Integer}. Equivalent to {\tt GETPARAM 6}. +\item {\tt F827} --- {\tt BALANCE} ( -- $t$), returns the remaining balance of the smart contract as a {\em Tuple\/} consisting of an {\em Integer} (the remaining Gram balance in nanograms) and a {\em Maybe Cell} (a dictionary with 32-bit keys representing the balance of ``extra currencies''). Equivalent to {\tt GETPARAM 7}. Note that {\tt RAW} primitives such as {\tt SENDRAWMSG} do not update this field. \item {\tt F828} --- {\tt MYADDR} ( -- $s$), returns the internal address of the current smart contract as a {\em Slice\/} with a {\tt MsgAddressInt}. If necessary, it can be parsed further using primitives such as {\tt PARSESTDADDR} or {\tt REWRITESTDADDR}. Equivalent to {\tt GETPARAM 8}. \item {\tt F829} --- {\tt CONFIGROOT} ( -- $D$), returns the {\em Maybe Cell\/}~$D$ with the current global configuration dictionary. Equivalent to {\tt GETPARAM 9}. \item {\tt F830} --- {\tt CONFIGDICT} ( -- $D$ $32$), returns the global configuration dictionary along with its key length (32). Equivalent to {\tt CONFIGROOT}; {\tt PUSHINT 32}. diff --git a/tddb/td/db/RocksDb.cpp b/tddb/td/db/RocksDb.cpp index 16f4b4eb80..47fdf4f5c2 100644 --- a/tddb/td/db/RocksDb.cpp +++ b/tddb/td/db/RocksDb.cpp @@ -19,6 +19,7 @@ #include "td/db/RocksDb.h" #include "rocksdb/db.h" +#include "rocksdb/table.h" #include "rocksdb/statistics.h" #include "rocksdb/write_batch.h" #include "rocksdb/utilities/optimistic_transaction_db.h" @@ -63,6 +64,13 @@ Result RocksDb::open(std::string path) { auto statistics = rocksdb::CreateDBStatistics(); { rocksdb::Options options; + + static auto cache = rocksdb::NewLRUCache(1 << 30); + + rocksdb::BlockBasedTableOptions table_options; + table_options.block_cache = cache; + options.table_factory.reset(rocksdb::NewBlockBasedTableFactory(table_options)); + options.manual_wal_flush = true; options.create_if_missing = true; options.max_background_compactions = 4; @@ -82,6 +90,10 @@ std::unique_ptr RocksDb::snapshot() { } std::string RocksDb::stats() const { + std::string out; + db_->GetProperty("rocksdb.stats", &out); + //db_->GetProperty("rocksdb.cur-size-all-mem-tables", &out); + return out; return statistics_->ToString(); } diff --git a/tddb/td/db/RocksDb.h b/tddb/td/db/RocksDb.h index 04abba778d..77ed46b34b 100644 --- a/tddb/td/db/RocksDb.h +++ b/tddb/td/db/RocksDb.h @@ -60,6 +60,10 @@ class RocksDb : public KeyValue { RocksDb &operator=(RocksDb &&); ~RocksDb(); + std::shared_ptr raw_db() const { + return db_; + }; + private: std::shared_ptr db_; std::shared_ptr statistics_; diff --git a/tddb/test/binlog.cpp b/tddb/test/binlog.cpp index fc46157ca6..d2cd35225f 100644 --- a/tddb/test/binlog.cpp +++ b/tddb/test/binlog.cpp @@ -679,7 +679,7 @@ TEST(Buffers, CyclicBufferSimple) { auto data = td::rand_string('a', 'z', 100001); td::Slice write_slice = data; td::Slice read_slice = data; - for (size_t i = 1; (int)i < options.count; i++) { + for (size_t i = 1; i < options.count; i++) { ASSERT_EQ((i - 1) * options.chunk_size, reader.reader_size()); ASSERT_EQ((i - 1) * options.chunk_size, writer.writer_size()); auto slice = writer.prepare_write(); diff --git a/tddb/test/key_value.cpp b/tddb/test/key_value.cpp index 550ccd2fad..cf147e5721 100644 --- a/tddb/test/key_value.cpp +++ b/tddb/test/key_value.cpp @@ -170,7 +170,11 @@ TEST(KeyValue, Bench) { TEST(KeyValue, Stress) { return; td::Slice db_name = "testdb"; - td::RocksDb::destroy(db_name).ignore(); + size_t N = 20; + auto db_name_i = [&](size_t i) { return PSTRING() << db_name << i; }; + for (size_t i = 0; i < N; i++) { + td::RocksDb::destroy(db_name_i(i)).ignore(); + } td::actor::Scheduler scheduler({6}); auto watcher = td::create_shared_destructor([] { td::actor::SchedulerContext::get()->stop(); }); @@ -186,9 +190,13 @@ TEST(KeyValue, Stress) { void tear_down() override { } void loop() override { + if (stat_at_.is_in_past()) { + stat_at_ = td::Timestamp::in(10); + LOG(ERROR) << db_->stats(); + } if (!kv_) { - kv_ = td::KeyValueAsync( - std::make_unique(td::RocksDb::open(db_name_).move_as_ok())); + db_ = std::make_shared(td::RocksDb::open(db_name_).move_as_ok()); + kv_ = td::KeyValueAsync(db_); set_start_at_ = td::Timestamp::now(); } if (next_set_ && next_set_.is_in_past()) { @@ -207,6 +215,7 @@ TEST(KeyValue, Stress) { private: std::shared_ptr watcher_; + std::shared_ptr db_; td::optional> kv_; std::string db_name_; int left_cnt_ = 1000000000; @@ -214,6 +223,7 @@ TEST(KeyValue, Stress) { td::Timestamp next_set_ = td::Timestamp::now(); td::Timestamp set_start_at_; td::Timestamp set_finish_at_; + td::Timestamp stat_at_ = td::Timestamp::in(10); void do_set() { td::UInt128 key = td::UInt128::zero(); @@ -236,8 +246,10 @@ TEST(KeyValue, Stress) { } } }; - scheduler.run_in_context([watcher = std::move(watcher), &db_name]() mutable { - td::actor::create_actor("Worker", watcher, db_name.str()).release(); + scheduler.run_in_context([watcher = std::move(watcher), &db_name_i, &N]() mutable { + for (size_t i = 0; i < N; i++) { + td::actor::create_actor("Worker", watcher, db_name_i(i)).release(); + } watcher.reset(); }); From acf16718e6385685828316ec116f12cb60455047 Mon Sep 17 00:00:00 2001 From: ton Date: Sun, 29 Dec 2019 12:14:12 +0300 Subject: [PATCH 027/667] updated func/fift - updated func/fift - updated liteclient/liteserver - bugfixes --- crypto/block/block-parse.cpp | 22 ++ crypto/block/block-parse.h | 2 + crypto/block/block.tlb | 30 ++ crypto/block/create-state.cpp | 83 +++++ crypto/block/dump-block.cpp | 33 +- crypto/fift/lib/Asm.fif | 6 +- crypto/fift/lib/Fift.fif | 12 + crypto/fift/words.cpp | 122 +++---- crypto/func/abscode.cpp | 6 + crypto/func/analyzer.cpp | 13 +- crypto/func/builtins.cpp | 55 ++- crypto/func/codegen.cpp | 130 +++++-- crypto/func/func.cpp | 5 + crypto/func/func.h | 51 ++- crypto/func/gen-abscode.cpp | 13 +- crypto/func/keywords.cpp | 1 + crypto/func/optimize.cpp | 46 ++- crypto/func/parse-func.cpp | 114 ++++++- crypto/func/stack-transform.cpp | 50 +++ crypto/func/test/a11.fc | 28 ++ crypto/func/test/a6_5.fc | 20 ++ crypto/func/test/c1.fc | 37 ++ crypto/func/test/c2.fc | 10 + crypto/func/test/c2_1.fc | 7 + crypto/parser/symtable.h | 2 +- crypto/smartcont/CreateState.fif | 4 + crypto/test/test-db.cpp | 317 +++++++++++++++++- crypto/tl/tlbc-gen-cpp.cpp | 26 ++ crypto/tl/tlblib.cpp | 21 ++ crypto/tl/tlblib.hpp | 44 +++ crypto/vm/cells/MerkleProof.cpp | 97 +++++- crypto/vm/cells/MerkleProof.h | 3 + crypto/vm/cells/MerkleUpdate.cpp | 2 +- crypto/vm/continuation.cpp | 14 +- crypto/vm/continuation.h | 8 +- crypto/vm/stack.hpp | 12 + doc/fiftbase.tex | 15 +- tdactor/test/actors_core.cpp | 4 +- tl/generate/scheme/lite_api.tl | 2 + tl/generate/scheme/lite_api.tlo | Bin 11676 -> 12368 bytes tl/generate/scheme/tonlib_api.tl | 4 +- tl/generate/scheme/tonlib_api.tlo | Bin 19324 -> 19324 bytes .../validator-engine-console.h | 1 + validator/impl/liteserver.cpp | 64 +++- validator/impl/liteserver.hpp | 7 +- 45 files changed, 1359 insertions(+), 184 deletions(-) create mode 100644 crypto/func/test/a11.fc create mode 100644 crypto/func/test/a6_5.fc create mode 100644 crypto/func/test/c1.fc create mode 100644 crypto/func/test/c2.fc create mode 100644 crypto/func/test/c2_1.fc diff --git a/crypto/block/block-parse.cpp b/crypto/block/block-parse.cpp index 29d6ebea0e..ae1df3438d 100644 --- a/crypto/block/block-parse.cpp +++ b/crypto/block/block-parse.cpp @@ -241,6 +241,28 @@ bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, ton::WorkchainId& wor return false; } +bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, + const ton::StdSmcAddress& addr) const { + if (workchain >= -128 && workchain < 128) { + return cb.store_long_bool(4, 3) // addr_std$10 anycast:(Maybe Anycast) + && cb.store_long_bool(workchain, 8) // workchain_id:int8 + && cb.store_bits_bool(addr); // address:bits256 = MsgAddressInt; + } else { + return cb.store_long_bool(0xd00, 12) // addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) + && cb.store_long_bool(workchain, 32) // workchain_id:int32 + && cb.store_bits_bool(addr); // address:(bits addr_len) = MsgAddressInt; + } +} + +Ref MsgAddressInt::pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const { + vm::CellBuilder cb; + if (store_std_address(cb, workchain, addr)) { + return vm::load_cell_slice_ref(cb.finalize()); + } else { + return {}; + } +} + const MsgAddressInt t_MsgAddressInt; bool MsgAddress::validate_skip(vm::CellSlice& cs, bool weak) const { diff --git a/crypto/block/block-parse.h b/crypto/block/block-parse.h index 410a8ce575..8ea3325e72 100644 --- a/crypto/block/block-parse.h +++ b/crypto/block/block-parse.h @@ -285,6 +285,8 @@ struct MsgAddressInt final : TLB_Complex { bool rewrite = true) const; bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr, bool rewrite = true) const; + bool store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const; + Ref pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const; }; extern const MsgAddressInt t_MsgAddressInt; diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 882e0d7b28..a30cd89a87 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -727,3 +727,33 @@ after:^VmCont = VmCont; vmc_while_body$110011 cond:^VmCont body:^VmCont after:^VmCont = VmCont; vmc_pushint$1111 value:int32 next:^VmCont = VmCont; + +// +// DNS RECORDS +// +_ (HashmapE 16 ^DNSRecord) = DNS_RecordSet; + +chunk_ref$_ {n:#} ref:^(TextChunks (n + 1)) = TextChunkRef (n + 1); +chunk_ref_empty$_ = TextChunkRef 0; +text_chunk$_ {n:#} len:(## 8) data:(bits (len * 8)) next:(TextChunkRef n) = TextChunks (n + 1); +text_chunk_empty$_ = TextChunks 0; +text$_ chunks:(## 8) rest:(TextChunks chunks) = Text; +dns_text#1eda _:Text = DNSRecord; + +dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; // usually in record #-1 + +dns_adnl_address#ad01 adnl_addr:bits256 flags:(## 8) { flags <= 1 } + proto_list:flags . 0?ProtoList = DNSRecord; // often in record #2 +proto_list_nil$0 = ProtoList; +proto_list_next$1 head:Protocol tail:ProtoList = ProtoList; +proto_http#4854 = Protocol; + +dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } + cap_list:flags . 0?SmcCapList = DNSRecord; // often in record #1 +cap_list_nil$0 = SmcCapList; +cap_list_next$1 head:SmcCapability tail:SmcCapList = SmcCapList; +cap_method_seqno#5371 = SmcCapability; +cap_method_pubkey#71f4 = SmcCapability; +cap_is_wallet#2177 = SmcCapability; +cap_name#ff name:Text = SmcCapability; + diff --git a/crypto/block/create-state.cpp b/crypto/block/create-state.cpp index 8b92a24fa2..02896e7b78 100644 --- a/crypto/block/create-state.cpp +++ b/crypto/block/create-state.cpp @@ -633,6 +633,88 @@ void init_words_custom(fift::Dictionary& d) { d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr); } +tlb::TypenameLookup tlb_dict; + +// ( S -- T -1 or 0 ) Looks up TLB type by name +void interpret_tlb_type_lookup(vm::Stack& stack) { + auto ptr = tlb_dict.lookup(stack.pop_string()); + if (ptr) { + stack.push_make_object(ptr); + } + stack.push_bool(ptr); +} + +td::Ref pop_tlb_type(vm::Stack& stack) { + auto res = stack.pop_object(); + if (res.is_null()) { + throw vm::VmError{vm::Excno::type_chk, "not a TLB type"}; + } + return res; +} + +// ( T -- S ) Gets TLB type name +void interpret_tlb_type_name(vm::Stack& stack) { + stack.push_string((*pop_tlb_type(stack))->get_type_name()); +} + +// ( T -- ) Prints TLB type name +void interpret_print_tlb_type(vm::Stack& stack) { + std::cout << (*pop_tlb_type(stack))->get_type_name(); +} + +// ( s T -- ) Dumps (part of) slice s as a value of TLB type T +void interpret_tlb_dump_as(vm::Stack& stack) { + auto tp = pop_tlb_type(stack); + (*tp)->print(std::cout, stack.pop_cellslice()); +} + +// ( s T -- s' S -1 or 0 ) +// Detects prefix of slice s that is a value of TLB type T, returns the remainder as s', and prints the value into String S. +void interpret_tlb_dump_to_str(vm::Stack& stack) { + auto tp = pop_tlb_type(stack); + auto cs = stack.pop_cellslice(); + std::ostringstream os; + bool ok = (*tp)->print_skip(os, cs.write()); + if (ok) { + stack.push(std::move(cs)); + stack.push_string(os.str()); + } + stack.push_bool(ok); +} + +// ( s T -- s' -1 or 0 ) Skips the only prefix of slice s that can be a value of TLB type T +void interpret_tlb_skip(vm::Stack& stack) { + auto tp = pop_tlb_type(stack); + auto cs = stack.pop_cellslice(); + bool ok = (*tp)->skip(cs.write()); + if (ok) { + stack.push(std::move(cs)); + } + stack.push_bool(ok); +} + +// ( s T -- s' -1 or 0 ) Checks whether a prefix of slice s is a valid value of TLB type T, and skips it +void interpret_tlb_validate_skip(vm::Stack& stack) { + auto tp = pop_tlb_type(stack); + auto cs = stack.pop_cellslice(); + bool ok = (*tp)->validate_skip(cs.write()); + if (ok) { + stack.push(std::move(cs)); + } + stack.push_bool(ok); +} + +void init_words_tlb(fift::Dictionary& d) { + tlb_dict.register_types(block::gen::register_simple_types); + d.def_stack_word("tlb-type-lookup ", interpret_tlb_type_lookup); + d.def_stack_word("tlb-type-name ", interpret_tlb_type_name); + d.def_stack_word("tlb. ", interpret_print_tlb_type); + d.def_stack_word("tlb-dump-as ", interpret_tlb_dump_as); + d.def_stack_word("(tlb-dump-str?) ", interpret_tlb_dump_to_str); + d.def_stack_word("tlb-skip ", interpret_tlb_skip); + d.def_stack_word("tlb-validate-skip ", interpret_tlb_validate_skip); +} + void usage(const char* progname) { std::cerr << "Creates initial state for a TON blockchain, using configuration defined by Fift-language source files\n"; @@ -739,6 +821,7 @@ int main(int argc, char* const argv[]) { fift::init_words_vm(config.dictionary); fift::init_words_ton(config.dictionary); init_words_custom(config.dictionary); + init_words_tlb(config.dictionary); if (script_mode) { fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind, diff --git a/crypto/block/dump-block.cpp b/crypto/block/dump-block.cpp index 86465bf64f..25eb0eed24 100644 --- a/crypto/block/dump-block.cpp +++ b/crypto/block/dump-block.cpp @@ -191,7 +191,8 @@ void test2(vm::CellSlice& cs) { } void usage() { - std::cout << "usage: dump-block [-S][]\n\tor dump-block -h\n\tDumps specified blockchain block or state " + std::cout << "usage: dump-block [-t][-S][]\n\tor dump-block -h\n\tDumps specified blockchain " + "block or state " "from , or runs some tests\n\t-S\tDump a blockchain state instead of a block\n"; std::exit(2); } @@ -199,15 +200,20 @@ void usage() { int main(int argc, char* const argv[]) { int i; int new_verbosity_level = VERBOSITY_NAME(INFO); - bool dump_state = false, dump_vmcont = false; + const char* tname = nullptr; + const tlb::TLB* type = &block::gen::t_Block; auto zerostate = std::make_unique(); - while ((i = getopt(argc, argv, "CShv:")) != -1) { + while ((i = getopt(argc, argv, "CSt:hv:")) != -1) { switch (i) { case 'C': - dump_vmcont = true; + type = &block::gen::t_VmCont; break; case 'S': - dump_state = true; + type = &block::gen::t_ShardStateUnsplit; + break; + case 't': + tname = optarg; + type = nullptr; break; case 'v': new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer(td::Slice(optarg))); @@ -233,13 +239,18 @@ int main(int argc, char* const argv[]) { vm::CellSlice cs{vm::NoVm(), boc}; cs.print_rec(std::cout); std::cout << std::endl; - auto& type = !dump_vmcont - ? (dump_state ? (const tlb::TLB&)block::gen::t_ShardStateUnsplit : block::gen::t_Block) - : block::gen::t_VmCont; - type.print_ref(std::cout, boc); + if (!type) { + tlb::TypenameLookup dict(block::gen::register_simple_types); + type = dict.lookup(tname); + if (!type) { + std::cerr << "unknown TL-B type " << tname << std::endl; + std::exit(3); + } + } + type->print_ref(std::cout, boc); std::cout << std::endl; - bool ok = type.validate_ref(boc); - std::cout << "(" << (ok ? "" : "in") << "valid " << type << ")" << std::endl; + bool ok = type->validate_ref(boc); + std::cout << "(" << (ok ? "" : "in") << "valid " << *type << ")" << std::endl; } } if (!done) { diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 997e06a335..f0035a00e5 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1064,19 +1064,23 @@ x{FFF0} @Defop SETCPX variable @proccnt variable @proclist variable @procdict +variable @gvarcnt 19 constant @procdictkeylen { @proclist @ cons @proclist ! } : @proclistadd { dup @procdictkeylen fits not abort"procedure index out of range" 1 'nop does swap dup @proclistadd 0 (create) } : @declproc +{ 1 'nop does swap 0 (create) } : @declglobvar { @proccnt @ 1+ dup @proccnt ! @declproc } : @newproc -{ 0 =: main @proclist null! @proccnt 0! +{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar +{ 0 =: main @proclist null! @proccnt 0! @gvarcnt 0! { bl word @newproc } : NEWPROC { bl word dup (def?) ' drop ' @newproc cond } : DECLPROC { bl word dup find { nip execute <> abort"method redefined with different id" } { swap @declproc } cond } : DECLMETHOD + { bl word @newglobvar } : DECLGLOBVAR "main" @proclistadd dictnew @procdict ! } : PROGRAM{ diff --git a/crypto/fift/lib/Fift.fif b/crypto/fift/lib/Fift.fif index 4a16aca661..edb4ad9299 100644 --- a/crypto/fift/lib/Fift.fif +++ b/crypto/fift/lib/Fift.fif @@ -116,3 +116,15 @@ variable base { 9 hold } : +tab { "" swap { 0 word 2dup $cmp } { rot swap $+ +cr swap } while 2drop } : scan-until-word { 0 word -trailing scan-until-word 1 'nop } ::_ $<< +{ 0x40 runvmx } : runvmcode +{ 0x48 runvmx } : gasrunvmcode +{ 0x43 runvmx } : runvmdict +{ 0x4b runvmx } : gasrunvmdict +{ 0x45 runvmx } : runvm +{ 0x4d runvmx } : gasrunvm +{ 0x55 runvmx } : runvmctx +{ 0x5d runvmx } : gasrunvmctx +{ 0x75 runvmx } : runvmctxact +{ 0x7d runvmx } : gasrunvmctxact +{ 0x35 runvmx } : runvmctxactq +{ 0x3d runvmx } : gasrunvmctxactq diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 043fc17f1c..1fc12a2c35 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -1512,11 +1512,12 @@ void interpret_store_dict(vm::Stack& stack) { } // val key dict keylen -- dict' ? -void interpret_dict_add_u(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder, bool sgnd) { +void interpret_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder, int sgnd) { int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits); vm::Dictionary dict{stack.pop_maybe_cell(), n}; unsigned char buffer[vm::Dictionary::max_key_bytes]; - vm::BitSlice key = dict.integer_key(stack.pop_int(), n, sgnd, buffer); + vm::BitSlice key = + (sgnd >= 0) ? dict.integer_key(stack.pop_int(), n, sgnd, buffer) : stack.pop_cellslice()->prefetch_bits(n); if (!key.is_valid()) { throw IntError{"not enough bits for a dictionary key"}; } @@ -1530,11 +1531,12 @@ void interpret_dict_add_u(vm::Stack& stack, vm::Dictionary::SetMode mode, bool a stack.push_bool(res); } -void interpret_dict_get_u(vm::Stack& stack, bool sgnd) { +void interpret_dict_get(vm::Stack& stack, int sgnd) { int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits); vm::Dictionary dict{stack.pop_maybe_cell(), n}; unsigned char buffer[vm::Dictionary::max_key_bytes]; - vm::BitSlice key = dict.integer_key(stack.pop_int(), n, sgnd, buffer); + vm::BitSlice key = + (sgnd >= 0) ? dict.integer_key(stack.pop_int(), n, sgnd, buffer) : stack.pop_cellslice()->prefetch_bits(n); if (!key.is_valid()) { throw IntError{"not enough bits for a dictionary key"}; } @@ -2169,6 +2171,7 @@ class StringLogger : public td::LogInterface { } std::string res; }; + class OstreamLogger : public td::LogInterface { public: explicit OstreamLogger(std::ostream* stream) : stream_(stream) { @@ -2191,59 +2194,42 @@ std::vector> get_vm_libraries() { } } -void interpret_run_vm_code(IntCtx& ctx, bool with_gas) { - long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty; - auto cs = ctx.stack.pop_cellslice(); - OstreamLogger ostream_logger(ctx.error_stream); - auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); - vm::GasLimits gas{gas_limit}; - int res = vm::run_vm_code(cs, ctx.stack, 0, nullptr, log, nullptr, &gas, get_vm_libraries()); - ctx.stack.push_smallint(res); - if (with_gas) { - ctx.stack.push_smallint(gas.gas_consumed()); +// mode: -1 = pop from stack +// +1 = same_c3 (set c3 to code) +// +2 = push_0 (push an implicit 0 before running the code) +// +4 = load c4 (persistent data) from stack and return its final value +// +8 = load gas limit from stack and return consumed gas +// +16 = load c7 (smart-contract context) +// +32 = return c5 (actions) +// +64 = log vm ops to stderr +void interpret_run_vm(IntCtx& ctx, int mode) { + if (mode < 0) { + mode = ctx.stack.pop_smallint_range(0xff); } -} - -void interpret_run_vm_dict(IntCtx& ctx, bool with_gas) { - long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty; - auto cs = ctx.stack.pop_cellslice(); - OstreamLogger ostream_logger(ctx.error_stream); - auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); - vm::GasLimits gas{gas_limit}; - int res = vm::run_vm_code(cs, ctx.stack, 3, nullptr, log, nullptr, &gas, get_vm_libraries()); - ctx.stack.push_smallint(res); - if (with_gas) { - ctx.stack.push_smallint(gas.gas_consumed()); + bool with_data = mode & 4; + Ref c7; + Ref data, actions; + long long gas_limit = (mode & 8) ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty; + if (mode & 16) { + c7 = ctx.stack.pop_tuple(); } -} - -void interpret_run_vm(IntCtx& ctx, bool with_gas) { - long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty; - auto data = ctx.stack.pop_cell(); - auto cs = ctx.stack.pop_cellslice(); - OstreamLogger ostream_logger(ctx.error_stream); - auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); - vm::GasLimits gas{gas_limit}; - int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries()); - ctx.stack.push_smallint(res); - ctx.stack.push_cell(std::move(data)); - if (with_gas) { - ctx.stack.push_smallint(gas.gas_consumed()); + if (with_data) { + data = ctx.stack.pop_cell(); } -} - -void interpret_run_vm_c7(IntCtx& ctx, bool with_gas) { - long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty; - auto c7 = ctx.stack.pop_tuple(); - auto data = ctx.stack.pop_cell(); auto cs = ctx.stack.pop_cellslice(); OstreamLogger ostream_logger(ctx.error_stream); - auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr); + auto log = create_vm_log((mode & 64) && ctx.error_stream ? &ostream_logger : nullptr); vm::GasLimits gas{gas_limit}; - int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7)); + int res = + vm::run_vm_code(cs, ctx.stack, mode & 3, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7), &actions); ctx.stack.push_smallint(res); - ctx.stack.push_cell(std::move(data)); - if (with_gas) { + if (with_data) { + ctx.stack.push_cell(std::move(data)); + } + if (mode & 32) { + ctx.stack.push_cell(std::move(actions)); + } + if (mode & 8) { ctx.stack.push_smallint(gas.gas_consumed()); } } @@ -2759,16 +2745,21 @@ void init_words_common(Dictionary& d) { d.def_stack_word("dict, ", interpret_store_dict); d.def_stack_word("dict@ ", std::bind(interpret_load_dict, _1, false)); d.def_stack_word("dict@+ ", std::bind(interpret_load_dict, _1, true)); - d.def_stack_word("udict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, false, false)); - d.def_stack_word("udict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, false, false)); - d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, true, false)); - d.def_stack_word("b>udict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, true, false)); - d.def_stack_word("udict@ ", std::bind(interpret_dict_get_u, _1, false)); - d.def_stack_word("idict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, false, true)); - d.def_stack_word("idict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, false, true)); - d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, true, true)); - d.def_stack_word("b>idict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, true, true)); - d.def_stack_word("idict@ ", std::bind(interpret_dict_get_u, _1, true)); + d.def_stack_word("sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, -1)); + d.def_stack_word("sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, -1)); + d.def_stack_word("b>sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, -1)); + d.def_stack_word("b>sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, -1)); + d.def_stack_word("sdict@ ", std::bind(interpret_dict_get, _1, -1)); + d.def_stack_word("udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 0)); + d.def_stack_word("udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 0)); + d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 0)); + d.def_stack_word("b>udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 0)); + d.def_stack_word("udict@ ", std::bind(interpret_dict_get, _1, 0)); + d.def_stack_word("idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 1)); + d.def_stack_word("idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 1)); + d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 1)); + d.def_stack_word("b>idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 1)); + d.def_stack_word("idict@ ", std::bind(interpret_dict_get, _1, 1)); d.def_stack_word("pfxdict!+ ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Add, false)); d.def_stack_word("pfxdict! ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Set, false)); d.def_stack_word("pfxdict@ ", interpret_pfx_dict_get); @@ -2867,14 +2858,9 @@ void init_words_vm(Dictionary& d) { vm::init_op_cp0(); // vm run d.def_stack_word("vmlibs ", std::bind(interpret_literal, _1, vm::StackEntry{vm_libraries})); - d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm_code, _1, false)); - d.def_ctx_word("gasrunvmcode ", std::bind(interpret_run_vm_code, _1, true)); - d.def_ctx_word("runvmdict ", std::bind(interpret_run_vm_dict, _1, false)); - d.def_ctx_word("gasrunvmdict ", std::bind(interpret_run_vm_dict, _1, true)); - d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, false)); - d.def_ctx_word("gasrunvm ", std::bind(interpret_run_vm, _1, true)); - d.def_ctx_word("runvmctx ", std::bind(interpret_run_vm_c7, _1, false)); - d.def_ctx_word("gasrunvmctx ", std::bind(interpret_run_vm_c7, _1, true)); + // d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm, _1, 0x40)); + // d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, 0x45)); + d.def_ctx_word("runvmx ", std::bind(interpret_run_vm, _1, -1)); d.def_ctx_word("dbrunvm ", interpret_db_run_vm); d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel); d.def_stack_word("vmcont, ", interpret_store_vm_cont); diff --git a/crypto/func/abscode.cpp b/crypto/func/abscode.cpp index 8ff204f13c..3d3348a166 100644 --- a/crypto/func/abscode.cpp +++ b/crypto/func/abscode.cpp @@ -343,6 +343,12 @@ void Op::show(std::ostream& os, const std::vector& vars, std::string pfx show_var_list(os, left, vars); os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl; break; + case _SetGlob: + os << pfx << dis << "SETGLOB "; + os << (fun_ref ? fun_ref->name() : "(null)") << " := "; + show_var_list(os, right, vars); + os << std::endl; + break; case _Repeat: os << pfx << dis << "REPEAT "; show_var_list(os, left, vars); diff --git a/crypto/func/analyzer.cpp b/crypto/func/analyzer.cpp index 354486cf03..14d0a08418 100644 --- a/crypto/func/analyzer.cpp +++ b/crypto/func/analyzer.cpp @@ -365,6 +365,13 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) { } return std_compute_used_vars(); } + case _SetGlob: { + // GLOB = right + if (right.empty() && edit) { + disable(); + } + return std_compute_used_vars(right.empty()); + } case _Let: { // left = right std::size_t cnt = next_var_info.count_used(left); @@ -531,6 +538,7 @@ bool prune_unreachable(std::unique_ptr& ops) { switch (op.cl) { case Op::_IntConst: case Op::_GlobVar: + case Op::_SetGlob: case Op::_Call: case Op::_CallInd: case Op::_Import: @@ -694,7 +702,6 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { values.add_newval(left[0]).set_const(int_const); break; } - case _GlobVar: case _Call: { prepare_args(values); auto func = dynamic_cast(fun_ref->value); @@ -717,12 +724,15 @@ VarDescrList Op::fwd_analyze(VarDescrList values) { } break; } + case _GlobVar: case _CallInd: { for (var_idx_t i : left) { values.add_newval(i); } break; } + case _SetGlob: + break; case _Let: { std::vector old_val; assert(left.size() == right.size()); @@ -832,6 +842,7 @@ bool Op::mark_noreturn() { case _Import: case _IntConst: case _Let: + case _SetGlob: case _GlobVar: case _CallInd: case _Call: diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 30beb7c82f..41951a53c9 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -27,8 +27,8 @@ using namespace std::literals::string_literals; * */ -int glob_func_cnt, undef_func_cnt; -std::vector glob_func; +int glob_func_cnt, undef_func_cnt, glob_var_cnt; +std::vector glob_func, glob_vars; SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) { sym_idx_t name_idx = sym::symbols.lookup(name, 1); @@ -44,30 +44,49 @@ SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) { } template -void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) { +SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) { SymDef* def = predefine_builtin_func(name, func_type); def->value = new SymValAsmFunc{func_type, func, impure}; + return def; } template -void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list arg_order, - std::initializer_list ret_order = {}, bool impure = false) { +SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list arg_order, + std::initializer_list ret_order = {}, bool impure = false) { SymDef* def = predefine_builtin_func(name, func_type); def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, impure}; + return def; } -void define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro, - std::initializer_list arg_order, std::initializer_list ret_order = {}, - bool impure = false) { +SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro, + std::initializer_list arg_order, std::initializer_list ret_order = {}, + bool impure = false) { SymDef* def = predefine_builtin_func(name, func_type); def->value = new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, impure}; + return def; +} + +SymDef* force_autoapply(SymDef* def) { + if (def) { + auto val = dynamic_cast(def->value); + if (val) { + val->auto_apply = true; + } + } + return def; +} + +template +SymDef* define_builtin_const(std::string name, TypeExpr* const_type, Args&&... args) { + return force_autoapply( + define_builtin_func(name, TypeExpr::new_map(TypeExpr::new_unit(), const_type), std::forward(args)...)); } -bool SymValAsmFunc::compile(AsmOpList& dest, std::vector& in, std::vector& out) const { +bool SymValAsmFunc::compile(AsmOpList& dest, std::vector& out, std::vector& in) const { if (simple_compile) { - return dest.append(simple_compile(in, out)); + return dest.append(simple_compile(out, in)); } else if (ext_compile) { - return ext_compile(dest, in, out); + return ext_compile(dest, out, in); } else { return false; } @@ -317,6 +336,12 @@ AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv) { return AsmOp::Custom(os.str(), args, retv); } +AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv) { + std::ostringstream os; + os << imm1 << ' ' << imm2 << ' ' << op; + return AsmOp::Custom(os.str(), args, retv); +} + AsmOp push_const(td::RefInt256 x) { return AsmOp::IntConst(std::move(x)); } @@ -918,11 +943,11 @@ void define_builtins() { define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6)); define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3)); define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7)); - define_builtin_func("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true)); - define_builtin_func("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false)); + define_builtin_const("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true)); + define_builtin_const("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false)); // define_builtin_func("null", Null, AsmOp::Const("PUSHNULL")); - define_builtin_func("nil", Tuple, AsmOp::Const("PUSHNULL")); - define_builtin_func("Nil", Tuple, AsmOp::Const("NIL")); + define_builtin_const("nil", Tuple, AsmOp::Const("PUSHNULL")); + define_builtin_const("Nil", Tuple, AsmOp::Const("NIL")); define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null); define_builtin_func("throw", impure_un_op, compile_throw, true); define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); diff --git a/crypto/func/codegen.cpp b/crypto/func/codegen.cpp index 70ae929df3..91bb0be930 100644 --- a/crypto/func/codegen.cpp +++ b/crypto/func/codegen.cpp @@ -84,7 +84,9 @@ void Stack::forget_const() { void Stack::issue_pop(int i) { validate(i); - o << AsmOp::Pop(i); + if (output_enabled()) { + o << AsmOp::Pop(i); + } at(i) = get(0); s.pop_back(); modified(); @@ -92,7 +94,9 @@ void Stack::issue_pop(int i) { void Stack::issue_push(int i) { validate(i); - o << AsmOp::Push(i); + if (output_enabled()) { + o << AsmOp::Push(i); + } s.push_back(get(i)); modified(); } @@ -101,7 +105,9 @@ void Stack::issue_xchg(int i, int j) { validate(i); validate(j); if (i != j && get(i) != get(j)) { - o << AsmOp::Xchg(i, j); + if (output_enabled()) { + o << AsmOp::Xchg(i, j); + } std::swap(at(i), at(j)); modified(); } @@ -183,6 +189,10 @@ void Stack::enforce_state(const StackLayout& req_stack) { if (i < depth() && s[i].first == x) { continue; } + while (depth() > 0 && std::find(req_stack.cbegin(), req_stack.cend(), get(0).first) == req_stack.cend()) { + // current TOS entry is unused in req_stack, drop it + issue_pop(0); + } int j = find(x); if (j >= depth() - i) { issue_push(j); @@ -292,27 +302,61 @@ bool Op::generate_code_step(Stack& stack) { } return true; } - case _GlobVar: { - assert(left.size() == 1); - auto p = next_var_info[left[0]]; - if (!p || p->is_unused() || disabled()) { - return true; - } - auto func = dynamic_cast(fun_ref->value); - if (func) { - std::vector res; - res.reserve(left.size()); - for (var_idx_t i : left) { - res.emplace_back(i); + case _GlobVar: + if (dynamic_cast(fun_ref->value)) { + bool used = false; + for (auto i : left) { + auto p = next_var_info[i]; + if (p && !p->is_unused()) { + used = true; + } + } + if (!used || disabled()) { + return true; } - func->compile(stack.o, res, args); // compile res := f (args) - } else { std::string name = sym::symbols.get_name(fun_ref->sym_idx); - stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size()); + stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1); + if (left.size() != 1) { + assert(left.size() <= 15); + stack.o << exec_arg_op("UNTUPLE", (int)left.size(), 1, (int)left.size()); + } + for (auto i : left) { + stack.push_new_var(i); + } + return true; + } else { + assert(left.size() == 1); + auto p = next_var_info[left[0]]; + if (!p || p->is_unused() || disabled()) { + return true; + } + stack.o << "CONT:<{"; + stack.o.indent(); + auto func = dynamic_cast(fun_ref->value); + if (func) { + // TODO: create and compile a true lambda instead of this (so that arg_order and ret_order would work correctly) + std::vector args0, res; + TypeExpr::remove_indirect(func->sym_type); + assert(func->get_type()->is_map()); + auto wr = func->get_type()->args.at(0)->get_width(); + auto wl = func->get_type()->args.at(1)->get_width(); + assert(wl >= 0 && wr >= 0); + for (int i = 0; i < wl; i++) { + res.emplace_back(0); + } + for (int i = 0; i < wr; i++) { + args0.emplace_back(0); + } + func->compile(stack.o, res, args0); // compile res := f (args0) + } else { + std::string name = sym::symbols.get_name(fun_ref->sym_idx); + stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size()); + } + stack.o.undent(); + stack.o << "}>"; + stack.push_new_var(left.at(0)); + return true; } - stack.push_new_var(left[0]); - return true; - } case _Let: { assert(left.size() == right.size()); int i = 0; @@ -395,7 +439,9 @@ bool Op::generate_code_step(Stack& stack) { assert(stack.s[k + i].first == right1[i]); } if (cl == _CallInd) { - stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, (int)right.size(), (int)left.size()); + // TODO: replace with exec_arg2_op() + stack.o << exec_arg2_op("CALLXARGS", (int)right.size() - 1, (int)left.size(), (int)right.size(), + (int)left.size()); } else { auto func = dynamic_cast(fun_ref->value); if (func) { @@ -420,6 +466,32 @@ bool Op::generate_code_step(Stack& stack) { } return true; } + case _SetGlob: { + assert(fun_ref && dynamic_cast(fun_ref->value)); + std::vector last; + for (var_idx_t x : right) { + last.push_back(var_info[x] && var_info[x]->is_last()); + } + stack.rearrange_top(right, std::move(last)); + stack.opt_show(); + int k = (int)stack.depth() - (int)right.size(); + assert(k >= 0); + for (int i = 0; i < (int)right.size(); i++) { + if (stack.s[k + i].first != right[i]) { + std::cerr << stack.o; + } + assert(stack.s[k + i].first == right[i]); + } + if (right.size() > 1) { + stack.o << exec_arg_op("TUPLE", (int)right.size(), (int)right.size(), 1); + } + if (!right.empty()) { + std::string name = sym::symbols.get_name(fun_ref->sym_idx); + stack.o << AsmOp::Custom(name + " SETGLOB", 1, 0); + } + stack.s.resize(k); + return true; + } case _If: { if (block0->is_empty() && block1->is_empty()) { return true; @@ -448,7 +520,9 @@ bool Op::generate_code_step(Stack& stack) { } stack.o << "IF:<{"; stack.o.indent(); - Stack stack_copy{stack}; + Stack stack_copy{stack}, stack_target{stack}; + stack_target.disable_output(); + stack_target.drop_vars_except(next->var_info); block0->generate_code_all(stack_copy); stack_copy.drop_vars_except(var_info); stack_copy.opt_show(); @@ -457,7 +531,8 @@ bool Op::generate_code_step(Stack& stack) { stack.o << "}>"; return true; } - stack_copy.drop_vars_except(next->var_info); + // stack_copy.drop_vars_except(next->var_info); + stack_copy.enforce_state(stack_target.vars()); stack_copy.opt_show(); if (stack_copy.vars() == stack.vars()) { stack.o.undent(); @@ -487,7 +562,9 @@ bool Op::generate_code_step(Stack& stack) { } stack.o << "IFNOT:<{"; stack.o.indent(); - Stack stack_copy{stack}; + Stack stack_copy{stack}, stack_target{stack}; + stack_target.disable_output(); + stack_target.drop_vars_except(next->var_info); block1->generate_code_all(stack_copy); stack_copy.drop_vars_except(var_info); stack_copy.opt_show(); @@ -497,7 +574,8 @@ bool Op::generate_code_step(Stack& stack) { stack.merge_const(stack_copy); return true; } - stack_copy.drop_vars_except(next->var_info); + // stack_copy.drop_vars_except(next->var_info); + stack_copy.enforce_state(stack_target.vars()); stack_copy.opt_show(); if (stack_copy.vars() == stack.vars()) { stack.o.undent(); diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index 2b98c332bd..5b937c8331 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -130,6 +130,11 @@ int generate_output() { *outs << func_val->method_id << " DECLMETHOD " << name << "\n"; } } + for (SymDef* gvar_sym : glob_vars) { + assert(dynamic_cast(gvar_sym->value)); + std::string name = sym::symbols.get_name(gvar_sym->sym_idx); + *outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n"; + } int errors = 0; for (SymDef* func_sym : glob_func) { try { diff --git a/crypto/func/func.h b/crypto/func/func.h index d1481299b8..789319c528 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -97,6 +97,7 @@ enum Keyword { _Forall, _Asm, _Impure, + _Global, _Extern, _Inline, _InlineRef, @@ -181,6 +182,9 @@ struct TypeExpr { bool is_var() const { return constr == te_Var; } + bool is_map() const { + return constr == te_Map; + } bool has_fixed_width() const { return minw == maxw; } @@ -498,6 +502,7 @@ struct Op { _Let, _IntConst, _GlobVar, + _SetGlob, _Import, _Return, _If, @@ -694,6 +699,7 @@ struct SymVal : sym::SymValBase { TypeExpr* sym_type; td::RefInt256 method_id; bool impure; + bool auto_apply{false}; short flags; // +1 = inline, +2 = inline_ref SymVal(int _type, int _idx, TypeExpr* _stype = nullptr, bool _impure = false) : sym::SymValBase(_type, _idx), sym_type(_stype), impure(_impure), flags(0) { @@ -745,8 +751,20 @@ struct SymValType : sym::SymValBase { } }; -extern int glob_func_cnt, undef_func_cnt; -extern std::vector glob_func; +struct SymValGlobVar : sym::SymValBase { + TypeExpr* sym_type; + int out_idx{0}; + SymValGlobVar(int val, TypeExpr* gvtype, int oidx = 0) + : sym::SymValBase(_GlobVar, val), sym_type(gvtype), out_idx(oidx) { + } + ~SymValGlobVar() override = default; + TypeExpr* get_type() const { + return sym_type; + } +}; + +extern int glob_func_cnt, undef_func_cnt, glob_var_cnt; +extern std::vector glob_func, glob_vars; /* * @@ -775,6 +793,7 @@ struct Expr { _Const, _Var, _Glob, + _GlobVar, _Letop, _LetFirst, _Hole, @@ -1155,6 +1174,7 @@ struct StackTransform { bool apply_push(int i); bool apply_pop(int i = 0); bool apply_push_newconst(); + bool apply_blkpop(int k); bool apply(const StackTransform& other); // this = this * other bool preapply(const StackTransform& other); // this = other * this // c := a * b @@ -1246,6 +1266,11 @@ struct StackTransform { bool is_nip_seq(int i, int j = 0) const; bool is_nip_seq(int* i) const; bool is_nip_seq(int* i, int* j) const; + bool is_pop_blkdrop(int i, int k) const; + bool is_pop_blkdrop(int* i, int* k) const; + bool is_2pop_blkdrop(int i, int j, int k) const; + bool is_2pop_blkdrop(int* i, int* j, int* k) const; + bool is_const_rot() const; void show(std::ostream& os, int mode = 0) const; @@ -1306,14 +1331,20 @@ struct Optimizer { bool rewrite_const_push_swap(); bool is_const_push_xchgs(); bool rewrite_const_push_xchgs(); + bool is_const_rot() const; + bool rewrite_const_rot(); bool simple_rewrite(int p, AsmOp&& new_op); bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2); + bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3); bool simple_rewrite(AsmOp&& new_op) { return simple_rewrite(p_, std::move(new_op)); } bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2) { return simple_rewrite(p_, std::move(new_op1), std::move(new_op2)); } + bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) { + return simple_rewrite(p_, std::move(new_op1), std::move(new_op2), std::move(new_op3)); + } bool simple_rewrite_nop(); bool is_pred(const std::function& pred, int min_p = 2); bool is_same_as(const StackTransform& trans, int min_p = 2); @@ -1345,6 +1376,8 @@ struct Optimizer { bool is_blkdrop(int* i); bool is_reverse(int* i, int* j); bool is_nip_seq(int* i, int* j); + bool is_pop_blkdrop(int* i, int* k); + bool is_2pop_blkdrop(int* i, int* j, int* k); AsmOpConsList extract_code(); }; @@ -1355,7 +1388,7 @@ void optimize_code(AsmOpList& ops); struct Stack { StackLayoutExt s; AsmOpList& o; - enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _Shown = 256, _Garbage = -0x10000 }; + enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _DisableOut = 128, _Shown = 256, _Garbage = -0x10000 }; int mode; Stack(AsmOpList& _o, int _mode = 0) : o(_o), mode(_mode) { } @@ -1381,6 +1414,15 @@ struct Stack { var_const_idx_t get(int i) const { return at(i); } + bool output_disabled() const { + return mode & _DisableOut; + } + bool output_enabled() const { + return !output_disabled(); + } + void disable_output() { + mode |= _DisableOut; + } StackLayout vars() const; int find(var_idx_t var, int from = 0) const; int find(var_idx_t var, int from, int to) const; @@ -1470,7 +1512,7 @@ struct SymValAsmFunc : SymValFunc { std::initializer_list ret_order = {}, bool impure = false) : SymValFunc(-1, ft, arg_order, ret_order, impure), ext_compile(std::move(_compile)) { } - bool compile(AsmOpList& dest, std::vector& in, std::vector& out) const; + bool compile(AsmOpList& dest, std::vector& out, std::vector& in) const; }; // defined in builtins.cpp @@ -1478,6 +1520,7 @@ AsmOp exec_arg_op(std::string op, long long arg); AsmOp exec_arg_op(std::string op, long long arg, int args, int retv = 1); AsmOp exec_arg_op(std::string op, td::RefInt256 arg); AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv = 1); +AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv = 1); AsmOp push_const(td::RefInt256 x); void define_builtins(); diff --git a/crypto/func/gen-abscode.cpp b/crypto/func/gen-abscode.cpp index a3cc52042d..87a4e2de60 100644 --- a/crypto/func/gen-abscode.cpp +++ b/crypto/func/gen-abscode.cpp @@ -282,15 +282,22 @@ std::vector Expr::pre_compile(CodeBlob& code) const { code.emplace_back(here, Op::_IntConst, rvect, intval); return rvect; } - case _Glob: { + case _Glob: + case _GlobVar: { auto rvect = new_tmp_vect(code); code.emplace_back(here, Op::_GlobVar, rvect, std::vector{}, sym); return rvect; } case _Letop: { auto right = args[1]->pre_compile(code); - auto left = args[0]->pre_compile(code); - code.emplace_back(here, Op::_Let, std::move(left), right); + if (args[0]->cls == Expr::_GlobVar) { + assert(args[0]->sym); + auto& op = code.emplace_back(here, Op::_SetGlob, std::vector{}, right, args[0]->sym); + op.flags |= Op::_Impure; + } else { + auto left = args[0]->pre_compile(code); + code.emplace_back(here, Op::_Let, std::move(left), right); + } return right; } case _LetFirst: { diff --git a/crypto/func/keywords.cpp b/crypto/func/keywords.cpp index c640f4fe71..91559f9686 100644 --- a/crypto/func/keywords.cpp +++ b/crypto/func/keywords.cpp @@ -113,6 +113,7 @@ void define_keywords() { .add_keyword("forall", Kw::_Forall); sym::symbols.add_keyword("extern", Kw::_Extern) + .add_keyword("global", Kw::_Global) .add_keyword("asm", Kw::_Asm) .add_keyword("impure", Kw::_Impure) .add_keyword("inline", Kw::_Inline) diff --git a/crypto/func/optimize.cpp b/crypto/func/optimize.cpp index 95b4403c46..cd27150c70 100644 --- a/crypto/func/optimize.cpp +++ b/crypto/func/optimize.cpp @@ -150,6 +150,21 @@ bool Optimizer::rewrite_const_push_swap() { return true; } +bool Optimizer::is_const_rot() const { + return pb_ >= 3 && pb_ <= l2_ && op_[0]->is_gconst() && tr_[pb_ - 1].is_const_rot(); +} + +bool Optimizer::rewrite_const_rot() { + p_ = pb_; + q_ = 2; + show_left(); + oq_[0] = std::move(op_[0]); + oq_[1] = std::move(op_[1]); + *oq_[1] = AsmOp::Custom("ROT", 3, 3); + show_right(); + return true; +} + bool Optimizer::is_const_push_xchgs() { if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) { return false; @@ -260,6 +275,21 @@ bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2) { return true; } +bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) { + assert(p > 2 && p <= l_); + p_ = p; + q_ = 3; + show_left(); + oq_[0] = std::move(op_[0]); + *oq_[0] = new_op1; + oq_[1] = std::move(op_[1]); + *oq_[1] = new_op2; + oq_[2] = std::move(op_[2]); + *oq_[2] = new_op3; + show_right(); + return true; +} + bool Optimizer::simple_rewrite_nop() { assert(p_ > 0 && p_ <= l_); q_ = 0; @@ -402,6 +432,16 @@ bool Optimizer::is_nip_seq(int* i, int* j) { return is_pred([i, j](const auto& t) { return t.is_nip_seq(i, j) && *i >= 3 && *i <= 15; }); } +bool Optimizer::is_pop_blkdrop(int* i, int* k) { + return is_pred([i, k](const auto& t) { return t.is_pop_blkdrop(i, k) && *i >= *k && *k >= 2 && *k <= 15; }, 3); +} + +bool Optimizer::is_2pop_blkdrop(int* i, int* j, int* k) { + return is_pred( + [i, j, k](const auto& t) { return t.is_2pop_blkdrop(i, j, k) && *i >= *k && *j >= *k && *k >= 2 && *k <= 15; }, + 3); +} + bool Optimizer::compute_stack_transforms() { StackTransform trans; for (int i = 0; i < l_; i++) { @@ -450,7 +490,7 @@ bool Optimizer::find_at_least(int pb) { // show_stack_transforms(); int i = -100, j = -100, k = -100; return (is_const_push_swap() && 3 >= pb && rewrite_const_push_swap()) || (is_nop() && simple_rewrite_nop()) || - (is_const_push_xchgs() && rewrite_const_push_xchgs()) || + (is_const_rot() && rewrite_const_rot()) || (is_const_push_xchgs() && rewrite_const_push_xchgs()) || (is_xchg(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && simple_rewrite(AsmOp::Push(i))) || (is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) || (is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) || (is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) || @@ -468,6 +508,10 @@ bool Optimizer::find_at_least(int pb) { (is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) || (is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) || (is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) || + (is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) || + (is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14 + ? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2)) + : simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) || (is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) || (is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) || (is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) || diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index e04077918c..01a43f6309 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -174,6 +174,53 @@ FormalArg parse_formal_arg(Lexer& lex, int fa_idx) { return std::make_tuple(arg_type, new_sym_def, loc); } +void parse_global_var_decl(Lexer& lex) { + TypeExpr* var_type = 0; + SrcLocation loc = lex.cur().loc; + if (lex.tp() == '_') { + lex.next(); + var_type = TypeExpr::new_hole(); + loc = lex.cur().loc; + } else if (lex.tp() != _Ident) { + var_type = parse_type(lex); + } else { + auto sym = sym::lookup_symbol(lex.cur().val); + if (sym && dynamic_cast(sym->value)) { + auto val = dynamic_cast(sym->value); + lex.next(); + var_type = val->get_type(); + } else { + var_type = TypeExpr::new_hole(); + } + } + if (lex.tp() != _Ident) { + lex.expect(_Ident, "global variable name"); + } + loc = lex.cur().loc; + SymDef* sym_def = sym::define_global_symbol(lex.cur().val, false, loc); + if (!sym_def) { + lex.cur().error_at("cannot define global symbol `", "`"); + } + if (sym_def->value) { + auto val = dynamic_cast(sym_def->value); + if (!val) { + lex.cur().error_at("symbol `", "` cannot be redefined as a global variable"); + } + try { + unify(var_type, val->sym_type); + } catch (UnifyError& ue) { + std::ostringstream os; + os << "cannot unify new type " << var_type << " of global variable `" << sym_def->name() + << "` with its previous type " << val->sym_type << ": " << ue; + lex.cur().error(os.str()); + } + } else { + sym_def->value = new SymValGlobVar{glob_var_cnt++, var_type}; + glob_vars.push_back(sym_def); + } + lex.next(); +} + FormalArgList parse_formal_args(Lexer& lex) { FormalArgList args; lex.expect('(', "formal argument list"); @@ -205,6 +252,18 @@ TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) { return TypeExpr::new_tensor(std::move(type_list)); } +void parse_global_var_decls(Lexer& lex) { + lex.expect(_Global); + while (true) { + parse_global_var_decl(lex); + if (lex.tp() != ',') { + break; + } + lex.expect(','); + } + lex.expect(';'); +} + SymValCodeFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type, bool impure = false) { SymValCodeFunc* res = new SymValCodeFunc{glob_func_cnt, func_type, impure}; func_sym->value = res; @@ -239,6 +298,22 @@ bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) { } } +Expr* make_func_apply(Expr* fun, Expr* x) { + Expr* res; + if (fun->cls == Expr::_Glob) { + if (x->cls == Expr::_Tuple) { + res = new Expr{Expr::_Apply, fun->sym, x->args}; + } else { + res = new Expr{Expr::_Apply, fun->sym, {x}}; + } + res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure); + } else { + res = new Expr{Expr::_VarApply, {fun, x}}; + res->flags = Expr::_IsRvalue; + } + return res; +} + Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false); // parse ( E { , E } ) | () | id | num | _ @@ -323,6 +398,16 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { lex.next(); return res; } + if (sym && dynamic_cast(sym->value)) { + auto val = dynamic_cast(sym->value); + Expr* res = new Expr{Expr::_GlobVar, lex.cur().loc}; + res->e_type = val->get_type(); + res->sym = sym; + res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure; + lex.next(); + return res; + } + bool auto_apply = false; Expr* res = new Expr{Expr::_Var, lex.cur().loc}; if (nv) { res->val = ~lex.cur().val; @@ -344,6 +429,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { } else if (val->type == SymVal::_Func) { res->e_type = val->get_type(); res->cls = Expr::_Glob; + auto_apply = val->auto_apply; } else if (val->idx < 0) { lex.cur().error_at("accessing variable `", "` being defined"); } else { @@ -354,6 +440,12 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { // std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl; res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0); } + if (auto_apply) { + int impure = res->flags & Expr::_IsImpure; + delete res; + res = new Expr{Expr::_Apply, sym, {}}; + res->flags = Expr::_IsRvalue | impure; + } res->deduce_type(lex.cur()); lex.next(); return res; @@ -362,22 +454,6 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) { return nullptr; } -Expr* make_func_apply(Expr* fun, Expr* x) { - Expr* res; - if (fun->cls == Expr::_Glob) { - if (x->cls == Expr::_Tuple) { - res = new Expr{Expr::_Apply, fun->sym, x->args}; - } else { - res = new Expr{Expr::_Apply, fun->sym, {x}}; - } - res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure); - } else { - res = new Expr{Expr::_VarApply, {fun, x}}; - res->flags = Expr::_IsRvalue; - } - return res; -} - // parse E { E } Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) { Expr* res = parse_expr100(lex, code, nv); @@ -1193,7 +1269,11 @@ bool parse_source(std::istream* is, const src::FileDescr* fdescr) { src::SourceReader reader{is, fdescr}; Lexer lex{reader, true}; while (lex.tp() != _Eof) { - parse_func_def(lex); + if (lex.tp() == _Global) { + parse_global_var_decls(lex); + } else { + parse_func_def(lex); + } } return true; } diff --git a/crypto/func/stack-transform.cpp b/crypto/func/stack-transform.cpp index f682b49075..069324dc7a 100644 --- a/crypto/func/stack-transform.cpp +++ b/crypto/func/stack-transform.cpp @@ -334,6 +334,13 @@ bool StackTransform::apply_pop(int i) { } } +bool StackTransform::apply_blkpop(int k) { + if (!is_valid() || k < 0) { + return invalidate(); + } + return !k || (touch(k - 1) && shift(k)); +} + bool StackTransform::equal(const StackTransform &other, bool relaxed) const { if (!is_valid() || !other.is_valid()) { return false; @@ -800,6 +807,49 @@ bool StackTransform::is_nip_seq(int *i, int *j) const { } } +// POP s(i); BLKDROP k (usually for i >= k >= 0) +bool StackTransform::is_pop_blkdrop(int i, int k) const { + StackTransform t; + return is_valid() && d == k + 1 && t.apply_pop(i) && t.apply_blkpop(k) && t <= *this; +} + +// POP s(i); BLKDROP k == XCHG s0,s(i); BLKDROP k+1 for i >= k >= 0 +// k+1 k+2 .. i-1 0 i+1 .. +bool StackTransform::is_pop_blkdrop(int *i, int *k) const { + if (is_valid() && n == 1 && d > 0 && !A[0].second) { + *k = d - 1; + *i = A[0].first; + return is_pop_blkdrop(*i, *k); + } else { + return false; + } +} + +// POP s(i); POP s(j); BLKDROP k (usually for i<>j >= k >= 0) +bool StackTransform::is_2pop_blkdrop(int i, int j, int k) const { + StackTransform t; + return is_valid() && d == k + 2 && t.apply_pop(i) && t.apply_pop(j) && t.apply_blkpop(k) && t <= *this; +} + +// POP s(i); POP s(j); BLKDROP k == XCHG s0,s(i); XCHG s1,s(j+1); BLKDROP k+2 (usually for i<>j >= k >= 2) +// k+2 k+3 .. i-1 0 i+1 ... j 1 j+2 ... +bool StackTransform::is_2pop_blkdrop(int *i, int *j, int *k) const { + if (is_valid() && n == 2 && d >= 2 && A[0].second + A[1].second == 1) { + *k = d - 2; + int t = (A[0].second > 0); + *i = A[t].first; + *j = A[1 - t].first - 1; + return is_2pop_blkdrop(*i, *j, *k); + } else { + return false; + } +} + +// PUSHCONST c ; ROT == 1 -1000 0 2 3 +bool StackTransform::is_const_rot() const { + return is_valid() && d == -1 && is_trivial_after(3) && get(0) == 1 && get(1) <= c_start && get(2) == 0; +} + void StackTransform::show(std::ostream &os, int mode) const { if (!is_valid()) { os << ""; diff --git a/crypto/func/test/a11.fc b/crypto/func/test/a11.fc new file mode 100644 index 0000000000..45bdf1f368 --- /dev/null +++ b/crypto/func/test/a11.fc @@ -0,0 +1,28 @@ +_ f(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, cell vdict, int elect) { + return (ds, elect, credits); +} + +_ g(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, int elect) { + return (ds, elect, credits); +} + +_ h(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) { + return (ds, elect, credits); +} + +_ h2(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) { + return (ds, credits, elect); +} + +_ h3(int pubkey, int adnl_addr, int stake, int tot_weight, tuple vinfo) { + return (pubkey, tot_weight, stake, vinfo); +} + +_ z(int a, int b) { + return (b, 0, a); +} + +_ z2(int a, int b) { + return (0, a, b); +} + diff --git a/crypto/func/test/a6_5.fc b/crypto/func/test/a6_5.fc new file mode 100644 index 0000000000..ece7cd38d2 --- /dev/null +++ b/crypto/func/test/a6_5.fc @@ -0,0 +1,20 @@ +var twice(f, x) { + return f (f x); +} + +_ sqr(x) { + return x * x; +} + +var main(x) { + var f = sqr; + return twice(f, x) * f(x); +} + +var pow6(x) { + return twice(sqr, x) * sqr(x); +} + +_ q() { + return false; +} diff --git a/crypto/func/test/c1.fc b/crypto/func/test/c1.fc new file mode 100644 index 0000000000..1a07baca10 --- /dev/null +++ b/crypto/func/test/c1.fc @@ -0,0 +1,37 @@ +global int x, y, z; +global (cell, slice) y; +global ((int, int) -> int) op; + +_ get() { + var t = z + 1; + return x; +} + +_ pre_next() { + return x + 1; +} + +() init() impure { + ;; global x; + x = 0; +} + +int next() impure { + ;; global x; + return x += 1; +} + +_ set_y(x, w) { + y = (w, x); +} + +_ get_y() impure { + return y; +} + +int main(int c) { + init(); + c += next(); + return c + pre_next(); +} + diff --git a/crypto/func/test/c2.fc b/crypto/func/test/c2.fc new file mode 100644 index 0000000000..277c70530a --- /dev/null +++ b/crypto/func/test/c2.fc @@ -0,0 +1,10 @@ +global ((int, int) -> int) op; + +int check_assoc(int a, int b, int c) { + return op(op(a, b), c) == op(a, op(b, c)); +} + +int main() { + op = _+_; + return check_assoc(2, 3, 9); +} diff --git a/crypto/func/test/c2_1.fc b/crypto/func/test/c2_1.fc new file mode 100644 index 0000000000..3bf863a6d7 --- /dev/null +++ b/crypto/func/test/c2_1.fc @@ -0,0 +1,7 @@ +_ check_assoc(op, a, b, c) { + return op(op(a, b), c) == op(a, op(b, c)); +} + +int main() { + return check_assoc(_+_, 2, 3, 9); +} diff --git a/crypto/parser/symtable.h b/crypto/parser/symtable.h index 6c77ca7b03..88f683bd3e 100644 --- a/crypto/parser/symtable.h +++ b/crypto/parser/symtable.h @@ -32,7 +32,7 @@ namespace sym { typedef int var_idx_t; struct SymValBase { - enum { _Param, _Var, _Func, _Typename }; + enum { _Param, _Var, _Func, _Typename, _GlobVar }; int type; int idx; SymValBase(int _type, int _idx) : type(_type), idx(_idx) { diff --git a/crypto/smartcont/CreateState.fif b/crypto/smartcont/CreateState.fif index ed82499b5f..1330a446f1 100644 --- a/crypto/smartcont/CreateState.fif +++ b/crypto/smartcont/CreateState.fif @@ -242,3 +242,7 @@ dictnew constant special-dict register_smc Masterchain swap 6 .Addr cr } : create-wallet0 + +{ dup tlb-type-lookup { nip } { "unknown TLB type " swap $+ abort } cond } : $>tlb +{ bl word $>tlb 1 'nop } ::_ tlb: +{ proof_union; { - proof_union = MerkleProof::combine(proof1, proof2); + auto check = [&](auto proof_union) { + auto virtualized_proof = MerkleProof::virtualize(proof_union, 1); + auto exploration_a = CellExplorer::explore(virtualized_proof, exploration1.ops); + auto exploration_b = CellExplorer::explore(virtualized_proof, exploration2.ops); + ASSERT_EQ(exploration_a.log, exploration1.log); + ASSERT_EQ(exploration_b.log, exploration2.log); + }; + auto proof_union = MerkleProof::combine(proof1, proof2); ASSERT_EQ(proof_union->get_hash(), proof12->get_hash()); + check(proof_union); - auto virtualized_proof = MerkleProof::virtualize(proof_union, 1); - auto exploration_a = CellExplorer::explore(virtualized_proof, exploration1.ops); - auto exploration_b = CellExplorer::explore(virtualized_proof, exploration2.ops); - ASSERT_EQ(exploration_a.log, exploration1.log); - ASSERT_EQ(exploration_b.log, exploration2.log); + auto proof_union_fast = MerkleProof::combine_fast(proof1, proof2); + check(proof_union_fast); } { auto cell = MerkleProof::virtualize(proof12, 1); @@ -1194,6 +1199,88 @@ TEST(Cell, MerkleProofArrayHands) { test_boc_deserializer_full(proof).ensure(); test_boc_deserializer_full(CellBuilder::create_merkle_proof(proof)).ensure(); } + +TEST(Cell, MerkleProofCombineArray) { + size_t n = 1 << 15; + std::vector data; + for (size_t i = 0; i < n; i++) { + data.push_back(i / 3); + } + CompactArray arr(data); + + td::Ref root = vm::CellBuilder::create_merkle_proof(arr.merkle_proof({})); + td::Timer timer; + for (size_t i = 0; i < n; i++) { + auto new_root = vm::CellBuilder::create_merkle_proof(arr.merkle_proof({i})); + root = vm::MerkleProof::combine_fast(root, new_root); + if ((i - 1) % 100 == 0) { + LOG(ERROR) << timer; + timer = {}; + } + } + + CompactArray arr2(n, vm::MerkleProof::virtualize(root, 1)); + for (size_t i = 0; i < n; i++) { + CHECK(arr.get(i) == arr2.get(i)); + } +} + +TEST(Cell, MerkleProofCombineArray2) { + auto a = vm::CellBuilder().store_long(1, 8).finalize(); + auto b = vm::CellBuilder().store_long(2, 8).finalize(); + auto c = vm::CellBuilder().store_long(3, 8).finalize(); + auto d = vm::CellBuilder().store_long(4, 8).finalize(); + auto left = vm::CellBuilder().store_ref(a).store_ref(b).finalize(); + auto right = vm::CellBuilder().store_ref(c).store_ref(d).finalize(); + auto x = vm::CellBuilder().store_ref(left).store_ref(right).finalize(); + size_t n = 18; + //TODO: n = 100, currently TL + for (size_t i = 0; i < n; i++) { + x = vm::CellBuilder().store_ref(x).store_ref(x).finalize(); + } + + td::Ref root; + auto apply_op = [&](auto op) { + auto usage_tree = std::make_shared(); + auto usage_cell = UsageCell::create(x, usage_tree->root_ptr()); + root = usage_cell; + op(); + return MerkleProof::generate(root, usage_tree.get()); + }; + + auto first = apply_op([&] { + auto x = root; + while (true) { + auto cs = vm::load_cell_slice(x); + if (cs.size_refs() == 0) { + break; + } + x = cs.prefetch_ref(0); + } + }); + auto second = apply_op([&] { + auto x = root; + while (true) { + auto cs = vm::load_cell_slice(x); + if (cs.size_refs() == 0) { + break; + } + x = cs.prefetch_ref(1); + } + }); + + { + td::Timer t; + auto x = vm::MerkleProof::combine(first, second); + LOG(ERROR) << "slow " << t; + } + { + td::Timer t; + auto x = vm::MerkleProof::combine_fast(first, second); + LOG(ERROR) << "fast " << t; + } +} + TEST(Cell, MerkleUpdateHands) { auto data = CellBuilder{}.store_bytes("pruned data").store_ref(CellBuilder{}.finalize()).finalize(); auto node = CellBuilder{}.store_bytes("protected data").store_ref(data).finalize(); @@ -1965,6 +2052,222 @@ TEST(Ref, AtomicRef) { LOG(ERROR) << String::total_strings.sum(); } +class FileMerkleTree { + public: + FileMerkleTree(size_t chunks_count, td::Ref root = {}) { + log_n_ = 0; + while ((size_t(1) << log_n_) < chunks_count) { + log_n_++; + } + n_ = size_t(1) << log_n_; + mark_.resize(n_ * 2); + proof_.resize(n_ * 2); + + CHECK(n_ == chunks_count); // TODO: support other chunks_count + //auto x = vm::CellBuilder().finalize(); + root_ = std::move(root); + } + + struct Chunk { + td::size_t index{0}; + td::Slice hash; + }; + + void remove_chunk(td::size_t index) { + CHECK(index < n_); + index += n_; + while (proof_[index].not_null()) { + proof_[index] = {}; + index /= 2; + } + } + + bool has_chunk(td::size_t index) const { + CHECK(index < n_); + index += n_; + return proof_[index].not_null(); + } + + void add_chunk(td::size_t index, td::Slice hash) { + CHECK(hash.size() == 32); + CHECK(index < n_); + index += n_; + auto cell = vm::CellBuilder().store_bytes(hash).finalize(); + CHECK(proof_[index].is_null()); + proof_[index] = std::move(cell); + for (index /= 2; index != 0; index /= 2) { + CHECK(proof_[index].is_null()); + auto &left = proof_[index * 2]; + auto &right = proof_[index * 2 + 1]; + if (left.not_null() && right.not_null()) { + proof_[index] = vm::CellBuilder().store_ref(left).store_ref(right).finalize(); + } else { + mark_[index] = mark_id_; + } + } + } + + td::Status validate_proof(td::Ref new_root) { + // TODO: check structure + return td::Status::OK(); + } + + td::Status add_proof(td::Ref new_root) { + TRY_STATUS(validate_proof(new_root)); + auto combined = vm::MerkleProof::combine_fast_raw(root_, new_root); + if (combined.is_null()) { + return td::Status::Error("Can't combine proofs"); + } + root_ = std::move(combined); + return td::Status::OK(); + } + + td::Status try_add_chunks(td::Span chunks) { + for (auto chunk : chunks) { + if (has_chunk(chunk.index)) { + return td::Status::Error("Already has chunk"); + } + } + mark_id_++; + for (auto chunk : chunks) { + add_chunk(chunk.index, chunk.hash); + } + auto r_new_root = merge(root_, 1); + if (r_new_root.is_error()) { + for (auto chunk : chunks) { + remove_chunk(chunk.index); + } + return r_new_root.move_as_error(); + } + root_ = r_new_root.move_as_ok(); + return td::Status::OK(); + } + + td::Result> merge(td::Ref root, size_t index) { + const auto &down = proof_[index]; + if (down.not_null()) { + if (down->get_hash() != root->get_hash(0)) { + return td::Status::Error("Hash mismatch"); + } + return down; + } + + if (mark_[index] != mark_id_) { + return root; + } + + vm::CellSlice cs(vm::NoVm(), root); + if (cs.is_special()) { + return td::Status::Error("Proof is not enough to validate chunks"); + } + + CHECK(cs.size_refs() == 2); + vm::CellBuilder cb; + cb.store_bits(cs.fetch_bits(cs.size())); + TRY_RESULT(left, merge(cs.fetch_ref(), index * 2)); + TRY_RESULT(right, merge(cs.fetch_ref(), index * 2 + 1)); + cb.store_ref(std::move(left)).store_ref(std::move(right)); + return cb.finalize(); + } + + void init_proof() { + CHECK(proof_[1].not_null()); + root_ = proof_[1]; + } + + td::Result> gen_proof(size_t l, size_t r) { + auto usage_tree = std::make_shared(); + auto usage_cell = vm::UsageCell::create(root_, usage_tree->root_ptr()); + TRY_STATUS(do_gen_proof(std::move(usage_cell), 0, n_ - 1, l, r)); + auto res = vm::MerkleProof::generate_raw(root_, usage_tree.get()); + CHECK(res.not_null()); + return res; + } + + private: + td::size_t n_; // n = 2^log_n + td::size_t log_n_; + td::size_t mark_id_{0}; + std::vector mark_; // n_ * 2 + std::vector> proof_; // n_ * 2 + td::Ref root_; + + td::Status do_gen_proof(td::Ref node, size_t il, size_t ir, size_t l, size_t r) { + if (ir < l || il > r) { + return td::Status::OK(); + } + if (l <= il && ir <= r) { + return td::Status::OK(); + } + vm::CellSlice cs(vm::NoVm(), std::move(node)); + if (cs.is_special()) { + return td::Status::Error("Can't generate a proof"); + } + CHECK(cs.size_refs() == 2); + auto ic = (il + ir) / 2; + TRY_STATUS(do_gen_proof(cs.fetch_ref(), il, ic, l, r)); + TRY_STATUS(do_gen_proof(cs.fetch_ref(), ic + 1, ir, l, r)); + return td::Status::OK(); + } +}; + +TEST(FileMerkleTree, Manual) { + // create big random file + size_t chunk_size = 768; + // for simplicity numer of chunks in a file is a power of two + size_t chunks_count = 1 << 16; + size_t file_size = chunk_size * chunks_count; + td::Timer timer; + LOG(INFO) << "Generate random string"; + const auto file = td::rand_string('a', 'z', td::narrow_cast(file_size)); + LOG(INFO) << timer; + + timer = {}; + LOG(INFO) << "Calculate all hashes"; + std::vector hashes(chunks_count); + for (size_t i = 0; i < chunks_count; i++) { + td::sha256(td::Slice(file).substr(i * chunk_size, chunk_size), hashes[i].as_slice()); + } + LOG(INFO) << timer; + + timer = {}; + LOG(INFO) << "Init merkle tree"; + FileMerkleTree tree(chunks_count); + for (size_t i = 0; i < chunks_count; i++) { + tree.add_chunk(i, hashes[i].as_slice()); + } + tree.init_proof(); + LOG(INFO) << timer; + + auto root_proof = tree.gen_proof(0, chunks_count - 1).move_as_ok(); + + // first download each chunk one by one + + for (size_t stride : {1 << 6, 1}) { + timer = {}; + LOG(INFO) << "Gen all proofs, stride = " << stride; + for (size_t i = 0; i < chunks_count; i += stride) { + tree.gen_proof(i, i + stride - 1).move_as_ok(); + } + LOG(INFO) << timer; + timer = {}; + LOG(INFO) << "Proof size: " << vm::std_boc_serialize(tree.gen_proof(0, stride - 1).move_as_ok()).ok().size(); + LOG(INFO) << "Download file, stride = " << stride; + { + FileMerkleTree new_tree(chunks_count, root_proof); + for (size_t i = 0; i < chunks_count; i += stride) { + new_tree.add_proof(tree.gen_proof(i, i + stride - 1).move_as_ok()).ensure(); + std::vector chunks; + for (size_t j = 0; j < stride; j++) { + chunks.push_back({i + j, hashes[i + j].as_slice()}); + } + new_tree.try_add_chunks(chunks).ensure(); + } + } + LOG(INFO) << timer; + } +} + //TEST(Tmp, Boc) { //LOG(ERROR) << "A"; //auto data = td::read_file("boc"); diff --git a/crypto/tl/tlbc-gen-cpp.cpp b/crypto/tl/tlbc-gen-cpp.cpp index 8dfbbb1f29..3de92bd50e 100644 --- a/crypto/tl/tlbc-gen-cpp.cpp +++ b/crypto/tl/tlbc-gen-cpp.cpp @@ -3257,6 +3257,31 @@ void generate_type_constants(std::ostream& os, int mode) { } } +void generate_register_function(std::ostream& os, int mode) { + os << "\n// " << (mode ? "definition" : "declaration") << " of type name registration function\n"; + if (!mode) { + os << "extern bool register_simple_types(std::function func);\n"; + return; + } + os << "bool register_simple_types(std::function func) {\n"; + os << " return "; + int k = 0; + for (int i = builtin_types_num; i < types_num; i++) { + Type& type = types[i]; + CppTypeCode& cc = *cpp_type[i]; + if (!cc.cpp_type_var_name.empty() && type.type_name) { + if (k++) { + os << "\n && "; + } + os << "func(\"" << type.get_name() << "\", &" << cc.cpp_type_var_name << ")"; + } + } + if (!k) { + os << "true"; + } + os << ";\n}\n\n"; +} + void assign_const_type_cpp_idents() { const_type_expr_cpp_idents.resize(const_type_expr_num + 1, ""); const_type_expr_simple.resize(const_type_expr_num + 1, false); @@ -3389,6 +3414,7 @@ void generate_cpp_output_to(std::ostream& os, int options = 0, std::vectorsecond : nullptr; +} + +} // namespace tlb diff --git a/crypto/tl/tlblib.hpp b/crypto/tl/tlblib.hpp index d90bfcc419..2f6794ec1b 100644 --- a/crypto/tl/tlblib.hpp +++ b/crypto/tl/tlblib.hpp @@ -18,6 +18,7 @@ */ #pragma once #include +#include #include "vm/cellslice.h" namespace tlb { @@ -268,6 +269,30 @@ struct TLB_Complex : TLB { } }; +class TlbTypeHolder : public td::CntObject { + const TLB* type{nullptr}; + char* data{nullptr}; + + public: + TlbTypeHolder() = default; + TlbTypeHolder(const TLB* _type) : type(_type), data(nullptr) { + } + TlbTypeHolder(const TLB* _type, char* _data) : type(_type), data(_data) { + } + ~TlbTypeHolder() override { + free(data); + } + const TLB* get() const { + return type; + } + const TLB& operator*() const { + return *type; + } + const TLB* operator->() const { + return type; + } +}; + static inline bool add_chk(int x, int y, int z) { return x + y == z && z >= 0; } @@ -508,6 +533,25 @@ struct PrettyPrinter { namespace tlb { +class TypenameLookup { + std::map types; + + public: + typedef std::function simple_register_func_t; + typedef std::function register_func_t; + TypenameLookup() = default; + TypenameLookup(register_func_t func) { + register_types(func); + } + bool register_type(const char* name, const TLB* tp); + bool register_types(register_func_t func); + const TLB* lookup(std::string str) const; +}; + +} // namespace tlb + +namespace tlb { + struct False final : TLB { int get_size(const vm::CellSlice& cs) const override { return -1; diff --git a/crypto/vm/cells/MerkleProof.cpp b/crypto/vm/cells/MerkleProof.cpp index a373e80468..105c74c167 100644 --- a/crypto/vm/cells/MerkleProof.cpp +++ b/crypto/vm/cells/MerkleProof.cpp @@ -143,19 +143,80 @@ Ref MerkleProof::virtualize(Ref cell, int virtualization) { return virtualize_raw(r_raw.move_as_ok(), {0 /*level*/, static_cast(virtualization)}); } +class MerkleProofCombineFast { + public: + MerkleProofCombineFast(Ref a, Ref b) : a_(std::move(a)), b_(std::move(b)) { + } + td::Result> run() { + TRY_RESULT_ASSIGN(a_, unpack_proof(a_)); + TRY_RESULT_ASSIGN(b_, unpack_proof(b_)); + TRY_RESULT(res, run_raw()); + return CellBuilder::create_merkle_proof(std::move(res)); + } + + td::Result> run_raw() { + if (a_->get_hash(0) != b_->get_hash(0)) { + return td::Status::Error("Can't combine MerkleProofs with different roots"); + } + return merge(a_, b_, 0); + } + + private: + Ref a_; + Ref b_; + + Ref merge(Ref a, Ref b, td::uint32 merkle_depth) { + if (a->get_hash() == b->get_hash()) { + return a; + } + if (a->get_level() == merkle_depth) { + return a; + } + if (b->get_level() == merkle_depth) { + return b; + } + + CellSlice csa(NoVm(), a); + CellSlice csb(NoVm(), b); + + if (csa.is_special() && csa.special_type() == vm::Cell::SpecialType::PrunnedBranch) { + return b; + } + if (csb.is_special() && csb.special_type() == vm::Cell::SpecialType::PrunnedBranch) { + return a; + } + + CHECK(csa.size_refs() != 0); + + auto child_merkle_depth = csa.child_merkle_depth(merkle_depth); + + CellBuilder cb; + cb.store_bits(csa.fetch_bits(csa.size())); + for (unsigned i = 0; i < csa.size_refs(); i++) { + cb.store_ref(merge(csa.prefetch_ref(i), csb.prefetch_ref(i), child_merkle_depth)); + } + return cb.finalize(csa.is_special()); + } +}; + class MerkleProofCombine { public: MerkleProofCombine(Ref a, Ref b) : a_(std::move(a)), b_(std::move(b)) { } td::Result> run() { - TRY_RESULT(a, unpack_proof(a_)); - TRY_RESULT(b, unpack_proof(b_)); - if (a->get_hash(0) != b->get_hash(0)) { + TRY_RESULT_ASSIGN(a_, unpack_proof(a_)); + TRY_RESULT_ASSIGN(b_, unpack_proof(b_)); + TRY_RESULT(res, run_raw()); + return CellBuilder::create_merkle_proof(std::move(res)); + } + + td::Result> run_raw() { + if (a_->get_hash(0) != b_->get_hash(0)) { return td::Status::Error("Can't combine MerkleProofs with different roots"); } - dfs(a, 0); - dfs(b, 0); - return CellBuilder::create_merkle_proof(create_A(a, 0, 0)); + dfs(a_, 0); + dfs(b_, 0); + return create_A(a_, 0, 0); } private: @@ -262,6 +323,30 @@ Ref MerkleProof::combine(Ref a, Ref b) { return res.move_as_ok(); } +Ref MerkleProof::combine_fast(Ref a, Ref b) { + auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run(); + if (res.is_error()) { + return {}; + } + return res.move_as_ok(); +} + +Ref MerkleProof::combine_raw(Ref a, Ref b) { + auto res = MerkleProofCombine(std::move(a), std::move(b)).run_raw(); + if (res.is_error()) { + return {}; + } + return res.move_as_ok(); +} + +Ref MerkleProof::combine_fast_raw(Ref a, Ref b) { + auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run_raw(); + if (res.is_error()) { + return {}; + } + return res.move_as_ok(); +} + MerkleProofBuilder::MerkleProofBuilder(Ref root) : usage_tree(std::make_shared()), orig_root(std::move(root)) { usage_root = UsageCell::create(orig_root, usage_tree->root_ptr()); diff --git a/crypto/vm/cells/MerkleProof.h b/crypto/vm/cells/MerkleProof.h index 5b3b8ebcef..41788c5f7f 100644 --- a/crypto/vm/cells/MerkleProof.h +++ b/crypto/vm/cells/MerkleProof.h @@ -38,12 +38,15 @@ class MerkleProof { static Ref virtualize(Ref cell, int virtualization); static Ref combine(Ref a, Ref b); + static Ref combine_fast(Ref a, Ref b); // works with upwrapped proofs // works fine with cell of non-zero level, but this is not supported (yet?) in MerkeProof special cell static Ref generate_raw(Ref cell, IsPrunnedFunction is_prunned); static Ref generate_raw(Ref cell, CellUsageTree *usage_tree); static Ref virtualize_raw(Ref cell, Cell::VirtualizationParameters virt); + static Ref combine_raw(Ref a, Ref b); + static Ref combine_fast_raw(Ref a, Ref b); }; class MerkleProofBuilder { diff --git a/crypto/vm/cells/MerkleUpdate.cpp b/crypto/vm/cells/MerkleUpdate.cpp index 30b5116ecf..d75f16675e 100644 --- a/crypto/vm/cells/MerkleUpdate.cpp +++ b/crypto/vm/cells/MerkleUpdate.cpp @@ -268,7 +268,7 @@ class MerkleCombine { // 1. Create maximum subtrees of X and Z with all cells in A, B, C and D // Max(V) - such maximum subtree // - // 2. Max(A) and Max(D) should be merkle update already. But win want to minimize it + // 2. Max(A) and Max(D) should be merkle update already. But we want to minimize it // So we cut all branches of Max(D) which are in maxA // When we cut branch q in Max(D) we mark some path to q in Max(A) // Then we cut all branches of Max(A) which are not marked. diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index 09d2be8946..f75aff755a 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -1055,7 +1055,7 @@ ControlRegs* force_cregs(Ref& cont) { } int run_vm_code(Ref code, Ref& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, - GasLimits* gas_limits, std::vector> libraries, Ref init_c7) { + GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr) { VmState vm{code, std::move(stack), gas_limits ? *gas_limits : GasLimits{}, @@ -1066,8 +1066,11 @@ int run_vm_code(Ref code, Ref& stack, int flags, Ref* da std::move(init_c7)}; int res = vm.run(); stack = vm.get_stack_ref(); - if (res == -1 && data_ptr) { - *data_ptr = vm.get_c4(); + if (vm.committed() && data_ptr) { + *data_ptr = vm.get_committed_state().c4; + } + if (vm.committed() && actions_ptr) { + *actions_ptr = vm.get_committed_state().c5; } if (steps) { *steps = vm.get_steps_count(); @@ -1090,11 +1093,12 @@ int run_vm_code(Ref code, Ref& stack, int flags, Ref* da } int run_vm_code(Ref code, Stack& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, - GasLimits* gas_limits, std::vector> libraries, Ref init_c7) { + GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr) { Ref stk{true}; stk.unique_write().set_contents(std::move(stack)); stack.clear(); - int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7)); + int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7), + actions_ptr); CHECK(stack.is_unique()); if (stk.is_null()) { stack.clear(); diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index 61cbc2d630..1c24c4cdfa 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -644,12 +644,12 @@ class VmState final : public VmStateInterface { void init_cregs(bool same_c3 = false, bool push_0 = true); }; -int run_vm_code(Ref _code, Ref& _stack, int flags = 0, Ref* data_ptr = 0, VmLog log = {}, +int run_vm_code(Ref _code, Ref& _stack, int flags = 0, Ref* data_ptr = nullptr, VmLog log = {}, long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, - Ref init_c7 = {}); -int run_vm_code(Ref _code, Stack& _stack, int flags = 0, Ref* data_ptr = 0, VmLog log = {}, + Ref init_c7 = {}, Ref* actions_ptr = nullptr); +int run_vm_code(Ref _code, Stack& _stack, int flags = 0, Ref* data_ptr = nullptr, VmLog log = {}, long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, - Ref init_c7 = {}); + Ref init_c7 = {}, Ref* actions_ptr = nullptr); ControlData* force_cdata(Ref& cont); ControlRegs* force_cregs(Ref& cont); diff --git a/crypto/vm/stack.hpp b/crypto/vm/stack.hpp index 830f434019..5174ad7ff4 100644 --- a/crypto/vm/stack.hpp +++ b/crypto/vm/stack.hpp @@ -481,6 +481,18 @@ class Stack : public td::CntObject { Ref pop_atom(); std::string pop_string(); std::string pop_bytes(); + template + Ref pop_object() { + return pop_chk().as_object(); + } + template + Ref pop_object_type_chk() { + auto res = pop_object(); + if (!res) { + throw VmError{Excno::type_chk, "not an object of required type"}; + } + return res; + } void push_null(); void push_int(td::RefInt256 val); void push_int_quiet(td::RefInt256 val, bool quiet = true); diff --git a/doc/fiftbase.tex b/doc/fiftbase.tex index d39bf3fc8f..162f67840c 100644 --- a/doc/fiftbase.tex +++ b/doc/fiftbase.tex @@ -1338,8 +1338,10 @@ \section*{Introduction} \item {\tt idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$. \item {\tt b>idict!}, {\tt b>idict!+}, variants of {\tt idict!} and {\tt idict!+} accepting the new value $v$ in a {\em Builder\/} instead of a {\em Slice}. \item {\tt udict!}, {\tt udict!+}, {\tt b>udict!}, {\tt b>udict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with an unsigned $n$-bit integer $x$ used as a key. +\item {\tt sdict!}, {\tt sdict!+}, {\tt b>sdict!}, {\tt b>sdict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with the first $n$ data bits of {\em Slice\/}~$x$ used as a key. \item {\tt idict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/}~$D$. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. \item {\tt udict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with an {\em un}signed big-endian $n$-bit {\em Integer\/}~$x$ used as a key. +\item {\tt sdict@} ($k$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with the key provided in the first $n$ bits of {\em Slice\/}~$k$. \item {\tt dictmap} ($D$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $D$ with $n$-bit keys. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary. \item {\tt dictmerge} ($D$ $D'$ $n$ $e$ -- $D''$), combines two dictionaries $D$ and $D'$ with $n$-bit keys into one dictionary $D''$ with the same keys. If a key is present in only one of the dictionaries $D$ and $D'$, this key and the corresponding value are copied verbatim to the new dictionary $D''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $D$ and $D'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary. \end{itemize} @@ -2001,10 +2003,12 @@ \section*{Introduction} \item {\tt b.} ($x$ -- ), prints the binary representation of an {\em Integer\/}~$x$, followed by a single space. Equivalent to {\tt b.\_ space}. \item {\tt b.\_} ($x$ -- ), prints the binary representation of an {\em Integer\/}~$x$ without any spaces. Equivalent to {\tt (b.)~type}. \item {\tt b>} ($b$ -- $c$), transforms a {\em Builder\/}~$b$ into a new {\em Cell\/}~$c$ containing the same data as~$b$, cf.~\ptref{p:builder.ops}. -\item {\tt b>idict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned. -\item {\tt b>idict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt b>idict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}. -\item {\tt b>udict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by unsigned big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned. -\item {\tt b>udict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt b>udict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}. +\item {\tt b>idict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned. +\item {\tt b>idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt b>idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}. +\item {\tt b>sdict!} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by the first $n$ bits of {\em Slice\/}~$k$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned. +\item {\tt b>sdict!+} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(k,v)$ into dictionary $D$ similarly to {\tt b>sdict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}. +\item {\tt b>udict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by unsigned big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned. +\item {\tt b>udict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt b>udict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}. \item {\tt bbitrefs} ($b$ -- $x$ $y$), returns both the number of data bits $x$ and the number of references $y$ already stored in {\em Builder\/}~$b$, cf.~\ptref{p:builder.ops}. \item {\tt bbits} ($b$ -- $x$), returns the number of data bits already stored in {\em Builder\/}~$b$. The result $x$ is an {\em Integer\/} in the range $0\dots1023$, cf.~\ptref{p:builder.ops}. \item {\tt bl} ( -- $x$), pushes the Unicode codepoint of a space, i.e., 32, cf.~\ptref{p:string.ops}. @@ -2129,6 +2133,9 @@ \section*{Introduction} \item {\tt s>c} ($s$ -- $c$), creates a {\em Cell}~$c$ directly from a {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt }. \item {\tt sbitrefs} ($s$ -- $x$ $y$), returns both the number of data bits $x$ and the number of cell references $y$ remaining in {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt remaining}. \item {\tt sbits} ($s$ -- $x$), returns the number of data bits $x$ remaining in {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. +\item {\tt sdict!} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by the first $n$ bits of {\em Slice\/}~$k$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned. +\item {\tt sdict!+} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(k,v)$ into dictionary $D$ similarly to {\tt sdict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}. +\item {\tt sdict@} ($k$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key given by the first $n$ data bits of {\em Slice\/}~$x$ in the dictionary represented by {\em Cell\/} or {\em Null\/}~$D$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$. \item {\tt second} ($t$ -- $x$), returns the second component of a {\em Tuple}, cf.~\ptref{p:tuples}. Equivalent to {\tt 1 []}. \item {\tt sgn} ($x$ -- $y$), computes the sign of an {\em Integer\/} $x$ (i.e., pushes $1$ if $x>0$, $-1$ if $x<0$, and $0$ if $x=0$), cf.~\ptref{p:int.comp}. Equivalent to {\tt 0 cmp}. \item {\tt shash} ($s$ -- $B$), computes the $\Sha$-based representation hash of a {\em Slice\/} by first transforming it into a cell, cf.~\ptref{p:hash.ops}. Equivalent to {\tt s>c hashB}. diff --git a/tdactor/test/actors_core.cpp b/tdactor/test/actors_core.cpp index d426f344b5..2e44d3bb2e 100644 --- a/tdactor/test/actors_core.cpp +++ b/tdactor/test/actors_core.cpp @@ -101,10 +101,10 @@ TEST(Actor2, locker) { kill_signal.add_signal(ActorSignals::Kill); ActorSignals wakeup_signal; - kill_signal.add_signal(ActorSignals::Wakeup); + wakeup_signal.add_signal(ActorSignals::Wakeup); ActorSignals cpu_signal; - kill_signal.add_signal(ActorSignals::Cpu); + cpu_signal.add_signal(ActorSignals::Cpu); { ActorLocker lockerA(&state); diff --git a/tl/generate/scheme/lite_api.tl b/tl/generate/scheme/lite_api.tl index 0dd3fa22f8..aa9f1c089f 100644 --- a/tl/generate/scheme/lite_api.tl +++ b/tl/generate/scheme/lite_api.tl @@ -35,6 +35,7 @@ liteServer.blockState id:tonNode.blockIdExt root_hash:int256 file_hash:int256 da liteServer.blockHeader id:tonNode.blockIdExt mode:# header_proof:bytes = liteServer.BlockHeader; liteServer.sendMsgStatus status:int = liteServer.SendMsgStatus; liteServer.accountState id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state:bytes = liteServer.AccountState; +liteServer.runMethodResult mode:# id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state_proof:bytes init_c7:bytes lib_extras:bytes exit_code:int result:bytes = liteServer.RunMethodResult; liteServer.shardInfo id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes shard_descr:bytes = liteServer.ShardInfo; liteServer.allShardsInfo id:tonNode.blockIdExt proof:bytes data:bytes = liteServer.AllShardsInfo; liteServer.transactionInfo id:tonNode.blockIdExt proof:bytes transaction:bytes = liteServer.TransactionInfo; @@ -63,6 +64,7 @@ liteServer.getState id:tonNode.blockIdExt = liteServer.BlockState; liteServer.getBlockHeader id:tonNode.blockIdExt mode:# = liteServer.BlockHeader; liteServer.sendMessage body:bytes = liteServer.SendMsgStatus; liteServer.getAccountState id:tonNode.blockIdExt account:liteServer.accountId = liteServer.AccountState; +liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:int params:bytes = liteServer.RunMethodResult; liteServer.getShardInfo id:tonNode.blockIdExt workchain:int shard:long exact:Bool = liteServer.ShardInfo; liteServer.getAllShardsInfo id:tonNode.blockIdExt = liteServer.AllShardsInfo; liteServer.getOneTransaction id:tonNode.blockIdExt account:liteServer.accountId lt:long = liteServer.TransactionInfo; diff --git a/tl/generate/scheme/lite_api.tlo b/tl/generate/scheme/lite_api.tlo index 02b507f40130729f588a1251a733640b2f7fdc44..c813541e2dd8e78dba1b672168e1e9323462587a 100644 GIT binary patch delta 276 zcmbOeeIbGO(QJJy1}MT$)p| znVDC|BtfHz|@hlmOXduCo{Nqn;T = tvm.Tuple; tvm.list elements:vector = tvm.List; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 82702148d4e32c17992b8bf5010bf852689c701e..5c9d506eca080cde8c3fb4f9d9886d5935119687 100644 GIT binary patch delta 187 zcmew}jq%Sk#tkVPEDIM$KcAezQNr@Pi%ntk1CBkiA|N4-lCoUA;Uq?RV)Ai diff --git a/validator-engine-console/validator-engine-console.h b/validator-engine-console/validator-engine-console.h index 8c4596dd36..5722c1e75e 100644 --- a/validator-engine-console/validator-engine-console.h +++ b/validator-engine-console/validator-engine-console.h @@ -85,6 +85,7 @@ class ValidatorEngineConsole : public td::actor::Actor { void add_cmd(td::BufferSlice data) { ex_mode_ = true; ex_queries_.push_back(std::move(data)); + set_readline_enabled(false); } void set_fail_timeout(td::Timestamp ts) { fail_timeout_ = ts; diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index b9f89f0cc6..8a11af71e8 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -129,7 +129,7 @@ void LiteQuery::start_up() { [&](lite_api::liteServer_getState& q) { this->perform_getState(ton::create_block_id(q.id_)); }, [&](lite_api::liteServer_getAccountState& q) { this->perform_getAccountState(ton::create_block_id(q.id_), static_cast(q.account_->workchain_), - q.account_->id_); + q.account_->id_, 0); }, [&](lite_api::liteServer_getOneTransaction& q) { this->perform_getOneTransaction(ton::create_block_id(q.id_), @@ -172,6 +172,10 @@ void LiteQuery::start_up() { q.mode_ & 1 ? q.start_after_ : td::Bits256::zero(), q.mode_ & 4 ? q.modified_after_ : 0); }, + [&](lite_api::liteServer_runSmcMethod& q) { + this->perform_runSmcMethod(ton::create_block_id(q.id_), static_cast(q.account_->workchain_), + q.account_->id_, q.mode_, q.method_id_, std::move(q.params_)); + }, [&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); })); } @@ -604,9 +608,9 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) { return true; } -void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr) { - LOG(INFO) << "started a getAccountState(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() - << ") liteserver query"; +void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode) { + LOG(INFO) << "started a getAccountState(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ", " + << mode << ") liteserver query"; if (blkid.id.workchain != masterchainId && blkid.id.workchain != workchain) { fatal_error("reference block for a getAccountState() must belong to the masterchain"); return; @@ -626,6 +630,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, } acc_workchain_ = workchain; acc_addr_ = addr; + mode_ = mode; if (blkid.id.workchain != masterchainId) { base_blk_id_ = blkid; set_continuation([&]() -> void { finish_getAccountState({}); }); @@ -659,6 +664,45 @@ void LiteQuery::continue_getAccountState_0(Ref request_mc_block_data(blkid); } +void LiteQuery::perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode, + int method_id, td::BufferSlice params) { + LOG(INFO) << "started a runSmcMethod(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ", " + << method_id << ", " << mode << ") liteserver query with " << params.size() << " parameter bytes"; + if (params.size() >= 65536) { + fatal_error("more than 64k parameter bytes passed"); + return; + } + if (mode & ~0xf) { + fatal_error("unsupported mode in runSmcMethod"); + return; + } + stack_.clear(); + try { + if (params.size()) { + auto res = vm::std_boc_deserialize(std::move(params)); + if (res.is_error()) { + fatal_error("cannot deserialize parameter list boc: "s + res.move_as_error().to_string()); + return; + } + auto cs = vm::load_cell_slice(res.move_as_ok()); + if (!(vm::Stack::deserialize_to(cs, stack_, 0) && cs.empty_ext())) { + fatal_error("parameter list boc cannot be deserialized as a VmStack"); + return; + } + } else { + stack_ = td::make_ref(); + } + stack_.write().push_smallint(method_id); + } catch (vm::VmError& vme) { + fatal_error("error deserializing parameter list: "s + vme.get_msg()); + return; + } catch (vm::VmVirtError& vme) { + fatal_error("virtualization error while deserializing parameter list: "s + vme.get_msg()); + return; + } + perform_getAccountState(blkid, workchain, addr, mode | 0x10000); +} + void LiteQuery::perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt) { LOG(INFO) << "started a getOneTransaction(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << "," << lt << ") liteserver query"; @@ -934,10 +978,15 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) { acc_root = acc_csr->prefetch_ref(); } auto proof = vm::std_boc_serialize_multi({std::move(proof1), pb.extract_proof()}); + pb.clear(); if (proof.is_error()) { fatal_error(proof.move_as_error()); return; } + if (mode_ & 0x10000) { + finish_runSmcMethod(std::move(shard_proof), proof.move_as_ok(), std::move(acc_root)); + return; + } td::BufferSlice data; if (acc_root.not_null()) { auto res = vm::std_boc_serialize(std::move(acc_root)); @@ -954,6 +1003,13 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) { finish_query(std::move(b)); } +void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref acc_root) { + LOG(INFO) << "completing runSmcMethod() query"; + // ... TODO ... + fatal_error("runSmcMethod not implemented"); + return; +} + void LiteQuery::continue_getOneTransaction() { LOG(INFO) << "completing getOneTransaction() query"; CHECK(block_.not_null()); diff --git a/validator/impl/liteserver.hpp b/validator/impl/liteserver.hpp index 36fc9a8092..4ebcfeb20b 100644 --- a/validator/impl/liteserver.hpp +++ b/validator/impl/liteserver.hpp @@ -37,6 +37,7 @@ class LiteQuery : public td::actor::Actor { td::Timestamp timeout_; td::Promise promise_; int pending_{0}; + int mode_{0}; WorkchainId acc_workchain_; StdSmcAddress acc_addr_; LogicalTime trans_lt_; @@ -55,6 +56,7 @@ class LiteQuery : public td::actor::Actor { std::vector> aux_objs_; std::vector blk_ids_; std::unique_ptr chain_; + Ref stack_; public: enum { @@ -91,10 +93,13 @@ class LiteQuery : public td::actor::Actor { void continue_getState(BlockIdExt blkid, Ref state); void continue_getZeroState(BlockIdExt blkid, td::BufferSlice state); void perform_sendMessage(td::BufferSlice ext_msg); - void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr); + void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode); void continue_getAccountState_0(Ref mc_state, BlockIdExt blkid); void continue_getAccountState(); void finish_getAccountState(td::BufferSlice shard_proof); + void perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode, int method_id, + td::BufferSlice params); + void finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref acc_root); void perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt); void continue_getOneTransaction(); void perform_getTransactions(WorkchainId workchain, StdSmcAddress addr, LogicalTime lt, Bits256 hash, unsigned count); From 53ec9684bd213983e1fe0f7610d3e3453a4ec628 Mon Sep 17 00:00:00 2001 From: ton Date: Sun, 2 Feb 2020 16:53:37 +0400 Subject: [PATCH 028/667] updated liteserver - new methods for liteserver/liteclient - added ADNL/DHT client-only work mode - fixed crash in ADNL --- GPLv2 | 2 +- LGPLv2 | 2 +- adnl/adnl-address-list.cpp | 10 + adnl/adnl-address-list.h | 2 + adnl/adnl-channel.cpp | 8 +- adnl/adnl-channel.h | 2 +- adnl/adnl-channel.hpp | 2 +- adnl/adnl-local-id.cpp | 13 +- adnl/adnl-local-id.h | 2 +- adnl/adnl-network-manager.cpp | 2 +- adnl/adnl-packet.h | 9 + adnl/adnl-peer-table.cpp | 8 +- adnl/adnl-peer-table.h | 2 +- adnl/adnl-peer-table.hpp | 2 +- adnl/adnl-peer.cpp | 16 +- .../blockchain-explorer-http.cpp | 18 +- .../blockchain-explorer-http.hpp | 2 + blockchain-explorer/blockchain-explorer.cpp | 4 + blockchain-explorer/blockchain-explorer.hpp | 2 + crypto/block/check-proof.cpp | 15 +- crypto/block/check-proof.h | 7 +- crypto/block/mc-config.cpp | 10 +- crypto/fift/lib/Asm.fif | 7 +- crypto/fift/words.cpp | 11 +- crypto/smartcont/highload-wallet-code.fc | 6 + crypto/smartcont/highload-wallet-v2-code.fc | 6 + crypto/smartcont/new-wallet-v2.fif | 4 +- crypto/smartcont/new-wallet-v3.fif | 4 +- crypto/smartcont/new-wallet.fif | 4 +- crypto/smartcont/restricted-wallet-code.fc | 6 + crypto/smartcont/restricted-wallet2-code.fc | 6 + crypto/smartcont/simple-wallet-ext-code.fc | 5 + crypto/smartcont/wallet-code.fc | 6 + crypto/smartcont/wallet3-code.fc | 6 + crypto/vm/cells/CellBuilder.cpp | 16 +- crypto/vm/cells/CellSlice.h | 3 +- crypto/vm/dict.cpp | 8 +- crypto/vm/dictops.cpp | 39 ++- crypto/vm/log.h | 8 +- crypto/vm/stack.cpp | 4 +- dht/dht-bucket.cpp | 6 +- dht/dht-bucket.hpp | 7 +- dht/dht-in.hpp | 6 +- dht/dht-query.cpp | 21 +- dht/dht-query.hpp | 27 +- dht/dht-remote-node.cpp | 67 ++-- dht/dht-remote-node.hpp | 3 +- dht/dht.cpp | 135 +++++--- dht/dht.h | 4 + dht/dht.hpp | 4 +- doc/tvm.tex | 5 + keyring/keyring.cpp | 16 +- lite-client/lite-client.cpp | 317 ++++++++++++------ lite-client/lite-client.h | 10 +- tdutils/td/utils/BufferedFd.h | 8 +- tdutils/td/utils/FileLog.cpp | 8 +- tdutils/td/utils/FileLog.h | 4 +- tdutils/td/utils/misc.h | 8 +- tdutils/td/utils/port/detail/Epoll.cpp | 3 +- tdutils/test/misc.cpp | 7 +- test/regression-tests.ans | 2 +- tl/generate/scheme/lite_api.tl | 4 +- tl/generate/scheme/lite_api.tlo | Bin 12368 -> 12416 bytes tl/generate/scheme/ton_api.tl | 22 ++ tl/generate/scheme/ton_api.tlo | Bin 56528 -> 58212 bytes validator/db/archive-manager.cpp | 8 +- validator/db/archive-slice.cpp | 1 + validator/db/package.cpp | 10 +- validator/impl/liteserver.cpp | 120 ++++++- validator/impl/liteserver.hpp | 16 +- 70 files changed, 816 insertions(+), 322 deletions(-) diff --git a/GPLv2 b/GPLv2 index c2a8d6835f..c1e0c158c3 100644 --- a/GPLv2 +++ b/GPLv2 @@ -23,5 +23,5 @@ 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 + Copyright 2017-2020 Telegram Systems LLP */ diff --git a/LGPLv2 b/LGPLv2 index d93ab1b9a3..ab2bc125c4 100644 --- a/LGPLv2 +++ b/LGPLv2 @@ -14,5 +14,5 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ diff --git a/adnl/adnl-address-list.cpp b/adnl/adnl-address-list.cpp index ab3951442e..8b92d9253e 100644 --- a/adnl/adnl-address-list.cpp +++ b/adnl/adnl-address-list.cpp @@ -155,6 +155,16 @@ td::Result AdnlAddressList::create(const tl_object_ptr(addr.get_ipv4(), static_cast(addr.get_port())); + addrs_.push_back(std::move(r)); + return td::Status::OK(); + } else { + return td::Status::Error(ErrorCode::protoviolation, "only works with ipv4"); + } +} + } // namespace adnl } // namespace ton diff --git a/adnl/adnl-address-list.h b/adnl/adnl-address-list.h index d13fce6f2c..686ced6588 100644 --- a/adnl/adnl-address-list.h +++ b/adnl/adnl-address-list.h @@ -88,6 +88,7 @@ class AdnlAddressList { void add_addr(AdnlAddress addr) { addrs_.push_back(addr); } + void update(td::IPAddress addr); bool public_only() const; td::uint32 size() const { return static_cast(addrs_.size()); @@ -98,6 +99,7 @@ class AdnlAddressList { } static td::Result create(const tl_object_ptr &addr_list); + td::Status add_udp_address(td::IPAddress addr); }; } // namespace adnl diff --git a/adnl/adnl-channel.cpp b/adnl/adnl-channel.cpp index 9314d54e2b..f4e09cb024 100644 --- a/adnl/adnl-channel.cpp +++ b/adnl/adnl-channel.cpp @@ -111,13 +111,15 @@ void AdnlChannelImpl::send_message(td::uint32 priority, td::actor::ActorId R) { + [peer = peer_pair_, channel_id = channel_in_id_, addr, id = print_id()](td::Result R) { if (R.is_error()) { VLOG(ADNL_WARNING) << id << ": dropping IN message: can not decrypt: " << R.move_as_error(); } else { - td::actor::send_closure(peer, &AdnlPeerPair::receive_packet_from_channel, channel_id, R.move_as_ok()); + auto packet = R.move_as_ok(); + packet.set_remote_addr(addr); + td::actor::send_closure(peer, &AdnlPeerPair::receive_packet_from_channel, channel_id, std::move(packet)); } }); diff --git a/adnl/adnl-channel.h b/adnl/adnl-channel.h index e56f040cf8..a8e2c448e7 100644 --- a/adnl/adnl-channel.h +++ b/adnl/adnl-channel.h @@ -35,7 +35,7 @@ class AdnlChannel : public td::actor::Actor { AdnlNodeIdShort local_id, AdnlNodeIdShort peer_id, AdnlChannelIdShort &out_id, AdnlChannelIdShort &in_id, td::actor::ActorId peer_pair); - virtual void receive(td::BufferSlice data) = 0; + virtual void receive(td::IPAddress addr, td::BufferSlice data) = 0; virtual void send_message(td::uint32 priority, td::actor::ActorId conn, td::BufferSlice data) = 0; virtual ~AdnlChannel() = default; diff --git a/adnl/adnl-channel.hpp b/adnl/adnl-channel.hpp index 1499f5c4e3..d4356f93e9 100644 --- a/adnl/adnl-channel.hpp +++ b/adnl/adnl-channel.hpp @@ -33,7 +33,7 @@ class AdnlChannelImpl : public AdnlChannel { AdnlChannelIdShort in_id, AdnlChannelIdShort out_id, std::unique_ptr encryptor, std::unique_ptr decryptor); void decrypt(td::BufferSlice data, td::Promise promise); - void receive(td::BufferSlice data) override; + void receive(td::IPAddress addr, td::BufferSlice data) override; void send_message(td::uint32 priority, td::actor::ActorId conn, td::BufferSlice data) override; struct AdnlChannelPrintId { diff --git a/adnl/adnl-local-id.cpp b/adnl/adnl-local-id.cpp index 79b2bcd2ae..cd9d384eab 100644 --- a/adnl/adnl-local-id.cpp +++ b/adnl/adnl-local-id.cpp @@ -40,13 +40,15 @@ AdnlAddressList AdnlLocalId::get_addr_list() const { return addr_list_; } -void AdnlLocalId::receive(td::BufferSlice data) { +void AdnlLocalId::receive(td::IPAddress addr, td::BufferSlice data) { auto P = td::PromiseCreator::lambda( - [peer_table = peer_table_, dst = short_id_, id = print_id()](td::Result R) { + [peer_table = peer_table_, dst = short_id_, addr, id = print_id()](td::Result R) { if (R.is_error()) { VLOG(ADNL_WARNING) << id << ": dropping IN message: cannot decrypt: " << R.move_as_error(); } else { - td::actor::send_closure(peer_table, &AdnlPeerTable::receive_decrypted_packet, dst, R.move_as_ok()); + auto packet = R.move_as_ok(); + packet.set_remote_addr(addr); + td::actor::send_closure(peer_table, &AdnlPeerTable::receive_decrypted_packet, dst, std::move(packet)); } }); @@ -117,7 +119,7 @@ void AdnlLocalId::update_address_list(AdnlAddressList addr_list) { } void AdnlLocalId::publish_address_list() { - if (dht_node_.empty() || addr_list_.empty()) { + if (dht_node_.empty() || addr_list_.empty() || addr_list_.size() == 0) { VLOG(ADNL_NOTICE) << this << ": skipping public addr list, because localid (or dht node) not fully initialized"; return; } @@ -178,7 +180,8 @@ AdnlLocalId::AdnlLocalId(AdnlNodeIdFull id, AdnlAddressList addr_list, td::actor id_ = std::move(id); short_id_ = id_.compute_short_id(); addr_list_ = std::move(addr_list); - if (addr_list_.addrs().size() > 0) { + if (!addr_list_.empty()) { + addr_list_.set_reinit_date(Adnl::adnl_start_time()); addr_list_.set_version(static_cast(td::Clocks::system())); } peer_table_ = peer_table; diff --git a/adnl/adnl-local-id.h b/adnl/adnl-local-id.h index 07326f7997..03998f7afd 100644 --- a/adnl/adnl-local-id.h +++ b/adnl/adnl-local-id.h @@ -54,7 +54,7 @@ class AdnlLocalId : public td::actor::Actor { void decrypt_message(td::BufferSlice data, td::Promise promise); void deliver(AdnlNodeIdShort src, td::BufferSlice data); void deliver_query(AdnlNodeIdShort src, td::BufferSlice data, td::Promise promise); - void receive(td::BufferSlice data); + void receive(td::IPAddress addr, td::BufferSlice data); void subscribe(std::string prefix, std::unique_ptr callback); void unsubscribe(std::string prefix); diff --git a/adnl/adnl-network-manager.cpp b/adnl/adnl-network-manager.cpp index c5c25463f8..02e73ce074 100644 --- a/adnl/adnl-network-manager.cpp +++ b/adnl/adnl-network-manager.cpp @@ -59,7 +59,7 @@ void AdnlNetworkManagerImpl::receive_udp_message(td::UdpMessage message) { } received_messages_++; if (received_messages_ % 64 == 0) { - VLOG(ADNL_DEBUG) << this << ": received " << received_messages_ << "udp messages"; + VLOG(ADNL_DEBUG) << this << ": received " << received_messages_ << " udp messages"; } VLOG(ADNL_EXTRA_DEBUG) << this << ": received message of size " << message.data.size(); diff --git a/adnl/adnl-packet.h b/adnl/adnl-packet.h index 6459e06bbe..107ed7e54b 100644 --- a/adnl/adnl-packet.h +++ b/adnl/adnl-packet.h @@ -132,6 +132,9 @@ class AdnlPacket { auto signature() const { return signature_.clone(); } + auto remote_addr() const { + return remote_addr_; + } void init_random(); @@ -188,6 +191,10 @@ class AdnlPacket { flags_ |= Flags::f_reinit_date; } + void set_remote_addr(td::IPAddress addr) { + remote_addr_ = addr; + } + private: td::BufferSlice rand1_; td::uint32 flags_{0}; @@ -204,6 +211,8 @@ class AdnlPacket { td::int32 dst_reinit_date_{0}; td::BufferSlice signature_; td::BufferSlice rand2_; + + td::IPAddress remote_addr_; }; } // namespace adnl diff --git a/adnl/adnl-peer-table.cpp b/adnl/adnl-peer-table.cpp index 04a7768497..b4880de263 100644 --- a/adnl/adnl-peer-table.cpp +++ b/adnl/adnl-peer-table.cpp @@ -49,7 +49,7 @@ td::actor::ActorOwn Adnl::create(std::string db, td::actor::ActorId(td::actor::create_actor("PeerTable", db, keyring)); } -void AdnlPeerTableImpl::receive_packet(td::BufferSlice data) { +void AdnlPeerTableImpl::receive_packet(td::IPAddress addr, td::BufferSlice data) { if (data.size() < 32) { VLOG(ADNL_WARNING) << this << ": dropping IN message [?->?]: message too short: len=" << data.size(); return; @@ -60,14 +60,14 @@ void AdnlPeerTableImpl::receive_packet(td::BufferSlice data) { auto it = local_ids_own_.find(dst); if (it != local_ids_own_.end()) { - td::actor::send_closure(it->second, &AdnlLocalId::receive, std::move(data)); + td::actor::send_closure(it->second, &AdnlLocalId::receive, addr, std::move(data)); return; } AdnlChannelIdShort dst_chan_id{dst.pubkey_hash()}; auto it2 = channels_.find(dst_chan_id); if (it2 != channels_.end()) { - td::actor::send_closure(it2->second, &AdnlChannel::receive, std::move(data)); + td::actor::send_closure(it2->second, &AdnlChannel::receive, addr, std::move(data)); return; } @@ -237,7 +237,7 @@ void AdnlPeerTableImpl::register_network_manager(td::actor::ActorId id) : id_(id) { } diff --git a/adnl/adnl-peer-table.h b/adnl/adnl-peer-table.h index 1050222cbd..42447a449f 100644 --- a/adnl/adnl-peer-table.h +++ b/adnl/adnl-peer-table.h @@ -89,7 +89,7 @@ class AdnlPeerTable : public Adnl { virtual void answer_query(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlQueryId query_id, td::BufferSlice data) = 0; - virtual void receive_packet(td::BufferSlice data) = 0; + virtual void receive_packet(td::IPAddress addr, td::BufferSlice data) = 0; virtual void receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket packet) = 0; virtual void send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message) = 0; diff --git a/adnl/adnl-peer-table.hpp b/adnl/adnl-peer-table.hpp index a48737277c..c5c6acd8bc 100644 --- a/adnl/adnl-peer-table.hpp +++ b/adnl/adnl-peer-table.hpp @@ -43,7 +43,7 @@ class AdnlPeerTableImpl : public AdnlPeerTable { void add_peer(AdnlNodeIdShort local_id, AdnlNodeIdFull id, AdnlAddressList addr_list) override; void add_static_nodes_from_config(AdnlNodesList nodes) override; - void receive_packet(td::BufferSlice data) override; + void receive_packet(td::IPAddress addr, td::BufferSlice data) override; void receive_decrypted_packet(AdnlNodeIdShort dst, AdnlPacket data) override; void send_message_in(AdnlNodeIdShort src, AdnlNodeIdShort dst, AdnlMessage message) override; void send_message(AdnlNodeIdShort src, AdnlNodeIdShort dst, td::BufferSlice data) override { diff --git a/adnl/adnl-peer.cpp b/adnl/adnl-peer.cpp index ba077682cf..4ff8044ad7 100644 --- a/adnl/adnl-peer.cpp +++ b/adnl/adnl-peer.cpp @@ -131,7 +131,12 @@ void AdnlPeerPairImpl::receive_packet_checked(AdnlPacket packet) { } if (packet.dst_reinit_date() > 0 && packet.dst_reinit_date() < d) { if (!packet.addr_list().empty()) { - update_addr_list(packet.addr_list()); + auto addr_list = packet.addr_list(); + if (packet.remote_addr().is_valid() && addr_list.size() == 0) { + VLOG(ADNL_DEBUG) << "adding implicit address " << packet.remote_addr(); + addr_list.add_udp_address(packet.remote_addr()); + } + update_addr_list(std::move(addr_list)); } if (!packet.priority_addr_list().empty()) { update_addr_list(packet.priority_addr_list()); @@ -174,7 +179,12 @@ void AdnlPeerPairImpl::receive_packet_checked(AdnlPacket packet) { } if (!packet.addr_list().empty()) { - update_addr_list(packet.addr_list()); + auto addr_list = packet.addr_list(); + if (packet.remote_addr().is_valid() && addr_list.size() == 0) { + VLOG(ADNL_DEBUG) << "adding implicit address " << packet.remote_addr(); + addr_list.add_udp_address(packet.remote_addr()); + } + update_addr_list(std::move(addr_list)); } if (!packet.priority_addr_list().empty()) { update_addr_list(packet.priority_addr_list()); @@ -642,7 +652,7 @@ void AdnlPeerPairImpl::update_addr_list(AdnlAddressList addr_list) { if (addr_list.empty()) { return; } - CHECK(addr_list.size() > 0); + //CHECK(addr_list.size() > 0); if (addr_list.reinit_date() > td::Clocks::system() + 60) { VLOG(ADNL_WARNING) << "dropping addr list with too new reinit date"; diff --git a/blockchain-explorer/blockchain-explorer-http.cpp b/blockchain-explorer/blockchain-explorer-http.cpp index d5d41a15dd..0d0e0c95b6 100644 --- a/blockchain-explorer/blockchain-explorer-http.cpp +++ b/blockchain-explorer/blockchain-explorer-http.cpp @@ -23,7 +23,7 @@ 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 + Copyright 2017-2020 Telegram Systems LLP */ #include "blockchain-explorer-http.hpp" #include "block/block-db.h" @@ -36,6 +36,8 @@ #include "block/mc-config.h" #include "ton/ton-shard.h" +bool local_scripts{false}; + HttpAnswer& HttpAnswer::operator<<(AddressCell addr_c) { ton::WorkchainId wc; ton::StdSmcAddress addr; @@ -425,7 +427,7 @@ HttpAnswer& HttpAnswer::operator<<(AccountCell acc_c) { HttpAnswer& HttpAnswer::operator<<(BlockHeaderCell head_c) { *this << "
"; - vm::CellSlice cs{vm::NoVm{}, head_c.root}; + vm::CellSlice cs{vm::NoVm(), head_c.root}; auto block_id = head_c.block_id; try { auto virt_root = vm::MerkleProof::virtualize(head_c.root, 1); @@ -676,13 +678,17 @@ std::string HttpAnswer::header() { "maximum-scale=1.0, user-scalable=no\" />\n" << "\n" << "\n" - << "\n" + << "\n" << "" - << "\n" + << "\n" << "\n" - << "\n" + << "\n" << "\n" - << "\n" + << "\n" << "\n" << "
\n" << "
"; return A.finish(); }(); - promise_.set_value( - MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(R, "Content-Type", "text/html"); + promise_.set_value(std::move(R)); } stop(); } @@ -1270,8 +1279,9 @@ void HttpQuerySend::finish_query() { } return A.finish(); }(); - promise_.set_value( - MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(R, "Content-Type", "text/html"); + promise_.set_value(std::move(R)); } stop(); } @@ -1434,8 +1444,9 @@ void HttpQueryRunMethod::finish_query() { return A.finish(); }(); - promise_.set_value( - MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(R, "Content-Type", "text/html"); + promise_.set_value(std::move(R)); } stop(); } @@ -1511,8 +1522,9 @@ void HttpQueryStatus::finish_query() { A << ""; return A.finish(); }(); - promise_.set_value( - MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY)); + auto R = MHD_create_response_from_buffer(page.length(), const_cast(page.c_str()), MHD_RESPMEM_MUST_COPY); + MHD_add_response_header(R, "Content-Type", "text/html"); + promise_.set_value(std::move(R)); } stop(); } diff --git a/blockchain-explorer/blockchain-explorer.cpp b/blockchain-explorer/blockchain-explorer.cpp index adaf408484..47ce7378b9 100644 --- a/blockchain-explorer/blockchain-explorer.cpp +++ b/blockchain-explorer/blockchain-explorer.cpp @@ -23,7 +23,7 @@ from all source files in the program, then also delete it here. along with TON Blockchain. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "adnl/adnl-ext-client.h" #include "adnl/utils.hpp" @@ -52,7 +52,6 @@ #include "vm/boc.h" #include "vm/cellops.h" #include "vm/cells/MerkleProof.h" -#include "vm/continuation.h" #include "vm/cp0.h" #include "auto/tl/lite_api.h" diff --git a/blockchain-explorer/blockchain-explorer.hpp b/blockchain-explorer/blockchain-explorer.hpp index 8e2ae0e62c..b6bc15f165 100644 --- a/blockchain-explorer/blockchain-explorer.hpp +++ b/blockchain-explorer/blockchain-explorer.hpp @@ -23,7 +23,7 @@ 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 + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index ff7d922791..0897166f1e 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -36,6 +36,7 @@ set(TON_CRYPTO_SOURCE vm/tonops.cpp vm/boc.cpp vm/utils.cpp + vm/vm.cpp tl/tlblib.cpp Ed25519.h @@ -82,6 +83,7 @@ set(TON_CRYPTO_SOURCE vm/tonops.h vm/vmstate.h vm/utils.h + vm/vm.h vm/cells.h vm/cellslice.h @@ -204,6 +206,8 @@ set(BLOCK_SOURCE set(SMC_ENVELOPE_SOURCE smc-envelope/GenericAccount.cpp smc-envelope/HighloadWallet.cpp + smc-envelope/HighloadWalletV2.cpp + smc-envelope/ManualDns.cpp smc-envelope/MultisigWallet.cpp smc-envelope/SmartContract.cpp smc-envelope/SmartContractCode.cpp @@ -214,12 +218,15 @@ set(SMC_ENVELOPE_SOURCE smc-envelope/GenericAccount.h smc-envelope/HighloadWallet.h + smc-envelope/HighloadWalletV2.h + smc-envelope/ManualDns.h smc-envelope/MultisigWallet.h smc-envelope/SmartContract.h smc-envelope/SmartContractCode.h smc-envelope/TestGiver.h smc-envelope/TestWallet.h smc-envelope/Wallet.h + smc-envelope/WalletInterface.h smc-envelope/WalletV3.h ) @@ -359,12 +366,14 @@ if (NOT CMAKE_CROSSCOMPILING) GenFif(DEST smartcont/auto/wallet3-code SOURCE smartcont/wallet3-code.fc NAME wallet3) GenFif(DEST smartcont/auto/simple-wallet-code SOURCE smartcont/simple-wallet-code.fc NAME simple-wallet) GenFif(DEST smartcont/auto/highload-wallet-code SOURCE smartcont/highload-wallet-code.fc NAME highload-wallet) - GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highoad-wallet-v2) + GenFif(DEST smartcont/auto/highload-wallet-v2-code SOURCE smartcont/highload-wallet-v2-code.fc NAME highload-wallet-v2) GenFif(DEST smartcont/auto/elector-code SOURCE smartcont/elector-code.fc NAME elector-code) GenFif(DEST smartcont/auto/multisig-code SOURCE smartcont/multisig-code.fc NAME multisig) GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet) GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2) + GenFif(DEST smartcont/auto/dns-manual-code SOURCE smartcont/dns-manual-code.fc NAME dns-manual) + GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext) endif() diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp index 46186caab9..719d76b037 100644 --- a/crypto/block/mc-config.cpp +++ b/crypto/block/mc-config.cpp @@ -567,6 +567,21 @@ td::Result Config::do_get_gas_limits_prices(td::Ref c } return res; } + +td::Result Config::get_dns_root_addr() const { + auto cell = get_config_param(4); + if (cell.is_null()) { + return td::Status::Error(PSLICE() << "configuration parameter " << 4 << " with dns root address is absent"); + } + auto cs = vm::load_cell_slice(std::move(cell)); + if (cs.size() != 0x100) { + return td::Status::Error(PSLICE() << "configuration parameter " << 4 << " with dns root address has wrong size"); + } + ton::StdSmcAddress res; + CHECK(cs.fetch_bits_to(res)); + return res; +} + td::Result Config::get_gas_limits_prices(bool is_masterchain) const { auto id = is_masterchain ? 20 : 21; auto cell = get_config_param(id); diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h index 55bf1122f0..8541110ffe 100644 --- a/crypto/block/mc-config.h +++ b/crypto/block/mc-config.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU General Public License along with TON Blockchain. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "common/refcnt.hpp" @@ -534,6 +534,7 @@ class Config { bool create_stats_enabled() const { return has_capability(ton::capCreateStatsEnabled); } + td::Result get_dns_root_addr() const; bool set_block_id_ext(const ton::BlockIdExt& block_id_ext); td::Result> get_special_smartcontracts(bool without_config = false) const; bool is_special_smartcontract(const ton::StdSmcAddress& addr) const; diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index df4cf883c2..2c5f448852 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "block/transaction.h" #include "block/block.h" @@ -23,7 +23,7 @@ #include "td/utils/bits.h" #include "td/utils/uint128.h" #include "ton/ton-shard.h" -#include "vm/continuation.h" +#include "vm/vm.h" namespace block { using td::Ref; @@ -691,12 +691,16 @@ bool ComputePhaseConfig::parse_GasLimitsPrices(Ref cs, td::RefInt } block::gen::GasLimitsPrices::Record_gas_flat_pfx flat; if (tlb::csr_unpack(cs, flat)) { - bool ok = parse_GasLimitsPrices(std::move(flat.other), freeze_due_limit, delete_due_limit); - flat_gas_limit = flat.flat_gas_limit; - flat_gas_price = flat.flat_gas_price; - return ok; + return parse_GasLimitsPrices_internal(std::move(flat.other), freeze_due_limit, delete_due_limit, + flat.flat_gas_limit, flat.flat_gas_price); + } else { + return parse_GasLimitsPrices_internal(std::move(cs), freeze_due_limit, delete_due_limit); } - flat_gas_limit = flat_gas_price = 0; +} + +bool ComputePhaseConfig::parse_GasLimitsPrices_internal(Ref cs, td::RefInt256& freeze_due_limit, + td::RefInt256& delete_due_limit, td::uint64 _flat_gas_limit, + td::uint64 _flat_gas_price) { auto f = [&](const auto& r, td::uint64 spec_limit) { gas_limit = r.gas_limit; special_gas_limit = spec_limit; @@ -716,6 +720,8 @@ bool ComputePhaseConfig::parse_GasLimitsPrices(Ref cs, td::RefInt return false; } } + flat_gas_limit = _flat_gas_limit; + flat_gas_price = _flat_gas_price; compute_threshold(); return true; } @@ -1010,10 +1016,9 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { total_fees += cp.gas_fees; balance -= cp.gas_fees; } - if (verbosity > 2) { - std::cerr << "gas fees: " << cp.gas_fees << " = " << cfg.gas_price256 << " * " << cp.gas_used - << " /2^16 ; price=" << cfg.gas_price << "; remaining balance=" << balance << std::endl; - } + LOG(DEBUG) << "gas fees: " << cp.gas_fees->to_dec_string() << " = " << cfg.gas_price256->to_dec_string() << " * " + << cp.gas_used << " /2^16 ; price=" << cfg.gas_price << "; flat rate=[" << cfg.flat_gas_price << " for " + << cfg.flat_gas_limit << "]; remaining balance=" << balance.to_str(); CHECK(td::sgn(balance.grams) >= 0); } return true; diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 10150f1c31..8b3a424eef 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "common/refcnt.hpp" @@ -130,6 +130,11 @@ struct ComputePhaseConfig { } bool parse_GasLimitsPrices(Ref cs, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit); bool parse_GasLimitsPrices(Ref cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit); + + private: + bool parse_GasLimitsPrices_internal(Ref cs, td::RefInt256& freeze_due_limit, + td::RefInt256& delete_due_limit, td::uint64 flat_gas_limit = 0, + td::uint64 flat_gas_price = 0); }; struct ActionPhaseConfig { diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 46fb956414..7c09e82d2c 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1005,6 +1005,11 @@ x{F902} @Defop SHA256U x{F910} @Defop CHKSIGNU x{F911} @Defop CHKSIGNS +x{F940} @Defop CDATASIZEQ +x{F941} @Defop CDATASIZE +x{F942} @Defop SDATASIZEQ +x{F943} @Defop SDATASIZE + x{FA00} dup @Defop LDGRAMS @Defop LDVARUINT16 x{FA01} @Defop LDVARINT16 x{FA02} dup @Defop STGRAMS @Defop STVARUINT16 diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif index 716247987e..796390f94c 100644 --- a/crypto/fift/lib/TonUtil.fif +++ b/crypto/fift/lib/TonUtil.fif @@ -34,8 +34,10 @@ library TonUtil // TON Blockchain Fift Library 1 and 0= } : parse-smc-addr +// ( x -- ) Displays a 64-digit hex number +{ 64 0x. } : 64x. // ( wc addr -- ) Show address in : form -{ swap ._ .":" 64 0x. } : .addr +{ swap ._ .":" 64x. } : .addr // ( wc addr flags -- ) Show address in base64url form { smca>$ type } : .Addr // ( wc addr fname -- ) Save address to file in 36-byte format diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 2a36cf8f52..a4067eb9f3 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -32,7 +32,7 @@ #include "vm/cells.h" #include "vm/cellslice.h" -#include "vm/continuation.h" +#include "vm/vm.h" #include "vm/cp0.h" #include "vm/dict.h" #include "vm/boc.h" @@ -1534,7 +1534,7 @@ void interpret_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add stack.push_bool(res); } -void interpret_dict_get(vm::Stack& stack, int sgnd) { +void interpret_dict_get(vm::Stack& stack, int sgnd, int mode) { int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits); vm::Dictionary dict{stack.pop_maybe_cell(), n}; unsigned char buffer[vm::Dictionary::max_key_bytes]; @@ -1543,12 +1543,16 @@ void interpret_dict_get(vm::Stack& stack, int sgnd) { if (!key.is_valid()) { throw IntError{"not enough bits for a dictionary key"}; } - auto res = dict.lookup(std::move(key)); - if (res.not_null()) { + auto res = (mode & 4 ? dict.lookup_delete(std::move(key)) : dict.lookup(std::move(key))); + if (mode & 4) { + stack.push_maybe_cell(std::move(dict).extract_root_cell()); + } + bool found = res.not_null(); + if (found && (mode & 2)) { stack.push_cellslice(std::move(res)); - stack.push_bool(true); - } else { - stack.push_bool(false); + } + if (mode & 1) { + stack.push_bool(found); } } @@ -1572,15 +1576,15 @@ void interpret_dict_map(IntCtx& ctx) { ctx.stack.push_maybe_cell(std::move(dict).extract_root_cell()); } -void interpret_dict_map_ext(IntCtx& ctx) { +void interpret_dict_map_ext(IntCtx& ctx, bool sgnd) { auto func = pop_exec_token(ctx); int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits); vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n}; - vm::Dictionary::map_func_t map_func = [&ctx, func](vm::CellBuilder& cb, Ref cs_ref, - td::ConstBitPtr key, int key_len) -> bool { + vm::Dictionary::map_func_t map_func = [&ctx, func, sgnd](vm::CellBuilder& cb, Ref cs_ref, + td::ConstBitPtr key, int key_len) -> bool { ctx.stack.push_builder(Ref(cb)); td::RefInt256 x{true}; - x.unique_write().import_bits(key, key_len, false); + x.unique_write().import_bits(key, key_len, sgnd); ctx.stack.push_int(std::move(x)); ctx.stack.push_cellslice(std::move(cs_ref)); func->run(ctx); @@ -1596,20 +1600,20 @@ void interpret_dict_map_ext(IntCtx& ctx) { ctx.stack.push_maybe_cell(std::move(dict).extract_root_cell()); } -void interpret_dict_foreach(IntCtx& ctx) { +void interpret_dict_foreach(IntCtx& ctx, bool sgnd) { auto func = pop_exec_token(ctx); int n = ctx.stack.pop_smallint_range(vm::Dictionary::max_key_bits); vm::Dictionary dict{ctx.stack.pop_maybe_cell(), n}; - vm::Dictionary::foreach_func_t foreach_func = [&ctx, func](Ref cs_ref, td::ConstBitPtr key, - int key_len) -> bool { + vm::Dictionary::foreach_func_t foreach_func = [&ctx, func, sgnd](Ref cs_ref, td::ConstBitPtr key, + int key_len) -> bool { td::RefInt256 x{true}; - x.unique_write().import_bits(key, key_len, false); + x.unique_write().import_bits(key, key_len, sgnd); ctx.stack.push_int(std::move(x)); ctx.stack.push_cellslice(std::move(cs_ref)); func->run(ctx); return ctx.stack.pop_bool(); }; - ctx.stack.push_bool(dict.check_for_each(std::move(foreach_func))); + ctx.stack.push_bool(dict.check_for_each(std::move(foreach_func), sgnd)); } void interpret_dict_merge(IntCtx& ctx) { @@ -2752,23 +2756,31 @@ void init_words_common(Dictionary& d) { d.def_stack_word("sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, -1)); d.def_stack_word("b>sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, -1)); d.def_stack_word("b>sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, -1)); - d.def_stack_word("sdict@ ", std::bind(interpret_dict_get, _1, -1)); + d.def_stack_word("sdict@ ", std::bind(interpret_dict_get, _1, -1, 3)); + d.def_stack_word("sdict@- ", std::bind(interpret_dict_get, _1, -1, 7)); + d.def_stack_word("sdict- ", std::bind(interpret_dict_get, _1, -1, 5)); d.def_stack_word("udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 0)); d.def_stack_word("udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 0)); d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 0)); d.def_stack_word("b>udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 0)); - d.def_stack_word("udict@ ", std::bind(interpret_dict_get, _1, 0)); + d.def_stack_word("udict@ ", std::bind(interpret_dict_get, _1, 0, 3)); + d.def_stack_word("udict@- ", std::bind(interpret_dict_get, _1, 0, 7)); + d.def_stack_word("udict- ", std::bind(interpret_dict_get, _1, 0, 5)); d.def_stack_word("idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 1)); d.def_stack_word("idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 1)); d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 1)); d.def_stack_word("b>idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 1)); - d.def_stack_word("idict@ ", std::bind(interpret_dict_get, _1, 1)); + d.def_stack_word("idict@ ", std::bind(interpret_dict_get, _1, 1, 3)); + d.def_stack_word("idict@- ", std::bind(interpret_dict_get, _1, 1, 7)); + d.def_stack_word("idict- ", std::bind(interpret_dict_get, _1, 1, 5)); d.def_stack_word("pfxdict!+ ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Add, false)); d.def_stack_word("pfxdict! ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Set, false)); d.def_stack_word("pfxdict@ ", interpret_pfx_dict_get); d.def_ctx_word("dictmap ", interpret_dict_map); - d.def_ctx_word("dictmapext ", interpret_dict_map_ext); - d.def_ctx_word("dictforeach ", interpret_dict_foreach); + d.def_ctx_word("dictmapext ", std::bind(interpret_dict_map_ext, _1, false)); + d.def_ctx_word("idictmapext ", std::bind(interpret_dict_map_ext, _1, true)); + d.def_ctx_word("dictforeach ", std::bind(interpret_dict_foreach, _1, false)); + d.def_ctx_word("idictforeach ", std::bind(interpret_dict_foreach, _1, true)); d.def_ctx_word("dictmerge ", interpret_dict_merge); d.def_ctx_word("dictdiff ", interpret_dict_diff); // slice/bitstring constants diff --git a/crypto/smartcont/dns-manual-code.fc b/crypto/smartcont/dns-manual-code.fc new file mode 100644 index 0000000000..7e34a1cc7d --- /dev/null +++ b/crypto/smartcont/dns-manual-code.fc @@ -0,0 +1,381 @@ +{- + Originally created by: + /------------------------------------------------------------------------\ + | Created for: Telegram (Open Network) Blockchain Contest | + | Task 3: DNS Resolver (Manually controlled) | + >------------------------------------------------------------------------< + | Author: Oleksandr Murzin (tg: @skydev / em: alexhacker64@gmail.com) | + | October 2019 | + \------------------------------------------------------------------------/ +-} + +;;===========================================================================;; +;; Custom ASM instructions ;; +;;===========================================================================;; + +;; Args: s D n | Success: s' x s'' -1 | Failure: s 0 -> s N N 0 +(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) + asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; + +;; Args: x k D n | Success: D' -1 | Failure: D 0 +(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) + asm(value key dict key_len) "PFXDICTSET"; + +;; Args: k D n | Success: D' -1 | Failure: D 0 +(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) + asm(key dict key_len) "PFXDICTDEL"; + +slice slice_last(slice s, int len) asm "SDCUTLAST"; + +;; Actually, equivalent to dictionaries, provided for clarity +(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF"; +builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; + +(cell, ()) pfxdict_set_ref(cell dict, int key_len, slice key, cell value) { + throw_unless(33, dict~pfxdict_set?(key_len, key, begin_cell().store_maybe_ref(value).end_cell().begin_parse())); + return (dict, ()); +} + +(slice, cell, slice, int) pfxdict_get_ref(cell dict, int key_len, slice key) { + (slice pfx, slice val, slice tail, int succ) = dict.pfxdict_get?(key_len, key); + cell res = null(); + if (succ) { + res = val~load_maybe_ref(); + } + return (pfx, res, tail, succ); +} + +;;===========================================================================;; +;; Utility functions ;; +;;===========================================================================;; + +(int, int, int, cell, cell) load_data() { + slice cs = get_data().begin_parse(); + var res = (cs~load_uint(32), cs~load_uint(64), cs~load_uint(256), cs~load_dict(), cs~load_dict()); + cs.end_parse(); + return res; +} + +() store_data(int subwallet, int last_cleaned, int public_key, cell root, old_queries) impure { + set_data(begin_cell() + .store_uint(subwallet, 32) + .store_uint(last_cleaned, 64) + .store_uint(public_key, 256) + .store_dict(root) + .store_dict(old_queries) + .end_cell()); +} + +;;===========================================================================;; +;; Internal message handler (Code 0) ;; +;;===========================================================================;; + +() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure { + ;; not interested at all +} + +;;===========================================================================;; +;; External message handler (Code -1) ;; +;;===========================================================================;; + +{- + External message structure: + [Bytes<512b>:signature] [UInt<32b>:seqno] [UInt<6b>:operation] + [Either b0: inline name (<= 58-x Bytes) or b1: reference-stored name) + x depends on operation + Use of 6-bit op instead of 32-bit allows to save 4 bytes for inline name + Inline [Name] structure: [UInt<6b>:length] [Bytes:data] + Operations (continuation of message): + 00 Contract initialization message (only if seqno = 0) (x=-) + 11 VSet: set specified value to specified subdomain->category (x=2) + [Int<16b>:category] [Name:subdomain] [Cell<1r>:value] + 12 VDel: delete specified subdomain->category (x=2) + [Int<16b>:category] [Name:subdomain] + 21 DSet: replace entire category dictionary of domain with provided (x=0) + [Name:subdomain] [Cell<1r>:new_cat_table] + 22 DDel: delete entire category dictionary of specified domain (x=0) + [Name:subdomain] + 31 TSet: replace ENTIRE DOMAIN TABLE with the provided tree root cell (x=-) + [Cell<1r>:new_domains_table] + 32 TDel: nullify ENTIRE DOMAIN TABLE (x=-) + 51 OSet: replace owner public key with a new one (x=-) + [UInt<256b>:new_public_key] +-} + +(cell, slice) process_op(cell root, slice ops) { + int op = ops~load_uint(6); + int is_name_ref = (ops~load_uint(1) == 1); + + ;; lets assume at this point that special operations 00..09 are handled + throw_if(45, op < 10); + slice name = ops; ;; anything! better do not begin or it costs much gas + cell cat_table = null(); + int cat = 0; + if (op < 20) { + ;; for operations with codes 10..19 category is required + cat = ops~load_int(16); + } + int zeros = 0; + if (op < 30) { + ;; for operations with codes 10..29 name is required + if (is_name_ref) { + ;; name is stored in separate referenced cell + name = ops~load_ref().begin_parse(); + } else { + ;; name is stored inline + int name_len = ops~load_uint(6) * 8; + name = ops~load_bits(name_len); + } + ;; at least one character not counting \0 + throw_unless(38, name.slice_bits() >= 16); + ;; name shall end with \0 + (_, int name_last_byte) = name.slice_last(8).load_uint(8); + throw_unless(40, name_last_byte == 0); + + ;; Multiple zero characters seem to be allowed as per github issue response + ;; Lets change the tactics! + + int loop = -1; + slice cname = name; + ;; better safe then sorry, dont want to catch any of loop bugs + while (loop) { + int lval = cname~load_uint(8); + if (lval == 0) { zeros += 1; } + if (cname.slice_bits() == 0) { loop = 0; } + } + ;; throw_unless(39, zeros == 1); + } + ;; operation with codes 10..19 manipulate category dict + ;; lets try to find it and store into a variable + ;; operations with codes 20..29 replace / delete dict, no need + name = begin_cell().store_uint(zeros, 7).store_slice(name).end_cell().begin_parse(); + if (op < 20) { + ;; lets resolve the name here so as not to duplicate the code + (slice pfx, cell val, slice tail, int succ) = + root.pfxdict_get_ref(1023, name); + if (succ) { + ;; must match EXACTLY to prevent accident changes + throw_unless(35, tail.slice_empty?()); + cat_table = val; + } + ;; otherwise cat_table is null which is reasonable for actions + } + ;; 11 VSet: set specified value to specified subdomain->category + if (op == 11) { + cell new_value = ops~load_maybe_ref(); + if (new_value.cell_null?()) { + cat_table~idict_delete?(16, cat); + } else { + cat_table~idict_set_ref(16, cat, new_value); + } + root~pfxdict_set_ref(1023, name, cat_table); + return (root, ops); + } + ;; 12 VDel: delete specified subdomain->category value + if (op == 12) { + ifnot (cat_table.dict_empty?()) { + cat_table~idict_delete?(16, cat); + root~pfxdict_set_ref(1023, name, cat_table); + } + return (root, ops); + } + ;; 21 DSet: replace entire category dictionary of domain with provided + if (op == 21) { + cell new_cat_table = ops~load_maybe_ref(); + root~pfxdict_set_ref(1023, name, new_cat_table); + return (root, ops); + } + ;; 22 DDel: delete entire category dictionary of specified domain + if (op == 22) { + root~pfxdict_delete?(1023, name); + return (root, ops); + } + ;; 31 TSet: replace ENTIRE DOMAIN TABLE with the provided tree root cell + if (op == 31) { + cell new_tree_root = ops~load_maybe_ref(); + ;; no sanity checks cause they would cost immense gas + return (new_tree_root, ops); + } + ;; 32 TDel: nullify ENTIRE DOMAIN TABLE + if (op == 32) { + return (null(), ops); + } + throw(44); ;; invalid operation + return (root, ops); +} + +cell process_ops(cell root, slice ops) { + int f = -1; + while (f) { + (root, ops) = process_op(root, ops); + if (ops.slice_refs_empty?()) { + f = 0; + } else { + ops = ops~load_ref().begin_parse(); + } + } + return root; +} + +() recv_external(slice in_msg) impure { + ;; Load data + (int stored_subwalet, int last_cleaned, int public_key, cell root, cell old_queries) = load_data(); + + ;; validate signature and seqno + slice signature = in_msg~load_bits(512); + int shash = slice_hash(in_msg); + var (subwallet_id, query_id) = (in_msg~load_uint(32), in_msg~load_uint(64)); + var bound = (now() << 32); + throw_if(35, query_id < bound); + (_, var found?) = old_queries.udict_get?(64, query_id); + throw_if(32, found?); + throw_unless(34, check_signature(shash, signature, public_key)); + accept_message(); ;; message is signed by owner, sanity not guaranteed yet + + + int op = in_msg.preload_uint(6); + ;; 00 Contract initialization message (only if seqno = 0) + if (op == 0) { + ;; noop + } else { if (op == 51) { + in_msg~skip_bits(6); + public_key = in_msg~load_uint(256); + } else { + root = process_ops(root, in_msg); + } } + + bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago + old_queries~udict_set_builder(64, query_id, begin_cell()); + var queries = old_queries; + do { + var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64); + f~touch(); + if (f) { + f = (i < bound); + } + if (f) { + old_queries = old_queries'; + last_cleaned = i; + } + } until (~ f); + + store_data(subwallet_id, last_cleaned, public_key, root, old_queries); +} + +{- + Data structure: + Root cell: [UInt<32b>:seqno] [UInt<256b>:owner_public_key] + [OptRef<1b+1r?>:HashmapCatTable>:domains] + := HashmapE 16 ^DNSRecord + := arbitary? not defined anywhere in documentation or internet! + + STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value) + #Zeros allows to simultaneously store, for example, com\0 and com\0google\0 + That will be stored as \1com\0 and \2com\0google\0 (pfx tree has restricitons) + This will allow to resolve more specific requests to subdomains, and resort + to parent domain next resolver lookup if subdomain is not found + com\0goo\0 lookup will, for example look up \2com\0goo\0 and then + \1com\0goo\0 which will return \1com\0 (as per pfx tree) with -1 cat +-} + +;;===========================================================================;; +;; Getter methods ;; +;;===========================================================================;; + +int get_seqno() method_id { ;; Retrieve sequence number + return get_data().begin_parse().preload_uint(32); +} + +;;8m dns-record-value +(int, cell) dnsresolve(slice subdomain, int category) method_id { + cell Null = null(); ;; pseudo-alias + throw_if(30, subdomain.slice_bits() % 8 != 0); ;; malformed input (~ 8n-bit) + if (subdomain.slice_bits() == 0) { return (0, Null); } ;; zero-length input + {- ;; Logic thrown away: return only first ZB-delimited subdomain, + appends \0 if neccessary (not required for pfx, \0 can be ap more eff) + builder b_name = begin_cell(); + slice remaining = subdomain; + int char = remaining~load_uint(8); ;; seems to be the most optimal way + do { + b_name~store_uint(char, 8); + char = remaining~load_uint(8); + } until ((remaining.slice_bits() == 0) | (char == 0)); + if (char == 0) { category = -1; } + if ((remaining.slice_bits() == 0) & (char != 0)) { + b_name~store_uint(0, 8); ;; string was not terminated with zero byte + } + cell c_name = b_name.end_cell(); + slice s_name = c_name.begin_parse(); + -} + (_, int name_last_byte) = subdomain.slice_last(8).load_uint(8); + if ((name_last_byte == 0) & (subdomain.slice_bits() == 8)) { + return (0, Null); ;; zero-length input, but with zero byte + } + slice s_name = subdomain; + if (name_last_byte != 0) { + s_name = begin_cell().store_slice(subdomain) ;; append zero byte + .store_uint(0, 8).end_cell().begin_parse(); + } + (_, _, _, cell root, _) = load_data(); + + ;; Multiple zero characters seem to be allowed as per github issue response + ;; Lets change the tactics! + + int zeros = 0; + int loop = -1; + slice cname = s_name; + ;; better safe then sorry, dont want to catch any of loop bugs + while (loop) { + int lval = cname~load_uint(8); + if (lval == 0) { zeros += 1; } + if (cname.slice_bits() == 0) { loop = 0; } + } + + ;; can't move below, will cause errors! + slice pfx = cname; cell val = null(); + slice tail = cname; int succ = 0; + + while (zeros > 0) { + slice pfname = begin_cell().store_uint(zeros, 7) + .store_slice(s_name).end_cell().begin_parse(); + (pfx, val, tail, succ) = root.pfxdict_get_ref(1023, pfname); + if (succ) { zeros = 1; } ;; break + zeros -= 1; + } + + + zeros = pfx.preload_uint(7); + + if (~ succ) { + return (0, Null); ;; failed to find entry in prefix dictionary + } + if (~ tail.slice_empty?()) { ;; if we have tail then len(pfx) < len(subdomain) + category = -1; ;; incomplete subdomain found, must return next resolver (-1) + } + int pfx_bits = pfx.slice_bits() - 7; + cell cat_table = val; + ;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain + ;; COUNTING for the zero byte (if structurally correct: no multiple-ZB keys) + ;; which corresponds to "8m, m=one plus the number of bytes in the subdomain found) + if (category == 0) { + return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0 + } else { + cell cat_found = cat_table.idict_get_ref(16, category); + {- it seems that if subdomain is found but cat is not need to return (8m, Null) + if (cat_found.cell_null?()) { + pfx_bits = 0; ;; to return (0, Null) instead of (8m, Null) + ;; my thoughts about this requirement are in next block comment + } -} + return (pfx_bits, cat_found); ;; no need to unslice and cellize the poor cat now + {- Old logic garbage, replaced with ref functions discovered + ;; dictionary category lookup + (slice cat_value, int cat_found) = cat_table.idict_get?(16, category); + if (~ cat_found) { + ;; we have failed to find the cat :( + return (0, Null); + } + ;; cat is found, turn it's slices into cells + return (pfx.slice_bits(), begin_cell().store_slice(cat_value).end_cell()); + -} + } +} diff --git a/crypto/smartcont/gen-zerostate.fif b/crypto/smartcont/gen-zerostate.fif index 5c4de5647d..e0a8646929 100644 --- a/crypto/smartcont/gen-zerostate.fif +++ b/crypto/smartcont/gen-zerostate.fif @@ -17,10 +17,10 @@ dup dup 31 boc+>B dup Bx. cr dup "basestate0" +suffix +".boc" tuck B>file ."(Initial basechain state saved to file " type .")" cr Bhashu dup =: basestate0_fhash -."file hash=" dup x. space 256 u>B dup B>base64url type cr +."file hash=" dup 64x. space 256 u>B dup B>base64url type cr "basestate0" +suffix +".fhash" B>file hashu dup =: basestate0_rhash -."root hash=" dup x. space 256 u>B dup B>base64url type cr +."root hash=" dup 64x. space 256 u>B dup B>base64url type cr "basestate0" +suffix +".rhash" B>file basestate0_rhash basestate0_fhash now 0 2 32 0 add-std-workchain @@ -128,7 +128,7 @@ GR$666 // balance 2 // mode: create register_smc dup make_special dup constant smc3_addr -."address = " x. cr +."address = " 64x. cr /* * @@ -160,7 +160,7 @@ Masterchain swap // 9 4 1 config.validator_num! 1000 100 13 config.validator_num! // min-stake max-stake min-total-stake max-factor -GR$10000 GR$10000000 GR$500000 sg~10 config.validator_stake_limits! +GR$10000 GR$10000000 GR$500000 sg~3 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 diff --git a/crypto/smartcont/show-addr.fif b/crypto/smartcont/show-addr.fif index 506aa49275..86b314630b 100755 --- a/crypto/smartcont/show-addr.fif +++ b/crypto/smartcont/show-addr.fif @@ -12,9 +12,9 @@ $1 "new-wallet" replace-if-null =: file-base file-base +".addr" dup ."Loading wallet address from " type cr file>B 32 B| dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc 256 B>u@ dup constant wallet_addr -."Source wallet address = " wallet_wc ._ .":" x. cr -wallet_wc wallet_addr 2dup 7 smca>$ ."Non-bounceable address (for init only): " type cr -6 smca>$ ."Bounceable address (for later access): " type cr +."Source wallet address = " wallet_wc swap 2dup .addr cr +."Non-bounceable address (for init only): " 2dup 7 .Addr cr +."Bounceable address (for later access): " 6 .Addr cr file-base +".pk" dup file-exists? { dup file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long" diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 9041bafe26..2dc5c13782 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -31,6 +31,9 @@ int string_hash(slice s) asm "SHA256U"; int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU"; int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS"; +(int, int, int) compute_data_size(cell c, int max_cells) asm "CDATASIZE"; +(int, int, int) slice_compute_data_size(slice s, int max_cells) asm "SDATASIZE"; + ;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; () dump_stack() impure asm "DUMPSTK"; diff --git a/crypto/smartcont/validator-elect-req.fif b/crypto/smartcont/validator-elect-req.fif index 215fe9c15c..a220b421d2 100755 --- a/crypto/smartcont/validator-elect-req.fif +++ b/crypto/smartcont/validator-elect-req.fif @@ -22,7 +22,7 @@ def? $5 { @' $5 } { "validator-to-sign.bin" } cond constant output_fname ."Creating a request to participate in validator elections at time " elect_time . ."from smart contract " -1 src_addr 2dup 1 .Addr ." = " .addr ." with maximal stake factor with respect to the minimal stake " max_factor ._ -."/65536 and validator ADNL address " adnl_addr x. cr +."/65536 and validator ADNL address " adnl_addr 64x. cr B{654c5074} elect_time 32 u>B B+ max_factor 32 u>B B+ src_addr 256 u>B B+ adnl_addr 256 u>B B+ dup Bx. cr diff --git a/crypto/smartcont/validator-elect-signed.fif b/crypto/smartcont/validator-elect-signed.fif index 55e3d9ddac..4e681f07dc 100755 --- a/crypto/smartcont/validator-elect-signed.fif +++ b/crypto/smartcont/validator-elect-signed.fif @@ -27,7 +27,7 @@ def? $7 { @' $7 } { "validator-query.boc" } cond constant output_fname ."Creating a request to participate in validator elections at time " elect_time . ."from smart contract " -1 src_addr 2dup 1 .Addr ." = " .addr ." with maximal stake factor with respect to the minimal stake " max_factor ._ -."/65536 and validator ADNL address " adnl_addr x. cr +."/65536 and validator ADNL address " adnl_addr 64x. cr B{654c5074} elect_time 32 u>B B+ max_factor 32 u>B B+ src_addr 256 u>B B+ adnl_addr 256 u>B B+ ."String to sign is: " dup Bx. cr constant to_sign diff --git a/crypto/smc-envelope/HighloadWallet.cpp b/crypto/smc-envelope/HighloadWallet.cpp index 5f54078088..e44ce97b45 100644 --- a/crypto/smc-envelope/HighloadWallet.cpp +++ b/crypto/smc-envelope/HighloadWallet.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "HighloadWallet.h" #include "GenericAccount.h" @@ -51,7 +51,7 @@ td::Ref HighloadWallet::get_init_message(const td::Ed25519::PrivateKey td::Ref HighloadWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id, td::uint32 seqno, td::uint32 valid_until, td::Span gifts) noexcept { - CHECK(gifts.size() <= 254); + CHECK(gifts.size() <= max_gifts_size); vm::Dictionary messages(16); for (size_t i = 0; i < gifts.size(); i++) { auto& gift = gifts[i]; @@ -64,7 +64,7 @@ td::Ref HighloadWallet::make_a_gift_message(const td::Ed25519::Private 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(); + 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); @@ -123,4 +123,20 @@ td::Result HighloadWallet::get_wallet_id_or_throw() const { return static_cast(cs.fetch_ulong(32)); } +td::Result HighloadWallet::get_public_key() const { + return TRY_VM(get_public_key_or_throw()); +} + +td::Result HighloadWallet::get_public_key_or_throw() const { + if (state_.data.is_null()) { + return td::Status::Error("data is null"); + } + //FIXME use get method + auto cs = vm::load_cell_slice(state_.data); + cs.skip_first(64); + td::SecureString res(td::Ed25519::PublicKey::LENGTH); + cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast(res.size())); + return td::Ed25519::PublicKey(std::move(res)); +} + } // namespace ton diff --git a/crypto/smc-envelope/HighloadWallet.h b/crypto/smc-envelope/HighloadWallet.h index aa1e3a4bcb..e1db23efb0 100644 --- a/crypto/smc-envelope/HighloadWallet.h +++ b/crypto/smc-envelope/HighloadWallet.h @@ -14,29 +14,26 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "smc-envelope/SmartContract.h" +#include "smc-envelope/WalletInterface.h" #include "vm/cells.h" #include "Ed25519.h" #include "block/block.h" #include "vm/cells/CellString.h" namespace ton { -class HighloadWallet : ton::SmartContract { +class HighloadWallet : ton::SmartContract, public WalletInterface { public: explicit HighloadWallet(State state) : ton::SmartContract(std::move(state)) { } static constexpr unsigned max_message_size = vm::CellString::max_bytes; + static constexpr unsigned max_gifts_size = 254; static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; static td::Ref 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 make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id, td::uint32 seqno, td::uint32 valid_until, td::Span gifts) noexcept; @@ -47,8 +44,20 @@ class HighloadWallet : ton::SmartContract { td::Result get_seqno() const; td::Result get_wallet_id() const; + td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until, + td::Span gifts) const override { + TRY_RESULT(seqno, get_seqno()); + TRY_RESULT(wallet_id, get_wallet_id()); + return make_a_gift_message(private_key, wallet_id, seqno, valid_until, gifts); + } + size_t get_max_gifts_size() const override { + return max_gifts_size; + } + td::Result get_public_key() const override; + private: td::Result get_seqno_or_throw() const; td::Result get_wallet_id_or_throw() const; + td::Result get_public_key_or_throw() const; }; } // namespace ton diff --git a/crypto/smc-envelope/HighloadWalletV2.cpp b/crypto/smc-envelope/HighloadWalletV2.cpp new file mode 100644 index 0000000000..823161b929 --- /dev/null +++ b/crypto/smc-envelope/HighloadWalletV2.cpp @@ -0,0 +1,151 @@ +/* + 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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ +#include "HighloadWalletV2.h" +#include "GenericAccount.h" +#include "SmartContractCode.h" + +#include "vm/boc.h" +#include "vm/cells/CellString.h" +#include "td/utils/base64.h" + +#include + +namespace ton { +td::optional HighloadWalletV2::guess_revision(const vm::Cell::Hash& code_hash) { + for (td::int32 i = 1; i <= 2; i++) { + if (get_init_code(i)->get_hash() == code_hash) { + return i; + } + } + return {}; +} +td::optional HighloadWalletV2::guess_revision(const block::StdAddress& address, + const td::Ed25519::PublicKey& public_key, + td::uint32 wallet_id) { + for (td::int32 i = 1; i <= 2; i++) { + if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) { + return i; + } + } + return {}; +} +td::Ref HighloadWalletV2::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, + td::int32 revision) noexcept { + auto code = get_init_code(revision); + auto data = get_init_data(public_key, wallet_id); + return GenericAccount::get_init_state(std::move(code), std::move(data)); +} + +td::Ref HighloadWalletV2::get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id, + td::uint32 valid_until) noexcept { + td::uint32 id = -1; + auto append_message = [&](auto&& cb) -> vm::CellBuilder& { + cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(id, 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 HighloadWalletV2::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, + td::uint32 wallet_id, td::uint32 valid_until, + td::Span gifts) noexcept { + CHECK(gifts.size() <= max_gifts_size); + 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); + } + std::string hash; + { + vm::CellBuilder cb; + CHECK(cb.store_maybe_ref(messages.get_root_cell())); + hash = cb.finalize()->get_hash().as_slice().substr(28, 4).str(); + } + + vm::CellBuilder cb; + cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_bytes(hash); + 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 HighloadWalletV2::get_init_code(td::int32 revision) noexcept { + return SmartContractCode::highload_wallet_v2(revision); +} + +vm::CellHash HighloadWalletV2::get_init_code_hash() noexcept { + return get_init_code(0)->get_hash(); +} + +td::Ref HighloadWalletV2::get_init_data(const td::Ed25519::PublicKey& public_key, + td::uint32 wallet_id) noexcept { + vm::CellBuilder cb; + cb.store_long(wallet_id, 32).store_long(0, 64).store_bytes(public_key.as_octet_string()); + CHECK(cb.store_maybe_ref({})); + return cb.finalize(); +} + +td::Result HighloadWalletV2::get_wallet_id() const { + return TRY_VM(get_wallet_id_or_throw()); +} + +td::Result HighloadWalletV2::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); + return static_cast(cs.fetch_ulong(32)); +} + +td::Result HighloadWalletV2::get_public_key() const { + return TRY_VM(get_public_key_or_throw()); +} + +td::Result HighloadWalletV2::get_public_key_or_throw() const { + if (state_.data.is_null()) { + return td::Status::Error("data is null"); + } + //FIXME use get method + auto cs = vm::load_cell_slice(state_.data); + cs.skip_first(96); + td::SecureString res(td::Ed25519::PublicKey::LENGTH); + cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast(res.size())); + return td::Ed25519::PublicKey(std::move(res)); +} + +} // namespace ton diff --git a/crypto/smc-envelope/HighloadWalletV2.h b/crypto/smc-envelope/HighloadWalletV2.h new file mode 100644 index 0000000000..2b6f99e7b0 --- /dev/null +++ b/crypto/smc-envelope/HighloadWalletV2.h @@ -0,0 +1,66 @@ +/* + 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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ +#pragma once + +#include "smc-envelope/SmartContract.h" +#include "smc-envelope/WalletInterface.h" +#include "vm/cells.h" +#include "Ed25519.h" +#include "block/block.h" +#include "vm/cells/CellString.h" + +namespace ton { +class HighloadWalletV2 : ton::SmartContract, public WalletInterface { + public: + explicit HighloadWalletV2(State state) : ton::SmartContract(std::move(state)) { + } + static constexpr unsigned max_message_size = vm::CellString::max_bytes; + static constexpr unsigned max_gifts_size = 254; + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, + td::int32 revision) noexcept; + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id, + td::uint32 valid_until) noexcept; + + static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id, + td::uint32 valid_until, td::Span gifts) noexcept; + + static td::Ref get_init_code(td::int32 revision) noexcept; + static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; + static td::optional guess_revision(const vm::Cell::Hash& code_hash); + static td::optional guess_revision(const block::StdAddress& address, + const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id); + + td::Result get_wallet_id() const; + + td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until, + td::Span gifts) const override { + TRY_RESULT(wallet_id, get_wallet_id()); + return make_a_gift_message(private_key, wallet_id, valid_until, gifts); + } + size_t get_max_gifts_size() const override { + return max_gifts_size; + } + td::Result get_public_key() const override; + + private: + td::Result get_wallet_id_or_throw() const; + td::Result get_public_key_or_throw() const; +}; +} // namespace ton diff --git a/crypto/smc-envelope/ManualDns.cpp b/crypto/smc-envelope/ManualDns.cpp new file mode 100644 index 0000000000..c932f49d1e --- /dev/null +++ b/crypto/smc-envelope/ManualDns.cpp @@ -0,0 +1,542 @@ +/* + 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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ +#include "ManualDns.h" + +#include "smc-envelope/SmartContractCode.h" + +#include "vm/dict.h" + +#include "td/utils/format.h" +#include "td/utils/overloaded.h" +#include "td/utils/Parser.h" +#include "td/utils/Random.h" + +#include "block/block-auto.h" +#include "block/block-parse.h" + +namespace ton { + +//proto_list_nil$0 = ProtoList; +//proto_list_next$1 head:Protocol tail:ProtoList = ProtoList; +//proto_http#4854 = Protocol; + +//cap_list_nil$0 = SmcCapList; +//cap_list_next$1 head:SmcCapability tail:SmcCapList = SmcCapList; +//cap_method_seqno#5371 = SmcCapability; +//cap_method_pubkey#71f4 = SmcCapability; +//cap_is_wallet#2177 = SmcCapability; +//cap_name#ff name:Text = SmcCapability; +// +td::Result> DnsInterface::EntryData::as_cell() const { + td::Ref res; + td::Status error; + data.visit(td::overloaded( + [&](const EntryDataText& text) { + block::gen::DNSRecord::Record_dns_text dns; + vm::CellBuilder cb; + vm::CellText::store(cb, text.text); + dns.x = vm::load_cell_slice_ref(cb.finalize()); + tlb::pack_cell(res, dns); + }, + [&](const EntryDataNextResolver& resolver) { + block::gen::DNSRecord::Record_dns_next_resolver dns; + vm::CellBuilder cb; + block::tlb::t_MsgAddressInt.store_std_address(cb, resolver.resolver.workchain, resolver.resolver.addr); + dns.resolver = vm::load_cell_slice_ref(cb.finalize()); + tlb::pack_cell(res, dns); + }, + [&](const EntryDataAdnlAddress& adnl_address) { + block::gen::DNSRecord::Record_dns_adnl_address dns; + dns.adnl_addr = adnl_address.adnl_address; + dns.flags = 0; + tlb::pack_cell(res, dns); + }, + [&](const EntryDataSmcAddress& smc_address) { + block::gen::DNSRecord::Record_dns_smc_address dns; + vm::CellBuilder cb; + block::tlb::t_MsgAddressInt.store_std_address(cb, smc_address.smc_address.workchain, + smc_address.smc_address.addr); + dns.smc_addr = vm::load_cell_slice_ref(cb.finalize()); + tlb::pack_cell(res, dns); + })); + if (error.is_error()) { + return error; + } + if (res.is_null()) { + return td::Status::Error("Entry data is emtpy"); + } + return res; + //dns_text#1eda _:Text = DNSRecord; + + //dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; // usually in record #-1 + //dns_adnl_address#ad01 adnl_addr:bits256 flags:(## 8) { flags <= 1 } proto_list:flags . 0?ProtoList = DNSRecord; // often in record #2 + + //dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 } cap_list:flags . 0?SmcCapList = DNSRecord; // often in record #1 +} + +td::Result DnsInterface::EntryData::from_cellslice(vm::CellSlice& cs) { + switch (block::gen::t_DNSRecord.get_tag(cs)) { + case block::gen::DNSRecord::dns_text: { + block::gen::DNSRecord::Record_dns_text dns; + tlb::unpack(cs, dns); + TRY_RESULT(text, vm::CellText::load(dns.x.write())); + return EntryData::text(std::move(text)); + } + case block::gen::DNSRecord::dns_next_resolver: { + block::gen::DNSRecord::Record_dns_next_resolver dns; + tlb::unpack(cs, dns); + ton::WorkchainId wc; + ton::StdSmcAddress addr; + if (!block::tlb::t_MsgAddressInt.extract_std_address(dns.resolver, wc, addr)) { + return td::Status::Error("Invalid address"); + } + return EntryData::next_resolver(block::StdAddress(wc, addr)); + } + case block::gen::DNSRecord::dns_adnl_address: { + block::gen::DNSRecord::Record_dns_adnl_address dns; + tlb::unpack(cs, dns); + return EntryData::adnl_address(dns.adnl_addr); + } + case block::gen::DNSRecord::dns_smc_address: { + block::gen::DNSRecord::Record_dns_smc_address dns; + tlb::unpack(cs, dns); + ton::WorkchainId wc; + ton::StdSmcAddress addr; + if (!block::tlb::t_MsgAddressInt.extract_std_address(dns.smc_addr, wc, addr)) { + return td::Status::Error("Invalid address"); + } + return EntryData::smc_address(block::StdAddress(wc, addr)); + } + } + return td::Status::Error("Unknown entry data"); +} + +td::Result> DnsInterface::resolve(td::Slice name, td::int32 category) const { + TRY_RESULT(raw_entries, resolve_raw(name, category)); + std::vector entries; + entries.reserve(raw_entries.size()); + for (auto& raw_entry : raw_entries) { + Entry entry; + entry.name = std::move(raw_entry.name); + entry.category = raw_entry.category; + auto cs = vm::load_cell_slice(raw_entry.data); + TRY_RESULT(data, EntryData::from_cellslice(cs)); + entry.data = std::move(data); + entries.push_back(std::move(entry)); + } + return entries; +} + +/* + External message structure: + [Bytes<512b>:signature] [UInt<32b>:seqno] [UInt<6b>:operation] + [Either b0: inline name (<= 58-x Bytes) or b1: reference-stored name) + x depends on operation + Use of 6-bit op instead of 32-bit allows to save 4 bytes for inline name + Inline [Name] structure: [UInt<6b>:length] [Bytes:data] + Operations (continuation of message): + 00 Contract initialization message (only if seqno = 0) (x=-) + 31 TSet: replace ENTIRE DOMAIN TABLE with the provided tree root cell (x=-) + [Cell<1r>:new_domains_table] + 51 OSet: replace owner public key with a new one (x=-) + [UInt<256b>:new_public_key] +*/ +// creation +td::Ref ManualDns::create(td::Ref data) { + return td::Ref(true, State{ton::SmartContractCode::dns_manual(), std::move(data)}); +} +td::Ref ManualDns::create(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) { + return create(create_init_data_fast(public_key, wallet_id)); +} + +td::Result ManualDns::get_wallet_id() const { + return TRY_VM(get_wallet_id_or_throw()); +} +td::Result ManualDns::get_wallet_id_or_throw() const { + if (state_.data.is_null()) { + return 0; + } + //FIXME use get method + return static_cast(vm::load_cell_slice(state_.data).fetch_ulong(32)); +} + +td::Result> ManualDns::create_set_value_unsigned(td::int16 category, td::Slice name, + td::Ref data) const { + //11 VSet: set specified value to specified subdomain->category (x=2) + //[Int<16b>:category] [Name:subdomain] [Cell<1r>:value] + vm::CellBuilder cb; + cb.store_long(11, 6); + if (name.size() <= 58 - 2) { + cb.store_long(0, 1); + cb.store_long(category, 16); + cb.store_long(name.size(), 6); + cb.store_bytes(name); + } else { + cb.store_long(1, 1); + cb.store_long(category, 16); + cb.store_ref(vm::CellBuilder().store_bytes(name).finalize()); + } + cb.store_maybe_ref(std::move(data)); + return cb.finalize(); +} +td::Result> ManualDns::create_delete_value_unsigned(td::int16 category, td::Slice name) const { + //12 VDel: delete specified subdomain->category (x=2) + //[Int<16b>:category] [Name:subdomain] + vm::CellBuilder cb; + cb.store_long(12, 6); + if (name.size() <= 58 - 2) { + cb.store_long(0, 1); + cb.store_long(category, 16); + cb.store_long(name.size(), 6); + cb.store_bytes(name); + } else { + cb.store_long(1, 1); + cb.store_long(category, 16); + cb.store_ref(vm::CellBuilder().store_bytes(name).finalize()); + } + cb.store_long(0, 1); + return cb.finalize(); +} + +td::Result> ManualDns::create_delete_all_unsigned() const { + // 32 TDel: nullify ENTIRE DOMAIN TABLE (x=-) + vm::CellBuilder cb; + cb.store_long(32, 6); + cb.store_long(0, 1); + return cb.finalize(); +} + +td::Result> ManualDns::create_set_all_unsigned(td::Span entries) const { + vm::PrefixDictionary pdict(1023); + for (auto& action : entries) { + auto name_key = encode_name(action.name); + int zero_cnt = 0; + for (auto c : name_key) { + if (c == 0) { + zero_cnt++; + } + } + auto new_name_key = vm::load_cell_slice(vm::CellBuilder().store_long(zero_cnt, 7).store_bytes(name_key).finalize()); + auto ptr = new_name_key.data_bits(); + auto ptr_size = new_name_key.size(); + auto o_dict = pdict.lookup(ptr, ptr_size); + td::Ref dict_root; + if (o_dict.not_null()) { + o_dict->prefetch_maybe_ref(dict_root); + } + vm::Dictionary dict(dict_root, 16); + if (!action.data.value().is_null()) { + auto key = dict.integer_key(td::make_refint(action.category), 16); + dict.set_ref(key.bits(), 16, action.data.value()); + } + pdict.set(ptr, ptr_size, dict.get_root()); + } + + vm::CellBuilder cb; + cb.store_long(31, 6); + cb.store_long(1, 1); + + cb.store_maybe_ref(pdict.get_root_cell()); + + return cb.finalize(); +} + +//21 DSet: replace entire category dictionary of domain with provided (x=0) +//[Name:subdomain] [Cell<1r>:new_cat_table] +//22 DDel: delete entire category dictionary of specified domain (x=0) +//[Name:subdomain] +td::Result> ManualDns::create_delete_name_unsigned(td::Slice name) const { + vm::CellBuilder cb; + cb.store_long(22, 6); + if (name.size() <= 58) { + cb.store_long(0, 1); + cb.store_long(name.size(), 6); + cb.store_bytes(name); + } else { + cb.store_long(1, 1); + cb.store_ref(vm::CellBuilder().store_bytes(name).finalize()); + } + cb.store_long(0, 1); + return cb.finalize(); +} +td::Result> ManualDns::create_set_name_unsigned(td::Slice name, td::Span entries) const { + vm::CellBuilder cb; + cb.store_long(21, 6); + if (name.size() <= 58) { + cb.store_long(0, 1); + cb.store_long(name.size(), 6); + cb.store_bytes(name); + } else { + cb.store_long(1, 1); + cb.store_ref(vm::CellBuilder().store_bytes(name).finalize()); + } + + vm::Dictionary dict(16); + + for (auto& action : entries) { + if (action.data.value().is_null()) { + continue; + } + auto key = dict.integer_key(td::make_refint(action.category), 16); + dict.set_ref(key.bits(), 16, action.data.value()); + } + cb.store_maybe_ref(dict.get_root_cell()); + + return cb.finalize(); +} + +td::Result> ManualDns::prepare(td::Ref data, td::uint32 valid_until) const { + TRY_RESULT(wallet_id, get_wallet_id()); + auto hash = data->get_hash().as_slice().substr(28, 4).str(); + + vm::CellBuilder cb; + cb.store_long(wallet_id, 32).store_long(valid_until, 32); + //cb.store_bytes(hash); + cb.store_long(td::Random::secure_uint32(), 32); + cb.append_cellslice(vm::load_cell_slice(data)); + return cb.finalize(); +} + +td::Result> ManualDns::sign(const td::Ed25519::PrivateKey& private_key, td::Ref data) { + auto signature = private_key.sign(data->get_hash().as_slice()).move_as_ok(); + vm::CellBuilder cb; + cb.store_bytes(signature.as_slice()); + cb.append_cellslice(vm::load_cell_slice(data)); + return cb.finalize(); +} + +td::Result> ManualDns::create_init_query(const td::Ed25519::PrivateKey& private_key, + td::uint32 valid_until) const { + vm::CellBuilder cb; + cb.store_long(0, 6); + cb.store_long(0, 1); + + TRY_RESULT(prepared, prepare(cb.finalize(), valid_until)); + return sign(private_key, std::move(prepared)); +} + +td::Ref ManualDns::create_init_data_fast(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) { + vm::CellBuilder cb; + cb.store_long(wallet_id, 32).store_long(0, 64).store_bytes(public_key.as_octet_string()); + CHECK(cb.store_maybe_ref({})); + CHECK(cb.store_maybe_ref({})); + return cb.finalize(); +} + +size_t ManualDns::get_max_name_size() const { + return 128; +} + +td::Result> ManualDns::resolve_raw(td::Slice name, td::int32 category_big) const { + return TRY_VM(resolve_raw_or_throw(name, category_big)); +} +td::Result> ManualDns::resolve_raw_or_throw(td::Slice name, + td::int32 category_big) const { + TRY_RESULT(category, td::narrow_cast_safe(category_big)); + if (name.size() > get_max_name_size()) { + return td::Status::Error("Name is too long"); + } + auto encoded_name = encode_name(name); + auto res = run_get_method( + "dnsresolve", + {vm::load_cell_slice_ref(vm::CellBuilder().store_bytes(encoded_name).finalize()), td::make_refint(category)}); + if (!res.success) { + return td::Status::Error("get method failed"); + } + std::vector vec; + auto data = res.stack.write().pop_maybe_cell(); + if (data.is_null()) { + return vec; + } + size_t prefix_size = res.stack.write().pop_smallint_range((int)encoded_name.size() * 8); + if (prefix_size % 8 != 0) { + return td::Status::Error("Prefix size is not divisible by 8"); + } + prefix_size /= 8; + if (prefix_size < encoded_name.size()) { + vec.push_back({decode_name(td::Slice(encoded_name).substr(0, prefix_size)), -1, data}); + } else { + if (category == 0) { + vm::Dictionary dict(std::move(data), 16); + dict.check_for_each([&](auto cs, auto x, auto y) { + td::BigInt256 cat; + cat.import_bits(x, y, true); + vec.push_back({name.str(), td::narrow_cast(cat.to_long()), cs->prefetch_ref()}); + return true; + }); + } else { + vec.push_back({name.str(), category, data}); + } + } + + return vec; +} + +td::Result> ManualDns::create_update_query(CombinedActions& combined) const { + if (combined.name.empty()) { + if (combined.actions.value().empty()) { + return create_delete_all_unsigned(); + } + return create_set_all_unsigned(combined.actions.value()); + } + if (combined.category == 0) { + if (!combined.actions) { + return create_delete_name_unsigned(encode_name(combined.name)); + } + return create_set_name_unsigned(encode_name(combined.name), combined.actions.value()); + } + CHECK(combined.actions.value().size() == 1); + auto& action = combined.actions.value()[0]; + if (action.data) { + return create_set_value_unsigned(action.category, encode_name(action.name), action.data.value()); + } else { + return create_delete_value_unsigned(action.category, encode_name(action.name)); + } +} + +td::Result> ManualDns::create_update_query(td::Ed25519::PrivateKey& pk, td::Span actions, + td::uint32 valid_until) const { + auto combined = combine_actions(actions); + std::vector> queries; + for (auto& c : combined) { + TRY_RESULT(q, create_update_query(c)); + queries.push_back(std::move(q)); + } + + td::Ref combined_query; + for (auto& query : td::reversed(queries)) { + if (combined_query.is_null()) { + combined_query = std::move(query); + } else { + auto next = vm::load_cell_slice(combined_query); + combined_query = vm::CellBuilder() + .append_cellslice(vm::load_cell_slice(query)) + .store_ref(vm::CellBuilder().append_cellslice(next).finalize()) + .finalize(); + } + } + + TRY_RESULT(prepared, prepare(std::move(combined_query), valid_until)); + return sign(pk, std::move(prepared)); +} + +std::string ManualDns::encode_name(td::Slice name) { + std::string res; + while (!name.empty()) { + auto pos = name.rfind('.'); + if (pos == name.npos) { + res += name.str(); + name = td::Slice(); + } else { + res += name.substr(pos + 1).str(); + name.truncate(pos); + } + res += '\0'; + } + return res; +} + +std::string ManualDns::decode_name(td::Slice name) { + std::string res; + if (!name.empty() && name.back() == 0) { + name.remove_suffix(1); + } + while (!name.empty()) { + auto pos = name.rfind('\0'); + if (!res.empty()) { + res += '.'; + } + if (pos == name.npos) { + res += name.str(); + name = td::Slice(); + } else { + res += name.substr(pos + 1).str(); + name.truncate(pos); + } + } + return res; +} + +std::string ManualDns::serialize_data(const EntryData& data) { + std::string res; + data.data.visit(td::overloaded([&](const ton::ManualDns::EntryDataText& text) { res = "UNSUPPORTED"; }, + [&](const ton::ManualDns::EntryDataNextResolver& resolver) { res = "UNSUPPORTED"; }, + [&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { res = "UNSUPPORTED"; }, + [&](const ton::ManualDns::EntryDataSmcAddress& text) { res = "UNSUPPORTED"; })); + return res; +} + +td::Result> ManualDns::parse_data(td::Slice cmd) { + td::ConstParser parser(cmd); + parser.skip_whitespaces(); + auto type = parser.read_till(':'); + parser.advance(1); + if (type == "TEXT") { + return ManualDns::EntryData::text(parser.read_all().str()); + } else if (type == "DELETED") { + return {}; + } + return td::Status::Error(PSLICE() << "Unknown entry type: " << type); +} + +td::Result ManualDns::parse_line(td::Slice cmd) { + // Cmd = + // set name category data | + // delete.name name | + // delete.all + // data = + // TEXT: | + // DELETED + td::ConstParser parser(cmd); + auto type = parser.read_word(); + if (type == "set") { + auto name = parser.read_word(); + auto category_str = parser.read_word(); + TRY_RESULT(category, td::to_integer_safe(category_str)); + TRY_RESULT(data, parse_data(parser.read_all())); + return ManualDns::ActionExt{name.str(), category, std::move(data)}; + } else if (type == "delete.name") { + auto name = parser.read_word(); + if (name.empty()) { + return td::Status::Error("name is empty"); + } + return ManualDns::ActionExt{name.str(), 0, {}}; + } else if (type == "delete.all") { + return ManualDns::ActionExt{"", 0, {}}; + } + return td::Status::Error(PSLICE() << "Unknown command: " << type); +} + +td::Result> ManualDns::parse(td::Slice cmd) { + auto lines = td::full_split(cmd, '\n'); + std::vector res; + res.reserve(lines.size()); + for (auto& line : lines) { + td::ConstParser parser(line); + parser.skip_whitespaces(); + if (parser.empty()) { + continue; + } + TRY_RESULT(action, parse_line(parser.read_all())); + res.push_back(std::move(action)); + } + return res; +} + +} // namespace ton diff --git a/crypto/smc-envelope/ManualDns.h b/crypto/smc-envelope/ManualDns.h new file mode 100644 index 0000000000..08db188e3d --- /dev/null +++ b/crypto/smc-envelope/ManualDns.h @@ -0,0 +1,339 @@ +/* + 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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ +#pragma once +#include "td/utils/Variant.h" +#include "td/utils/Status.h" +#include "vm/cells/Cell.h" +#include "vm/cells/CellSlice.h" +#include "vm/cells/CellString.h" + +#include "smc-envelope/SmartContract.h" + +#include "Ed25519.h" + +#include + +namespace ton { +class DnsInterface { + public: + struct EntryDataText { + std::string text; + bool operator==(const EntryDataText& other) const { + return text == other.text; + } + }; + + struct EntryDataNextResolver { + block::StdAddress resolver; + bool operator==(const EntryDataNextResolver& other) const { + return resolver == other.resolver; + } + }; + + struct EntryDataAdnlAddress { + ton::Bits256 adnl_address; + // TODO: proto + bool operator==(const EntryDataAdnlAddress& other) const { + return adnl_address == other.adnl_address; + } + }; + + struct EntryDataSmcAddress { + block::StdAddress smc_address; + bool operator==(const EntryDataSmcAddress& other) const { + return smc_address == other.smc_address; + } + // TODO: capability + }; + + struct EntryData { + enum Type { Empty, Text, NextResolver, AdnlAddress, SmcAddress } type{Empty}; + td::Variant data; + + static EntryData text(std::string text) { + return {Text, EntryDataText{text}}; + } + static EntryData next_resolver(block::StdAddress resolver) { + return {NextResolver, EntryDataNextResolver{resolver}}; + } + static EntryData adnl_address(ton::Bits256 adnl_address) { + return {AdnlAddress, EntryDataAdnlAddress{adnl_address}}; + } + static EntryData smc_address(block::StdAddress smc_address) { + return {SmcAddress, EntryDataSmcAddress{smc_address}}; + } + + bool operator==(const EntryData& other) const { + return data == other.data; + } + friend td::StringBuilder& operator<<(td::StringBuilder& sb, const EntryData& data) { + switch (data.type) { + case Type::Empty: + return sb << ""; + case Type::Text: + return sb << "text{" << data.data.get().text << "}"; + case Type::NextResolver: + return sb << "next{" << data.data.get().resolver.rserialize() << "}"; + case Type::AdnlAddress: + return sb << "adnl{" << data.data.get().adnl_address.to_hex() << "}"; + case Type::SmcAddress: + return sb << "smc{" << data.data.get().smc_address.rserialize() << "}"; + } + return sb << ""; + } + + td::Result> as_cell() const; + static td::Result from_cellslice(vm::CellSlice& cs); + }; + + struct Entry { + std::string name; + td::int16 category; + EntryData data; + auto key() const { + return std::tie(name, category); + } + bool operator<(const Entry& other) const { + return key() < other.key(); + } + bool operator==(const Entry& other) const { + return key() == other.key() && data == other.data; + } + friend td::StringBuilder& operator<<(td::StringBuilder& sb, const Entry& entry) { + sb << entry.name << ":" << entry.category << ":" << entry.data; + return sb; + } + }; + struct RawEntry { + std::string name; + td::int16 category; + td::Ref data; + }; + + struct ActionExt { + std::string name; + td::int16 category; + td::optional data; + static td::Result parse(td::Slice); + }; + + struct Action { + std::string name; + td::int16 category; + td::optional> data; + + bool does_create_category() const { + CHECK(!name.empty()); + CHECK(category != 0); + return static_cast(data); + } + bool does_change_empty() const { + CHECK(!name.empty()); + CHECK(category != 0); + return static_cast(data) && data.value().not_null(); + } + void make_non_empty() { + CHECK(!name.empty()); + CHECK(category != 0); + if (!data) { + data = td::Ref(); + } + } + friend td::StringBuilder& operator<<(td::StringBuilder& sb, const Action& action) { + sb << action.name << ":" << action.category << ":"; + if (action.data) { + if (action.data.value().is_null()) { + sb << ""; + } else { + sb << ""; + } + } else { + sb << ""; + } + return sb; + } + }; + + virtual ~DnsInterface() { + } + virtual size_t get_max_name_size() const = 0; + virtual td::Result> resolve_raw(td::Slice name, td::int32 category) const = 0; + virtual td::Result> create_update_query( + td::Ed25519::PrivateKey& pk, td::Span actions, + td::uint32 valid_until = std::numeric_limits::max()) const = 0; + + td::Result> resolve(td::Slice name, td::int32 category) const; +}; + +class ManualDns : public ton::SmartContract, public DnsInterface { + public: + ManualDns(State state) : SmartContract(std::move(state)) { + } + + ManualDns* make_copy() const override { + return new ManualDns{state_}; + } + + // creation + static td::Ref create(State state) { + return td::Ref(true, std::move(state)); + } + static td::Ref create(td::Ref data = {}); + static td::Ref create(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id); + + static std::string serialize_data(const EntryData& data); + static td::Result> parse_data(td::Slice cmd); + static td::Result parse_line(td::Slice cmd); + static td::Result> parse(td::Slice cmd); + + td::Ref create_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 valid_until) const { + return create_init_data_fast(public_key, valid_until); + } + + td::Result get_wallet_id() const; + td::Result get_wallet_id_or_throw() const; + + td::Result> create_set_value_unsigned(td::int16 category, td::Slice name, + td::Ref data) const; + td::Result> create_delete_value_unsigned(td::int16 category, td::Slice name) const; + td::Result> create_delete_all_unsigned() const; + td::Result> create_set_all_unsigned(td::Span entries) const; + td::Result> create_delete_name_unsigned(td::Slice name) const; + td::Result> create_set_name_unsigned(td::Slice name, td::Span entries) const; + + td::Result> prepare(td::Ref data, td::uint32 valid_until) const; + + static td::Result> sign(const td::Ed25519::PrivateKey& private_key, td::Ref data); + static td::Ref create_init_data_fast(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id); + + size_t get_max_name_size() const override; + td::Result> resolve_raw(td::Slice name, td::int32 category_big) const override; + td::Result> resolve_raw_or_throw(td::Slice name, td::int32 category_big) const; + + td::Result> create_init_query( + const td::Ed25519::PrivateKey& private_key, + td::uint32 valid_until = std::numeric_limits::max()) const; + td::Result> create_update_query( + td::Ed25519::PrivateKey& pk, td::Span actions, + td::uint32 valid_until = std::numeric_limits::max()) const override; + + static std::string encode_name(td::Slice name); + static std::string decode_name(td::Slice name); + + template + struct CombinedActions { + std::string name; + td::int16 category{0}; + td::optional> actions; + friend td::StringBuilder& operator<<(td::StringBuilder& sb, const CombinedActions& action) { + sb << action.name << ":" << action.category << ":"; + if (action.actions) { + sb << "" << action.actions.value().size(); + } else { + sb << ""; + } + return sb; + } + }; + + template + static std::vector> combine_actions(td::Span actions) { + struct Info { + std::set known_category; + std::vector actions; + bool closed{false}; + bool non_empty{false}; + }; + + std::map mp; + std::vector> res; + for (auto& action : td::reversed(actions)) { + if (action.name.empty()) { + CombinedActions set_all; + set_all.actions = std::vector(); + for (auto& it : mp) { + for (auto& e : it.second.actions) { + if (e.does_create_category()) { + set_all.actions.value().push_back(std::move(e)); + } + } + } + res.push_back(std::move(set_all)); + return res; + } + + Info& info = mp[action.name]; + if (info.closed) { + continue; + } + if (action.category != 0 && action.does_create_category()) { + info.non_empty = true; + } + if (!info.known_category.insert(action.category).second) { + continue; + } + if (action.category == 0) { + info.closed = true; + auto old_actions = std::move(info.actions); + bool is_empty = true; + for (auto& action : old_actions) { + if (is_empty && action.does_create_category()) { + info.actions.push_back(std::move(action)); + is_empty = false; + } else if (!is_empty && action.does_change_empty()) { + info.actions.push_back(std::move(action)); + } + } + } else { + info.actions.push_back(std::move(action)); + } + } + + for (auto& it : mp) { + auto& info = it.second; + if (info.closed) { + CombinedActions ca; + ca.name = it.first; + ca.category = 0; + if (!info.actions.empty() || info.non_empty) { + ca.actions = std::move(info.actions); + } + res.push_back(std::move(ca)); + } else { + bool need_non_empty = info.non_empty; + for (auto& a : info.actions) { + if (need_non_empty) { + a.make_non_empty(); + need_non_empty = false; + } + CombinedActions ca; + ca.name = a.name; + ca.category = a.category; + ca.actions = std::vector(); + ca.actions.value().push_back(std::move(a)); + res.push_back(ca); + } + } + } + return res; + } + td::Result> create_update_query(CombinedActions& combined) const; +}; + +} // namespace ton diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp index 02964a7c87..17477de973 100644 --- a/crypto/smc-envelope/SmartContract.cpp +++ b/crypto/smc-envelope/SmartContract.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "SmartContract.h" @@ -24,7 +24,7 @@ #include "block/block-auto.h" #include "vm/cellslice.h" #include "vm/cp0.h" -#include "vm/continuation.h" +#include "vm/vm.h" #include "td/utils/crypto.h" diff --git a/crypto/smc-envelope/SmartContract.h b/crypto/smc-envelope/SmartContract.h index d586436195..14b5b94712 100644 --- a/crypto/smc-envelope/SmartContract.h +++ b/crypto/smc-envelope/SmartContract.h @@ -14,13 +14,13 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "vm/cells.h" #include "vm/stack.hpp" -#include "vm/continuation.h" +#include "vm/vm.h" #include "td/utils/optional.h" #include "td/utils/crypto.h" diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp index e61cab2422..c97a689cf5 100644 --- a/crypto/smc-envelope/SmartContractCode.cpp +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "SmartContractCode.h" @@ -25,6 +25,11 @@ namespace ton { namespace { +constexpr static int WALLET_REVISION = 2; +constexpr static int WALLET2_REVISION = 2; +constexpr static int WALLET3_REVISION = 2; +constexpr static int HIGHLOAD_WALLET_REVISION = 2; +constexpr static int HIGHLOAD_WALLET2_REVISION = 2; const auto& get_map() { static auto map = [] { std::map, std::less<>> map; @@ -36,6 +41,58 @@ const auto& get_map() { #include "smartcont/auto/simple-wallet-code.cpp" #include "smartcont/auto/wallet-code.cpp" #include "smartcont/auto/highload-wallet-code.cpp" +#include "smartcont/auto/highload-wallet-v2-code.cpp" +#include "smartcont/auto/dns-manual-code.cpp" + + with_tvm_code("highload-wallet-r1", + "te6ccgEBBgEAhgABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/" + "0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhghgBD0eG+hb6EgmALTB9QwAfsAkTLiAbPmWwGkyMsfyx/L/" + "8ntVAAE0DAAEaCZL9qJoa4WPw=="); + with_tvm_code("highload-wallet-r2", + "te6ccgEBCAEAmQABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/" + "0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhghgBD0eG+hb6EgmALTB9QwAfsAkTLiAbPmWwGkyMsfyx/L/" + "8ntVAAE0DACAUgGBwAXuznO1E0NM/MdcL/4ABG4yX7UTQ1wsfg="); + with_tvm_code("highload-wallet-v2-r1", + "te6ccgEBBwEA1gABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQHu8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//" + "QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44YIYAQ9HhvoW+" + "hIJgC0wfUMAH7AJEy4gGz5luDJaHIQDSAQPRDiuYxyBLLHxPLP8v/9ADJ7VQGAATQMABBoZfl2omhpj5jpn+n/" + "mPoCaKkQQCB6BzfQmMktv8ld0fFADgggED0lm+hb6EyURCUMFMDud4gkzM2AZIyMOKz"); + with_tvm_code("highload-wallet-v2-r2", + "te6ccgEBCQEA6QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHu8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//" + "QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44YIYAQ9HhvoW+" + "hIJgC0wfUMAH7AJEy4gGz5luDJaHIQDSAQPRDiuYxyBLLHxPLP8v/9ADJ7VQIAATQMAIBIAYHABe9nOdqJoaa+Y64X/" + "wAQb5fl2omhpj5jpn+n/mPoCaKkQQCB6BzfQmMktv8ld0fFAA4IIBA9JZvoW+hMlEQlDBTA7neIJMzNgGSMjDisw=="); + with_tvm_code("simple-wallet-r1", + "te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/" + "0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA=="); + with_tvm_code("simple-wallet-r2", + "te6ccgEBAQEAXwAAuv8AIN0gggFMl7ohggEznLqxnHGw7UTQ0x/XC//jBOCk8mCBAgDXGCDXCx/tRNDTH9P/" + "0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA=="); + with_tvm_code("wallet-r1", + "te6ccgEBAQEAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/0VExuvKhA/" + "kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ="); + with_tvm_code("wallet-r2", + "te6ccgEBAQEAYwAAwv8AIN0gggFMl7ohggEznLqxnHGw7UTQ0x/XC//jBOCk8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" + "0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ="); + with_tvm_code("wallet3-r1", + "te6ccgEBAQEAYgAAwP8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/T/" + "9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA=="); + with_tvm_code("wallet3-r2", + "te6ccgEBAQEAcQAA3v8AIN0gggFMl7ohggEznLqxn3Gw7UTQ0x/THzHXC//jBOCk8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/" + "T/9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA=="); + auto check_revision = [&](td::Slice name, td::int32 default_revision) { + auto it = map.find(name); + CHECK(it != map.end()); + auto other_it = map.find(PSLICE() << name << "-r" << default_revision); + CHECK(other_it != map.end()); + CHECK(it->second->get_hash() == other_it->second->get_hash()); + }; + check_revision("highload-wallet", HIGHLOAD_WALLET_REVISION); + check_revision("highload-wallet-v2", HIGHLOAD_WALLET2_REVISION); + + //check_revision("simple-wallet", WALLET_REVISION); + //check_revision("wallet", WALLET2_REVISION); + //check_revision("wallet3", WALLET3_REVISION); return map; }(); return map; @@ -46,7 +103,7 @@ td::Result> SmartContractCode::load(td::Slice name) { auto& map = get_map(); auto it = map.find(name); if (it == map.end()) { - return td::Status::Error(PSLICE() << "Can't load td::ref " << name); } return it->second; } @@ -54,20 +111,47 @@ td::Ref SmartContractCode::multisig() { auto res = load("multisig").move_as_ok(); return res; } -td::Ref SmartContractCode::wallet() { - auto res = load("wallet").move_as_ok(); +td::Ref SmartContractCode::wallet3(int revision) { + if (revision == 0) { + revision = WALLET3_REVISION; + } + auto res = load(PSLICE() << "wallet3-r" << revision).move_as_ok(); + return res; +} +td::Ref SmartContractCode::wallet(int revision) { + if (revision == 0) { + revision = WALLET2_REVISION; + } + auto res = load(PSLICE() << "wallet-r" << revision).move_as_ok(); return res; } -td::Ref SmartContractCode::simple_wallet() { - auto res = load("simple-wallet").move_as_ok(); +td::Ref SmartContractCode::simple_wallet(int revision) { + if (revision == 0) { + revision = WALLET_REVISION; + } + auto res = load(PSLICE() << "simple-wallet-r" << revision).move_as_ok(); return res; } td::Ref SmartContractCode::simple_wallet_ext() { static auto res = load("simple-wallet-ext").move_as_ok(); return res; } -td::Ref SmartContractCode::highload_wallet() { - static auto res = load("highload-wallet").move_as_ok(); +td::Ref SmartContractCode::highload_wallet(int revision) { + if (revision == 0) { + revision = HIGHLOAD_WALLET_REVISION; + } + auto res = load(PSLICE() << "highload-wallet-r" << revision).move_as_ok(); + return res; +} +td::Ref SmartContractCode::highload_wallet_v2(int revision) { + if (revision == 0) { + revision = HIGHLOAD_WALLET2_REVISION; + } + auto res = load(PSLICE() << "highload-wallet-v2-r" << revision).move_as_ok(); + return res; +} +td::Ref SmartContractCode::dns_manual() { + static auto res = load("dns-manual").move_as_ok(); return res; } } // namespace ton diff --git a/crypto/smc-envelope/SmartContractCode.h b/crypto/smc-envelope/SmartContractCode.h index 0c9e476443..439dc868af 100644 --- a/crypto/smc-envelope/SmartContractCode.h +++ b/crypto/smc-envelope/SmartContractCode.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "vm/cells.h" @@ -23,9 +23,12 @@ class SmartContractCode { public: static td::Result> load(td::Slice name); static td::Ref multisig(); - static td::Ref wallet(); - static td::Ref simple_wallet(); + static td::Ref wallet3(int revision = 0); + static td::Ref wallet(int revision = 0); + static td::Ref simple_wallet(int revision = 0); static td::Ref simple_wallet_ext(); - static td::Ref highload_wallet(); + static td::Ref highload_wallet(int revision = 0); + static td::Ref highload_wallet_v2(int revision = 0); + static td::Ref dns_manual(); }; } // namespace ton diff --git a/crypto/smc-envelope/TestGiver.cpp b/crypto/smc-envelope/TestGiver.cpp index 2d44d73073..f9ed60bfd7 100644 --- a/crypto/smc-envelope/TestGiver.cpp +++ b/crypto/smc-envelope/TestGiver.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "TestGiver.h" #include "GenericAccount.h" @@ -35,14 +35,23 @@ vm::CellHash TestGiver::get_init_code_hash() noexcept { //return vm::CellHash::from_slice(td::base64_decode("YV/IANhoI22HVeatFh6S5LbCHp+5OilARfzW+VQPZgQ=").move_as_ok()); } -td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept { +td::Ref TestGiver::make_a_gift_message_static(td::uint32 seqno, td::Span gifts) noexcept { + CHECK(gifts.size() <= max_gifts_size); + 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(); - return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); + cb.store_long(seqno, 32); + + for (auto& gift : gifts) { + td::int32 send_mode = 1; + auto gramms = gift.gramms; + vm::CellBuilder cbi; + GenericAccount::store_int_message(cbi, gift.destination, gramms); + store_gift_message(cbi, gift); + auto message_inner = cbi.finalize(); + cb.store_long(send_mode, 8).store_ref(std::move(message_inner)); + } + + return cb.finalize(); } td::Result TestGiver::get_seqno() const { diff --git a/crypto/smc-envelope/TestGiver.h b/crypto/smc-envelope/TestGiver.h index 210b691240..b51ac7dbe0 100644 --- a/crypto/smc-envelope/TestGiver.h +++ b/crypto/smc-envelope/TestGiver.h @@ -14,25 +14,38 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "SmartContract.h" +#include "smc-envelope/WalletInterface.h" #include "block/block.h" #include "vm/cells/CellString.h" namespace ton { -class TestGiver : public SmartContract { +class TestGiver : public SmartContract, public WalletInterface { public: explicit TestGiver(State state) : ton::SmartContract(std::move(state)) { } + TestGiver() : ton::SmartContract({}) { + } static constexpr unsigned max_message_size = vm::CellString::max_bytes; + static constexpr unsigned max_gifts_size = 1; static const block::StdAddress& address() noexcept; static vm::CellHash get_init_code_hash() noexcept; - static td::Ref make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept; + static td::Ref make_a_gift_message_static(td::uint32 seqno, td::Span) noexcept; td::Result get_seqno() const; + using WalletInterface::get_init_message; + td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until, + td::Span gifts) const override { + TRY_RESULT(seqno, get_seqno()); + return make_a_gift_message_static(seqno, gifts); + } + size_t get_max_gifts_size() const override { + return max_gifts_size; + } + private: td::Result get_seqno_or_throw() const; }; diff --git a/crypto/smc-envelope/TestWallet.cpp b/crypto/smc-envelope/TestWallet.cpp index 1edf59f911..6249da09c9 100644 --- a/crypto/smc-envelope/TestWallet.cpp +++ b/crypto/smc-envelope/TestWallet.cpp @@ -13,17 +13,19 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . -Copyright 2017-2019 Telegram Systems LLP +Copyright 2017-2020 Telegram Systems LLP */ #include "TestWallet.h" #include "GenericAccount.h" +#include "SmartContractCode.h" + #include "vm/boc.h" #include "td/utils/base64.h" namespace ton { -td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { - auto code = get_init_code(); +td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& public_key, td::int32 revision) noexcept { + auto code = get_init_code(revision); auto data = get_init_data(public_key); return GenericAccount::get_init_state(std::move(code), std::move(data)); } @@ -35,42 +37,46 @@ td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr return vm::CellBuilder().store_bytes(signature).store_bytes(seq_no).finalize(); } -td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, - 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; - } +td::Ref TestWallet::make_a_gift_message_static(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, + td::Span gifts) noexcept { + CHECK(gifts.size() <= max_gifts_size); + 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(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize(); + cb.store_long(seqno, 32); + + for (auto& gift : gifts) { + td::int32 send_mode = 3; + auto gramms = gift.gramms; + if (gramms == -1) { + gramms = 0; + send_mode += 128; + } + vm::CellBuilder cbi; + GenericAccount::store_int_message(cbi, gift.destination, gramms); + store_gift_message(cbi, gift); + auto message_inner = cbi.finalize(); + cb.store_long(send_mode, 8).store_ref(std::move(message_inner)); + } + + 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 TestWallet::get_init_code() noexcept { - static auto res = [] { - auto serialized_code = td::base64_decode( - "te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/" - "0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA==") - .move_as_ok(); - return vm::std_boc_deserialize(serialized_code).move_as_ok(); - }(); - return res; +td::Ref TestWallet::get_init_code(td::int32 revision) noexcept { + return ton::SmartContractCode::simple_wallet(revision); } vm::CellHash TestWallet::get_init_code_hash() noexcept { return get_init_code()->get_hash(); } +td::Ref TestWallet::get_data(const td::Ed25519::PublicKey& public_key, td::uint32 seqno) noexcept { + return vm::CellBuilder().store_long(seqno, 32).store_bytes(public_key.as_octet_string()).finalize(); +} + td::Ref TestWallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { - return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); + return get_data(public_key, 0); } td::Result TestWallet::get_seqno() const { @@ -88,4 +94,20 @@ td::Result TestWallet::get_seqno_or_throw() const { return static_cast(seqno); } +td::Result TestWallet::get_public_key() const { + return TRY_VM(get_public_key_or_throw()); +} + +td::Result TestWallet::get_public_key_or_throw() const { + if (state_.data.is_null()) { + return td::Status::Error("data is null"); + } + //FIXME use get method + auto cs = vm::load_cell_slice(state_.data); + cs.skip_first(32); + td::SecureString res(td::Ed25519::PublicKey::LENGTH); + cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast(res.size())); + return td::Ed25519::PublicKey(std::move(res)); +} + } // namespace ton diff --git a/crypto/smc-envelope/TestWallet.h b/crypto/smc-envelope/TestWallet.h index 161aef58b5..46891aa961 100644 --- a/crypto/smc-envelope/TestWallet.h +++ b/crypto/smc-envelope/TestWallet.h @@ -14,35 +14,53 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "smc-envelope/SmartContract.h" +#include "smc-envelope/WalletInterface.h" #include "vm/cells.h" #include "Ed25519.h" #include "block/block.h" #include "vm/cells/CellString.h" namespace ton { -class TestWallet : public ton::SmartContract { +class TestWallet : public ton::SmartContract, public WalletInterface { public: explicit TestWallet(State state) : ton::SmartContract(std::move(state)) { } + explicit TestWallet(const td::Ed25519::PublicKey& public_key, td::uint32 seqno) + : TestWallet(State{get_init_code(), get_data(public_key, seqno)}) { + } static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static constexpr unsigned max_gifts_size = 1; + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::int32 revision = 0) noexcept; static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; - static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, - td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept; + static td::Ref make_a_gift_message_static(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, + td::Span gifts) noexcept; - static td::Ref get_init_code() noexcept; + static td::Ref get_init_code(td::int32 revision = 0) noexcept; static vm::CellHash get_init_code_hash() noexcept; + static td::Ref get_data(const td::Ed25519::PublicKey& public_key, td::uint32 seqno) noexcept; static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; td::Result get_seqno() const; + using WalletInterface::get_init_message; + td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until, + td::Span gifts) const override { + TRY_RESULT(seqno, get_seqno()); + return make_a_gift_message_static(private_key, seqno, gifts); + } + size_t get_max_gifts_size() const override { + return max_gifts_size; + } + + td::Result get_public_key() const override; + private: td::Result get_seqno_or_throw() const; + td::Result get_public_key_or_throw() const; }; } // namespace ton diff --git a/crypto/smc-envelope/Wallet.cpp b/crypto/smc-envelope/Wallet.cpp index 61958ed959..e354a7a0d3 100644 --- a/crypto/smc-envelope/Wallet.cpp +++ b/crypto/smc-envelope/Wallet.cpp @@ -14,10 +14,11 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "Wallet.h" #include "GenericAccount.h" +#include "SmartContractCode.h" #include "vm/boc.h" #include "vm/cells/CellString.h" @@ -26,8 +27,8 @@ #include namespace ton { -td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) noexcept { - auto code = get_init_code(); +td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key, td::int32 revision) noexcept { + auto code = get_init_code(revision); auto data = get_init_data(public_key); return GenericAccount::get_init_state(std::move(code), std::move(data)); } @@ -43,46 +44,45 @@ td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& privat } td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, 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; - } + td::uint32 valid_until, td::Span gifts) noexcept { + CHECK(gifts.size() <= max_gifts_size); + 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(seqno, 32) - .store_long(valid_until, 32) - .store_long(send_mode, 8) - .store_ref(message_inner) - .finalize(); + cb.store_long(seqno, 32).store_long(valid_until, 32); + + for (auto& gift : gifts) { + td::int32 send_mode = 3; + auto gramms = gift.gramms; + if (gramms == -1) { + gramms = 0; + send_mode += 128; + } + vm::CellBuilder cbi; + GenericAccount::store_int_message(cbi, gift.destination, gramms); + store_gift_message(cbi, gift); + auto message_inner = cbi.finalize(); + cb.store_long(send_mode, 8).store_ref(std::move(message_inner)); + } + + 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 Wallet::get_init_code() noexcept { - static auto res = [] { - auto serialized_code = td::base64_decode( - "te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" - "0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=") - .move_as_ok(); - return vm::std_boc_deserialize(serialized_code).move_as_ok(); - }(); - return res; +td::Ref Wallet::get_init_code(td::int32 revision) noexcept { + return SmartContractCode::wallet(revision); } vm::CellHash Wallet::get_init_code_hash() noexcept { return get_init_code()->get_hash(); } +td::Ref Wallet::get_data(const td::Ed25519::PublicKey& public_key, td::uint32 seqno) noexcept { + return vm::CellBuilder().store_long(seqno, 32).store_bytes(public_key.as_octet_string()).finalize(); +} + td::Ref Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) noexcept { - return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); + return get_data(public_key, 0); } td::Result Wallet::get_seqno() const { @@ -97,4 +97,20 @@ td::Result Wallet::get_seqno_or_throw() const { return static_cast(vm::load_cell_slice(state_.data).fetch_ulong(32)); } +td::Result Wallet::get_public_key() const { + return TRY_VM(get_public_key_or_throw()); +} + +td::Result Wallet::get_public_key_or_throw() const { + if (state_.data.is_null()) { + return td::Status::Error("data is null"); + } + //FIXME use get method + auto cs = vm::load_cell_slice(state_.data); + cs.skip_first(32); + td::SecureString res(td::Ed25519::PublicKey::LENGTH); + cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast(res.size())); + return td::Ed25519::PublicKey(std::move(res)); +} + } // namespace ton diff --git a/crypto/smc-envelope/Wallet.h b/crypto/smc-envelope/Wallet.h index 7cd33c81d1..2299be7cca 100644 --- a/crypto/smc-envelope/Wallet.h +++ b/crypto/smc-envelope/Wallet.h @@ -14,35 +14,53 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "smc-envelope/SmartContract.h" +#include "smc-envelope/WalletInterface.h" #include "vm/cells.h" #include "Ed25519.h" #include "block/block.h" #include "vm/cells/CellString.h" namespace ton { -class Wallet : ton::SmartContract { +class Wallet : ton::SmartContract, public WalletInterface { public: explicit Wallet(State state) : ton::SmartContract(std::move(state)) { } + explicit Wallet(const td::Ed25519::PublicKey& public_key, td::uint32 seqno) + : Wallet(State{get_init_code(), get_data(public_key, seqno)}) { + } static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key) noexcept; + static constexpr unsigned max_gifts_size = 4; + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::int32 revision = 0) noexcept; static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept; static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, - td::uint32 valid_until, td::int64 gramms, td::Slice message, - const block::StdAddress& dest_address) noexcept; + td::uint32 valid_until, td::Span gifts) noexcept; - static td::Ref get_init_code() noexcept; + static td::Ref get_init_code(td::int32 revision = 0) noexcept; static vm::CellHash get_init_code_hash() noexcept; static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key) noexcept; + static td::Ref get_data(const td::Ed25519::PublicKey& public_key, td::uint32 seqno) noexcept; td::Result get_seqno() const; + using WalletInterface::get_init_message; + td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until, + td::Span gifts) const override { + TRY_RESULT(seqno, get_seqno()); + return make_a_gift_message(private_key, seqno, valid_until, gifts); + } + size_t get_max_gifts_size() const override { + return max_gifts_size; + } + + td::Result get_public_key() const override; + private: td::Result get_seqno_or_throw() const; + td::Result get_public_key_or_throw() const; }; } // namespace ton diff --git a/crypto/smc-envelope/WalletInterface.h b/crypto/smc-envelope/WalletInterface.h new file mode 100644 index 0000000000..e0e439c3b0 --- /dev/null +++ b/crypto/smc-envelope/WalletInterface.h @@ -0,0 +1,62 @@ +/* + 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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ +#pragma once + +#include "td/utils/common.h" +#include "Ed25519.h" +#include "block/block.h" +#include "vm/cells/CellString.h" + +#include "SmartContract.h" + +namespace ton { +class WalletInterface { + public: + struct Gift { + block::StdAddress destination; + td::int64 gramms; + bool is_encrypted{false}; + std::string message; + }; + + virtual ~WalletInterface() { + } + + virtual size_t get_max_gifts_size() const = 0; + virtual td::Result> make_a_gift_message(const td::Ed25519::PrivateKey &private_key, + td::uint32 valid_until, td::Span gifts) const = 0; + virtual td::Result get_public_key() const { + return td::Status::Error("TODO"); + } + + td::Result> get_init_message(const td::Ed25519::PrivateKey &private_key, + td::uint32 valid_until = std::numeric_limits::max()) { + return make_a_gift_message(private_key, valid_until, {}); + } + static void store_gift_message(vm::CellBuilder &cb, const Gift &gift) { + if (gift.is_encrypted) { + cb.store_long(1, 32); + } else { + cb.store_long(0, 32); + } + vm::CellString::store(cb, gift.message, 35 * 8).ensure(); + } +}; + +} // namespace ton diff --git a/crypto/smc-envelope/WalletV3.cpp b/crypto/smc-envelope/WalletV3.cpp index db39c725ef..39b9b4a9d3 100644 --- a/crypto/smc-envelope/WalletV3.cpp +++ b/crypto/smc-envelope/WalletV3.cpp @@ -14,10 +14,11 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "WalletV3.h" #include "GenericAccount.h" +#include "SmartContractCode.h" #include "vm/boc.h" #include "vm/cells/CellString.h" @@ -26,76 +27,70 @@ #include namespace ton { -td::Ref WalletV3::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept { - auto code = get_init_code(); +td::Ref WalletV3::get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, + td::int32 revision) noexcept { + auto code = get_init_code(revision); auto data = get_init_data(public_key, wallet_id); return GenericAccount::get_init_state(std::move(code), std::move(data)); } -td::Ref 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::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::optional WalletV3::guess_revision(const vm::Cell::Hash& code_hash) { + for (td::int32 i = 1; i <= 2; i++) { + if (get_init_code(i)->get_hash() == code_hash) { + return i; + } + } + return {}; +} +td::optional WalletV3::guess_revision(const block::StdAddress& address, + const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) { + for (td::int32 i = 1; i <= 2; i++) { + if (GenericAccount::get_address(address.workchain, get_init_state(public_key, wallet_id, i)) == address) { + return i; + } + } + return {}; } td::Ref 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; - } + td::uint32 seqno, td::uint32 valid_until, + td::Span gifts) noexcept { + CHECK(gifts.size() <= max_gifts_size); + 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(); + cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32); + + for (auto& gift : gifts) { + td::int32 send_mode = 3; + auto gramms = gift.gramms; + if (gramms == -1) { + gramms = 0; + send_mode += 128; + } + vm::CellBuilder cbi; + GenericAccount::store_int_message(cbi, gift.destination, gramms); + store_gift_message(cbi, gift); + auto message_inner = cbi.finalize(); + cb.store_long(send_mode, 8).store_ref(std::move(message_inner)); + } + + 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 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; +td::Ref WalletV3::get_init_code(td::int32 revision) noexcept { + return SmartContractCode::wallet3(revision); } vm::CellHash WalletV3::get_init_code_hash() noexcept { return get_init_code()->get_hash(); } -td::Ref WalletV3::get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept { +td::Ref WalletV3::get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, + td::uint32 seqno) noexcept { return vm::CellBuilder() - .store_long(0, 32) + .store_long(seqno, 32) .store_long(wallet_id, 32) .store_bytes(public_key.as_octet_string()) .finalize(); @@ -127,4 +122,20 @@ td::Result WalletV3::get_wallet_id_or_throw() const { return static_cast(cs.fetch_ulong(32)); } +td::Result WalletV3::get_public_key() const { + return TRY_VM(get_public_key_or_throw()); +} + +td::Result WalletV3::get_public_key_or_throw() const { + if (state_.data.is_null()) { + return td::Status::Error("data is null"); + } + //FIXME use get method + auto cs = vm::load_cell_slice(state_.data); + cs.skip_first(64); + td::SecureString res(td::Ed25519::PublicKey::LENGTH); + cs.fetch_bytes(res.as_mutable_slice().ubegin(), td::narrow_cast(res.size())); + return td::Ed25519::PublicKey(std::move(res)); +} + } // namespace ton diff --git a/crypto/smc-envelope/WalletV3.h b/crypto/smc-envelope/WalletV3.h index a6e4162df2..b7c211ca54 100644 --- a/crypto/smc-envelope/WalletV3.h +++ b/crypto/smc-envelope/WalletV3.h @@ -14,37 +14,59 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "smc-envelope/SmartContract.h" +#include "smc-envelope/WalletInterface.h" #include "vm/cells.h" #include "Ed25519.h" #include "block/block.h" #include "vm/cells/CellString.h" namespace ton { -class WalletV3 : ton::SmartContract { +class WalletV3 : ton::SmartContract, public WalletInterface { public: explicit WalletV3(State state) : ton::SmartContract(std::move(state)) { } + explicit WalletV3(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, td::uint32 seqno = 0) + : WalletV3(State{get_init_code(), get_init_data(public_key, wallet_id, seqno)}) { + } static constexpr unsigned max_message_size = vm::CellString::max_bytes; - static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; - static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key, td::uint32 wallet_id) noexcept; + static constexpr unsigned max_gifts_size = 4; + + static td::optional guess_revision(const vm::Cell::Hash& code_hash); + static td::optional guess_revision(const block::StdAddress& address, + const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id); + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, + td::int32 revision = 0) noexcept; static td::Ref 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::uint32 seqno, td::uint32 valid_until, td::Span gifts) noexcept; - static td::Ref get_init_code() noexcept; + static td::Ref get_init_code(td::int32 revision = 0) noexcept; static vm::CellHash get_init_code_hash() noexcept; - static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) noexcept; + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, + td::uint32 seqno = 0) noexcept; td::Result get_seqno() const; td::Result get_wallet_id() const; + using WalletInterface::get_init_message; + td::Result> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until, + td::Span gifts) const override { + TRY_RESULT(seqno, get_seqno()); + TRY_RESULT(wallet_id, get_wallet_id()); + return make_a_gift_message(private_key, wallet_id, seqno, valid_until, gifts); + } + size_t get_max_gifts_size() const override { + return max_gifts_size; + } + td::Result get_public_key() const override; + private: td::Result get_seqno_or_throw() const; td::Result get_wallet_id_or_throw() const; + td::Result get_public_key_or_throw() const; }; } // namespace ton diff --git a/crypto/test/fift/testdict.fif b/crypto/test/fift/testdict.fif index 307bd2e805..b9491ea8c8 100644 --- a/crypto/test/fift/testdict.fif +++ b/crypto/test/fift/testdict.fif @@ -1,4 +1,4 @@ -"Lisp.fif" include +"Lists.fif" include 16 constant key-bits 16 constant val-bits { val-bits u, } : val, diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp index f129e0b35a..f6f66b80e0 100644 --- a/crypto/test/test-smartcont.cpp +++ b/crypto/test/test-smartcont.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "vm/dict.h" #include "common/bigint.hpp" @@ -28,6 +28,7 @@ #include "fift/utils.h" #include "smc-envelope/GenericAccount.h" +#include "smc-envelope/ManualDns.h" #include "smc-envelope/MultisigWallet.h" #include "smc-envelope/SmartContract.h" #include "smc-envelope/SmartContractCode.h" @@ -36,6 +37,7 @@ #include "smc-envelope/Wallet.h" #include "smc-envelope/WalletV3.h" #include "smc-envelope/HighloadWallet.h" +#include "smc-envelope/HighloadWalletV2.h" #include "td/utils/base64.h" #include "td/utils/crypto.h" @@ -47,6 +49,7 @@ #include "td/utils/PathView.h" #include "td/utils/filesystem.h" #include "td/utils/port/path.h" +#include "td/utils/Variant.h" #include #include @@ -63,8 +66,8 @@ std::string load_source(std::string name) { td::Ref get_test_wallet_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 +DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods + 1 INT AND c4 PUSHCTR CTOS 32 LDU 256 PLDU CONDSEL // cnt or pubk }> INC 32 THROWIF // fail unless recv_external 512 INT LDSLICEX DUP 32 PLDU // sign cs cnt @@ -91,8 +94,8 @@ INC NEWC 32 STU 256 STU ENDC c4 POPCTR td::Ref get_wallet_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 + DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods + 1 INT AND c4 PUSHCTR CTOS 32 LDU 256 PLDU CONDSEL // cnt or pubk }> INC 32 THROWIF // fail unless recv_external 9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs @@ -119,8 +122,8 @@ SETCP0 DUP IFNOTRET // return if recv_internal td::Ref 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 + DUP 85143 INT EQUAL OVER 78748 INT EQUAL OR IFJMP:<{ // "seqno" and "get_public_key" get-methods + 1 INT AND c4 PUSHCTR CTOS 32 LDU 32 LDU NIP 256 PLDU CONDSEL // cnt or pubk }> 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 @@ -176,8 +179,15 @@ TEST(Tonlib, TestWallet) { "321", "-C", "TEST"}) .move_as_ok(); auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; + ton::TestWallet::Gift gift; + gift.destination = dest; + gift.message = "TEST"; + gift.gramms = 321000000000ll; + ton::TestWallet wallet(priv_key.get_public_key().move_as_ok(), 123); + ASSERT_EQ(123u, wallet.get_seqno().ok()); + CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet.get_public_key().ok().as_octet_string()); auto gift_message = ton::GenericAccount::create_ext_message( - address, {}, ton::TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", dest)); + address, {}, wallet.make_a_gift_message(priv_key, 0, {gift}).move_as_ok()); LOG(ERROR) << "-------"; vm::load_cell_slice(gift_message).print_rec(std::cerr); LOG(ERROR) << "-------"; @@ -224,13 +234,20 @@ TEST(Tonlib, Wallet) { }; fift_output.source_lookup.set_os_time(std::make_unique()); 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", "-C", "TESTv2", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"}) - .move_as_ok(); + fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup), + {"aba", "new-wallet", "-C", "TESTv2", + "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"}) + .move_as_ok(); auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; + ton::TestWallet::Gift gift; + gift.destination = dest; + gift.message = "TESTv2"; + gift.gramms = 321000000000ll; + ton::Wallet wallet(priv_key.get_public_key().move_as_ok(), 123); + ASSERT_EQ(123u, wallet.get_seqno().ok()); + CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet.get_public_key().ok().as_octet_string()); auto gift_message = ton::GenericAccount::create_ext_message( - address, {}, ton::Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", dest)); + address, {}, wallet.make_a_gift_message(priv_key, 60, {gift}).move_as_ok()); LOG(ERROR) << "-------"; vm::load_cell_slice(gift_message).print_rec(std::cerr); LOG(ERROR) << "-------"; @@ -251,7 +268,8 @@ TEST(Tonlib, WalletV3) { 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 init_message = + ton::WalletV3(priv_key.get_public_key().move_as_ok(), 239).get_init_message(priv_key).move_as_ok(); auto address = ton::GenericAccount::get_address(0, init_state); CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); @@ -273,13 +291,24 @@ TEST(Tonlib, WalletV3) { }; fift_output.source_lookup.set_os_time(std::make_unique()); 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", "-C", "TESTv3", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "239", "123", "321"}) - .move_as_ok(); + fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup), + {"aba", "new-wallet", "-C", "TESTv3", + "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; + + ton::WalletV3::Gift gift; + gift.destination = dest; + gift.message = "TESTv3"; + gift.gramms = 321000000000ll; + + ton::WalletV3 wallet(priv_key.get_public_key().move_as_ok(), 239, 123); + ASSERT_EQ(239u, wallet.get_wallet_id().ok()); + ASSERT_EQ(123u, wallet.get_seqno().ok()); + CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet.get_public_key().ok().as_octet_string()); + auto gift_message = ton::GenericAccount::create_ext_message( - address, {}, ton::WalletV3::make_a_gift_message(priv_key, 239, 123, 60, 321000000000ll, "TESTv3", dest)); + address, {}, wallet.make_a_gift_message(priv_key, 60, {gift}).move_as_ok()); LOG(ERROR) << "-------"; vm::load_cell_slice(gift_message).print_rec(std::cerr); LOG(ERROR) << "-------"; @@ -304,6 +333,11 @@ TEST(Tonlib, HighloadWallet) { auto init_message = ton::HighloadWallet::get_init_message(priv_key, 239); auto address = ton::GenericAccount::get_address(0, init_state); + ton::HighloadWallet wallet({ton::HighloadWallet::get_init_code(), ton::HighloadWallet::get_init_data(pub_key, 239)}); + ASSERT_EQ(239u, wallet.get_wallet_id().ok()); + ASSERT_EQ(0u, wallet.get_seqno().ok()); + CHECK(pub_key.as_octet_string() == wallet.get_public_key().ok().as_octet_string()); + CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); td::Ref res = ton::GenericAccount::create_ext_message(address, init_state, init_message); @@ -354,6 +388,80 @@ TEST(Tonlib, HighloadWallet) { CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash()); } +TEST(Tonlib, HighloadWalletV2) { + auto source_lookup = fift::create_mem_source_lookup(load_source("smartcont/new-highload-wallet-v2.fif")).move_as_ok(); + source_lookup + .write_file("/auto/highload-wallet-v2-code.fif", load_source("smartcont/auto/highload-wallet-v2-code.fif")) + .ensure(); + class ZeroOsTime : public fift::OsTime { + public: + td::uint32 now() override { + return 0; + } + }; + source_lookup.set_os_time(std::make_unique()); + 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::HighloadWalletV2::get_init_state(pub_key, 239, 0); + auto init_message = ton::HighloadWalletV2::get_init_message(priv_key, 239, 65535); + auto address = ton::GenericAccount::get_address(0, init_state); + + ton::HighloadWalletV2 wallet( + {ton::HighloadWalletV2::get_init_code(0), ton::HighloadWalletV2::get_init_data(pub_key, 239)}); + ASSERT_EQ(239u, wallet.get_wallet_id().ok()); + CHECK(pub_key.as_octet_string() == wallet.get_public_key().ok().as_octet_string()); + + CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); + + td::Ref 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-v2.fif")).ensure(); + std::string order; + std::vector 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::HighloadWalletV2::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(); + fift_output.source_lookup.set_os_time(std::make_unique()); + fift_output = + fift::mem_run_fift(std::move(fift_output.source_lookup), {"aba", "new-wallet", "239", "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::HighloadWalletV2::make_a_gift_message(priv_key, 239, 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(); @@ -365,9 +473,13 @@ TEST(Tonlib, TestGiver) { auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; - auto res = ton::GenericAccount::create_ext_message( - ton::TestGiver::address(), {}, - ton::TestGiver::make_a_gift_message(0, 1000000000ll * 6666 / 1000, "GIFT", address)); + ton::TestGiver::Gift gift; + gift.gramms = 1000000000ll * 6666 / 1000; + gift.message = "GIFT"; + gift.destination = address; + td::Ed25519::PrivateKey key{td::SecureString()}; + auto res = ton::GenericAccount::create_ext_message(ton::TestGiver::address(), {}, + ton::TestGiver().make_a_gift_message(key, 0, {gift}).move_as_ok()); vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr); CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash()); } @@ -670,3 +782,457 @@ TEST(Smartcont, MultisigStress) { LOG(INFO) << "Final code size: " << ms->code_size(); LOG(INFO) << "Final data size: " << ms->data_size(); } + +class MapDns { + public: + using ManualDns = ton::ManualDns; + struct Entry { + std::string name; + td::int16 category{0}; + std::string text; + + auto key() const { + return std::tie(name, category); + } + bool operator<(const Entry& other) const { + return key() < other.key(); + } + bool operator==(const ton::DnsInterface::Entry& other) const { + return key() == other.key() && other.data.type == ManualDns::EntryData::Type::Text && + other.data.data.get().text == text; + } + bool operator==(const Entry& other) const { + return key() == other.key() && text == other.text; + } + friend td::StringBuilder& operator<<(td::StringBuilder& sb, const Entry& entry) { + return sb << "[" << entry.name << ":" << entry.category << ":" << entry.text << "]"; + } + }; + struct Action { + std::string name; + td::int16 category{0}; + td::optional text; + + bool does_create_category() const { + CHECK(!name.empty()); + CHECK(category != 0); + return static_cast(text); + } + bool does_change_empty() const { + CHECK(!name.empty()); + CHECK(category != 0); + return static_cast(text) && !text.value().empty(); + } + void make_non_empty() { + CHECK(!name.empty()); + CHECK(category != 0); + if (!text) { + text = ""; + } + } + friend td::StringBuilder& operator<<(td::StringBuilder& sb, const Action& entry) { + return sb << "[" << entry.name << ":" << entry.category << ":" << (entry.text ? entry.text.value() : "") + << "]"; + } + }; + void update(td::Span actions) { + for (auto& action : actions) { + do_update(action); + } + } + using CombinedActions = ton::ManualDns::CombinedActions; + void update_combined(td::Span actions) { + LOG(ERROR) << "BEGIN"; + LOG(ERROR) << td::format::as_array(actions); + auto combined_actions = ton::ManualDns::combine_actions(actions); + for (auto& c : combined_actions) { + LOG(ERROR) << c.name << ":" << c.category; + if (c.actions) { + LOG(ERROR) << td::format::as_array(c.actions.value()); + } + } + LOG(ERROR) << "END"; + for (auto& combined_action : combined_actions) { + do_update(combined_action); + } + } + + std::vector resolve(td::Slice name, td::int16 category) { + std::vector res; + if (name.empty()) { + for (auto& a : entries_) { + for (auto& b : a.second) { + res.push_back({a.first, b.first, b.second}); + } + } + } else { + auto it = entries_.find(name); + while (it == entries_.end()) { + auto sz = name.find('.'); + category = -1; + if (sz != td::Slice::npos) { + name = name.substr(sz + 1); + } else { + break; + } + it = entries_.find(name); + } + if (it != entries_.end()) { + for (auto& b : it->second) { + if (category == 0 || category == b.first) { + res.push_back({name.str(), b.first, b.second}); + } + } + } + } + + std::sort(res.begin(), res.end()); + return res; + } + + private: + std::map, std::less<>> entries_; + void do_update(const Action& action) { + if (action.name.empty()) { + entries_.clear(); + return; + } + if (action.category == 0) { + entries_.erase(action.name); + return; + } + if (action.text) { + if (action.text.value().empty()) { + entries_[action.name].erase(action.category); + } else { + entries_[action.name][action.category] = action.text.value(); + } + } else { + auto it = entries_.find(action.name); + if (it != entries_.end()) { + it->second.erase(action.category); + } + } + } + + void do_update(const CombinedActions& actions) { + if (actions.name.empty()) { + entries_.clear(); + LOG(ERROR) << "CLEAR"; + if (!actions.actions) { + return; + } + for (auto& action : actions.actions.value()) { + CHECK(!action.name.empty()); + CHECK(action.category != 0); + CHECK(action.text); + if (action.text.value().empty()) { + entries_[action.name]; + } else { + entries_[action.name][action.category] = action.text.value(); + } + } + return; + } + if (actions.category == 0) { + entries_.erase(actions.name); + LOG(ERROR) << "CLEAR " << actions.name; + if (!actions.actions) { + return; + } + entries_[actions.name]; + for (auto& action : actions.actions.value()) { + CHECK(action.name == actions.name); + CHECK(action.category != 0); + CHECK(action.text); + if (action.text.value().empty()) { + entries_[action.name]; + } else { + entries_[action.name][action.category] = action.text.value(); + } + } + return; + } + CHECK(actions.actions); + CHECK(actions.actions.value().size() == 1); + for (auto& action : actions.actions.value()) { + CHECK(action.name == actions.name); + CHECK(action.category != 0); + if (action.text) { + if (action.text.value().empty()) { + entries_[action.name].erase(action.category); + } else { + entries_[action.name][action.category] = action.text.value(); + } + } else { + auto it = entries_.find(action.name); + if (it != entries_.end()) { + it->second.erase(action.category); + } + } + } + } +}; + +class CheckedDns { + public: + explicit CheckedDns(bool check_smc = true, bool check_combine = true) { + if (check_smc) { + key_ = td::Ed25519::generate_private_key().move_as_ok(); + dns_ = ManualDns::create(ManualDns::create_init_data_fast(key_.value().get_public_key().move_as_ok(), 123)); + } + if (check_combine) { + combined_map_dns_ = MapDns(); + } + } + using Action = MapDns::Action; + using Entry = MapDns::Entry; + void update(td::Span entries) { + if (dns_.not_null()) { + auto smc_actions = td::transform(entries, [](auto& entry) { + ton::DnsInterface::Action action; + action.name = entry.name; + action.category = entry.category; + if (entry.text) { + if (entry.text.value().empty()) { + action.data = td::Ref(); + } else { + action.data = ManualDns::EntryData::text(entry.text.value()).as_cell().move_as_ok(); + } + } + return action; + }); + auto query = dns_->create_update_query(key_.value(), smc_actions).move_as_ok(); + CHECK(dns_.write().send_external_message(std::move(query)).code == 0); + } + map_dns_.update(entries); + if (combined_map_dns_) { + combined_map_dns_.value().update_combined(entries); + } + } + void update(const Action& action) { + return update(td::Span(&action, 1)); + } + + std::vector resolve(td::Slice name, td::int16 category) { + LOG(ERROR) << "RESOLVE: " << name << " " << category; + auto res = map_dns_.resolve(name, category); + LOG(ERROR) << td::format::as_array(res); + + if (dns_.not_null()) { + auto other_res = dns_->resolve(name, category).move_as_ok(); + + std::sort(other_res.begin(), other_res.end()); + if (res.size() != other_res.size()) { + LOG(ERROR) << td::format::as_array(res); + LOG(FATAL) << td::format::as_array(other_res); + } + for (size_t i = 0; i < res.size(); i++) { + if (!(res[i] == other_res[i])) { + LOG(ERROR) << td::format::as_array(res); + LOG(FATAL) << td::format::as_array(other_res); + } + } + } + if (combined_map_dns_) { + auto other_res = combined_map_dns_.value().resolve(name, category); + + std::sort(other_res.begin(), other_res.end()); + if (res.size() != other_res.size()) { + LOG(ERROR) << td::format::as_array(res); + LOG(FATAL) << td::format::as_array(other_res); + } + for (size_t i = 0; i < res.size(); i++) { + if (!(res[i] == other_res[i])) { + LOG(ERROR) << td::format::as_array(res); + LOG(FATAL) << td::format::as_array(other_res); + } + } + } + + return res; + } + + private: + using ManualDns = ton::ManualDns; + td::optional key_; + td::Ref dns_; + + MapDns map_dns_; + td::optional combined_map_dns_; + + void do_update_smc(const Action& entry) { + LOG(ERROR) << td::format::escaped(ManualDns::encode_name(entry.name)); + ton::DnsInterface::Action action; + action.name = entry.name; + action.category = entry.category; + action.data = ManualDns::EntryData::text(entry.text.value()).as_cell().move_as_ok(); + } +}; + +void do_dns_test(CheckedDns&& dns) { + using Action = CheckedDns::Action; + std::vector actions; + + td::Random::Xorshift128plus rnd(123); + + auto gen_name = [&] { + auto cnt = rnd.fast(1, 2); + std::string res; + for (int i = 0; i < cnt; i++) { + if (i != 0) { + res += '.'; + } + auto len = rnd.fast(1, 1); + for (int j = 0; j < len; j++) { + res += static_cast(rnd.fast('a', 'b')); + } + } + return res; + }; + auto gen_text = [&] { + std::string res; + int len = 5; + for (int j = 0; j < len; j++) { + res += static_cast(rnd.fast('a', 'b')); + } + return res; + }; + + auto gen_action = [&] { + Action action; + if (rnd.fast(0, 1000) == 0) { + return action; + } + action.name = gen_name(); + if (rnd.fast(0, 20) == 0) { + return action; + } + action.category = td::narrow_cast(rnd.fast(1, 5)); + if (rnd.fast(0, 4) == 0) { + return action; + } + if (rnd.fast(0, 4) == 0) { + action.text = ""; + return action; + } + action.text = gen_text(); + return action; + }; + + SET_VERBOSITY_LEVEL(VERBOSITY_NAME(ERROR)); + for (int i = 0; i < 100000; i++) { + actions.push_back(gen_action()); + if (rnd.fast(0, 10) == 0) { + dns.update(actions); + actions.clear(); + } + dns.resolve(gen_name(), td::narrow_cast(rnd.fast(0, 5))); + } +}; + +TEST(Smartcont, DnsManual) { + using ManualDns = ton::ManualDns; + auto test_entry_data = [](auto&& entry_data) { + auto cell = entry_data.as_cell().move_as_ok(); + auto cs = vm::load_cell_slice(cell); + auto new_entry_data = ManualDns::EntryData::from_cellslice(cs).move_as_ok(); + ASSERT_EQ(entry_data, new_entry_data); + }; + test_entry_data(ManualDns::EntryData::text("abcd")); + test_entry_data(ManualDns::EntryData::adnl_address(ton::Bits256{})); + + CHECK(td::Slice("a\0b\0") == ManualDns::encode_name("b.a")); + CHECK(td::Slice("a\0b\0") == ManualDns::encode_name(".b.a")); + ASSERT_EQ("b.a", ManualDns::decode_name("a\0b\0")); + ASSERT_EQ("b.a", ManualDns::decode_name("a\0b")); + ASSERT_EQ("", ManualDns::decode_name("")); + + auto key = td::Ed25519::generate_private_key().move_as_ok(); + + auto manual = ManualDns::create(ManualDns::create_init_data_fast(key.get_public_key().move_as_ok(), 123)); + CHECK(manual->get_wallet_id().move_as_ok() == 123); + auto init_query = manual->create_init_query(key).move_as_ok(); + LOG(ERROR) << "A"; + CHECK(manual.write().send_external_message(init_query).code == 0); + LOG(ERROR) << "B"; + CHECK(manual.write().send_external_message(init_query).code != 0); + + auto value = vm::CellBuilder().store_bytes("hello world").finalize(); + auto set_query = + manual + ->sign(key, + manual->prepare(manual->create_set_value_unsigned(1, "a\0b\0", value).move_as_ok(), 1).move_as_ok()) + .move_as_ok(); + CHECK(manual.write().send_external_message(set_query).code == 0); + + auto res = manual->run_get_method( + "dnsresolve", {vm::load_cell_slice_ref(vm::CellBuilder().store_bytes("a\0b\0").finalize()), td::make_refint(1)}); + CHECK(res.code == 0); + CHECK(res.stack.write().pop_cell()->get_hash() == value->get_hash()); + + CheckedDns dns; + dns.update(CheckedDns::Action{"a.b.c", 1, "hello"}); + CHECK(dns.resolve("a.b.c", 1).at(0).text == "hello"); + dns.resolve("a", 1); + dns.resolve("a.b", 1); + CHECK(dns.resolve("a.b.c", 2).empty()); + dns.update(CheckedDns::Action{"a.b.c", 2, "test"}); + CHECK(dns.resolve("a.b.c", 2).at(0).text == "test"); + dns.resolve("a.b.c", 1); + dns.resolve("a.b.c", 2); + LOG(ERROR) << "Test zero category"; + dns.resolve("a.b.c", 0); + dns.update(CheckedDns::Action{"", 0, ""}); + CHECK(dns.resolve("a.b.c", 2).empty()); + + LOG(ERROR) << "Test multipe update"; + { + CheckedDns::Action e[4] = {CheckedDns::Action{"", 0, ""}, CheckedDns::Action{"a.b.c", 1, "hello"}, + CheckedDns::Action{"a.b.c", 2, "world"}, CheckedDns::Action{"x.y.z", 3, "abc"}}; + dns.update(td::Span(e, 4)); + } + dns.resolve("a.b.c", 1); + dns.resolve("a.b.c", 2); + dns.resolve("x.y.z", 3); + + { + CheckedDns::Action e[1] = {CheckedDns::Action{"x.y.z", 0, ""}}; + dns.update(td::Span(e, 1)); + } + + dns.resolve("a.b.c", 1); + dns.resolve("a.b.c", 2); + dns.resolve("x.y.z", 3); + + { + CheckedDns::Action e[3] = {CheckedDns::Action{"x.y.z", 0, ""}, CheckedDns::Action{"x.y.z", 1, "xxx"}, + CheckedDns::Action{"x.y.z", 2, "yyy"}}; + dns.update(td::Span(e, 3)); + } + dns.resolve("a.b.c", 1); + dns.resolve("a.b.c", 2); + dns.resolve("x.y.z", 1); + dns.resolve("x.y.z", 2); + dns.resolve("x.y.z", 3); + + { + auto actions_ext = + ton::ManualDns::parse("delete.name one\nset one 1 TEXT:one\ndelete.name two\nset two 2 TEXT:two").move_as_ok(); + + auto actions = td::transform(actions_ext, [](auto& action) { + td::optional data; + if (action.data) { + data = action.data.value().data.template get().text; + } + return CheckedDns::Action{action.name, action.category, std::move(data)}; + }); + + dns.update(actions); + } + dns.resolve("one", 1); + dns.resolve("two", 2); + + // TODO: rethink semantic of creating an empty dictionary + do_dns_test(CheckedDns(true, true)); +} diff --git a/crypto/test/vm.cpp b/crypto/test/vm.cpp index 72e2f8c61b..77157f6d84 100644 --- a/crypto/test/vm.cpp +++ b/crypto/test/vm.cpp @@ -14,9 +14,9 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ -#include "vm/continuation.h" +#include "vm/vm.h" #include "vm/cp0.h" #include "vm/dict.h" #include "fift/utils.h" diff --git a/crypto/vm/arithops.cpp b/crypto/vm/arithops.cpp index 19279cf167..69e6f0830d 100644 --- a/crypto/vm/arithops.cpp +++ b/crypto/vm/arithops.cpp @@ -14,15 +14,15 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include #include "vm/arithops.h" #include "vm/log.h" #include "vm/opctable.h" #include "vm/stack.hpp" -#include "vm/continuation.h" #include "vm/excno.hpp" +#include "vm/vm.h" #include "common/bigint.hpp" #include "common/refint.h" diff --git a/crypto/vm/cellops.cpp b/crypto/vm/cellops.cpp index 5346e16851..0cd7d5e14a 100644 --- a/crypto/vm/cellops.cpp +++ b/crypto/vm/cellops.cpp @@ -14,16 +14,16 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include #include "vm/cellops.h" #include "vm/log.h" #include "vm/opctable.h" #include "vm/stack.hpp" -#include "vm/continuation.h" #include "vm/excno.hpp" #include "vm/vmstate.h" +#include "vm/vm.h" #include "common/bigint.hpp" #include "common/refint.h" diff --git a/crypto/vm/cells/CellSlice.cpp b/crypto/vm/cells/CellSlice.cpp index 74a78970e3..8e46bb9588 100644 --- a/crypto/vm/cells/CellSlice.cpp +++ b/crypto/vm/cells/CellSlice.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "vm/cells/CellSlice.h" #include "vm/excno.hpp" @@ -1026,7 +1026,7 @@ std::ostream& operator<<(std::ostream& os, Ref cs_ref) { VirtualCell::LoadedCell load_cell_slice_impl(const Ref& cell, bool* can_be_special) { auto* vm_state_interface = VmStateInterface::get(); if (vm_state_interface) { - vm_state_interface->register_cell_load(); + vm_state_interface->register_cell_load(cell->get_hash()); } auto r_loaded_cell = cell->load_cell(); if (r_loaded_cell.is_error()) { diff --git a/crypto/vm/cells/CellString.cpp b/crypto/vm/cells/CellString.cpp index ad2cbf5f8f..9889dc8e2b 100644 --- a/crypto/vm/cells/CellString.cpp +++ b/crypto/vm/cells/CellString.cpp @@ -1,3 +1,21 @@ +/* + 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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #include "CellString.h" #include "td/utils/misc.h" @@ -61,4 +79,79 @@ td::Result CellString::load(CellSlice &cs, unsigned int top_bits) { CHECK(to.offs == (int)size); return res; } + +td::Status CellText::store(CellBuilder &cb, td::Slice slice, unsigned int top_bits) { + td::uint32 size = td::narrow_cast(slice.size() * 8); + return store(cb, td::BitSlice(slice.ubegin(), size), top_bits); +} + +td::Status CellText::store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits) { + if (slice.size() > max_bytes * 8) { + return td::Status::Error("String is too long (1)"); + } + if (cb.remaining_bits() < 16) { + return td::Status::Error("Not enough space in a builder"); + } + if (top_bits < 16) { + return td::Status::Error("Need at least 16 top bits"); + } + if (slice.size() == 0) { + cb.store_long(0, 8); + return td::Status::OK(); + } + unsigned int head = td::min(slice.size(), td::min(cb.remaining_bits(), top_bits) - 16) / 8 * 8; + auto max_bits = vm::Cell::max_bits / 8 * 8; + auto depth = 1 + (slice.size() - head + max_bits - 8 - 1) / (max_bits - 8); + if (depth > max_chain_length) { + return td::Status::Error("String is too long (2)"); + } + cb.store_long(depth, 8); + cb.store_long(head / 8, 8); + cb.append_bitslice(slice.subslice(0, head)); + slice.advance(head); + if (slice.size() == 0) { + return td::Status::OK(); + } + cb.store_ref(do_store(std::move(slice))); + return td::Status::OK(); +} + +td::Ref CellText::do_store(td::BitSlice slice) { + vm::CellBuilder cb; + unsigned int head = td::min(slice.size(), cb.remaining_bits() - 8) / 8 * 8; + cb.store_long(head / 8, 8); + cb.append_bitslice(slice.subslice(0, head)); + slice.advance(head); + if (slice.size() != 0) { + cb.store_ref(do_store(std::move(slice))); + } + return cb.finalize(); +} + +template +void CellText::for_each(F &&f, CellSlice cs) { + auto depth = cs.fetch_ulong(8); + + for (td::uint32 i = 0; i < depth; i++) { + auto size = cs.fetch_ulong(8); + f(cs.fetch_bits(td::narrow_cast(size) * 8)); + if (i + 1 < depth) { + cs = vm::load_cell_slice(cs.prefetch_ref()); + } + } +} + +td::Result CellText::load(CellSlice &cs) { + unsigned int size = 0; + for_each([&](auto slice) { size += slice.size(); }, cs); + if (size % 8 != 0) { + return td::Status::Error("Size is not divisible by 8"); + } + std::string res(size / 8, 0); + + td::BitPtr to(td::MutableSlice(res).ubegin()); + for_each([&](auto slice) { to.concat(slice); }, cs); + CHECK(to.offs == (int)size); + return res; +} } // namespace vm diff --git a/crypto/vm/cells/CellString.h b/crypto/vm/cells/CellString.h index 89c933d876..ab93203f88 100644 --- a/crypto/vm/cells/CellString.h +++ b/crypto/vm/cells/CellString.h @@ -1,3 +1,21 @@ +/* + 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 . + + Copyright 2019-2020 Telegram Systems LLP +*/ #pragma once #include "td/utils/Status.h" @@ -13,10 +31,35 @@ class CellString { static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits); static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits); static td::Result load(CellSlice &cs, unsigned int top_bits = Cell::max_bits); + static td::Result> create(td::Slice slice, unsigned int top_bits = Cell::max_bits) { + vm::CellBuilder cb; + TRY_STATUS(store(cb, slice, top_bits)); + return cb.finalize(); + } private: template static void for_each(F &&f, CellSlice &cs, unsigned int top_bits = Cell::max_bits); }; +class CellText { + public: + static constexpr unsigned int max_bytes = 1024; + static constexpr unsigned int max_chain_length = 16; + + static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits); + static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits); + static td::Result load(CellSlice &cs); + static td::Result> create(td::Slice slice, unsigned int top_bits = Cell::max_bits) { + vm::CellBuilder cb; + TRY_STATUS(store(cb, slice, top_bits)); + return cb.finalize(); + } + + private: + template + static void for_each(F &&f, CellSlice cs); + static td::Ref do_store(td::BitSlice slice); +}; + } // namespace vm diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index f75aff755a..fdd27ce68a 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -14,12 +14,13 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "vm/dispatch.h" #include "vm/continuation.h" #include "vm/dict.h" #include "vm/log.h" +#include "vm/vm.h" namespace vm { @@ -625,562 +626,4 @@ void VmState::init_cregs(bool same_c3, bool push_0) { } } -VmState::VmState() : cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), quit1(true, 1) { - ensure_throw(init_cp(0)); - init_cregs(); -} - -VmState::VmState(Ref _code) - : code(std::move(_code)), cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), quit1(true, 1) { - ensure_throw(init_cp(0)); - init_cregs(); -} - -VmState::VmState(Ref _code, Ref _stack, int flags, Ref _data, VmLog log, - std::vector> _libraries, Ref init_c7) - : code(std::move(_code)) - , stack(std::move(_stack)) - , cp(-1) - , dispatch(&dummy_dispatch_table) - , quit0(true, 0) - , quit1(true, 1) - , log(log) - , libraries(std::move(_libraries)) { - ensure_throw(init_cp(0)); - set_c4(std::move(_data)); - if (init_c7.not_null()) { - set_c7(std::move(init_c7)); - } - init_cregs(flags & 1, flags & 2); -} - -VmState::VmState(Ref _code, Ref _stack, const GasLimits& gas, int flags, Ref _data, VmLog log, - std::vector> _libraries, Ref init_c7) - : code(std::move(_code)) - , stack(std::move(_stack)) - , cp(-1) - , dispatch(&dummy_dispatch_table) - , quit0(true, 0) - , quit1(true, 1) - , log(log) - , gas(gas) - , libraries(std::move(_libraries)) { - ensure_throw(init_cp(0)); - set_c4(std::move(_data)); - if (init_c7.not_null()) { - set_c7(std::move(init_c7)); - } - init_cregs(flags & 1, flags & 2); -} - -Ref VmState::convert_code_cell(Ref code_cell) { - if (code_cell.is_null()) { - return {}; - } - Ref csr{true, NoVmOrd(), code_cell}; - if (csr->is_valid()) { - return csr; - } - return load_cell_slice_ref(CellBuilder{}.store_ref(std::move(code_cell)).finalize()); -} - -bool VmState::init_cp(int new_cp) { - const DispatchTable* dt = DispatchTable::get_table(new_cp); - if (dt) { - cp = new_cp; - dispatch = dt; - return true; - } else { - return false; - } -} - -bool VmState::set_cp(int new_cp) { - return new_cp == cp || init_cp(new_cp); -} - -void VmState::force_cp(int new_cp) { - if (!set_cp(new_cp)) { - throw VmError{Excno::inv_opcode, "unsupported codepage"}; - } -} - -// simple call to a continuation cont -int VmState::call(Ref cont) { - const ControlData* cont_data = cont->get_cdata(); - if (cont_data) { - if (cont_data->save.c[0].not_null()) { - // call reduces to a jump - return jump(std::move(cont)); - } - if (cont_data->stack.not_null() || cont_data->nargs >= 0) { - // if cont has non-empty stack or expects fixed number of arguments, call is not simple - return call(std::move(cont), -1, -1); - } - // create return continuation, to be stored into new c0 - Ref ret = Ref{true, std::move(code), cp}; - ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0])); - cr.set_c0( - std::move(ret)); // set c0 to its final value before switching to cont; notice that cont.save.c0 is not set - return jump_to(std::move(cont)); - } - // create return continuation, to be stored into new c0 - Ref ret = Ref{true, std::move(code), cp}; - ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0])); - // general implementation of a simple call - cr.set_c0(std::move(ret)); // set c0 to its final value before switching to cont; notice that cont.save.c0 is not set - return jump_to(std::move(cont)); -} - -// call with parameters to continuation cont -int VmState::call(Ref cont, int pass_args, int ret_args) { - const ControlData* cont_data = cont->get_cdata(); - if (cont_data) { - if (cont_data->save.c[0].not_null()) { - // call reduces to a jump - return jump(std::move(cont), pass_args); - } - int depth = stack->depth(); - if (pass_args > depth || cont_data->nargs > depth) { - throw VmError{Excno::stk_und, "stack underflow while calling a continuation: not enough arguments on stack"}; - } - if (cont_data->nargs > pass_args && pass_args >= 0) { - throw VmError{Excno::stk_und, - "stack underflow while calling a closure continuation: not enough arguments passed"}; - } - auto old_c0 = std::move(cr.c[0]); - // optimization(?): decrease refcnts of unused continuations in c[i] as early as possible - preclear_cr(cont_data->save); - // no exceptions should be thrown after this point - int copy = cont_data->nargs, skip = 0; - if (pass_args >= 0) { - if (copy >= 0) { - skip = pass_args - copy; - } else { - copy = pass_args; - } - } - // copy=-1 : pass whole stack, else pass top `copy` elements, drop next `skip` elements. - Ref new_stk; - if (cont_data->stack.not_null() && !cont_data->stack->is_empty()) { - // `cont` already has a stack, create resulting stack from it - if (copy < 0) { - copy = stack->depth(); - } - if (cont->is_unique()) { - // optimization: avoid copying stack if we hold the only copy of `cont` - new_stk = std::move(cont.unique_write().get_cdata()->stack); - } else { - new_stk = cont_data->stack; - } - new_stk.write().move_from_stack(get_stack(), copy); - if (skip > 0) { - get_stack().pop_many(skip); - } - } else if (copy >= 0) { - new_stk = get_stack().split_top(copy, skip); - } else { - new_stk = std::move(stack); - stack.clear(); - } - // create return continuation using the remainder of current stack - Ref ret = Ref{true, std::move(code), cp, std::move(stack), ret_args}; - ret.unique_write().get_cdata()->save.set_c0(std::move(old_c0)); - Ref ord_cont = static_cast>(cont); - set_stack(std::move(new_stk)); - cr.set_c0(std::move(ret)); // ??? if codepage of code in ord_cont is unknown, will end up with incorrect c0 - return jump_to(std::move(cont)); - } else { - // have no continuation data, situation is somewhat simpler - int depth = stack->depth(); - if (pass_args > depth) { - throw VmError{Excno::stk_und, "stack underflow while calling a continuation: not enough arguments on stack"}; - } - // create new stack from the top `pass_args` elements of the current stack - Ref new_stk = (pass_args >= 0 ? get_stack().split_top(pass_args) : std::move(stack)); - // create return continuation using the remainder of the current stack - Ref ret = Ref{true, std::move(code), cp, std::move(stack), ret_args}; - ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0])); - set_stack(std::move(new_stk)); - cr.set_c0(std::move(ret)); // ??? if codepage of code in ord_cont is unknown, will end up with incorrect c0 - return jump_to(std::move(cont)); - } -} - -// simple jump to continuation cont -int VmState::jump(Ref cont) { - const ControlData* cont_data = cont->get_cdata(); - if (cont_data && (cont_data->stack.not_null() || cont_data->nargs >= 0)) { - // if cont has non-empty stack or expects fixed number of arguments, jump is not simple - return jump(std::move(cont), -1); - } else { - return jump_to(std::move(cont)); - } -} - -// general jump to continuation cont -int VmState::jump(Ref cont, int pass_args) { - const ControlData* cont_data = cont->get_cdata(); - if (cont_data) { - // first do the checks - int depth = stack->depth(); - if (pass_args > depth || cont_data->nargs > depth) { - throw VmError{Excno::stk_und, "stack underflow while jumping to a continuation: not enough arguments on stack"}; - } - if (cont_data->nargs > pass_args && pass_args >= 0) { - throw VmError{Excno::stk_und, - "stack underflow while jumping to closure continuation: not enough arguments passed"}; - } - // optimization(?): decrease refcnts of unused continuations in c[i] as early as possible - preclear_cr(cont_data->save); - // no exceptions should be thrown after this point - int copy = cont_data->nargs; - if (pass_args >= 0 && copy < 0) { - copy = pass_args; - } - // copy=-1 : pass whole stack, else pass top `copy` elements, drop the remainder. - if (cont_data->stack.not_null() && !cont_data->stack->is_empty()) { - // `cont` already has a stack, create resulting stack from it - if (copy < 0) { - copy = get_stack().depth(); - } - Ref new_stk; - if (cont->is_unique()) { - // optimization: avoid copying the stack if we hold the only copy of `cont` - new_stk = std::move(cont.unique_write().get_cdata()->stack); - } else { - new_stk = cont_data->stack; - } - new_stk.write().move_from_stack(get_stack(), copy); - set_stack(std::move(new_stk)); - } else { - if (copy >= 0) { - get_stack().drop_bottom(stack->depth() - copy); - } - } - return jump_to(std::move(cont)); - } else { - // have no continuation data, situation is somewhat simpler - if (pass_args >= 0) { - int depth = get_stack().depth(); - if (pass_args > depth) { - throw VmError{Excno::stk_und, "stack underflow while jumping to a continuation: not enough arguments on stack"}; - } - get_stack().drop_bottom(depth - pass_args); - } - return jump_to(std::move(cont)); - } -} - -int VmState::ret() { - Ref cont = quit0; - cont.swap(cr.c[0]); - return jump(std::move(cont)); -} - -int VmState::ret(int ret_args) { - Ref cont = quit0; - cont.swap(cr.c[0]); - return jump(std::move(cont), ret_args); -} - -int VmState::ret_alt() { - Ref cont = quit1; - cont.swap(cr.c[1]); - return jump(std::move(cont)); -} - -int VmState::ret_alt(int ret_args) { - Ref cont = quit1; - cont.swap(cr.c[1]); - return jump(std::move(cont), ret_args); -} - -Ref VmState::extract_cc(int save_cr, int stack_copy, int cc_args) { - Ref new_stk; - if (stack_copy < 0 || stack_copy == stack->depth()) { - new_stk = std::move(stack); - stack.clear(); - } else if (stack_copy > 0) { - stack->check_underflow(stack_copy); - new_stk = get_stack().split_top(stack_copy); - } else { - new_stk = Ref{true}; - } - Ref cc = Ref{true, std::move(code), cp, std::move(stack), cc_args}; - stack = std::move(new_stk); - if (save_cr & 7) { - ControlData* cdata = cc.unique_write().get_cdata(); - if (save_cr & 1) { - cdata->save.set_c0(std::move(cr.c[0])); - cr.set_c0(quit0); - } - if (save_cr & 2) { - cdata->save.set_c1(std::move(cr.c[1])); - cr.set_c1(quit1); - } - if (save_cr & 4) { - cdata->save.set_c2(std::move(cr.c[2])); - // cr.set_c2(Ref{true}); - } - } - return cc; -} - -int VmState::throw_exception(int excno) { - Stack& stack_ref = get_stack(); - stack_ref.clear(); - stack_ref.push_smallint(0); - stack_ref.push_smallint(excno); - code.clear(); - consume_gas(exception_gas_price); - return jump(get_c2()); -} - -int VmState::throw_exception(int excno, StackEntry&& arg) { - Stack& stack_ref = get_stack(); - stack_ref.clear(); - stack_ref.push(std::move(arg)); - stack_ref.push_smallint(excno); - code.clear(); - consume_gas(exception_gas_price); - return jump(get_c2()); -} - -void GasLimits::gas_exception() const { - throw VmNoGas{}; -} - -void GasLimits::set_limits(long long _max, long long _limit, long long _credit) { - gas_max = _max; - gas_limit = _limit; - gas_credit = _credit; - change_base(_limit + _credit); -} - -void GasLimits::change_limit(long long _limit) { - _limit = std::min(std::max(_limit, 0LL), gas_max); - gas_credit = 0; - gas_limit = _limit; - change_base(_limit); -} - -bool VmState::set_gas_limits(long long _max, long long _limit, long long _credit) { - gas.set_limits(_max, _limit, _credit); - return true; -} - -void VmState::change_gas_limit(long long new_limit) { - VM_LOG(this) << "changing gas limit to " << std::min(new_limit, gas.gas_max); - gas.change_limit(new_limit); -} - -int VmState::step() { - assert(!code.is_null()); - //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, 3); - } - ++steps; - if (code->size()) { - return dispatch->dispatch(this, code.write()); - } else if (code->size_refs()) { - VM_LOG(this) << "execute implicit JMPREF\n"; - Ref cont = Ref{true, load_cell_slice_ref(code->prefetch_ref()), get_cp()}; - return jump(std::move(cont)); - } else { - VM_LOG(this) << "execute implicit RET\n"; - return ret(); - } -} - -int VmState::run() { - if (code.is_null()) { - throw VmError{Excno::fatal, "cannot run an uninitialized VM"}; - } - int res; - Guard guard(this); - do { - // LOG(INFO) << "[BS] data cells: " << DataCell::get_total_data_cells(); - try { - try { - res = step(); - gas.check(); - } catch (vm::CellBuilder::CellWriteError) { - throw VmError{Excno::cell_ov}; - } catch (vm::CellBuilder::CellCreateError) { - throw VmError{Excno::cell_ov}; - } catch (vm::CellSlice::CellReadError) { - throw VmError{Excno::cell_und}; - } - } catch (const VmError& vme) { - VM_LOG(this) << "handling exception code " << vme.get_errno() << ": " << vme.get_msg(); - try { - // LOG(INFO) << "[EX] data cells: " << DataCell::get_total_data_cells(); - ++steps; - res = throw_exception(vme.get_errno()); - } catch (const VmError& vme2) { - VM_LOG(this) << "exception " << vme2.get_errno() << " while handling exception: " << vme.get_msg(); - // LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells(); - return ~vme2.get_errno(); - } - } catch (VmNoGas vmoog) { - ++steps; - VM_LOG(this) << "unhandled out-of-gas exception: gas consumed=" << gas.gas_consumed() - << ", limit=" << gas.gas_limit; - get_stack().clear(); - get_stack().push_smallint(gas.gas_consumed()); - return vmoog.get_errno(); // no ~ for unhandled exceptions (to make their faking impossible) - } - } while (!res); - // LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells(); - if ((res | 1) == -1) { - commit(); - } - return res; -} - -ControlData* force_cdata(Ref& cont) { - if (!cont->get_cdata()) { - cont = Ref{true, cont}; - return cont.unique_write().get_cdata(); - } else { - return cont.write().get_cdata(); - } -} - -ControlRegs* force_cregs(Ref& cont) { - return &force_cdata(cont)->save; -} - -int run_vm_code(Ref code, Ref& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, - GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr) { - VmState vm{code, - std::move(stack), - gas_limits ? *gas_limits : GasLimits{}, - flags, - data_ptr ? *data_ptr : Ref{}, - log, - std::move(libraries), - std::move(init_c7)}; - int res = vm.run(); - stack = vm.get_stack_ref(); - if (vm.committed() && data_ptr) { - *data_ptr = vm.get_committed_state().c4; - } - if (vm.committed() && actions_ptr) { - *actions_ptr = vm.get_committed_state().c5; - } - if (steps) { - *steps = vm.get_steps_count(); - } - if (gas_limits) { - *gas_limits = vm.get_gas_limits(); - LOG(INFO) << "steps: " << vm.get_steps_count() << " gas: used=" << gas_limits->gas_consumed() - << ", max=" << gas_limits->gas_max << ", limit=" << gas_limits->gas_limit - << ", credit=" << gas_limits->gas_credit; - } - if ((vm.get_log().log_mask & vm::VmLog::DumpStack) != 0) { - VM_LOG(&vm) << "BEGIN_STACK_DUMP"; - for (int i = stack->depth(); i > 0; i--) { - VM_LOG(&vm) << (*stack)[i - 1].to_string(); - } - VM_LOG(&vm) << "END_STACK_DUMP"; - } - - return ~res; -} - -int run_vm_code(Ref code, Stack& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, - GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr) { - Ref stk{true}; - stk.unique_write().set_contents(std::move(stack)); - stack.clear(); - int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7), - actions_ptr); - CHECK(stack.is_unique()); - if (stk.is_null()) { - stack.clear(); - } else if (&(*stk) != &stack) { - VmState* st = nullptr; - if (stk->is_unique()) { - VM_LOG(st) << "move resulting stack (" << stk->depth() << " entries)"; - stack.set_contents(std::move(stk.unique_write())); - } else { - VM_LOG(st) << "copying resulting stack (" << stk->depth() << " entries)"; - stack.set_contents(*stk); - } - } - return res; -} - -// may throw a dictionary exception; returns nullptr if library is not found in context -Ref VmState::load_library(td::ConstBitPtr hash) { - std::unique_ptr tmp_ctx; - // install temporary dummy vm state interface to prevent charging for cell load operations during library lookup - VmStateInterface::Guard(tmp_ctx.get()); - for (const auto& lib_collection : libraries) { - auto lib = lookup_library_in(hash, lib_collection); - if (lib.not_null()) { - return lib; - } - } - return {}; -} - -bool VmState::register_library_collection(Ref lib) { - if (lib.is_null()) { - return true; - } - libraries.push_back(std::move(lib)); - return true; -} - -void VmState::register_cell_load() { - consume_gas(cell_load_gas_price); -} - -void VmState::register_cell_create() { - consume_gas(cell_create_gas_price); -} - -td::BitArray<256> VmState::get_state_hash() const { - // TODO: implement properly, by serializing the stack etc, and computing the Merkle hash - td::BitArray<256> res; - res.clear(); - return res; -} - -td::BitArray<256> VmState::get_final_state_hash(int exit_code) const { - // TODO: implement properly, by serializing the stack etc, and computing the Merkle hash - td::BitArray<256> res; - res.clear(); - return res; -} - -Ref lookup_library_in(td::ConstBitPtr key, vm::Dictionary& dict) { - try { - auto val = dict.lookup(key, 256); - if (val.is_null() || !val->have_refs()) { - return {}; - } - auto root = val->prefetch_ref(); - if (root.not_null() && !root->get_hash().bits().compare(key, 256)) { - return root; - } - return {}; - } catch (vm::VmError) { - return {}; - } -} - -Ref lookup_library_in(td::ConstBitPtr key, Ref lib_root) { - if (lib_root.is_null()) { - return lib_root; - } - vm::Dictionary dict{std::move(lib_root), 256}; - return lookup_library_in(key, dict); -} - } // namespace vm diff --git a/crypto/vm/continuation.h b/crypto/vm/continuation.h index 1c24c4cdfa..37abe86996 100644 --- a/crypto/vm/continuation.h +++ b/crypto/vm/continuation.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once @@ -371,289 +371,7 @@ class OrdCont : public Continuation { static Ref deserialize(CellSlice& cs, int mode = 0); }; -struct GasLimits { - static constexpr long long infty = (1ULL << 63) - 1; - long long gas_max, gas_limit, gas_credit, gas_remaining, gas_base; - GasLimits() : gas_max(infty), gas_limit(infty), gas_credit(0), gas_remaining(infty), gas_base(infty) { - } - GasLimits(long long _limit, long long _max = infty, long long _credit = 0) - : gas_max(_max) - , gas_limit(_limit) - , gas_credit(_credit) - , gas_remaining(_limit + _credit) - , gas_base(gas_remaining) { - } - long long gas_consumed() const { - return gas_base - gas_remaining; - } - void set_limits(long long _max, long long _limit, long long _credit = 0); - void change_base(long long _base) { - gas_remaining += _base - gas_base; - gas_base = _base; - } - void change_limit(long long _limit); - void consume(long long amount) { - // LOG(DEBUG) << "consume " << amount << " gas (" << gas_remaining << " remaining)"; - gas_remaining -= amount; - } - bool try_consume(long long amount) { - // LOG(DEBUG) << "try consume " << amount << " gas (" << gas_remaining << " remaining)"; - return (gas_remaining -= amount) >= 0; - } - void gas_exception() const; - void gas_exception(bool cond) const { - if (!cond) { - gas_exception(); - } - } - void consume_chk(long long amount) { - gas_exception(try_consume(amount)); - } - void check() const { - gas_exception(gas_remaining >= 0); - } - bool final_ok() const { - return gas_remaining >= gas_credit; - } -}; - -struct CommittedState { - Ref c4, c5; - bool committed{false}; -}; - -class VmState final : public VmStateInterface { - Ref code; - Ref stack; - ControlRegs cr; - CommittedState cstate; - int cp; - long long steps{0}; - const DispatchTable* dispatch; - Ref quit0, quit1; - VmLog log; - GasLimits gas; - std::vector> libraries; - int stack_trace{0}, debug_off{0}; - - bool chksig_always_succeed{false}; - - public: - static constexpr unsigned cell_load_gas_price = 100, cell_create_gas_price = 500, exception_gas_price = 50, - tuple_entry_gas_price = 1; - VmState(); - VmState(Ref _code); - VmState(Ref _code, Ref _stack, int flags = 0, Ref _data = {}, VmLog log = {}, - std::vector> _libraries = {}, Ref init_c7 = {}); - VmState(Ref _code, Ref _stack, const GasLimits& _gas, int flags = 0, Ref _data = {}, - VmLog log = {}, std::vector> _libraries = {}, Ref init_c7 = {}); - template - VmState(Ref code_cell, Args&&... args) - : VmState(convert_code_cell(std::move(code_cell)), std::forward(args)...) { - } - VmState(const VmState&) = delete; - VmState(VmState&&) = delete; - VmState& operator=(const VmState&) = delete; - VmState& operator=(VmState&&) = delete; - bool set_gas_limits(long long _max, long long _limit, long long _credit = 0); - bool final_gas_ok() const { - return gas.final_ok(); - } - long long gas_consumed() const { - return gas.gas_consumed(); - } - bool committed() const { - return cstate.committed; - } - const CommittedState& get_committed_state() const { - return cstate; - } - void consume_gas(long long amount) { - gas.consume(amount); - } - void consume_tuple_gas(unsigned tuple_len) { - consume_gas(tuple_len * tuple_entry_gas_price); - } - void consume_tuple_gas(const Ref& tup) { - if (tup.not_null()) { - consume_tuple_gas((unsigned)tup->size()); - } - } - GasLimits get_gas_limits() const { - return gas; - } - void change_gas_limit(long long new_limit); - template - void check_underflow(Args... args) { - stack->check_underflow(args...); - } - bool register_library_collection(Ref lib); - Ref load_library( - td::ConstBitPtr hash) override; // may throw a dictionary exception; returns nullptr if library is not found - void register_cell_load() override; - void register_cell_create() override; - bool init_cp(int new_cp); - bool set_cp(int new_cp); - void force_cp(int new_cp); - int get_cp() const { - return cp; - } - int incr_stack_trace(int v) { - return stack_trace += v; - } - long long get_steps_count() const { - return steps; - } - td::BitArray<256> get_state_hash() const; - td::BitArray<256> get_final_state_hash(int exit_code) const; - int step(); - int run(); - Stack& get_stack() { - return stack.write(); - } - const Stack& get_stack_const() const { - return *stack; - } - Ref get_stack_ref() const { - return stack; - } - Ref get_c0() const { - return cr.c[0]; - } - Ref get_c1() const { - return cr.c[1]; - } - Ref get_c2() const { - return cr.c[2]; - } - Ref get_c3() const { - return cr.c[3]; - } - Ref get_c4() const { - return cr.d[0]; - } - Ref get_c7() const { - return cr.c7; - } - Ref get_c(unsigned idx) const { - return cr.get_c(idx); - } - Ref get_d(unsigned idx) const { - return cr.get_d(idx); - } - StackEntry get(unsigned idx) const { - return cr.get(idx); - } - const VmLog& get_log() const { - return log; - } - void define_c0(Ref cont) { - cr.define_c0(std::move(cont)); - } - void set_c0(Ref cont) { - cr.set_c0(std::move(cont)); - } - void set_c1(Ref cont) { - cr.set_c1(std::move(cont)); - } - void set_c2(Ref cont) { - cr.set_c2(std::move(cont)); - } - bool set_c(unsigned idx, Ref val) { - return cr.set_c(idx, std::move(val)); - } - bool set_d(unsigned idx, Ref val) { - return cr.set_d(idx, std::move(val)); - } - void set_c4(Ref val) { - cr.set_c4(std::move(val)); - } - bool set_c7(Ref val) { - return cr.set_c7(std::move(val)); - } - bool set(unsigned idx, StackEntry val) { - return cr.set(idx, std::move(val)); - } - void set_stack(Ref new_stk) { - stack = std::move(new_stk); - } - Ref swap_stack(Ref new_stk) { - stack.swap(new_stk); - return new_stk; - } - void ensure_throw(bool cond) const { - if (!cond) { - fatal(); - } - } - void set_code(Ref _code, int _cp) { - code = std::move(_code); - force_cp(_cp); - } - Ref get_code() const { - return code; - } - void push_code() { - get_stack().push_cellslice(get_code()); - } - void adjust_cr(const ControlRegs& save) { - cr ^= save; - } - void adjust_cr(ControlRegs&& save) { - cr ^= save; - } - void preclear_cr(const ControlRegs& save) { - cr &= save; - } - int call(Ref cont); - int call(Ref cont, int pass_args, int ret_args = -1); - int jump(Ref cont); - int jump(Ref cont, int pass_args); - int ret(); - int ret(int ret_args); - int ret_alt(); - int ret_alt(int ret_args); - int repeat(Ref body, Ref after, long long count); - int again(Ref body); - int until(Ref body, Ref after); - int loop_while(Ref cond, Ref body, Ref after); - int throw_exception(int excno, StackEntry&& arg); - int throw_exception(int excno); - Ref extract_cc(int save_cr = 1, int stack_copy = -1, int cc_args = -1); - void fatal(void) const { - throw VmFatal{}; - } - int jump_to(Ref cont) { - return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this); - } - static Ref convert_code_cell(Ref code_cell); - void commit() { - cstate.c4 = cr.d[0]; - cstate.c5 = cr.d[1]; - cstate.committed = true; - } - - void set_chksig_always_succeed(bool flag) { - chksig_always_succeed = flag; - } - bool get_chksig_always_succeed() const { - return chksig_always_succeed; - } - - private: - void init_cregs(bool same_c3 = false, bool push_0 = true); -}; - -int run_vm_code(Ref _code, Ref& _stack, int flags = 0, Ref* data_ptr = nullptr, VmLog log = {}, - long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, - Ref init_c7 = {}, Ref* actions_ptr = nullptr); -int run_vm_code(Ref _code, Stack& _stack, int flags = 0, Ref* data_ptr = nullptr, VmLog log = {}, - long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, - Ref init_c7 = {}, Ref* actions_ptr = nullptr); - ControlData* force_cdata(Ref& cont); ControlRegs* force_cregs(Ref& cont); -Ref lookup_library_in(td::ConstBitPtr key, Ref lib_root); - } // namespace vm diff --git a/crypto/vm/contops.cpp b/crypto/vm/contops.cpp index 6fcda8de62..63d5570539 100644 --- a/crypto/vm/contops.cpp +++ b/crypto/vm/contops.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include #include "vm/contops.h" @@ -24,6 +24,7 @@ #include "vm/continuation.h" #include "vm/cellops.h" #include "vm/excno.hpp" +#include "vm/vm.h" namespace vm { diff --git a/crypto/vm/debugops.cpp b/crypto/vm/debugops.cpp index 5c22740a42..f8dc439687 100644 --- a/crypto/vm/debugops.cpp +++ b/crypto/vm/debugops.cpp @@ -14,15 +14,15 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include #include "vm/debugops.h" #include "vm/log.h" #include "vm/opctable.h" #include "vm/stack.hpp" -#include "vm/continuation.h" #include "vm/excno.hpp" +#include "vm/vm.h" namespace vm { diff --git a/crypto/vm/dictops.cpp b/crypto/vm/dictops.cpp index 820e1e977f..c798b33397 100644 --- a/crypto/vm/dictops.cpp +++ b/crypto/vm/dictops.cpp @@ -20,8 +20,8 @@ #include "vm/log.h" #include "vm/opctable.h" #include "vm/stack.hpp" -#include "vm/continuation.h" #include "vm/excno.hpp" +#include "vm/vm.h" #include "common/bigint.hpp" #include "common/refint.h" #include "vm/dictops.h" diff --git a/crypto/vm/opctable.cpp b/crypto/vm/opctable.cpp index fbdc2578c1..d4f0f3e977 100644 --- a/crypto/vm/opctable.cpp +++ b/crypto/vm/opctable.cpp @@ -14,14 +14,14 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include #include #include "vm/opctable.h" #include "vm/cellslice.h" #include "vm/excno.hpp" -#include "vm/continuation.h" +#include "vm/vm.h" #include #include #include diff --git a/crypto/vm/stackops.cpp b/crypto/vm/stackops.cpp index 7fdbcba044..ac7d9f88ae 100644 --- a/crypto/vm/stackops.cpp +++ b/crypto/vm/stackops.cpp @@ -14,14 +14,14 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "vm/log.h" #include "vm/stackops.h" #include "vm/opctable.h" #include "vm/stack.hpp" -#include "vm/continuation.h" #include "vm/excno.hpp" +#include "vm/vm.h" namespace vm { diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index 5dcd0d54ad..5fd8bd6405 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -14,15 +14,15 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include #include "vm/tonops.h" #include "vm/log.h" #include "vm/opctable.h" #include "vm/stack.hpp" -#include "vm/continuation.h" #include "vm/excno.hpp" +#include "vm/vm.h" #include "vm/dict.h" #include "Ed25519.h" @@ -397,6 +397,83 @@ void register_ton_crypto_ops(OpcodeTable& cp0) { .insert(OpcodeInstr::mksimple(0xf911, 16, "CHKSIGNS", std::bind(exec_ed25519_check_signature, _1, true))); } +struct VmStorageStat { + td::uint64 cells{0}, bits{0}, refs{0}, limit; + td::HashSet visited; + VmStorageStat(td::uint64 _limit) : limit(_limit) { + } + bool add_storage(Ref cell); + bool add_storage(const CellSlice& cs); + bool check_visited(const CellHash& cell_hash) { + return visited.insert(cell_hash).second; + } + bool check_visited(const Ref& cell) { + return check_visited(cell->get_hash()); + } +}; + +bool VmStorageStat::add_storage(Ref cell) { + if (cell.is_null() || !check_visited(cell)) { + return true; + } + if (cells >= limit) { + return false; + } + ++cells; + bool special; + auto cs = load_cell_slice_special(std::move(cell), special); + return cs.is_valid() && add_storage(std::move(cs)); +} + +bool VmStorageStat::add_storage(const CellSlice& cs) { + bits += cs.size(); + refs += cs.size_refs(); + for (unsigned i = 0; i < cs.size_refs(); i++) { + if (!add_storage(cs.prefetch_ref(i))) { + return false; + } + } + return true; +} + +int exec_compute_data_size(VmState* st, int mode) { + VM_LOG(st) << (mode & 2 ? 'S' : 'C') << "DATASIZE" << (mode & 1 ? "Q" : ""); + Stack& stack = st->get_stack(); + stack.check_underflow(2); + auto bound = stack.pop_int(); + Ref cell; + Ref cs; + if (mode & 2) { + cs = stack.pop_cellslice(); + } else { + cell = stack.pop_maybe_cell(); + } + if (!bound->is_valid() || bound->sgn() < 0) { + throw VmError{Excno::range_chk, "finite non-negative integer expected"}; + } + VmStorageStat stat{bound->unsigned_fits_bits(63) ? bound->to_long() : (1ULL << 63) - 1}; + bool ok = (mode & 2 ? stat.add_storage(cs.write()) : stat.add_storage(std::move(cell))); + if (ok) { + stack.push_smallint(stat.cells); + stack.push_smallint(stat.bits); + stack.push_smallint(stat.refs); + } else if (!(mode & 1)) { + throw VmError{Excno::cell_ov, "scanned too many cells"}; + } + if (mode & 1) { + stack.push_bool(ok); + } + return 0; +} + +void register_ton_misc_ops(OpcodeTable& cp0) { + using namespace std::placeholders; + cp0.insert(OpcodeInstr::mksimple(0xf940, 16, "CDATASIZEQ", std::bind(exec_compute_data_size, _1, 1))) + .insert(OpcodeInstr::mksimple(0xf941, 16, "CDATASIZE", std::bind(exec_compute_data_size, _1, 0))) + .insert(OpcodeInstr::mksimple(0xf942, 16, "SDATASIZEQ", std::bind(exec_compute_data_size, _1, 3))) + .insert(OpcodeInstr::mksimple(0xf943, 16, "SDATASIZE", std::bind(exec_compute_data_size, _1, 2))); +} + int exec_load_var_integer(VmState* st, int len_bits, bool sgnd, bool quiet) { if (len_bits == 4 && !sgnd) { VM_LOG(st) << "execute LDGRAMS" << (quiet ? "Q" : ""); @@ -822,6 +899,7 @@ void register_ton_ops(OpcodeTable& cp0) { register_prng_ops(cp0); register_ton_config_ops(cp0); register_ton_crypto_ops(cp0); + register_ton_misc_ops(cp0); register_ton_currency_address_ops(cp0); register_ton_message_ops(cp0); } diff --git a/crypto/vm/tupleops.cpp b/crypto/vm/tupleops.cpp index de4de83465..96f78fe39e 100644 --- a/crypto/vm/tupleops.cpp +++ b/crypto/vm/tupleops.cpp @@ -14,14 +14,14 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "vm/log.h" #include "vm/stackops.h" #include "vm/opctable.h" #include "vm/stack.hpp" -#include "vm/continuation.h" #include "vm/excno.hpp" +#include "vm/vm.h" namespace vm { diff --git a/crypto/vm/vm.cpp b/crypto/vm/vm.cpp new file mode 100644 index 0000000000..cba973bc77 --- /dev/null +++ b/crypto/vm/vm.cpp @@ -0,0 +1,593 @@ +/* + 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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ +#include "vm/dispatch.h" +#include "vm/continuation.h" +#include "vm/dict.h" +#include "vm/log.h" +#include "vm/vm.h" + +namespace vm { + +VmState::VmState() : cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), quit1(true, 1) { + ensure_throw(init_cp(0)); + init_cregs(); +} + +VmState::VmState(Ref _code) + : code(std::move(_code)), cp(-1), dispatch(&dummy_dispatch_table), quit0(true, 0), quit1(true, 1) { + ensure_throw(init_cp(0)); + init_cregs(); +} + +VmState::VmState(Ref _code, Ref _stack, int flags, Ref _data, VmLog log, + std::vector> _libraries, Ref init_c7) + : code(std::move(_code)) + , stack(std::move(_stack)) + , cp(-1) + , dispatch(&dummy_dispatch_table) + , quit0(true, 0) + , quit1(true, 1) + , log(log) + , libraries(std::move(_libraries)) { + ensure_throw(init_cp(0)); + set_c4(std::move(_data)); + if (init_c7.not_null()) { + set_c7(std::move(init_c7)); + } + init_cregs(flags & 1, flags & 2); +} + +VmState::VmState(Ref _code, Ref _stack, const GasLimits& gas, int flags, Ref _data, VmLog log, + std::vector> _libraries, Ref init_c7) + : code(std::move(_code)) + , stack(std::move(_stack)) + , cp(-1) + , dispatch(&dummy_dispatch_table) + , quit0(true, 0) + , quit1(true, 1) + , log(log) + , gas(gas) + , libraries(std::move(_libraries)) { + ensure_throw(init_cp(0)); + set_c4(std::move(_data)); + if (init_c7.not_null()) { + set_c7(std::move(init_c7)); + } + init_cregs(flags & 1, flags & 2); +} + +Ref VmState::convert_code_cell(Ref code_cell) { + if (code_cell.is_null()) { + return {}; + } + Ref csr{true, NoVmOrd(), code_cell}; + if (csr->is_valid()) { + return csr; + } + return load_cell_slice_ref(CellBuilder{}.store_ref(std::move(code_cell)).finalize()); +} + +bool VmState::init_cp(int new_cp) { + const DispatchTable* dt = DispatchTable::get_table(new_cp); + if (dt) { + cp = new_cp; + dispatch = dt; + return true; + } else { + return false; + } +} + +bool VmState::set_cp(int new_cp) { + return new_cp == cp || init_cp(new_cp); +} + +void VmState::force_cp(int new_cp) { + if (!set_cp(new_cp)) { + throw VmError{Excno::inv_opcode, "unsupported codepage"}; + } +} + +// simple call to a continuation cont +int VmState::call(Ref cont) { + const ControlData* cont_data = cont->get_cdata(); + if (cont_data) { + if (cont_data->save.c[0].not_null()) { + // call reduces to a jump + return jump(std::move(cont)); + } + if (cont_data->stack.not_null() || cont_data->nargs >= 0) { + // if cont has non-empty stack or expects fixed number of arguments, call is not simple + return call(std::move(cont), -1, -1); + } + // create return continuation, to be stored into new c0 + Ref ret = Ref{true, std::move(code), cp}; + ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0])); + cr.set_c0( + std::move(ret)); // set c0 to its final value before switching to cont; notice that cont.save.c0 is not set + return jump_to(std::move(cont)); + } + // create return continuation, to be stored into new c0 + Ref ret = Ref{true, std::move(code), cp}; + ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0])); + // general implementation of a simple call + cr.set_c0(std::move(ret)); // set c0 to its final value before switching to cont; notice that cont.save.c0 is not set + return jump_to(std::move(cont)); +} + +// call with parameters to continuation cont +int VmState::call(Ref cont, int pass_args, int ret_args) { + const ControlData* cont_data = cont->get_cdata(); + if (cont_data) { + if (cont_data->save.c[0].not_null()) { + // call reduces to a jump + return jump(std::move(cont), pass_args); + } + int depth = stack->depth(); + if (pass_args > depth || cont_data->nargs > depth) { + throw VmError{Excno::stk_und, "stack underflow while calling a continuation: not enough arguments on stack"}; + } + if (cont_data->nargs > pass_args && pass_args >= 0) { + throw VmError{Excno::stk_und, + "stack underflow while calling a closure continuation: not enough arguments passed"}; + } + auto old_c0 = std::move(cr.c[0]); + // optimization(?): decrease refcnts of unused continuations in c[i] as early as possible + preclear_cr(cont_data->save); + // no exceptions should be thrown after this point + int copy = cont_data->nargs, skip = 0; + if (pass_args >= 0) { + if (copy >= 0) { + skip = pass_args - copy; + } else { + copy = pass_args; + } + } + // copy=-1 : pass whole stack, else pass top `copy` elements, drop next `skip` elements. + Ref new_stk; + if (cont_data->stack.not_null() && !cont_data->stack->is_empty()) { + // `cont` already has a stack, create resulting stack from it + if (copy < 0) { + copy = stack->depth(); + } + if (cont->is_unique()) { + // optimization: avoid copying stack if we hold the only copy of `cont` + new_stk = std::move(cont.unique_write().get_cdata()->stack); + } else { + new_stk = cont_data->stack; + } + new_stk.write().move_from_stack(get_stack(), copy); + if (skip > 0) { + get_stack().pop_many(skip); + } + } else if (copy >= 0) { + new_stk = get_stack().split_top(copy, skip); + } else { + new_stk = std::move(stack); + stack.clear(); + } + // create return continuation using the remainder of current stack + Ref ret = Ref{true, std::move(code), cp, std::move(stack), ret_args}; + ret.unique_write().get_cdata()->save.set_c0(std::move(old_c0)); + Ref ord_cont = static_cast>(cont); + set_stack(std::move(new_stk)); + cr.set_c0(std::move(ret)); // ??? if codepage of code in ord_cont is unknown, will end up with incorrect c0 + return jump_to(std::move(cont)); + } else { + // have no continuation data, situation is somewhat simpler + int depth = stack->depth(); + if (pass_args > depth) { + throw VmError{Excno::stk_und, "stack underflow while calling a continuation: not enough arguments on stack"}; + } + // create new stack from the top `pass_args` elements of the current stack + Ref new_stk = (pass_args >= 0 ? get_stack().split_top(pass_args) : std::move(stack)); + // create return continuation using the remainder of the current stack + Ref ret = Ref{true, std::move(code), cp, std::move(stack), ret_args}; + ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0])); + set_stack(std::move(new_stk)); + cr.set_c0(std::move(ret)); // ??? if codepage of code in ord_cont is unknown, will end up with incorrect c0 + return jump_to(std::move(cont)); + } +} + +// simple jump to continuation cont +int VmState::jump(Ref cont) { + const ControlData* cont_data = cont->get_cdata(); + if (cont_data && (cont_data->stack.not_null() || cont_data->nargs >= 0)) { + // if cont has non-empty stack or expects fixed number of arguments, jump is not simple + return jump(std::move(cont), -1); + } else { + return jump_to(std::move(cont)); + } +} + +// general jump to continuation cont +int VmState::jump(Ref cont, int pass_args) { + const ControlData* cont_data = cont->get_cdata(); + if (cont_data) { + // first do the checks + int depth = stack->depth(); + if (pass_args > depth || cont_data->nargs > depth) { + throw VmError{Excno::stk_und, "stack underflow while jumping to a continuation: not enough arguments on stack"}; + } + if (cont_data->nargs > pass_args && pass_args >= 0) { + throw VmError{Excno::stk_und, + "stack underflow while jumping to closure continuation: not enough arguments passed"}; + } + // optimization(?): decrease refcnts of unused continuations in c[i] as early as possible + preclear_cr(cont_data->save); + // no exceptions should be thrown after this point + int copy = cont_data->nargs; + if (pass_args >= 0 && copy < 0) { + copy = pass_args; + } + // copy=-1 : pass whole stack, else pass top `copy` elements, drop the remainder. + if (cont_data->stack.not_null() && !cont_data->stack->is_empty()) { + // `cont` already has a stack, create resulting stack from it + if (copy < 0) { + copy = get_stack().depth(); + } + Ref new_stk; + if (cont->is_unique()) { + // optimization: avoid copying the stack if we hold the only copy of `cont` + new_stk = std::move(cont.unique_write().get_cdata()->stack); + } else { + new_stk = cont_data->stack; + } + new_stk.write().move_from_stack(get_stack(), copy); + set_stack(std::move(new_stk)); + } else { + if (copy >= 0) { + get_stack().drop_bottom(stack->depth() - copy); + } + } + return jump_to(std::move(cont)); + } else { + // have no continuation data, situation is somewhat simpler + if (pass_args >= 0) { + int depth = get_stack().depth(); + if (pass_args > depth) { + throw VmError{Excno::stk_und, "stack underflow while jumping to a continuation: not enough arguments on stack"}; + } + get_stack().drop_bottom(depth - pass_args); + } + return jump_to(std::move(cont)); + } +} + +int VmState::ret() { + Ref cont = quit0; + cont.swap(cr.c[0]); + return jump(std::move(cont)); +} + +int VmState::ret(int ret_args) { + Ref cont = quit0; + cont.swap(cr.c[0]); + return jump(std::move(cont), ret_args); +} + +int VmState::ret_alt() { + Ref cont = quit1; + cont.swap(cr.c[1]); + return jump(std::move(cont)); +} + +int VmState::ret_alt(int ret_args) { + Ref cont = quit1; + cont.swap(cr.c[1]); + return jump(std::move(cont), ret_args); +} + +Ref VmState::extract_cc(int save_cr, int stack_copy, int cc_args) { + Ref new_stk; + if (stack_copy < 0 || stack_copy == stack->depth()) { + new_stk = std::move(stack); + stack.clear(); + } else if (stack_copy > 0) { + stack->check_underflow(stack_copy); + new_stk = get_stack().split_top(stack_copy); + } else { + new_stk = Ref{true}; + } + Ref cc = Ref{true, std::move(code), cp, std::move(stack), cc_args}; + stack = std::move(new_stk); + if (save_cr & 7) { + ControlData* cdata = cc.unique_write().get_cdata(); + if (save_cr & 1) { + cdata->save.set_c0(std::move(cr.c[0])); + cr.set_c0(quit0); + } + if (save_cr & 2) { + cdata->save.set_c1(std::move(cr.c[1])); + cr.set_c1(quit1); + } + if (save_cr & 4) { + cdata->save.set_c2(std::move(cr.c[2])); + // cr.set_c2(Ref{true}); + } + } + return cc; +} + +int VmState::throw_exception(int excno) { + Stack& stack_ref = get_stack(); + stack_ref.clear(); + stack_ref.push_smallint(0); + stack_ref.push_smallint(excno); + code.clear(); + consume_gas(exception_gas_price); + return jump(get_c2()); +} + +int VmState::throw_exception(int excno, StackEntry&& arg) { + Stack& stack_ref = get_stack(); + stack_ref.clear(); + stack_ref.push(std::move(arg)); + stack_ref.push_smallint(excno); + code.clear(); + consume_gas(exception_gas_price); + return jump(get_c2()); +} + +void GasLimits::gas_exception() const { + throw VmNoGas{}; +} + +void GasLimits::set_limits(long long _max, long long _limit, long long _credit) { + gas_max = _max; + gas_limit = _limit; + gas_credit = _credit; + change_base(_limit + _credit); +} + +void GasLimits::change_limit(long long _limit) { + _limit = std::min(std::max(_limit, 0LL), gas_max); + gas_credit = 0; + gas_limit = _limit; + change_base(_limit); +} + +bool VmState::set_gas_limits(long long _max, long long _limit, long long _credit) { + gas.set_limits(_max, _limit, _credit); + return true; +} + +void VmState::change_gas_limit(long long new_limit) { + VM_LOG(this) << "changing gas limit to " << std::min(new_limit, gas.gas_max); + gas.change_limit(new_limit); +} + +int VmState::step() { + assert(!code.is_null()); + //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, 3); + } + ++steps; + if (code->size()) { + return dispatch->dispatch(this, code.write()); + } else if (code->size_refs()) { + VM_LOG(this) << "execute implicit JMPREF\n"; + Ref cont = Ref{true, load_cell_slice_ref(code->prefetch_ref()), get_cp()}; + return jump(std::move(cont)); + } else { + VM_LOG(this) << "execute implicit RET\n"; + return ret(); + } +} + +int VmState::run() { + if (code.is_null()) { + throw VmError{Excno::fatal, "cannot run an uninitialized VM"}; + } + int res; + Guard guard(this); + do { + // LOG(INFO) << "[BS] data cells: " << DataCell::get_total_data_cells(); + try { + try { + res = step(); + gas.check(); + } catch (vm::CellBuilder::CellWriteError) { + throw VmError{Excno::cell_ov}; + } catch (vm::CellBuilder::CellCreateError) { + throw VmError{Excno::cell_ov}; + } catch (vm::CellSlice::CellReadError) { + throw VmError{Excno::cell_und}; + } + } catch (const VmError& vme) { + VM_LOG(this) << "handling exception code " << vme.get_errno() << ": " << vme.get_msg(); + try { + // LOG(INFO) << "[EX] data cells: " << DataCell::get_total_data_cells(); + ++steps; + res = throw_exception(vme.get_errno()); + } catch (const VmError& vme2) { + VM_LOG(this) << "exception " << vme2.get_errno() << " while handling exception: " << vme.get_msg(); + // LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells(); + return ~vme2.get_errno(); + } + } catch (VmNoGas vmoog) { + ++steps; + VM_LOG(this) << "unhandled out-of-gas exception: gas consumed=" << gas.gas_consumed() + << ", limit=" << gas.gas_limit; + get_stack().clear(); + get_stack().push_smallint(gas.gas_consumed()); + return vmoog.get_errno(); // no ~ for unhandled exceptions (to make their faking impossible) + } + } while (!res); + // LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells(); + if ((res | 1) == -1) { + commit(); + } + return res; +} + +ControlData* force_cdata(Ref& cont) { + if (!cont->get_cdata()) { + cont = Ref{true, cont}; + return cont.unique_write().get_cdata(); + } else { + return cont.write().get_cdata(); + } +} + +ControlRegs* force_cregs(Ref& cont) { + return &force_cdata(cont)->save; +} + +int run_vm_code(Ref code, Ref& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, + GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr) { + VmState vm{code, + std::move(stack), + gas_limits ? *gas_limits : GasLimits{}, + flags, + data_ptr ? *data_ptr : Ref{}, + log, + std::move(libraries), + std::move(init_c7)}; + int res = vm.run(); + stack = vm.get_stack_ref(); + if (vm.committed() && data_ptr) { + *data_ptr = vm.get_committed_state().c4; + } + if (vm.committed() && actions_ptr) { + *actions_ptr = vm.get_committed_state().c5; + } + if (steps) { + *steps = vm.get_steps_count(); + } + if (gas_limits) { + *gas_limits = vm.get_gas_limits(); + LOG(INFO) << "steps: " << vm.get_steps_count() << " gas: used=" << gas_limits->gas_consumed() + << ", max=" << gas_limits->gas_max << ", limit=" << gas_limits->gas_limit + << ", credit=" << gas_limits->gas_credit; + } + if ((vm.get_log().log_mask & vm::VmLog::DumpStack) != 0) { + VM_LOG(&vm) << "BEGIN_STACK_DUMP"; + for (int i = stack->depth(); i > 0; i--) { + VM_LOG(&vm) << (*stack)[i - 1].to_string(); + } + VM_LOG(&vm) << "END_STACK_DUMP"; + } + + return ~res; +} + +int run_vm_code(Ref code, Stack& stack, int flags, Ref* data_ptr, VmLog log, long long* steps, + GasLimits* gas_limits, std::vector> libraries, Ref init_c7, Ref* actions_ptr) { + Ref stk{true}; + stk.unique_write().set_contents(std::move(stack)); + stack.clear(); + int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7), + actions_ptr); + CHECK(stack.is_unique()); + if (stk.is_null()) { + stack.clear(); + } else if (&(*stk) != &stack) { + VmState* st = nullptr; + if (stk->is_unique()) { + VM_LOG(st) << "move resulting stack (" << stk->depth() << " entries)"; + stack.set_contents(std::move(stk.unique_write())); + } else { + VM_LOG(st) << "copying resulting stack (" << stk->depth() << " entries)"; + stack.set_contents(*stk); + } + } + return res; +} + +// may throw a dictionary exception; returns nullptr if library is not found in context +Ref VmState::load_library(td::ConstBitPtr hash) { + std::unique_ptr tmp_ctx; + // install temporary dummy vm state interface to prevent charging for cell load operations during library lookup + VmStateInterface::Guard(tmp_ctx.get()); + for (const auto& lib_collection : libraries) { + auto lib = lookup_library_in(hash, lib_collection); + if (lib.not_null()) { + return lib; + } + } + return {}; +} + +bool VmState::register_library_collection(Ref lib) { + if (lib.is_null()) { + return true; + } + libraries.push_back(std::move(lib)); + return true; +} + +void VmState::register_cell_load(const CellHash& cell_hash) { + if (cell_load_gas_price == cell_reload_gas_price) { + consume_gas(cell_load_gas_price); + } else { + auto ok = loaded_cells.insert(cell_hash); // check whether this is the first time this cell is loaded + if (ok.second) { + loaded_cells_count++; + } + consume_gas(ok.second ? cell_load_gas_price : cell_reload_gas_price); + } +} + +void VmState::register_cell_create() { + consume_gas(cell_create_gas_price); +} + +td::BitArray<256> VmState::get_state_hash() const { + // TODO: implement properly, by serializing the stack etc, and computing the Merkle hash + td::BitArray<256> res; + res.clear(); + return res; +} + +td::BitArray<256> VmState::get_final_state_hash(int exit_code) const { + // TODO: implement properly, by serializing the stack etc, and computing the Merkle hash + td::BitArray<256> res; + res.clear(); + return res; +} + +Ref lookup_library_in(td::ConstBitPtr key, vm::Dictionary& dict) { + try { + auto val = dict.lookup(key, 256); + if (val.is_null() || !val->have_refs()) { + return {}; + } + auto root = val->prefetch_ref(); + if (root.not_null() && !root->get_hash().bits().compare(key, 256)) { + return root; + } + return {}; + } catch (vm::VmError) { + return {}; + } +} + +Ref lookup_library_in(td::ConstBitPtr key, Ref lib_root) { + if (lib_root.is_null()) { + return lib_root; + } + vm::Dictionary dict{std::move(lib_root), 256}; + return lookup_library_in(key, dict); +} + +} // namespace vm diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h new file mode 100644 index 0000000000..eac5f2fd05 --- /dev/null +++ b/crypto/vm/vm.h @@ -0,0 +1,320 @@ +/* + 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 . + + Copyright 2017-2020 Telegram Systems LLP +*/ +#pragma once + +#include "common/refcnt.hpp" +#include "vm/cellslice.h" +#include "vm/stack.hpp" +#include "vm/vmstate.h" +#include "vm/log.h" +#include "vm/continuation.h" +#include "td/utils/HashSet.h" + +namespace vm { + +using td::Ref; +struct GasLimits { + static constexpr long long infty = (1ULL << 63) - 1; + long long gas_max, gas_limit, gas_credit, gas_remaining, gas_base; + GasLimits() : gas_max(infty), gas_limit(infty), gas_credit(0), gas_remaining(infty), gas_base(infty) { + } + GasLimits(long long _limit, long long _max = infty, long long _credit = 0) + : gas_max(_max) + , gas_limit(_limit) + , gas_credit(_credit) + , gas_remaining(_limit + _credit) + , gas_base(gas_remaining) { + } + long long gas_consumed() const { + return gas_base - gas_remaining; + } + void set_limits(long long _max, long long _limit, long long _credit = 0); + void change_base(long long _base) { + gas_remaining += _base - gas_base; + gas_base = _base; + } + void change_limit(long long _limit); + void consume(long long amount) { + // LOG(DEBUG) << "consume " << amount << " gas (" << gas_remaining << " remaining)"; + gas_remaining -= amount; + } + bool try_consume(long long amount) { + // LOG(DEBUG) << "try consume " << amount << " gas (" << gas_remaining << " remaining)"; + return (gas_remaining -= amount) >= 0; + } + void gas_exception() const; + void gas_exception(bool cond) const { + if (!cond) { + gas_exception(); + } + } + void consume_chk(long long amount) { + gas_exception(try_consume(amount)); + } + void check() const { + gas_exception(gas_remaining >= 0); + } + bool final_ok() const { + return gas_remaining >= gas_credit; + } +}; + +struct CommittedState { + Ref c4, c5; + bool committed{false}; +}; + +class VmState final : public VmStateInterface { + Ref code; + Ref stack; + ControlRegs cr; + CommittedState cstate; + int cp; + long long steps{0}; + const DispatchTable* dispatch; + Ref quit0, quit1; + VmLog log; + GasLimits gas; + std::vector> libraries; + td::HashSet loaded_cells; + td::int64 loaded_cells_count{0}; + int stack_trace{0}, debug_off{0}; + bool chksig_always_succeed{false}; + + public: + enum { + cell_load_gas_price = 100, + cell_reload_gas_price = 25, + cell_create_gas_price = 500, + exception_gas_price = 50, + tuple_entry_gas_price = 1 + }; + VmState(); + VmState(Ref _code); + VmState(Ref _code, Ref _stack, int flags = 0, Ref _data = {}, VmLog log = {}, + std::vector> _libraries = {}, Ref init_c7 = {}); + VmState(Ref _code, Ref _stack, const GasLimits& _gas, int flags = 0, Ref _data = {}, + VmLog log = {}, std::vector> _libraries = {}, Ref init_c7 = {}); + template + VmState(Ref code_cell, Args&&... args) + : VmState(convert_code_cell(std::move(code_cell)), std::forward(args)...) { + } + VmState(const VmState&) = delete; + VmState(VmState&&) = delete; + VmState& operator=(const VmState&) = delete; + VmState& operator=(VmState&&) = delete; + bool set_gas_limits(long long _max, long long _limit, long long _credit = 0); + bool final_gas_ok() const { + return gas.final_ok(); + } + long long gas_consumed() const { + return gas.gas_consumed(); + } + bool committed() const { + return cstate.committed; + } + const CommittedState& get_committed_state() const { + return cstate; + } + void consume_gas(long long amount) { + gas.consume(amount); + } + void consume_tuple_gas(unsigned tuple_len) { + consume_gas(tuple_len * tuple_entry_gas_price); + } + void consume_tuple_gas(const Ref& tup) { + if (tup.not_null()) { + consume_tuple_gas((unsigned)tup->size()); + } + } + GasLimits get_gas_limits() const { + return gas; + } + void change_gas_limit(long long new_limit); + template + void check_underflow(Args... args) { + stack->check_underflow(args...); + } + bool register_library_collection(Ref lib); + Ref load_library( + td::ConstBitPtr hash) override; // may throw a dictionary exception; returns nullptr if library is not found + void register_cell_load(const CellHash& cell_hash) override; + void register_cell_create() override; + bool init_cp(int new_cp); + bool set_cp(int new_cp); + void force_cp(int new_cp); + int get_cp() const { + return cp; + } + int incr_stack_trace(int v) { + return stack_trace += v; + } + long long get_steps_count() const { + return steps; + } + td::BitArray<256> get_state_hash() const; + td::BitArray<256> get_final_state_hash(int exit_code) const; + int step(); + int run(); + Stack& get_stack() { + return stack.write(); + } + const Stack& get_stack_const() const { + return *stack; + } + Ref get_stack_ref() const { + return stack; + } + Ref get_c0() const { + return cr.c[0]; + } + Ref get_c1() const { + return cr.c[1]; + } + Ref get_c2() const { + return cr.c[2]; + } + Ref get_c3() const { + return cr.c[3]; + } + Ref get_c4() const { + return cr.d[0]; + } + Ref get_c7() const { + return cr.c7; + } + Ref get_c(unsigned idx) const { + return cr.get_c(idx); + } + Ref get_d(unsigned idx) const { + return cr.get_d(idx); + } + StackEntry get(unsigned idx) const { + return cr.get(idx); + } + const VmLog& get_log() const { + return log; + } + void define_c0(Ref cont) { + cr.define_c0(std::move(cont)); + } + void set_c0(Ref cont) { + cr.set_c0(std::move(cont)); + } + void set_c1(Ref cont) { + cr.set_c1(std::move(cont)); + } + void set_c2(Ref cont) { + cr.set_c2(std::move(cont)); + } + bool set_c(unsigned idx, Ref val) { + return cr.set_c(idx, std::move(val)); + } + bool set_d(unsigned idx, Ref val) { + return cr.set_d(idx, std::move(val)); + } + void set_c4(Ref val) { + cr.set_c4(std::move(val)); + } + bool set_c7(Ref val) { + return cr.set_c7(std::move(val)); + } + bool set(unsigned idx, StackEntry val) { + return cr.set(idx, std::move(val)); + } + void set_stack(Ref new_stk) { + stack = std::move(new_stk); + } + Ref swap_stack(Ref new_stk) { + stack.swap(new_stk); + return new_stk; + } + void ensure_throw(bool cond) const { + if (!cond) { + fatal(); + } + } + void set_code(Ref _code, int _cp) { + code = std::move(_code); + force_cp(_cp); + } + Ref get_code() const { + return code; + } + void push_code() { + get_stack().push_cellslice(get_code()); + } + void adjust_cr(const ControlRegs& save) { + cr ^= save; + } + void adjust_cr(ControlRegs&& save) { + cr ^= save; + } + void preclear_cr(const ControlRegs& save) { + cr &= save; + } + int call(Ref cont); + int call(Ref cont, int pass_args, int ret_args = -1); + int jump(Ref cont); + int jump(Ref cont, int pass_args); + int ret(); + int ret(int ret_args); + int ret_alt(); + int ret_alt(int ret_args); + int repeat(Ref body, Ref after, long long count); + int again(Ref body); + int until(Ref body, Ref after); + int loop_while(Ref cond, Ref body, Ref after); + int throw_exception(int excno, StackEntry&& arg); + int throw_exception(int excno); + Ref extract_cc(int save_cr = 1, int stack_copy = -1, int cc_args = -1); + void fatal(void) const { + throw VmFatal{}; + } + int jump_to(Ref cont) { + return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this); + } + static Ref convert_code_cell(Ref code_cell); + void commit() { + cstate.c4 = cr.d[0]; + cstate.c5 = cr.d[1]; + cstate.committed = true; + } + + void set_chksig_always_succeed(bool flag) { + chksig_always_succeed = flag; + } + bool get_chksig_always_succeed() const { + return chksig_always_succeed; + } + + private: + void init_cregs(bool same_c3 = false, bool push_0 = true); +}; + +int run_vm_code(Ref _code, Ref& _stack, int flags = 0, Ref* data_ptr = nullptr, VmLog log = {}, + long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, + Ref init_c7 = {}, Ref* actions_ptr = nullptr); +int run_vm_code(Ref _code, Stack& _stack, int flags = 0, Ref* data_ptr = nullptr, VmLog log = {}, + long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector> libraries = {}, + Ref init_c7 = {}, Ref* actions_ptr = nullptr); + +Ref lookup_library_in(td::ConstBitPtr key, Ref lib_root); + +} // namespace vm diff --git a/crypto/vm/vmstate.h b/crypto/vm/vmstate.h index 312b662688..39e5d48ae8 100644 --- a/crypto/vm/vmstate.h +++ b/crypto/vm/vmstate.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "common/refcnt.hpp" @@ -32,7 +32,7 @@ class VmStateInterface : public td::Context { td::ConstBitPtr hash) { // may throw a dictionary exception; returns nullptr if library is not found return {}; } - virtual void register_cell_load(){}; + virtual void register_cell_load(const CellHash& cell_hash){}; virtual void register_cell_create(){}; }; diff --git a/dht/dht-bucket.cpp b/dht/dht-bucket.cpp index bc30a45a54..4f9d75ebc5 100644 --- a/dht/dht-bucket.cpp +++ b/dht/dht-bucket.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "td/utils/tl_storers.h" #include "td/utils/crypto.h" diff --git a/dht/dht-bucket.hpp b/dht/dht-bucket.hpp index 3d16a5d872..812f670d76 100644 --- a/dht/dht-bucket.hpp +++ b/dht/dht-bucket.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/dht/dht-in.hpp b/dht/dht-in.hpp index 5407c824c5..c4f67819dd 100644 --- a/dht/dht-in.hpp +++ b/dht/dht-in.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/dht/dht-query.cpp b/dht/dht-query.cpp index beefbc5c49..7e1f1b925a 100644 --- a/dht/dht-query.cpp +++ b/dht/dht-query.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "dht.hpp" diff --git a/dht/dht-query.hpp b/dht/dht-query.hpp index af39f7cff2..aa607f56e6 100644 --- a/dht/dht-query.hpp +++ b/dht/dht-query.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/dht/dht-remote-node.cpp b/dht/dht-remote-node.cpp index 381a332e3c..f1ea2197ed 100644 --- a/dht/dht-remote-node.cpp +++ b/dht/dht-remote-node.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "dht.hpp" diff --git a/dht/dht-remote-node.hpp b/dht/dht-remote-node.hpp index ed37415c25..e65c0429d4 100644 --- a/dht/dht-remote-node.hpp +++ b/dht/dht-remote-node.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/dht/dht.h b/dht/dht.h index c21a2e8558..eacb2e4b54 100644 --- a/dht/dht.h +++ b/dht/dht.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/dht/dht.hpp b/dht/dht.hpp index 5e83e548c8..b8d73c8ede 100644 --- a/dht/dht.hpp +++ b/dht/dht.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once diff --git a/doc/TonSites-HOWTO b/doc/TonSites-HOWTO new file mode 100644 index 0000000000..9d4005f0e5 --- /dev/null +++ b/doc/TonSites-HOWTO @@ -0,0 +1,78 @@ +The aim of this document is to provide a gentle introduction into TON Sites, which are (TON) Web sites accessed through the TON Network. TON Sites may be used as a convenient entry point for other TON Services. In particular, HTML pages downloaded from TON Sites may contain links to ton://... URIs representing payments that can be performed by the user by clicking to the link, provided a TON Wallet is installed on the user's device. + +From the technical perspective, TON Sites are very much like the usual Web sites, but they are accessed through the TON Network (which is an overlay network inside the Internet) instead of the Internet. More specifically, they have an ADNL address (instead of a more customary IPv4 or IPv6 address), and they accept HTTP queries via RLDP protocol (which is a higher-level RPC protocol built upon ADNL, the main protocol of TON Network) instead of the usual TCP/IP. All encryption is handled by ADNL, so there is no need to use HTTPS (i.e., TLS). + +In order to access existing and create new TON Sites one needs special gateways between the "ordinary" internet and the TON Network. Essentially, TON Sites are accessed with the aid of a HTTP->RLDP proxy running locally on the client's machine, and they are created by means of a reverse RLDP->HTTP proxy running on a remote web server. + +1. Compiling RLDP-HTTP Proxy +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The RLDP-HTTP Proxy is a special utility specially designed for accessing and creating TON Sites. Its current (alpha) version is a part of the general TON Blockchain source tree, available at GitHub repository ton-blockchain/ton. In order to compile the RLDP-HTTP Proxy, follow the instructions outlined in README and Validator-HOWTO. The Proxy binary will be located as + + rldp-http-proxy/rldp-http-proxy + +in the build directory. Alternatively, you may want to build just the Proxy instead of building all TON Blockchain projects. This can be done by invoking + + cmake --build . --target rldp-http-proxy + +in the build directory. + +2. Running RLDP-HTTP Proxy to access TON Sites +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In order to access existing TON Sites, you need a running instance of RLDP-HTTP Proxy on your computer. It can be invoked as follows: + + rldp-http-proxy/rldp-http-proxy -p 8080 -c 3333 -C ton-global.config.json + +or + + rldp-http-proxy/rldp-http-proxy -p 8080 -a :3333 -C ton-global.config.json + +where is your public IPv4 address, provided you have one on your home computer. The TON Network global configuration file `ton-global.config.json` can be downloaded at https://test.ton.org/ton-global.config.json : + + wget https://test.ton.org/ton-global.config.json + +In the above example, 8080 is the TCP port that will be listened to at localhost for incoming HTTP queries, and 3333 is the UDP port that will be used for all outbound and inbound RLDP and ADNL activity, i.e., for connecting to the TON Sites via the TON Network. + +If you have done everything correctly, the Proxy will not terminate, but it will continue running in the terminal. It can be used now for accessing TON Sites. When you don't need it anymore, you can terminate it by pressing Ctrl-C, or simply by closing the terminal window. + +3. Accessing TON Sites +~~~~~~~~~~~~~~~~~~~~~~ + +Now suppose that you have a running instance of the RLDP-HTTP Proxy running on your computer and listening on localhost:8080 for inbound TCP connections, as explained above in Section 2. + +A simple test that everything is working property may be performed using programs such as Curl or WGet. For example, + + curl -x 127.0.0.1:8080 http://test.ton + +attempts to download the main page of (TON) Site `test.ton` using the proxy at `127.0.0.1:8080`. If the proxy is up and running, you'll see something like + + +

TON Blockchain Test Network — files and resources

+

News

+

params: "; + for (int i : params_) { + auto value = config->get_config_param(i); + if (value.not_null()) { + A << "" << i << " "; + } + } + A << "