diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index 074a5d4b..65537a1a 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 f2da3409..a091c049 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 1adb8f32..b2725cda 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 724ef812..4a16aca6 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 ee8a59af..d354dd3f 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 7f1a376b..71624798 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 8c453cde..15c87b38 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 b8f0be72..9651aa53 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 e851016c..f0c0c411 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 187519c9..658df598 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 51b2df8c..506aa492 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 a954ff14..39243632 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 3d36643b..a131d8c5 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 + +{ 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-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 -{ ."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 - +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 b23f941a..1568e198 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 e5cf6ad4..c3d628d4 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 0ec52f58..f129e0b3 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 bbf3adf7..8dfbbb1f 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 b5d51c31..ba9c783f 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 0095018e..e709725c 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 bb550a69..74a78970 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 433344f9..da081469 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 83ba6e10..3946f10d 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 c6c9d320..a5ff599b 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 b2ba081f..f3c80504 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 507f98df..1fce43f2 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 4743253a..a259c505 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 c110abbe..ccb0b703 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 71919d21..405efdfb 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); } };