diff --git a/adnl/adnl-local-id.cpp b/adnl/adnl-local-id.cpp index 12da6b01..3461efe1 100644 --- a/adnl/adnl-local-id.cpp +++ b/adnl/adnl-local-id.cpp @@ -79,7 +79,9 @@ void AdnlLocalId::deliver_query(AdnlNodeIdShort src, td::BufferSlice data, td::P } VLOG(ADNL_INFO) << this << ": dropping IN message from " << src << ": no callbacks for custom query. firstint=" << td::TlParser(s.as_slice()).fetch_int(); - promise.set_error(td::Status::Error(ErrorCode::warning, "no callbacks for query")); + promise.set_error(td::Status::Error(ErrorCode::warning, PSTRING() << "dropping IN message from " << src + << ": no callbacks for custom query. firstint=" + << td::TlParser(s.as_slice()).fetch_int())); } void AdnlLocalId::subscribe(std::string prefix, std::unique_ptr callback) { diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb index f43e1bea..5f122d9d 100644 --- a/crypto/block/block.tlb +++ b/crypto/block/block.tlb @@ -817,7 +817,7 @@ cap_name#ff name:Text = SmcCapability; // chan_config$_ init_timeout:uint32 close_timeout:uint32 a_key:bits256 b_key:bits256 - a_addr:^MsgAddressInt b_addr:^MsgAddressInt channel_id:uint64 = ChanConfig; + a_addr:^MsgAddressInt b_addr:^MsgAddressInt channel_id:uint64 min_A_extra:Grams = ChanConfig; chan_state_init$000 signed_A:Bool signed_B:Bool min_A:Grams min_B:Grams expire_at:uint32 A:Grams B:Grams = ChanState; chan_state_close$001 signed_A:Bool signed_B:Bool promise_A:Grams promise_B:Grams expire_at:uint32 A:Grams B:Grams = ChanState; @@ -829,8 +829,12 @@ chan_signed_promise#_ sig:(Maybe ^bits512) promise:ChanPromise = ChanSignedPromi chan_msg_init#27317822 inc_A:Grams inc_B:Grams min_A:Grams min_B:Grams channel_id:uint64 = ChanMsg; chan_msg_close#f28ae183 extra_A:Grams extra_B:Grams promise:ChanSignedPromise = ChanMsg; chan_msg_timeout#43278a28 = ChanMsg; +chan_msg_payout#37fe7810 = ChanMsg; chan_signed_msg$_ sig_A:(Maybe ^bits512) sig_B:(Maybe ^bits512) msg:ChanMsg = ChanSignedMsg; +chan_op_cmd#912838d1 msg:ChanSignedMsg = ChanOp; + + chan_data$_ config:^ChanConfig state:^ChanState = ChanData; diff --git a/crypto/block/create-state.cpp b/crypto/block/create-state.cpp index efee71f8..15c3e800 100644 --- a/crypto/block/create-state.cpp +++ b/crypto/block/create-state.cpp @@ -61,8 +61,8 @@ #include "mc-config.h" #if defined(_INTERNAL_COMPILE) || defined(_TONLIB_COMPILE) -# define WITH_TONLIB -# include "tonlib/keys/Mnemonic.h" +#define WITH_TONLIB +#include "tonlib/keys/Mnemonic.h" #endif #define PDO(__op) \ @@ -271,6 +271,10 @@ td::RefInt256 create_smartcontract(td::RefInt256 smc_addr, Ref code, R PDO(sgn(balance) >= 0); THRERR("balance cannot be negative"); if (!mode) { + if (verbosity > 2) { + std::cerr << "StateInit used for computing address: "; + block::gen::t_StateInit.print_ref(std::cerr, state_init); + } return smc_addr; // compute address only } auto it = smart_contracts.find(addr); @@ -651,6 +655,10 @@ void interpret_sub_extra_currencies(vm::Stack& stack) { stack.push_bool(ok); } +void interpret_allocated_balance(vm::Stack& stack) { + stack.push_int(total_smc_balance); +} + #ifdef WITH_TONLIB void interpret_mnemonic_to_privkey(vm::Stack& stack, int mode) { td::SecureString str{td::Slice{stack.pop_string()}}; @@ -689,6 +697,7 @@ void init_words_custom(fift::Dictionary& d) { d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr); d.def_stack_word("CC+? ", interpret_add_extra_currencies); d.def_stack_word("CC-? ", interpret_sub_extra_currencies); + d.def_stack_word("allocated-balance ", interpret_allocated_balance); #ifdef WITH_TONLIB d.def_stack_word("mnemo>priv ", std::bind(interpret_mnemonic_to_privkey, _1, 0)); d.def_stack_word("mnemo>pub ", std::bind(interpret_mnemonic_to_privkey, _1, 1)); diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index 1c2b3d12..3c9e01dd 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -592,7 +592,7 @@ void interpret_str_split(vm::Stack& stack) { 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); + stack.push_smallint(pos == std::string::npos ? -1 : static_cast(pos)); } void interpret_str_reverse(vm::Stack& stack) { diff --git a/crypto/smartcont/CreateState.fif b/crypto/smartcont/CreateState.fif index 0b2ee9f7..3254f072 100644 --- a/crypto/smartcont/CreateState.fif +++ b/crypto/smartcont/CreateState.fif @@ -15,6 +15,11 @@ constant empty_cell +variable @default-subwallet-id +@default-subwallet-id 0! +{ @default-subwallet-id @ } : default-subwallet-id +{ @default-subwallet-id ! } : default-subwallet-id! + // b x --> b' ( serializes a Gram amount ) { -1 { 1+ 2dup 8 * ufits } until rot over 4 u, -rot 8 * u, } : Gram, @@ -228,9 +233,37 @@ variable special-dict // restricted wallet creation + +// same as in new-wallet-v3.fif +<{ SETCP0 DUP IFNOTRET // return if recv_internal + 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 + 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 +=: WCode3 + "auto/wallet-code.fif" include =: WCode0 "auto/restricted-wallet-code.fif" include =: RWCode1 "auto/restricted-wallet2-code.fif" include =: RWCode2 +"auto/restricted-wallet3-code.fif" include =: RWCode3 // pubkey amount -- { over ."Key " pubkey>$ type ." -> " @@ -245,10 +278,21 @@ variable special-dict Masterchain swap 6 .Addr cr } : create-wallet1 +// pubkey amount +{ over ."W0 Key " pubkey>$ type space dup .GR ." -> " + WCode3 // code + // data + empty_cell // libs + 3 roll // balance + 0 0 2 register_smc + Masterchain swap 6 .Addr cr +} : create-wallet0 + // D x t -- D' { idict! not abort"cannot add value" } : rdict-entry { 86400 * } : days* +{ 365 * days* } : years* // balance -- dict { dictnew over 31 -1<< rdict-entry @@ -256,14 +300,27 @@ variable special-dict over 1/2 */ 183 days* rdict-entry swap 1/4 */ 365 days* rdict-entry 0 548 days* rdict-entry -} : make-rdict +} : make-rdict1 +{ dictnew + over 31 -1<< rdict-entry + over .9 */ 0 rdict-entry + over .6775 */ 1 years* rdict-entry + over .445 */ 2 years* rdict-entry + swap .2225 */ 3 years* rdict-entry + 0 4 years* 86400 + rdict-entry +} : make-rdict2 + +variable 'make-rdict +{ 'make-rdict @ execute } : make-rdict + +variable rwallet-start-at rwallet-start-at 0! +now 86400 / 1+ 86400 * rwallet-start-at ! -variable wallet2-start-at wallet2-start-at 0! -now 86400 / 1+ 86400 * wallet2-start-at ! // pubkey amount -- { over ."Key " pubkey>$ type ." -> " RWCode2 // code - // data + // data empty_cell // libs 3 roll // balance 0 // split_depth @@ -273,8 +330,42 @@ now 86400 / 1+ 86400 * wallet2-start-at ! Masterchain swap 6 .Addr cr } : create-wallet2 +variable rwallet-init-pubkey + +// pubkey -- addr +{ RWCode3 // code + // data + empty_cell // libs + 0 // balance + 0 0 0 register_smc // compute address only +} : precompute-wallet3-addr + +variable w3-addr + +// pubkey amount 'rdict -- +{ 'make-rdict ! over precompute-wallet3-addr w3-addr ! + over ."RW3 Key " pubkey>$ type space dup .GR ." -> " + RWCode3 // code + // data + empty_cell // libs + 3 roll // balance + 0 // split_depth + 0 // ticktock + w3-addr @ // address + 6 // mode: create+setaddr + register_smc + Masterchain swap 6 .Addr cr +} : create-wallet3-internal + +{ ' make-rdict1 create-wallet3-internal } : create-wallet3 +{ ' make-rdict2 create-wallet3-internal } : create-wallet3b + // pubkey amount -{ over ."Key " pubkey>$ type ." -> " +{ over ."Key " pubkey>$ type space dup .GR ." -> " WCode0 // code // data empty_cell // libs @@ -284,7 +375,7 @@ now 86400 / 1+ 86400 * wallet2-start-at ! 2 // mode: create register_smc Masterchain swap 6 .Addr cr -} : create-wallet0 +} : create-wallet0a { dup tlb-type-lookup { nip } { "unknown TLB type " swap $+ abort } cond } : $>tlb { bl word $>tlb 1 'nop } ::_ tlb: diff --git a/crypto/smartcont/new-restricted-wallet3.fif b/crypto/smartcont/new-restricted-wallet3.fif new file mode 100644 index 00000000..d12dbe62 --- /dev/null +++ b/crypto/smartcont/new-restricted-wallet3.fif @@ -0,0 +1,110 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include +"Asm.fif" include +"GetOpt.fif" include + +{ show-options-help 1 halt } : usage +0 =: restrict-mode +-1 =: wc +null =: subwallet-id +86400 =: expires-in +now =: start-at +0x5EA62080 =: start-at +0x4BA92D8A =: subwallet-base + +begin-options + " [-w][-r][-i][-t] []" +cr +tab + +"Creates a restricted lockup wallet v3 in the masterchain initialized by key loaded from " + +"and controlled by the private key corresponding to " +cr +tab + +"and saves its initialization query into new-.boc and its address into .addr ('rwallet.addr' by default)" + disable-digit-options generic-help-setopt + "r" "--restrict-mode" { parse-int =: restrict-mode } short-long-option-arg + "Selects a standard restriction mode: 1 for 18-month lockup, 2 for 4-year lockup" option-help + "w" "--workchain" { parse-int =: wc } short-long-option-arg + "Selects a workchain (" wc (.) $+ +" by default)" option-help + "i" "--subwallet-id" { parse-int =: subwallet-id } short-long-option-arg + "Sets 32-bit subwallet id (workchain plus " subwallet-base (.) $+ +" by default)" option-help + "x" "--expires-in" { parse-int =: expires-in } short-long-option-arg + "Expiration time of the initialization message (" expires-in (.) $+ + +" seconds by default)" option-help + "t" "--start-at" { parse-int =: start-at } short-long-option-arg + "Restriction start Unixtime (now by default)" option-help + "h" "--help" { usage } short-long-option + "Shows a help message" option-help +parse-options + +$# dup 3 < swap 4 > or ' usage if +4 :$1..n + +$1 =: filename-base +$2 parse-pubkey =: PubKey +$3 $>GR =: amount +$4 "rwallet" replace-if-null =: savefile-base +subwallet-id subwallet-base wc + replace-if-null =: subwallet-id +expires-in now + =: expires-at + +"new-" savefile-base $+ +".boc" =: savefile +savefile-base +".addr" =: savefile-addr + +wc 8 fits not abort"invalid workchain id" +subwallet-id 32 fits not abort"invalid subwallet-id" +expires-at 32 ufits not abort"invalid expiration time" +start-at 32 ufits not abort"invalid restriction start time" +restrict-mode dup 1 < swap 2 > or abort"unknown restriction mode" +filename-base +".pk" load-keypair =: init_pk =: init_pubkey + +."Creating new restricted lockup wallet v3 in workchain " wc . +."with restriction mode " restrict-mode . ."and nominal amount " amount .GR cr +."controlled by public key " PubKey .pubkey ." and initialized by public key " init_pubkey 256 B>u@ .pubkey cr +."(subwallet id is " subwallet-id ._ .")" cr + +// D x t -- D' +{ idict! not abort"cannot add value" +} : rdict-entry +{ 86400 * } : days* +{ 365 * days* } : years* +// balance -- dict +{ dictnew + over 31 -1<< rdict-entry + over 3/4 */ 91 days* rdict-entry + over 1/2 */ 183 days* rdict-entry + swap 1/4 */ 365 days* rdict-entry + 0 548 days* rdict-entry +} : make-rdict1 +{ dictnew + over 31 -1<< rdict-entry + over .9 */ 0 rdict-entry + over .6775 */ 1 years* rdict-entry + over .445 */ 2 years* rdict-entry + swap .2225 */ 3 years* rdict-entry + 0 4 years* 86400 + rdict-entry +} : make-rdict2 + +amount +restrict-mode 1 = { make-rdict1 } { make-rdict2 } cond =: rdict + +."Restrictions start at " start-at ._ .": " cr +rdict 32 { swap . ."-> " Gram@ .GR cr true } idictforeach cr + +// Create new restricted wallet v3; code taken from `auto/restricted-wallet3-code.fif` +"auto/restricted-wallet3-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 +savefile tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/crypto/smartcont/payment-channel-code.fc b/crypto/smartcont/payment-channel-code.fc index 30d08375..b4b14d5c 100644 --- a/crypto/smartcont/payment-channel-code.fc +++ b/crypto/smartcont/payment-channel-code.fc @@ -7,8 +7,13 @@ int err:replay_protection() asm "34 PUSHINT"; int err:no_timeout() asm "35 PUSHINT"; int err:expected_init() asm "36 PUSHINT"; int err:expected_close() asm "37 PUSHINT"; +int err:expected_payout() asm "37 PUSHINT"; int err:no_promise_signature() asm "38 PUSHINT"; int err:wrong_channel_id() asm "39 PUSHINT"; +int err:unknown_op() asm "40 PUSHINT"; +int err:not_enough_fee() asm "41 PUSHINT"; + +int op:pchan_cmd() asm "0x912838d1 PUSHINT"; int msg:init() asm "0x27317822 PUSHINT"; int msg:close() asm "0xf28ae183 PUSHINT"; @@ -19,6 +24,8 @@ int state:init() asm "0 PUSHINT"; int state:close() asm "1 PUSHINT"; int state:payout() asm "2 PUSHINT"; +int min_fee() asm "1000000000 PUSHINT"; + ;; A - initial balance of Alice, ;; B - initial balance of B @@ -60,7 +67,8 @@ _ unpack_config(cell config) { cs~load_uint(256), cs~load_ref().begin_parse(), cs~load_ref().begin_parse(), - cs~load_uint(64)); + cs~load_uint(64), + cs~load_grams()); cs.end_parse(); return res; } @@ -164,8 +172,8 @@ cell do_payout(int promise_A, int promise_B, int A, int B, slice a_addr, slice b A += diff; B -= diff; - send_payout(a_addr, A, channel_id, 3); send_payout(b_addr, B, channel_id, 3); + send_payout(a_addr, A, channel_id, 3 + 128); return begin_cell() .store_int(state:payout(), 3) @@ -179,7 +187,7 @@ cell do_payout(int promise_A, int promise_B, int A, int B, slice a_addr, slice b ;; init$000 inc_A:Grams inc_B:Grams min_A:Grams min_B:Grams = Message; ;; cell with_init(slice state, int msg_value, slice msg, int msg_signed_A?, int msg_signed_B?, - slice a_addr, slice b_addr, int init_timeout, int channel_id) { + slice a_addr, slice b_addr, int init_timeout, int channel_id, int min_A_extra) { ;; parse state (int signed_A?, int signed_B?, int min_A, int min_B, int expire_at, int A, int B) = unpack_state_init(state); @@ -217,6 +225,7 @@ cell with_init(slice state, int msg_value, slice msg, int msg_signed_A?, int msg } if (signed_A? & signed_B?) { + A -= min_A_extra; if ((min_A > A) | (min_B > B)) { return do_payout(0, 0, A, B, a_addr, b_addr, channel_id); } @@ -289,24 +298,57 @@ cell with_close(slice cs, slice msg, int msg_signed_A?, int msg_signed_B?, int a return pack_state_close(signed_A?, signed_B?, promise_A, promise_B, expire_at, A, B); } +() with_payout(slice cs, slice msg, slice a_addr, slice b_addr, int channel_id) impure { + int op = msg~load_uint(32); + throw_unless(err:expected_payout(), op == msg:payout()); + (int A, int B) = (cs~load_grams(), cs~load_grams()); + throw_unless(err:not_enough_fee(), A + B + 1000000000 < get_balance().pair_first()); + accept_message(); + send_payout(b_addr, B, channel_id, 3); + send_payout(a_addr, A, channel_id, 3 + 128); +} + () recv_any(int msg_value, slice msg) impure { + if (msg.slice_empty?()) { + return(); + } + ;; op is not signed, but we don't need it to be signed. + int op = msg~load_uint(32); + if (op <= 1) { + ;; simple transfer with comment, return + ;; external message will be aborted + ;; internal message will be accepted + return (); + } + throw_unless(err:unknown_op(), op == op:pchan_cmd()); + (cell config, cell state) = unpack_data(); - (int init_timeout, int close_timeout, int a_key, int b_key, slice a_addr, slice b_addr, int channel_id) = config.unpack_config(); + (int init_timeout, int close_timeout, int a_key, int b_key, + slice a_addr, slice b_addr, int channel_id, int min_A_extra) = config.unpack_config(); (int msg_signed_A?, int msg_signed_B?) = msg~unwrap_signatures(a_key, b_key); slice cs = state.begin_parse(); int state_type = cs~load_uint(3); if (state_type == state:init()) { ;; init - state = with_init(cs, msg_value, msg, msg_signed_A?, msg_signed_B?, a_addr, b_addr, init_timeout, channel_id); + state = with_init(cs, msg_value, msg, msg_signed_A?, msg_signed_B?, a_addr, b_addr, init_timeout, channel_id, min_A_extra); } if (state_type == state:close()) { state = with_close(cs, msg, msg_signed_A?, msg_signed_B?, a_key, b_key, a_addr, b_addr, close_timeout, channel_id); + } if (state_type == state:payout()) { + with_payout(cs, msg, a_addr, b_addr, channel_id); } pack_data(config, state); } () recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure { + ;; TODO: uncomment when supported in tests + ;; var cs = in_msg_cell.begin_parse(); + ;; var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + ;; if (flags & 1) { + ;; ;; ignore all bounced messages + ;; return (); + ;; } recv_any(msg_value, in_msg); } diff --git a/crypto/smc-envelope/PaymentChannel.cpp b/crypto/smc-envelope/PaymentChannel.cpp index a7367922..e70e23c1 100644 --- a/crypto/smc-envelope/PaymentChannel.cpp +++ b/crypto/smc-envelope/PaymentChannel.cpp @@ -30,6 +30,7 @@ td::Ref Config::serialize() const { rec.init_timeout = init_timeout; rec.close_timeout = close_timeout; rec.channel_id = channel_id; + rec.min_A_extra = pack_grams(min_A_extra); td::Ref res; CHECK(tlb::pack_cell(res, rec)); @@ -94,6 +95,13 @@ td::Ref MsgTimeout::serialize() const { return res; } +td::Ref MsgPayout::serialize() const { + block::gen::ChanMsg::Record_chan_msg_payout rec; + td::Ref res; + CHECK(tlb::pack_cell(res, rec)); + return res; +} + td::SecureString SignedPromise::signature(const td::Ed25519::PrivateKey* key, const td::Ref& promise) { return sign(promise, key); } diff --git a/crypto/smc-envelope/PaymentChannel.h b/crypto/smc-envelope/PaymentChannel.h index f050f330..db6b159e 100644 --- a/crypto/smc-envelope/PaymentChannel.h +++ b/crypto/smc-envelope/PaymentChannel.h @@ -24,6 +24,7 @@ struct Config { block::StdAddress a_addr; block::StdAddress b_addr; td::uint64 channel_id{0}; + td::uint64 min_A_extra{0}; td::Ref serialize() const; }; @@ -59,6 +60,10 @@ struct MsgTimeout { td::Ref serialize() const; }; +struct MsgPayout { + td::Ref serialize() const; +}; + struct SignedPromise { Promise promise; td::optional o_signature; @@ -125,8 +130,11 @@ struct MsgBuilder { rec.msg = vm::load_cell_slice_ref(msg); rec.sig_A = maybe_ref(maybe_sign(msg, a_key)); rec.sig_B = maybe_ref(maybe_sign(msg, b_key)); + block::gen::ChanOp::Record op_rec; + CHECK(tlb::csr_pack(op_rec.msg, rec)); + LOG(ERROR) << op_rec.msg->size(); td::Ref res; - CHECK(tlb::pack_cell(res, rec)); + CHECK(tlb::pack_cell(res, op_rec)); return res; } }; @@ -160,6 +168,10 @@ struct MsgTimeoutBuilder : public MsgBuilder { MsgTimeout msg; }; +struct MsgPayoutBuilder : public MsgBuilder { + MsgPayout msg; +}; + struct MsgCloseBuilder : public MsgBuilder { MsgClose msg; diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp index 99ed8384..5ae940a1 100644 --- a/crypto/smc-envelope/SmartContractCode.cpp +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -160,7 +160,7 @@ td::Span SmartContractCode::get_revisions(Type type) { return res; } case Type::RestrictedWallet: { - static int res[] = {-1, 1}; + static int res[] = {1}; return res; } } diff --git a/crypto/smc-envelope/TestWallet.cpp b/crypto/smc-envelope/TestWallet.cpp index f218cf7c..41a66b68 100644 --- a/crypto/smc-envelope/TestWallet.cpp +++ b/crypto/smc-envelope/TestWallet.cpp @@ -30,7 +30,7 @@ td::Ref TestWallet::get_init_state(const td::Ed25519::PublicKey& publi return GenericAccount::get_init_state(std::move(code), std::move(data)); } -td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& private_key) noexcept { +td::Ref TestWallet::get_init_message_new(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(); diff --git a/crypto/smc-envelope/TestWallet.h b/crypto/smc-envelope/TestWallet.h index 46891aa9..38e0f12b 100644 --- a/crypto/smc-envelope/TestWallet.h +++ b/crypto/smc-envelope/TestWallet.h @@ -36,7 +36,7 @@ class TestWallet : public ton::SmartContract, public WalletInterface { static constexpr unsigned max_message_size = vm::CellString::max_bytes; 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 get_init_message_new(const td::Ed25519::PrivateKey& private_key) noexcept; static td::Ref make_a_gift_message_static(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::Span gifts) noexcept; diff --git a/crypto/smc-envelope/Wallet.cpp b/crypto/smc-envelope/Wallet.cpp index a09ddff9..fac4f1a6 100644 --- a/crypto/smc-envelope/Wallet.cpp +++ b/crypto/smc-envelope/Wallet.cpp @@ -33,7 +33,7 @@ td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_ke 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::Ref Wallet::get_init_message_new(const td::Ed25519::PrivateKey& private_key) noexcept { td::uint32 seqno = 0; td::uint32 valid_until = std::numeric_limits::max(); auto signature = diff --git a/crypto/smc-envelope/Wallet.h b/crypto/smc-envelope/Wallet.h index 459db7ea..db074f6a 100644 --- a/crypto/smc-envelope/Wallet.h +++ b/crypto/smc-envelope/Wallet.h @@ -36,7 +36,7 @@ class Wallet : public ton::SmartContract, public WalletInterface { static constexpr unsigned max_message_size = vm::CellString::max_bytes; 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 get_init_message_new(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::Span gifts) noexcept; diff --git a/crypto/test/test-smartcont.cpp b/crypto/test/test-smartcont.cpp index fdce6a82..fb82493e 100644 --- a/crypto/test/test-smartcont.cpp +++ b/crypto/test/test-smartcont.cpp @@ -162,7 +162,7 @@ TEST(Tonlib, TestWallet) { 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 init_message = ton::TestWallet::get_init_message_new(priv_key); auto address = ton::GenericAccount::get_address(0, init_state); CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); @@ -215,7 +215,7 @@ TEST(Tonlib, Wallet) { 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 init_message = ton::Wallet::get_init_message_new(priv_key); auto address = ton::GenericAccount::get_address(0, init_state); CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); @@ -1273,7 +1273,8 @@ void do_dns_test(CheckedDns&& dns) { dns.update(actions); actions.clear(); } - dns.resolve(gen_name(), td::narrow_cast(rnd.fast(0, 5))); + auto name = gen_name(); + dns.resolve(name, td::narrow_cast(rnd.fast(0, 5))); } }; diff --git a/crypto/vm/stack.cpp b/crypto/vm/stack.cpp index 90fea7f6..c580a3d0 100644 --- a/crypto/vm/stack.cpp +++ b/crypto/vm/stack.cpp @@ -222,7 +222,7 @@ StackEntry::StackEntry(Ref stack_ref) : ref(std::move(stack_ref)), tp(t_s StackEntry::StackEntry(Ref cont_ref) : ref(std::move(cont_ref)), tp(t_vmcont) { } -Ref StackEntry::as_cont() const & { +Ref StackEntry::as_cont() const& { return as(); } @@ -233,7 +233,7 @@ Ref StackEntry::as_cont() && { StackEntry::StackEntry(Ref box_ref) : ref(std::move(box_ref)), tp(t_box) { } -Ref StackEntry::as_box() const & { +Ref StackEntry::as_box() const& { return as(); } @@ -252,7 +252,7 @@ StackEntry::StackEntry(std::vector&& tuple_components) : ref(Ref{true, std::move(tuple_components)}), tp(t_tuple) { } -Ref StackEntry::as_tuple() const & { +Ref StackEntry::as_tuple() const& { return as(); } @@ -260,7 +260,7 @@ Ref StackEntry::as_tuple() && { return move_as(); } -Ref StackEntry::as_tuple_range(unsigned max_len, unsigned min_len) const & { +Ref StackEntry::as_tuple_range(unsigned max_len, unsigned min_len) const& { auto t = as(); if (t.not_null() && t->size() <= max_len && t->size() >= min_len) { return t; @@ -281,7 +281,7 @@ Ref StackEntry::as_tuple_range(unsigned max_len, unsigned min_len) && { StackEntry::StackEntry(Ref atom_ref) : ref(std::move(atom_ref)), tp(t_atom) { } -Ref StackEntry::as_atom() const & { +Ref StackEntry::as_atom() const& { return as(); } diff --git a/tdutils/td/utils/FileLog.cpp b/tdutils/td/utils/FileLog.cpp index 7f36deed..9dbedab7 100644 --- a/tdutils/td/utils/FileLog.cpp +++ b/tdutils/td/utils/FileLog.cpp @@ -95,7 +95,7 @@ void FileLog::append(CSlice cslice, int log_level) { process_fatal_error(cslice); } - if (size_ > rotate_threshold_) { + if (size_ > rotate_threshold_ || want_rotate_.load(std::memory_order_relaxed)) { auto status = rename(path_, PSLICE() << path_ << ".old"); if (status.is_error()) { process_fatal_error(PSLICE() << status.error() << " in " << __FILE__ << " at " << __LINE__); @@ -111,9 +111,13 @@ void FileLog::rotate() { do_rotate(); } +void FileLog::lazy_rotate() { + want_rotate_ = true; +} + void FileLog::do_rotate() { - auto current_verbosity_level = GET_VERBOSITY_LEVEL(); - SET_VERBOSITY_LEVEL(std::numeric_limits::min()); // to ensure that nothing will be printed to the closed log + want_rotate_ = false; + td::ScopedDisableLog disable_log; // to ensure that nothing will be printed to the closed log CHECK(!path_.empty()); fd_.close(); auto r_fd = FileFd::open(path_, FileFd::Create | FileFd::Truncate | FileFd::Write); @@ -125,7 +129,6 @@ void FileLog::do_rotate() { fd_.get_native_fd().duplicate(Stderr().get_native_fd()).ignore(); } size_ = 0; - SET_VERBOSITY_LEVEL(current_verbosity_level); } Result> FileLog::create(string path, int64 rotate_threshold, bool redirect_stderr) { diff --git a/tdutils/td/utils/FileLog.h b/tdutils/td/utils/FileLog.h index 2cead1be..68bb7e7e 100644 --- a/tdutils/td/utils/FileLog.h +++ b/tdutils/td/utils/FileLog.h @@ -46,12 +46,15 @@ class FileLog : public LogInterface { void rotate() override; + void lazy_rotate(); + private: FileFd fd_; string path_; int64 size_ = 0; int64 rotate_threshold_ = 0; bool redirect_stderr_; + std::atomic want_rotate_{}; void do_rotate(); }; diff --git a/tdutils/td/utils/Random.cpp b/tdutils/td/utils/Random.cpp index 252f2f85..827628a0 100644 --- a/tdutils/td/utils/Random.cpp +++ b/tdutils/td/utils/Random.cpp @@ -166,10 +166,10 @@ double Random::fast(double min, double max) { Random::Xorshift128plus::Xorshift128plus(uint64 seed) { auto next = [&]() { // splitmix64 - seed += static_cast(0x9E3779B97F4A7C15); + seed += static_cast(0x9E3779B97F4A7C15ull); uint64 z = seed; - z = (z ^ (z >> 30)) * static_cast(0xBF58476D1CE4E5B9); - z = (z ^ (z >> 27)) * static_cast(0x94D049BB133111EB); + z = (z ^ (z >> 30)) * static_cast(0xBF58476D1CE4E5B9ull); + z = (z ^ (z >> 27)) * static_cast(0x94D049BB133111EBull); return z ^ (z >> 31); }; seed_[0] = next(); diff --git a/tdutils/td/utils/TsFileLog.cpp b/tdutils/td/utils/TsFileLog.cpp index 865faa4b..6b06a02c 100644 --- a/tdutils/td/utils/TsFileLog.cpp +++ b/tdutils/td/utils/TsFileLog.cpp @@ -89,7 +89,7 @@ class TsFileLog : public LogInterface { void rotate() override { for (auto &info : logs_) { if (info.is_inited.load(std::memory_order_consume)) { - info.log.rotate(); + info.log.lazy_rotate(); } } } diff --git a/tdutils/td/utils/logging.cpp b/tdutils/td/utils/logging.cpp index cf98b2f5..29f6b0bd 100644 --- a/tdutils/td/utils/logging.cpp +++ b/tdutils/td/utils/logging.cpp @@ -26,6 +26,7 @@ #include #include +#include #if TD_ANDROID #include @@ -272,4 +273,26 @@ void process_fatal_error(CSlice message) { std::abort(); } +namespace { +std::mutex sdl_mutex; +int sdl_cnt = 0; +int sdl_verbosity = 0; + +} // namespace +ScopedDisableLog::ScopedDisableLog() { + std::unique_lock guard(sdl_mutex); + if (sdl_cnt == 0) { + sdl_verbosity = set_verbosity_level(std::numeric_limits::min()); + } + sdl_cnt++; +} + +ScopedDisableLog::~ScopedDisableLog() { + std::unique_lock guard(sdl_mutex); + sdl_cnt--; + if (sdl_cnt == 0) { + set_verbosity_level(sdl_verbosity); + } +} + } // namespace td diff --git a/tdutils/td/utils/logging.h b/tdutils/td/utils/logging.h index 0cccadc1..e5278b4b 100644 --- a/tdutils/td/utils/logging.h +++ b/tdutils/td/utils/logging.h @@ -52,8 +52,8 @@ #define VERBOSITY_NAME(x) verbosity_##x -#define GET_VERBOSITY_LEVEL() (::td::log_options.level) -#define SET_VERBOSITY_LEVEL(new_level) (::td::log_options.level = (new_level)) +#define GET_VERBOSITY_LEVEL() (::td::get_verbosity_level()) +#define SET_VERBOSITY_LEVEL(new_level) (::td::set_verbosity_level(new_level)) #ifndef STRIP_LOG #define STRIP_LOG VERBOSITY_NAME(DEBUG) @@ -64,7 +64,7 @@ #define LOGGER(interface, options, level, comment) ::td::Logger(interface, options, level, __FILE__, __LINE__, comment) #define LOG_IMPL_FULL(interface, options, strip_level, runtime_level, condition, comment) \ - LOG_IS_STRIPPED(strip_level) || runtime_level > options.level || !(condition) \ + LOG_IS_STRIPPED(strip_level) || runtime_level > options.get_level() || !(condition) \ ? (void)0 \ : ::td::detail::Voidify() & LOGGER(interface, options, runtime_level, comment) @@ -133,11 +133,18 @@ extern int VERBOSITY_NAME(files); extern int VERBOSITY_NAME(sqlite); struct LogOptions { - int level{VERBOSITY_NAME(DEBUG) + 1}; + std::atomic level{VERBOSITY_NAME(DEBUG) + 1}; bool fix_newlines{true}; bool add_info{true}; - static constexpr LogOptions plain() { + int get_level() const { + return level.load(std::memory_order_relaxed); + } + int set_level(int new_level) { + return level.exchange(new_level); + } + + static LogOptions plain() { return LogOptions{0, false, false}; } @@ -145,9 +152,31 @@ struct LogOptions { constexpr LogOptions(int level, bool fix_newlines, bool add_info) : level(level), fix_newlines(fix_newlines), add_info(add_info) { } + + LogOptions(const LogOptions &other) : LogOptions(other.level.load(), other.fix_newlines, other.add_info) { + } + + LogOptions &operator=(const LogOptions &other) { + level = other.level.load(); + fix_newlines = other.fix_newlines; + add_info = other.add_info; + return *this; + } }; extern LogOptions log_options; +inline int set_verbosity_level(int level) { + return log_options.set_level(level); +} +inline int get_verbosity_level() { + return log_options.get_level(); +} + +class ScopedDisableLog { + public: + ScopedDisableLog(); + ~ScopedDisableLog(); +}; class LogInterface { public: diff --git a/tdutils/test/log.cpp b/tdutils/test/log.cpp index 707bb070..04d99dee 100644 --- a/tdutils/test/log.cpp +++ b/tdutils/test/log.cpp @@ -62,6 +62,9 @@ class LogBenchmark : public td::Benchmark { void run_thread(int n) { auto str = PSTRING() << "#" << n << " : fsjklfdjsklfjdsklfjdksl\n"; for (int i = 0; i < n; i++) { + if (i % 10000 == 0) { + log_->rotate(); + } log_->append(str); } } diff --git a/tdutils/test/misc.cpp b/tdutils/test/misc.cpp index f43fcdcc..768aebd3 100644 --- a/tdutils/test/misc.cpp +++ b/tdutils/test/misc.cpp @@ -962,3 +962,11 @@ TEST(Misc, CancellationToken) { source = CancellationTokenSource{}; CHECK(token4); } + +TEST(Misc, Xorshift128plus) { + Random::Xorshift128plus rnd(123); + ASSERT_EQ(11453256657207062272ull, rnd()); + ASSERT_EQ(14917490455889357332ull, rnd()); + ASSERT_EQ(5645917797309401285ull, rnd()); + ASSERT_EQ(13554822455746959330ull, rnd()); +} diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index aed8db00..1d1b1488 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -41,7 +41,6 @@ bip39Hints words:vector = Bip39Hints; adnlAddress adnl_address:string = AdnlAddress; accountAddress account_address:string = AccountAddress; -accountRevisionList revisions:vector = AccountRevisionList; unpackedAccountAddress workchain_id:int32 bounceable:Bool testnet:Bool addr:bytes = UnpackedAccountAddress; @@ -89,7 +88,10 @@ pchan.statePayout A:int64 B:int64 = pchan.State; pchan.accountState config:pchan.config state:pchan.State description:string = AccountState; uninited.accountState frozen_hash:bytes = AccountState; -fullAccountState balance:int64 last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState = FullAccountState; +fullAccountState address:accountAddress balance:int64 last_transaction_id:internal.transactionId block_id:ton.blockIdExt sync_utime:int53 account_state:AccountState revision:int32 = FullAccountState; + +accountRevisionList revisions:vector = AccountRevisionList; +accountList accounts:vector = AccountList; syncStateDone = SyncState; syncStateInProgress from_seqno:int32 to_seqno:int32 current_seqno:int32 = SyncState; @@ -247,8 +249,14 @@ sync = ton.BlockIdExt; // revision = 0 -- use default revision // revision = x (x > 0) -- use revision x // revision = -1 -- use experimental (newest) revision. Only for debug purpose -getAccountAddress initial_account_state:InitialAccountState revision:int32 = AccountAddress; -guessAccountRevision initial_account_state:InitialAccountState = AccountRevisionList; +// +// workchain_id = -1 or 0. -1 for masterchain, 0 for basechain +// NB: use wallet_id = default_wallet_id + workchain_id +getAccountAddress initial_account_state:InitialAccountState revision:int32 workchain_id:int32 = AccountAddress; +guessAccountRevision initial_account_state:InitialAccountState workchain_id:int32 = AccountRevisionList; + +guessAccount public_key:string rwallet_init_public_key:string = AccountRevisionList; + getAccountState account_address:accountAddress = FullAccountState; createQuery private_key:InputKey address:accountAddress timeout:int32 action:Action initial_account_state:InitialAccountState = query.Info; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 0998c4b9..278fac75 100644 Binary files a/tl/generate/scheme/tonlib_api.tlo and b/tl/generate/scheme/tonlib_api.tlo differ diff --git a/tonlib/test/offline.cpp b/tonlib/test/offline.cpp index 2c35d934..b532e7f5 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -145,7 +145,7 @@ TEST(Tonlib, InitClose) { sync_send(client, make_object(cfg(bad_config.str()))).ensure_error(); auto address = sync_send(client, make_object( - make_object(), 0)) + make_object(), 0, -1)) .move_as_ok(); sync_send(client, make_object(std::move(address))).ensure_error(); sync_send(client, make_object()).ensure(); diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index acafc199..1ab6ab71 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -143,7 +143,7 @@ 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, default_wallet_id), 0)) + make_object(key.public_key, default_wallet_id), 0, 0)) .move_as_ok() ->account_address_; } @@ -152,7 +152,7 @@ std::string highload_wallet_address(Client& client, const Key& key) { return sync_send(client, make_object( make_object(key.public_key, default_wallet_id), - 1 /*TODO: guess revision!*/)) + 1 /*TODO: guess revision!*/, 0)) .move_as_ok() ->account_address_; } @@ -449,7 +449,7 @@ Wallet create_empty_wallet(Client& client) { sync_send( client, make_object( - make_object(wallet.key.public_key, default_wallet_id), 0)) + make_object(wallet.key.public_key, default_wallet_id), 0, 0)) .move_as_ok(); wallet.address = account_address->account_address_; @@ -470,8 +470,9 @@ Wallet create_empty_dns(Client& client) { Wallet dns{"", {key->public_key_, std::move(key->secret_)}}; auto account_address = - sync_send(client, make_object( - make_object(dns.key.public_key, default_wallet_id), 0)) + sync_send(client, + make_object( + make_object(dns.key.public_key, default_wallet_id), 0, 0)) .move_as_ok(); dns.address = account_address->account_address_; @@ -528,11 +529,19 @@ void test_back_and_forth_transfer(Client& client, const Wallet& giver_wallet, bo 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); + // Temporary turn off test of flag 128 + if (false) { + // 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); + } else if (state.balance > 1 * Gramm / 10) { + transfer_grams(client, wallet_a, giver_wallet.address, state.balance - 1 * Gramm / 10).ensure(); + state = get_account_state(client, wallet_a.address); + ASSERT_TRUE(state.balance < 1 * Gramm / 10); + ASSERT_EQ(AccountState::Wallet, state.type); + } } void test_multisig(Client& client, const Wallet& giver_wallet) { @@ -609,7 +618,7 @@ void test_paychan(Client& client, const Wallet& giver_wallet) { bob.key.public_key, bob.get_address(), init_timeout, close_timeout, channel_id)); }; - auto account_address = sync_send(client, make_object(get_initial_state(), -1)) + auto account_address = sync_send(client, make_object(get_initial_state(), -1, 0)) .move_as_ok() ->account_address_; auto get_account_address = [&] { return make_object(account_address); }; diff --git a/tonlib/tonlib/Config.cpp b/tonlib/tonlib/Config.cpp index f4a6dc80..3cd9d50e 100644 --- a/tonlib/tonlib/Config.cpp +++ b/tonlib/tonlib/Config.cpp @@ -84,10 +84,10 @@ td::Result Config::parse(std::string str) { //return td::Status::Error("Invalid config (4)"); //} - TRY_RESULT(ip, td::get_json_object_int_field(object, "ip", false)); + TRY_RESULT(ip, td::get_json_object_long_field(object, "ip", false)); TRY_RESULT(port, td::get_json_object_int_field(object, "port", false)); Config::LiteClient client; - TRY_STATUS(client.address.init_host_port(td::IPAddress::ipv4_to_str(ip), port)); + TRY_STATUS(client.address.init_host_port(td::IPAddress::ipv4_to_str(static_cast(ip)), port)); TRY_RESULT(id_obj, td::get_json_object_field(object, "id", td::JsonValue::Type::Object, false)); auto &id = id_obj.get_object(); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index f7e9223c..9afc0019 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -62,6 +62,7 @@ namespace int_api { struct GetAccountState { block::StdAddress address; td::optional block_id; + td::optional public_key; using ReturnType = td::unique_ptr; }; @@ -421,11 +422,13 @@ class AccountState { td::Result> to_fullAccountState() const { TRY_RESULT(account_state, to_accountState()); - return tonlib_api::make_object(get_balance(), to_transaction_id(raw().info), - to_tonlib_api(raw().block_id), get_sync_time(), - std::move(account_state)); + return tonlib_api::make_object( + tonlib_api::make_object(get_address().rserialize(true)), get_balance(), + to_transaction_id(raw().info), to_tonlib_api(raw().block_id), get_sync_time(), std::move(account_state), + get_wallet_revision()); } + //NB: Order is important! Used during guessAccountRevision enum WalletType { Empty, Unknown, @@ -442,6 +445,9 @@ class AccountState { WalletType get_wallet_type() const { return wallet_type_; } + td::int32 get_wallet_revision() const { + return wallet_revision_; + } bool is_wallet() const { switch (get_wallet_type()) { case AccountState::Empty: @@ -529,7 +535,8 @@ class AccountState { continue; } auto wallet = ton::RestrictedWallet::create(r_init_data.move_as_ok(), revision); - if (!(wallet->get_address() == address_)) { + if (!(wallet->get_address(ton::masterchainId) == address_ || + wallet->get_address(ton::basechainId) == address_)) { continue; } wallet_type_ = WalletType::RestrictedWallet; @@ -545,8 +552,9 @@ class AccountState { continue; } auto conf = r_conf.move_as_ok(); - auto wallet = ton::PaymentChannel::create(conf, -1); - if (!(wallet->get_address() == address_)) { + auto wallet = ton::PaymentChannel::create(conf, revision); + if (!(wallet->get_address(ton::masterchainId) == address_ || + wallet->get_address(ton::basechainId) == address_)) { continue; } wallet_type_ = WalletType::PaymentChannel; @@ -562,34 +570,35 @@ class AccountState { if (wallet_type_ != WalletType::Empty) { return wallet_type_; } - auto o_revision = ton::WalletV3::guess_revision(address_, key, wallet_id_); + auto wallet_id = address_.workchain + wallet_id_; + auto o_revision = ton::WalletV3::guess_revision(address_, key, wallet_id); if (o_revision) { wallet_type_ = WalletType::WalletV3; wallet_revision_ = o_revision.value(); - set_new_state({ton::WalletV3::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)}); + set_new_state({ton::WalletV3::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id)}); return wallet_type_; } - o_revision = ton::HighloadWalletV2::guess_revision(address_, key, wallet_id_); + o_revision = ton::HighloadWalletV2::guess_revision(address_, key, wallet_id + address_.workchain); if (o_revision) { wallet_type_ = WalletType::HighloadWalletV2; wallet_revision_ = o_revision.value(); set_new_state({ton::HighloadWalletV2::get_init_code(wallet_revision_), - ton::HighloadWalletV2::get_init_data(key, wallet_id_)}); + ton::HighloadWalletV2::get_init_data(key, wallet_id + address_.workchain)}); return wallet_type_; } - o_revision = ton::HighloadWallet::guess_revision(address_, key, wallet_id_); + o_revision = ton::HighloadWallet::guess_revision(address_, key, wallet_id); if (o_revision) { wallet_type_ = WalletType::HighloadWalletV1; wallet_revision_ = o_revision.value(); set_new_state( - {ton::HighloadWallet::get_init_code(wallet_revision_), ton::HighloadWallet::get_init_data(key, wallet_id_)}); + {ton::HighloadWallet::get_init_code(wallet_revision_), ton::HighloadWallet::get_init_data(key, wallet_id)}); return wallet_type_; } - o_revision = ton::ManualDns::guess_revision(address_, key, wallet_id_); + o_revision = ton::ManualDns::guess_revision(address_, key, wallet_id); if (o_revision) { wallet_type_ = WalletType::ManualDns; wallet_revision_ = o_revision.value(); - auto dns = ton::ManualDns::create(key, wallet_id_, wallet_revision_); + auto dns = ton::ManualDns::create(key, wallet_id, wallet_revision_); set_new_state(dns->get_state()); return wallet_type_; } @@ -871,7 +880,8 @@ class Query { break; } case block::gen::OutAction::action_reserve_currency: - return td::Status::Error("estimate_fee: action_reserve_currency unsupported"); + LOG(INFO) << "skip action_reserve_currency"; + continue; } } return res; @@ -899,8 +909,11 @@ class Query { } 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)); + auto res = smc.write().send_external_message(raw_.message_body, ton::SmartContract::Args() + .set_limits(gas_limits) + .set_balance(raw_.source->get_balance()) + .set_now(raw_.source->get_sync_time()) + .set_ignore_chksig(ignore_chksig)); td::int64 fwd_fee = 0; if (res.success) { LOG(DEBUG) << "output actions:\n" @@ -910,7 +923,7 @@ class Query { } 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; + LOG(INFO) << storage_fee << " " << in_fwd_fee << " " << gas_fee << " " << fwd_fee << " " << res.gas_used; Fee fee; fee.in_fwd_fee = in_fwd_fee; @@ -1581,74 +1594,77 @@ tonlib_api::object_ptr TonlibClient::do_static_request(const } td::Result get_account_address(const tonlib_api::raw_initialAccountState& raw_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { 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 ton::GenericAccount::get_address(0 /*zerochain*/, + return ton::GenericAccount::get_address(workchain_id, ton::GenericAccount::get_init_state(std::move(code), std::move(data))); } td::Result get_account_address(const tonlib_api::testGiver_initialAccountState& test_wallet_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { return ton::TestGiver::address(); } td::Result get_account_address(const tonlib_api::testWallet_initialAccountState& test_wallet_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { 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::TestWallet::get_init_state(key, revision)); + return ton::GenericAccount::get_address(workchain_id, ton::TestWallet::get_init_state(key, revision)); } td::Result get_account_address(const tonlib_api::wallet_initialAccountState& wallet_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { TRY_RESULT(key_bytes, get_public_key(wallet_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); - return ton::GenericAccount::get_address(0 /*zerochain*/, ton::Wallet::get_init_state(key, revision)); + return ton::GenericAccount::get_address(workchain_id, ton::Wallet::get_init_state(key, revision)); } td::Result get_account_address(const tonlib_api::wallet_v3_initialAccountState& test_wallet_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { 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*/, + workchain_id, ton::WalletV3::get_init_state(key, static_cast(test_wallet_state.wallet_id_), revision)); } td::Result get_account_address( - const tonlib_api::wallet_highload_v1_initialAccountState& test_wallet_state, td::int32 revision) { + const tonlib_api::wallet_highload_v1_initialAccountState& test_wallet_state, td::int32 revision, + ton::WorkchainId workchain_id) { 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*/, + workchain_id, ton::HighloadWallet::get_init_state(key, static_cast(test_wallet_state.wallet_id_), revision)); } td::Result get_account_address( - const tonlib_api::wallet_highload_v2_initialAccountState& test_wallet_state, td::int32 revision) { + const tonlib_api::wallet_highload_v2_initialAccountState& test_wallet_state, td::int32 revision, + ton::WorkchainId workchain_id) { 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*/, + workchain_id, ton::HighloadWalletV2::get_init_state(key, static_cast(test_wallet_state.wallet_id_), revision)); } td::Result get_account_address(const tonlib_api::dns_initialAccountState& dns_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { TRY_RESULT(key_bytes, get_public_key(dns_state.public_key_)); auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); - return ton::ManualDns::create(key, static_cast(dns_state.wallet_id_), revision)->get_address(); + return ton::ManualDns::create(key, static_cast(dns_state.wallet_id_), revision) + ->get_address(workchain_id); } td::Result get_account_address(const tonlib_api::pchan_initialAccountState& pchan_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { TRY_RESULT(config, to_pchan_config(pchan_state)); - return ton::PaymentChannel::create(config, revision)->get_address(); + return ton::PaymentChannel::create(config, revision)->get_address(workchain_id); } td::Result get_account_address(const tonlib_api::rwallet_initialAccountState& rwallet_state, - td::int32 revision) { + td::int32 revision, ton::WorkchainId workchain_id) { TRY_RESULT(init_data, to_init_data(rwallet_state)); - return ton::RestrictedWallet::create(init_data, revision)->get_address(); + return ton::RestrictedWallet::create(init_data, revision)->get_address(workchain_id); } td::Result get_adnl_address(td::Slice adnl_address) { @@ -1691,99 +1707,182 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } auto r_account_address = downcast_call2>( *request.initial_account_state_, - [&request](auto&& state) { return get_account_address(state, request.revision_); }); + [&request](auto&& state) { return get_account_address(state, request.revision_, request.workchain_id_); }); 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)); } -td::Status TonlibClient::do_request(const tonlib_api::guessAccountRevision& request, +td::Status TonlibClient::do_request(tonlib_api::guessAccountRevision& request, td::Promise>&& promise) { - if (!request.initial_account_state_) { - return TonlibError::EmptyField("initial_account_state"); - } - auto o_type = get_wallet_type(*request.initial_account_state_); - if (!o_type) { - promise.set_value(tonlib_api::make_object(std::vector{0})); - return td::Status::OK(); - } - auto revisions = ton::SmartContractCode::get_revisions(o_type.value()); - - std::vector> addresses; - TRY_STATUS(downcast_call2(*request.initial_account_state_, [&revisions, &addresses](const auto& state) { - for (auto revision : revisions) { - TRY_RESULT(address, get_account_address(state, revision)); - addresses.push_back(std::make_pair(revision, address)); + std::vector targets; + std::vector> states; + states.push_back(std::move(request.initial_account_state_)); + for (auto& initial_account_state : states) { + if (!initial_account_state) { + return TonlibError::EmptyField("initial_account_state"); } - return td::Status::OK(); - })); + auto o_type = get_wallet_type(*initial_account_state); + if (!o_type) { + continue; + } + auto type = o_type.unwrap(); + auto revisions = ton::SmartContractCode::get_revisions(type); + auto workchains = std::vector{request.workchain_id_}; + TRY_STATUS(downcast_call2( + *initial_account_state, [&revisions, &targets, &workchains, &type](const auto& state) { + for (auto workchain : workchains) { + for (auto revision : revisions) { + TRY_RESULT(address, get_account_address(state, revision, workchain)); + Target target; + target.can_be_empty = type != ton::SmartContractCode::Type::RestrictedWallet; + target.address = address; + targets.push_back(std::move(target)); + } + } + return td::Status::OK(); + })); + } + + return guess_revisions(std::move(targets), std::move(promise)); +} + +td::Status TonlibClient::do_request(tonlib_api::guessAccount& request, + td::Promise>&& promise) { + std::vector targets; + struct Source { + tonlib_api::object_ptr init_state; + ton::WorkchainId workchain_id; + }; + std::vector sources; + std::string rwallet_init_public_key = request.rwallet_init_public_key_; + if (rwallet_init_public_key.empty()) { + rwallet_init_public_key = rwallet_init_public_key_; + } + TRY_RESULT(key_bytes, get_public_key(request.public_key_)); + sources.push_back(Source{tonlib_api::make_object( + rwallet_init_public_key, request.public_key_, wallet_id_ + ton::masterchainId), + ton::masterchainId}); + sources.push_back(Source{tonlib_api::make_object( + request.public_key_, wallet_id_ + ton::masterchainId), + ton::masterchainId}); + sources.push_back(Source{tonlib_api::make_object( + request.public_key_, wallet_id_ + ton::basechainId), + ton::basechainId}); + for (Source& source : sources) { + auto o_type = get_wallet_type(*source.init_state); + if (!o_type) { + continue; + } + auto type = o_type.unwrap(); + auto revisions = ton::SmartContractCode::get_revisions(type); + auto workchains = std::vector{source.workchain_id}; + + TRY_STATUS(downcast_call2( + *source.init_state, [&revisions, &targets, &workchains, &type, &key_bytes](const auto& state) { + for (auto workchain : workchains) { + for (auto revision : revisions) { + TRY_RESULT(address, get_account_address(state, revision, workchain)); + Target target; + target.can_be_uninited = + type == ton::SmartContractCode::Type::WalletV3 && revision == 2 && workchain == ton::basechainId; + target.can_be_empty = type != ton::SmartContractCode::Type::RestrictedWallet || target.can_be_uninited; + target.address = address; + target.public_key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); + targets.push_back(std::move(target)); + } + } + return td::Status::OK(); + })); + } + + return guess_revisions(std::move(targets), std::move(promise)); +} + +td::Status TonlibClient::guess_revisions(std::vector targets, + td::Promise>&& promise) { auto actor_id = actor_id_++; class GuessRevisions : public TonlibQueryActor { public: GuessRevisions(td::actor::ActorShared client, td::optional block_id, - std::vector> addresses, td::Promise> promise) + std::vector targets, td::Promise>> promise) : TonlibQueryActor(std::move(client)) , block_id_(std::move(block_id)) - , addresses_(std::move(addresses)) + , targets_(std::move(targets)) , promise_(std::move(promise)) { } private: td::optional block_id_; - std::vector> addresses_; - td::Promise> promise_; + std::vector targets_; + td::Promise>> promise_; - size_t left_{0}; - struct Item { - bool is_inited; - td::int64 balance; - int revision; - auto key() const { - return std::make_tuple(is_inited, balance, revision); - } - bool operator<(const Item& other) const { - return key() > other.key(); - } - }; - std::vector res; + size_t left_{1}; + std::vector> res; - void start_up() { - left_ += addresses_.size(); - for (auto& p : addresses_) { - send_query(int_api::GetAccountState{p.second, block_id_.copy()}, - promise_send_closure(td::actor::actor_id(this), &GuessRevisions::on_account_state, p.first)); + void start_up() override { + left_ += targets_.size(); + for (auto& p : targets_) { + send_query(int_api::GetAccountState{p.address, block_id_.copy(), std::move(p.public_key)}, + promise_send_closure(td::actor::actor_id(this), &GuessRevisions::on_account_state, std::move(p))); } + on_account_state_finish(); } - void on_account_state(int revision, td::Result> r_state) { + void hangup() override { + promise_.set_error(TonlibError::Cancelled()); + return; + } + void on_account_state(Target target, td::Result> r_state) { + if (!r_state.is_ok()) { + promise_.set_error(r_state.move_as_error()); + stop(); + return; + } SCOPE_EXIT { on_account_state_finish(); }; - if (!r_state.is_ok()) { + auto state = r_state.move_as_ok(); + if (state->get_balance() < 0 && !target.can_be_uninited) { return; } - auto state = r_state.move_as_ok(); - if (state->get_balance() < 0) { + if (state->get_wallet_type() == AccountState::WalletType::Empty && !target.can_be_empty) { return; } - res.push_back({state->get_wallet_type() != AccountState::WalletType::Empty, state->get_balance(), revision}); + res.push_back(std::move(state)); } void on_account_state_finish() { left_--; if (left_ == 0) { - std::sort(res.begin(), res.end()); - promise_.set_value(td::transform(std::move(res), [](auto x) { return x.revision; })); + std::sort(res.begin(), res.end(), [](auto& x, auto& y) { + auto key = [](const td::unique_ptr& state) { + return std::make_tuple(state->get_wallet_type() != AccountState::WalletType::Empty, + state->get_wallet_type(), state->get_balance(), state->get_wallet_revision()); + }; + return key(x) > key(y); + }); + promise_.set_value(std::move(res)); stop(); } } }; actors_[actor_id] = td::actor::create_actor( - "GuessRevisions", actor_shared(this, actor_id), query_context_.block_id.copy(), std::move(addresses), - promise.wrap( - [](auto&& x) mutable { return tonlib_api::make_object(std::move(x)); })); + "GuessRevisions", actor_shared(this, actor_id), query_context_.block_id.copy(), std::move(targets), + promise.wrap([](auto&& v) mutable { + std::vector> res; + for (auto& x : v) { + auto r_state = x->to_fullAccountState(); + if (r_state.is_error()) { + LOG(ERROR) << "to_fullAccountState failed: " << r_state.error(); + continue; + } + res.push_back(r_state.move_as_ok()); + } + return tonlib_api::make_object(std::move(res)); + })); return td::Status::OK(); } @@ -1918,20 +2017,6 @@ const MasterConfig& get_default_master_config() { }, "init_block": {"workchain":-1,"shard":-9223372036854775808,"seqno":2908451,"root_hash":"5+7X1QHVUBFLFMwa/yd/2fGzt2KeQtwr+o6UUFOQ7Qc=","file_hash":"gmiUgrtAbvEJZYDEkcbeNOhGPS3g+qCepSOEBFLZFzk="} } -})abc"); - res.add_config("mainnet", R"abc({ - "liteservers": [ - ], - "validator": { - "@type": "validator.config.global", - "zero_state": { - "workchain": -1, - "shard": -9223372036854775808, - "seqno": 0, - "root_hash": "KbTmuSarbve4ce9SET3BRyDnlPZr4GMdWswt1nDQAl4=", - "file_hash": "yRCjDRa-ChWiAGf5n3b25U17aqKzVL13C2Tzz8sqtJA=" - } - } })abc"); return res; }(); @@ -1958,7 +2043,18 @@ td::Result TonlibClient::validate_config(tonlib_api::o o_master_config = get_default_master_config().by_root_hash(new_config.zero_state_id.root_hash); } else { last_state_key = config->blockchain_name_; + new_config.name = config->blockchain_name_; o_master_config = get_default_master_config().by_name(config->blockchain_name_); + if (!o_master_config) { + o_master_config = get_default_master_config().by_root_hash(new_config.zero_state_id.root_hash); + } + } + + if (o_master_config) { + auto name = o_master_config.value().name; + if (!name.empty() && !new_config.name.empty() && new_config.name != name && name == "mainnet") { + return TonlibError::InvalidConfig(PSLICE() << "Invalid blockchain_id: expected '" << name << "'"); + } } if (o_master_config && o_master_config.value().zero_state_id != new_config.zero_state_id) { @@ -2030,8 +2126,8 @@ td::Result TonlibClient::validate_config(tonlib_api::o res.config = std::move(new_config); 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()); - if (res.config.name.empty()) { // TODO == "mainnet" - res.wallet_id = 0x4BA92D89; + if (res.config.name == "mainnet") { + res.wallet_id = 0x4BA92D89 + 1; // user will subtract -1 for basechain } res.rwallet_init_public_key = "Puasxr0QfFZZnYISRphVse7XHKfW7pZU5SJarVHXvQ+rpzkD"; res.last_state_key = std::move(last_state_key); @@ -2044,6 +2140,7 @@ void TonlibClient::set_config(FullConfig full_config) { config_ = std::move(full_config.config); config_generation_++; wallet_id_ = full_config.wallet_id; + rwallet_init_public_key_ = full_config.rwallet_init_public_key; last_state_key_ = full_config.last_state_key; use_callbacks_for_network_ = full_config.use_callbacks_for_network; @@ -2327,7 +2424,7 @@ 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_)); - make_request(int_api::GetAccountState{std::move(account_address), query_context_.block_id.copy()}, + make_request(int_api::GetAccountState{std::move(account_address), query_context_.block_id.copy(), {}}, promise.wrap([](auto&& res) { return res->to_raw_fullAccountState(); })); return td::Status::OK(); } @@ -2396,7 +2493,7 @@ td::Status TonlibClient::do_request(const tonlib_api::getAccountState& request, 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), query_context_.block_id.copy()}, + make_request(int_api::GetAccountState{std::move(account_address), query_context_.block_id.copy(), {}}, promise.wrap([](auto&& res) { return res->to_fullAccountState(); })); return td::Status::OK(); } @@ -2628,13 +2725,13 @@ class GenericCreateSendGrams : public TonlibQueryActor { } TRY_STATUS(parse_action(*query_.action_)); - send_query(int_api::GetAccountState{source_address, block_id_.copy()}, + send_query(int_api::GetAccountState{source_address, block_id_.copy(), {}}, promise_send_closure(actor_id(this), &GenericCreateSendGrams::on_source_state)); destinations_.resize(actions_.size()); destinations_left_ = destinations_.size(); for (size_t i = 0; i < actions_.size(); i++) { - send_query(int_api::GetAccountState{actions_[i].destination, block_id_.copy()}, + send_query(int_api::GetAccountState{actions_[i].destination, block_id_.copy(), {}}, promise_send_closure(actor_id(this), &GenericCreateSendGrams::on_destination_state, i)); } @@ -2875,7 +2972,6 @@ class GenericCreateSendGrams : public TonlibQueryActor { return TonlibError::EmptyField("private_key"); } auto rwallet = ton::RestrictedWallet::create(source_->get_smc_state()); - LOG(ERROR) << rwallet->get_address().rserialize(true); return downcast_call2( *query_.action_, td::overloaded([&](auto& cell) { return td::Status::Error("UNREACHABLE"); }, @@ -2896,7 +2992,7 @@ class GenericCreateSendGrams : public TonlibQueryActor { if (source_->get_wallet_type() == AccountState::PaymentChannel) { return do_pchan_loop(); } - if (rwallet_action_) { + if (rwallet_action_ && source_->get_wallet_type() == AccountState::RestrictedWallet) { return do_rwallet_action(); } @@ -2922,6 +3018,19 @@ class GenericCreateSendGrams : public TonlibQueryActor { return TonlibError::NotEnoughFunds(); } + // Temporary turn off this dangerous transfer + if (amount == source_->get_balance()) { + return TonlibError::NotEnoughFunds(); + } + + if (source_->get_wallet_type() == AccountState::RestrictedWallet) { + auto r_unlocked_balance = ton::RestrictedWallet::create(source_->get_smc_state()) + ->get_balance(source_->get_balance(), source_->get_sync_time()); + if (r_unlocked_balance.is_ok() && amount > static_cast(r_unlocked_balance.ok())) { + return TonlibError::NotEnoughFunds(); + } + } + auto valid_until = source_->get_sync_time(); valid_until += query_.timeout_ == 0 ? 60 : query_.timeout_; std::vector gifts; @@ -2931,9 +3040,12 @@ class GenericCreateSendGrams : public TonlibQueryActor { auto& destination = destinations_[i]; gift.destination = destinations_[i]->get_address(); gift.gramms = action.amount; - if (action.amount == source_->get_balance()) { + + // Temporary turn off this dangerous transfer + if (false && action.amount == source_->get_balance()) { gift.gramms = -1; } + if (action.body.not_null()) { gift.body = action.body; gift.init_state = action.init_state; @@ -3114,7 +3226,7 @@ td::Status TonlibClient::do_request(const tonlib_api::raw_createQuery& request, td::Promise> new_promise = promise.send_closure(actor_id(this), &TonlibClient::finish_create_query); - make_request(int_api::GetAccountState{account_address, query_context_.block_id.copy()}, + make_request(int_api::GetAccountState{account_address, query_context_.block_id.copy(), {}}, new_promise.wrap([smc_state = std::move(smc_state), body = std::move(body)](auto&& source) mutable { Query::Raw raw; if (smc_state) { @@ -3217,7 +3329,7 @@ td::Status TonlibClient::do_request(const tonlib_api::smc_load& request, 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), query_context_.block_id.copy()}, + make_request(int_api::GetAccountState{std::move(account_address), query_context_.block_id.copy(), {}}, promise.send_closure(actor_id(this), &TonlibClient::finish_load_smc)); return td::Status::OK(); } @@ -3454,7 +3566,7 @@ void TonlibClient::do_dns_request(std::string name, td::int32 category, td::int3 promise.send_closure(actor_id(this), &TonlibClient::finish_dns_resolve, name, category, ttl, std::move(block_id)); if (0) { - make_request(int_api::GetAccountState{address, std::move(block_id_copy)}, + make_request(int_api::GetAccountState{address, std::move(block_id_copy), {}}, new_promise.wrap([](auto&& account_state) { return DnsFinishData{account_state->get_block_id(), account_state->get_smc_state()}; })); @@ -3844,8 +3956,13 @@ td::Status TonlibClient::do_request(int_api::GetAccountState request, actors_[actor_id] = td::actor::create_actor( "GetAccountState", client_.get_client(), request.address, std::move(request.block_id), actor_shared(this, actor_id), - promise.wrap([address = request.address, wallet_id = wallet_id_](auto&& state) mutable { - return td::make_unique(std::move(address), std::move(state), wallet_id); + promise.wrap([address = request.address, wallet_id = wallet_id_, + o_public_key = std::move(request.public_key)](auto&& state) mutable { + auto res = td::make_unique(std::move(address), std::move(state), wallet_id); + if (false && o_public_key) { + res->guess_type_by_public_key(o_public_key.value()); + } + return res; })); return td::Status::OK(); } diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index a1f9d875..f13a432b 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -85,6 +85,7 @@ class TonlibClient : public td::actor::Actor { Config config_; td::uint32 config_generation_{0}; td::uint32 wallet_id_; + std::string rwallet_init_public_key_; std::string last_state_key_; bool use_callbacks_for_network_{false}; @@ -234,7 +235,9 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::getAccountState& request, td::Promise>&& promise); - td::Status do_request(const tonlib_api::guessAccountRevision& request, + td::Status do_request(tonlib_api::guessAccountRevision& request, + td::Promise>&& promise); + td::Status do_request(tonlib_api::guessAccount& request, td::Promise>&& promise); td::Status do_request(tonlib_api::sync& request, td::Promise>&& promise); @@ -341,5 +344,14 @@ class TonlibClient : public td::actor::Actor { void proxy_request(td::int64 query_id, std::string data); friend class TonlibQueryActor; + struct Target { + bool can_be_empty{true}; + bool can_be_uninited{false}; + block::StdAddress address; + td::optional public_key; + }; + + td::Status guess_revisions(std::vector targets, + td::Promise>&& promise); }; } // namespace tonlib diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index b5d23c90..4fec3db6 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -118,6 +118,7 @@ class TonlibCli : public td::actor::Actor { td::int32 wallet_version = 2; td::int32 wallet_revision = 0; td::optional wallet_id; + td::optional workchain_id; bool ignore_cache{false}; bool one_shot{false}; @@ -133,6 +134,7 @@ class TonlibCli : public td::actor::Actor { std::uint64_t next_query_id_{1}; td::Promise cont_; td::uint32 wallet_id_; + td::int32 workchain_id_; ton::tonlib_api::object_ptr current_block_; enum class BlockMode { Auto, Manual } block_mode_ = BlockMode::Auto; @@ -242,6 +244,11 @@ class TonlibCli : public td::actor::Actor { } else { wallet_id_ = static_cast(r_ok.ok()->config_info_->default_wallet_id_); } + if (options_.workchain_id) { + workchain_id_ = options_.workchain_id.value(); + } else { + workchain_id_ = 0; + } } load_channnels(); td::TerminalIO::out() << "Tonlib is inited\n"; @@ -361,6 +368,7 @@ class TonlibCli : public td::actor::Actor { " with specified parameters\n"; td::TerminalIO::out() << "getstate \tget state of wallet with requested key\n"; td::TerminalIO::out() << "guessrevision \tsearch of existing accounts corresponding to the given key\n"; + td::TerminalIO::out() << "guessaccount \tsearch of existing accounts corresponding to the given key\n"; td::TerminalIO::out() << "getaddress \tget address of wallet with requested key\n"; dns_help(); @@ -473,6 +481,10 @@ class TonlibCli : public td::actor::Actor { get_history(parser.read_word(), std::move(cmd_promise)); } else if (cmd == "guessrevision") { guess_revision(parser.read_word(), std::move(cmd_promise)); + } else if (cmd == "guessaccount") { + auto key = parser.read_word(); + auto init_key = parser.read_word(); + guess_account(key, init_key, std::move(cmd_promise)); } else { cmd_promise.set_error(td::Status::Error(PSLICE() << "Unkwnown query `" << cmd << "`")); } @@ -487,7 +499,8 @@ class TonlibCli : public td::actor::Actor { TRY_RESULT_PROMISE( promise, addr, sync_send_query(make_object( - make_object(address.public_key, public_key, wallet_id_), 1))); + make_object(address.public_key, public_key, wallet_id_ - 1), 1, + -1))); td::TerminalIO::out() << addr->account_address_ << "\n"; promise.set_value(td::Unit()); } @@ -496,11 +509,12 @@ class TonlibCli : public td::actor::Actor { TRY_RESULT_PROMISE(promise, address, to_account_address(parser.read_word(), false)); auto public_key = parser.read_word().str(); auto initial_state = - make_object(address.public_key, public_key, wallet_id_); + make_object(address.public_key, public_key, wallet_id_ - 1); TRY_RESULT_PROMISE( promise, addr, sync_send_query(make_object( - make_object(address.public_key, public_key, wallet_id_), 1))); + make_object(address.public_key, public_key, wallet_id_ - 1), 1, + -1))); TRY_RESULT_PROMISE(promise, start_at, td::to_integer_safe(parser.read_word())); std::vector> limits; @@ -690,7 +704,7 @@ class TonlibCli : public td::actor::Actor { td::Status do_pchan_create(td::ConstParser& parser, bool gen_channel_id) { Channel channel; TRY_STATUS(channel.parse(parser, gen_channel_id)); - TRY_RESULT(addr, sync_send_query(make_object(channel.to_init_state(), -1))); + TRY_RESULT(addr, sync_send_query(make_object(channel.to_init_state(), -1, 0))); channel.address = addr->account_address_; auto find_id = [&](td::Slice public_key, td::Slice address) -> td::optional { @@ -1594,9 +1608,10 @@ class TonlibCli : public td::actor::Actor { td::Result
to_account_address(td::Slice public_key) { auto r_addr = [&, self = this](td::int32 version, td::int32 revision) { auto do_request = [revision, self](auto x) { - return self->sync_send_query(make_object(std::move(x), revision)); + return self->sync_send_query( + make_object(std::move(x), revision, self->workchain_id_)); }; - return with_account_state(version, public_key.str(), wallet_id_, do_request); + return with_account_state(version, public_key.str(), wallet_id_ + workchain_id_, do_request); }(options_.wallet_version, options_.wallet_revision); TRY_RESULT(addr, std::move(r_addr)); Address res; @@ -1641,7 +1656,7 @@ class TonlibCli : public td::actor::Actor { } if (key == "giver") { auto obj = tonlib::TonlibClient::static_request( - make_object(make_object(), 0)); + make_object(make_object(), 0, -1)); if (obj->get_id() != tonlib_api::error::ID) { Address res; res.address = ton::move_tl_object_as(obj); @@ -1846,13 +1861,22 @@ class TonlibCli : public td::actor::Actor { void guess_revision(td::Slice key, td::Promise promise) { TRY_RESULT_PROMISE(promise, key_i, to_key_i(key)); with_account_state(options_.wallet_version, keys_[key_i].public_key, wallet_id_, [&](auto state) { - send_query(make_object(std::move(state)), promise.wrap([](auto revisions) { + send_query(make_object(std::move(state), 0), promise.wrap([](auto revisions) { td::TerminalIO::out() << to_string(revisions); return td::Unit(); })); }); } + void guess_account(td::Slice key, td::Slice init_public_key, td::Promise promise) { + TRY_RESULT_PROMISE(promise, address, to_account_address(key, false)); + send_query(make_object(address.public_key, init_public_key.str()), + promise.wrap([](auto revisions) { + td::TerminalIO::out() << to_string(revisions); + return td::Unit(); + })); + } + void get_history2(td::Slice key, td::Result> r_state, td::Promise promise) { TRY_RESULT_PROMISE(promise, state, std::move(r_state)); @@ -2105,6 +2129,12 @@ int main(int argc, char* argv[]) { options.wallet_id = wallet_id; return td::Status::OK(); }); + p.add_option('x', "workchain", "default workchain", [&](td::Slice arg) { + TRY_RESULT(workchain_id, td::to_integer_safe((arg))); + options.workchain_id = workchain_id; + LOG(INFO) << "Use workchain_id = " << workchain_id; + return td::Status::OK(); + }); p.add_option('W', "wallet-version", "do not use this (version[.revision])", [&](td::Slice arg) { td::ConstParser parser(arg); TRY_RESULT(version, td::to_integer_safe((parser.read_till_nofail('.')))); diff --git a/validator/db/archive-manager.cpp b/validator/db/archive-manager.cpp index 2ea3b583..fb8f2c00 100644 --- a/validator/db/archive-manager.cpp +++ b/validator/db/archive-manager.cpp @@ -289,6 +289,22 @@ void ArchiveManager::get_file(ConstBlockHandle handle, FileReference ref_id, td: return; } } + if (handle->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) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this), ref_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_file_short_cont, ref_id, idx, std::move(promise)); + } + }); + td::actor::send_closure(f->file_actor_id(), &ArchiveSlice::get_file, std::move(handle), std::move(ref_id), + std::move(P)); + return; + } + } get_file_short_cont(std::move(ref_id), get_max_temp_file_desc_idx(), std::move(promise)); } @@ -806,9 +822,6 @@ PackageId ArchiveManager::get_max_temp_file_desc_idx() { 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); } diff --git a/validator/db/archiver.cpp b/validator/db/archiver.cpp index 88094e3c..c8cbd7b9 100644 --- a/validator/db/archiver.cpp +++ b/validator/db/archiver.cpp @@ -58,7 +58,7 @@ void BlockArchiver::moved_handle() { td::actor::send_closure(SelfId, &BlockArchiver::got_proof, R.move_as_ok()); }); - td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::Proof{handle_->id()}, std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::get_file, handle_, fileref::Proof{handle_->id()}, std::move(P)); } void BlockArchiver::got_proof(td::BufferSlice data) { @@ -81,7 +81,7 @@ void BlockArchiver::written_proof() { td::actor::send_closure(SelfId, &BlockArchiver::got_proof_link, R.move_as_ok()); }); - td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::ProofLink{handle_->id()}, + td::actor::send_closure(archive_, &ArchiveManager::get_file, handle_, fileref::ProofLink{handle_->id()}, std::move(P)); } @@ -104,7 +104,7 @@ void BlockArchiver::written_proof_link() { td::actor::send_closure(SelfId, &BlockArchiver::got_block_data, R.move_as_ok()); }); - td::actor::send_closure(archive_, &ArchiveManager::get_temp_file_short, fileref::Block{handle_->id()}, std::move(P)); + td::actor::send_closure(archive_, &ArchiveManager::get_file, handle_, fileref::Block{handle_->id()}, std::move(P)); } void BlockArchiver::got_block_data(td::BufferSlice data) { diff --git a/validator/full-node-shard.cpp b/validator/full-node-shard.cpp index b70904fa..a4986bd5 100644 --- a/validator/full-node-shard.cpp +++ b/validator/full-node-shard.cpp @@ -579,7 +579,11 @@ void FullNodeShardImpl::process_broadcast(PublicKeyHash src, ton_api::tonNode_bl auto P = td::PromiseCreator::lambda([](td::Result R) { if (R.is_error()) { - LOG(INFO) << "dropped broadcast: " << R.move_as_error(); + if (R.error().code() == ErrorCode::notready) { + LOG(DEBUG) << "dropped broadcast: " << R.move_as_error(); + } else { + LOG(INFO) << "dropped broadcast: " << R.move_as_error(); + } } }); td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::prevalidate_block, std::move(B),