diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index ab9f72c2..9a70fdb8 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1082,6 +1082,7 @@ variable @proclist variable @procdict variable @procinfo variable @gvarcnt +variable asm-mode 1 asm-mode ! 19 constant @procdictkeylen 32 constant @zcount { pair @proclist @ cons @proclist ! } : @proclistadd @@ -1144,8 +1145,18 @@ variable @gvarcnt { ."Procedure `" over type ."` index=" 2 pick . ." flags=0x" dup x. cr } : @showprocinfo // ( proc_name proc_idx f -- ) f:+1=declared, +2=defined, +4=inlined, +8=called, +16=method { // @showprocinfo - dup 0x1a and 2 = { 2 pick @remove-proc // over ."Removing " type cr + dup 0x1a and 2 = asm-mode @ 3 and and ?dup { + 2 and { + over ."Warning: removing (inlined) procedure `" type ."` from call dictionary" cr + } if + 2 pick @remove-proc } if // remove unused procs + dup 0xc and 0xc = asm-mode @ 4 and and { + over ."Warning: inline procedure `" type ."` is not always inlined" cr + } if + dup 0x1e and 2 = asm-mode @ 8 and and { + over ."Warning: procedure `" type ."` defined but not used" cr + } if drop 2drop } : @chkprocdef { @chkmaindef @@ -1170,6 +1181,14 @@ forget @proclist forget @proccnt -3 constant split_prepare -4 constant split_install +{ asm-mode 0 3 ~! } : asm-no-remove-unused +{ asm-mode 1 1 ~! } : asm-remove-unused // enabled by default +{ asm-mode 3 3 ~! } : asm-warn-remove-unused +{ asm-mode 4 4 ~! } : asm-warn-inline-mix +{ asm-mode 0 4 ~! } : asm-no-warn-inline-mix // disabled by default +{ asm-mode 8 8 ~! } : asm-warn-unused +{ asm-mode 0 8 ~! } : asm-no-warn-unused // disabled by default + // ( c -- ) add vm library for later use with runvmcode { = -rot <= and } : s-fits? +// b s x -- ? +{ swap sbitrefs -rot + rot brembitrefs -rot <= -rot <= and } : s-fits-with? { 0 swap ! } : 0! { tuck @ + swap ! } : +! { tuck @ swap - swap ! } : -! { 1 swap +! } : 1+! { -1 swap +! } : 1-! { null swap ! } : null! +{ not 2 pick @ and xor swap ! } : ~! 0 tuple constant nil { 1 tuple } : single { 2 tuple } : pair diff --git a/crypto/fift/lib/TonUtil.fif b/crypto/fift/lib/TonUtil.fif index ea4fa926..313b2639 100644 --- a/crypto/fift/lib/TonUtil.fif +++ b/crypto/fift/lib/TonUtil.fif @@ -69,6 +69,9 @@ library TonUtil // TON Blockchain Fift Library // ( b wc addr -- b' ) Serializes address into Builder b { -rot 8 i, swap 256 u, } : addr, +{ over 8 fits { rot b{100} s, -rot addr, } { + rot b{110} s, 256 9 u, rot 32 i, swap 256 u, } cond +} : Addr, // Gram utilities 1000000000 constant Gram @@ -171,3 +174,24 @@ recursive append-long-bytes { { dup $len 55 <> abort"not 55 alphanumeric characters" "F" swap $+ Base32>B 33 B| 16 B>u@ over crc16 <> abort"crc16 checksum mismatch" 8 B>u@+ 0x2D <> abort"not a valid adnl address" 256 B>u@ } : $>adnl + +{ 65 - dup 0>= { -33 and 10 + dup 16 < } { 17 + dup 0>= over 10 < and } cond ?dup nip } : hex-digit? +// ( S -- x -1 or 0 ) Parses a hexadecimal integer +{ dup $len { + 0 { + 4 << swap 1 $| -rot (char) hex-digit? // S a d -1 or S a 0 + { + over $len 0= } { drop -1 true } cond + } until + dup 0< { 2drop false } { nip true } cond + } { drop false } cond +} : hex$>u? +// ( S -- x ) +{ hex$>u? not abort"not a hexadecimal number" } : hex$>u + +{ dup $len 64 = { hex$>u } { + dup $len 55 = { $>adnl } { + true abort"invalid adnl address" + } cond } cond +} : parse-adnl-addr +{ adnl>$ type } : .adnl +{ bl word parse-adnl-addr 1 'nop } ::_ adnl: diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index 01a43f63..c83eedd1 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -18,6 +18,7 @@ */ #include "func.h" #include "td/utils/crypto.h" +#include "common/refint.h" #include namespace sym { @@ -1244,8 +1245,9 @@ void parse_func_def(Lexer& lex) { } if (val->method_id.is_null()) { val->method_id = std::move(method_id); - } else if (val->method_id != method_id) { - lex.cur().error("integer method identifier for `"s + func_name.str + "` changed to a different value"); + } else if (td::cmp(val->method_id, method_id) != 0) { + lex.cur().error("integer method identifier for `"s + func_name.str + "` changed from " + + val->method_id->to_dec_string() + " to a different value " + method_id->to_dec_string()); } } if (f) { diff --git a/crypto/smartcont/dns-manual-code.fc b/crypto/smartcont/dns-manual-code.fc index 4a4b82fa..f1e7e7aa 100644 --- a/crypto/smartcont/dns-manual-code.fc +++ b/crypto/smartcont/dns-manual-code.fc @@ -99,22 +99,44 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; [UInt<256b>:new_public_key] -} +() after_code_upgrade(cell root, slice ops, cont old_code) impure method_id(1666); + (cell, slice) process_op(cell root, slice ops) inline_ref { int op = ops~load_uint(6); - int is_name_ref = (ops~load_uint(1) == 1); - - ;; lets assume at this point that special operations 00..09 are handled - throw_if(45, op < 10); - slice name = ops; ;; anything! better do not begin or it costs much gas - cell cat_table = null(); + if (op < 10) { + ifnot (op) { + ;; 00 Noop: No operation + return (root, ops); + } + if (op == 1) { + ;; 01 SMsg: Send Message + var mode = ops~load_uint(8); + send_raw_message(ops~load_ref(), mode); + return (root, ops); + } + if (op == 9) { + ;; 09 CodeUpgrade + var new_code = ops~load_ref(); + set_code(new_code); + var old_code = get_c3(); + set_c3(new_code.begin_parse().bless()); + after_code_upgrade(root, ops, old_code); + throw(0); + return (root, ops); + } + throw(45); + return (root, ops); + } int cat = 0; if (op < 20) { ;; for operations with codes 10..19 category is required cat = ops~load_int(16); } - int zeros = 0; + slice name = null(); ;; any slice value + cell cat_table = null(); if (op < 30) { ;; for operations with codes 10..29 name is required + int is_name_ref = (ops~load_uint(1) == 1); if (is_name_ref) { ;; name is stored in separate referenced cell name = ops~load_ref().begin_parse(); @@ -129,17 +151,18 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; int name_last_byte = name.slice_last(8).preload_uint(8); throw_if(40, name_last_byte); ;; count zero separators + int zeros = 0; slice cname = name; repeat (cname.slice_bits() ^>> 3) { int c = cname~load_uint(8); zeros -= (c == 0); } ;; throw_unless(39, zeros == 1); + name = begin_cell().store_uint(zeros, 7).store_slice(name).end_cell().begin_parse(); } ;; operation with codes 10..19 manipulate category dict ;; lets try to find it and store into a variable ;; operations with codes 20..29 replace / delete dict, no need - name = begin_cell().store_uint(zeros, 7).store_slice(name).end_cell().begin_parse(); if (op < 20) { ;; lets resolve the name here so as not to duplicate the code (slice pfx, cell val, slice tail, int succ) = @@ -187,17 +210,21 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; return (null(), ops); } throw(44); ;; invalid operation - return (root, ops); + return (null(), ops); } cell process_ops(cell root, slice ops) inline_ref { var stop = false; + root~touch(); + ops~touch(); do { (root, ops) = process_op(root, ops); - if (ops.slice_refs_empty?()) { - stop = true; - } else { - ops = ops~load_ref().begin_parse(); + if (ops.slice_data_empty?()) { + if (ops.slice_refs()) { + ops = ops~load_ref().begin_parse(); + } else { + stop = true; + } } } until (stop); return root; @@ -205,24 +232,25 @@ cell process_ops(cell root, slice ops) inline_ref { () recv_external(slice in_msg) impure { ;; Load data - (int stored_subwalet, int last_cleaned, int public_key, cell root, cell old_queries) = load_data(); + (int contract_id, int last_cleaned, int public_key, cell root, cell old_queries) = load_data(); ;; validate signature and seqno slice signature = in_msg~load_bits(512); int shash = slice_hash(in_msg); - var (contract_id, query_id) = (in_msg~load_uint(32), in_msg~load_uint(64)); + var (query_contract, query_id) = (in_msg~load_uint(32), in_msg~load_uint(64)); var bound = (now() << 32); throw_if(35, query_id < bound); (_, var found?) = old_queries.udict_get?(64, query_id); throw_if(32, found?); - throw_unless(34, check_signature(shash, signature, public_key)); + throw_unless(34, contract_id == query_contract); + throw_unless(35, check_signature(shash, signature, public_key)); accept_message(); ;; message is signed by owner, sanity not guaranteed yet int op = in_msg.preload_uint(6); if (op == 51) { in_msg~skip_bits(6); public_key = in_msg~load_uint(256); - } elseif (op) { ;; 00 Contract initialization message + } else { root = process_ops(root, in_msg); } @@ -244,6 +272,9 @@ cell process_ops(cell root, slice ops) inline_ref { store_data(contract_id, last_cleaned, public_key, root, old_queries); } +() after_code_upgrade(cell root, slice ops, cont old_code) impure method_id(1666) { +} + {- Data structure: Root cell: [UInt<32b>:seqno] [UInt<256b>:owner_public_key] @@ -268,6 +299,12 @@ int get_contract_id() method_id { return get_data().begin_parse().preload_uint(32); } +int get_public_key() method_id { + var cs = get_data().begin_parse(); + cs~load_uint(32 + 64); + return cs.preload_uint(256); +} + ;;8m dns-record-value (int, cell) dnsresolve(slice subdomain, int category) method_id { int bits = subdomain.slice_bits(); diff --git a/crypto/smartcont/highload-wallet-v2.fif b/crypto/smartcont/highload-wallet-v2.fif index ba1fc801..fd71f52a 100755 --- a/crypto/smartcont/highload-wallet-v2.fif +++ b/crypto/smartcont/highload-wallet-v2.fif @@ -50,7 +50,7 @@ variable order# order# 0! orders ! order# 1+! } : add-order // b body -- b' -{ tuck [-t] [-o] [...]" +cr +tab + +"Creates a request to managed DNS smart contract created by new-manual-dns.fif, with private key loaded from file .pk " + +"and address from -dns.addr, and saves it into ('" savefile $+ +"' by default)" + +cr +" is an operation description, one of" +cr +tab + +"add cat (smc | next | adnl | text )" +cr +tab + +"delete cat " +cr +tab + +"drop " + disable-digit-options generic-help-setopt + "t" "--timeout" { parse-int =: timeout } short-long-option-arg + "Sets expiration timeout in seconds (" timeout (.) $+ +" by default)" option-help + "o" "--output" { =: savefile } short-long-option-arg + "Sets output file for generated initialization message ('" savefile $+ +"' by default)" option-help + "h" "--help" { usage } short-long-option + "Shows a help message" option-help +parse-options + +$# 2 < ' usage if +2 :$1..n + +$1 =: file-base +$2 parse-int dup =: contract-id 32 fits ' usage ifnot +{ contract-id (.) $+ } : +contractid + +{ $* @ dup null? { second $@ ! } { drop } cond } : @skip +{ $* @ null? } : @end? +{ $* @ uncons $* ! } : @next +@next @next 2drop + +variable Actions +{ Actions @ cons Actions ! } : register-action + +{ @end? abort"subdomain name expected" @next dup $len 127 > abort"subdomain name too long" +} : parse-domain +{ @end? abort"category number expected" @next (number) 1 <> abort"category must be integer" + dup 16 fits not abort"category does not fit into 16 bit integer" + dup 0= abort"category must be non-zero" +} : parse-cat-num +{ @end? abort"`cat` expected" @next "cat" $= not abort"`cat` expected" parse-cat-num +} : parse-cat +{ @end? abort"smart contract address expected" + @next false parse-load-address drop triple +} : cl-parse-smc-addr +{ @end? abort"adnl address expected" + `adnl @next parse-adnl-addr pair +} : cl-parse-adnl-addr +{ @end? abort"subdomain record value expected" @next + dup "smc" $= { drop `smc cl-parse-smc-addr } { + dup "next" $= { drop `next cl-parse-smc-addr } { + dup "adnl" $= { drop cl-parse-adnl-addr } { + dup "text" $= { drop `text @next pair } { + "unknown record type "' swap $+ +"'" abort + } cond } cond } cond } cond +} : parse-value +{ ."Loading new code BoC from " dup type cr + file>B B>boc +} : load-new-code-from +{ @next dup "add" $= { drop `add parse-domain parse-cat parse-value 4 tuple register-action } { + dup "delete" $= { drop `delete parse-domain parse-cat triple register-action } { + dup "drop" $= { drop `drop parse-domain pair register-action } { + dup "upgrade" $= { drop `upgrade @next load-new-code-from pair register-action } { + "unknown action '" swap $+ +"'" abort + } cond } cond } cond } cond +} : parse-action +{ { @end? not } { parse-action } while } : parse-actions +parse-actions + +file-base +".pk" load-keypair nip constant wallet_pk +file-base +"-dns" +contractid +".addr" load-address +2dup 2constant smc_addr +."Managed manual DNS smart contract address = " 2dup .addr cr 6 .Addr cr + +."Actions: " Actions @ list-reverse .l cr + +// ( S -- S1 .. Sn n ) +{ 1 swap { dup "." $pos dup 0>= } { $| 1 $| nip rot 1+ swap } while drop swap +} : split-by-dots +// ( S -- s ) +{ dup $len dup 0= abort"subdomain cannot be empty" 126 > abort"subdomain too long" + dup 0 chr $pos 1+ abort"subdomain contains null characters" + split-by-dots s +// ( b V -- b' ) +{ dup first + dup `smc eq? { drop untriple rot nip rot x{9fd3} s, -rot Addr, 0 8 u, } { + dup `next eq? { drop untriple rot nip rot x{ba93} s, -rot Addr, 0 8 u, } { + dup `adnl eq? { drop second swap x{ad01} s, swap 256 u, } { + dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } { + abort"unknown value type" + } cond } cond } cond } cond +} : value, +{ subdomain>s dup sbits 3 >> + dup 63 > { drop s>c dict, } { rot swap 7 u, swap s, } cond +} : subdomain, +// ( A -- b ) +{ dup first + dup `add eq? { + drop 4 untuple -rot + b +// ( -- b ) +{ Actions @ dup null? { drop } { + uncons swap action>b { over null? not } { + b> swap uncons swap action>b rot ref, + } while nip } cond +} : serialize-actions +serialize-actions +dup brembits 888 < { b> +."Serialized actions are " hashu 32 1<<1- and + =: query_id + +dup ."signing message: " +dup ."resulting external message: " B dup Bx. cr +."Query_id is " query_id dup . ."= 0x" X. cr +."Query expires in " timeout . ."seconds" cr +savefile tuck B>file +."(Saved to file " type .")" cr diff --git a/crypto/smartcont/new-manual-dns.fif b/crypto/smartcont/new-manual-dns.fif new file mode 100644 index 00000000..47b57bbe --- /dev/null +++ b/crypto/smartcont/new-manual-dns.fif @@ -0,0 +1,63 @@ +#!/usr/bin/fift -s +"TonUtil.fif" include +"Asm.fif" include +"GetOpt.fif" include + +{ show-options-help 1 halt } : usage + +Basechain =: wc // create smart contract in basechain +65536 =: timeout +"new-dns-query.boc" =: savefile +variable dns-dict dictnew dns-dict ! + +begin-options + " [-w] [-t] [-o]" +cr +tab + +"Creates a new manual dns smart contract with 32-bit identifier managed by private key .pk, " + +"and saves it into ('" savefile $+ +"' by default)" + disable-digit-options generic-help-setopt + "w" "--workchain" { parse-workchain-id =: wc } short-long-option-arg + "Selects workchain to create smart contract (" wc (.) $+ +" by default)" option-help + "t" "--timeout" { parse-int =: timeout } short-long-option-arg + "Sets expiration timeout for the initialization message in seconds (" timeout (.) $+ +" by default)" option-help + "o" "--output" { =: savefile } short-long-option-arg + "Sets output file for generated initialization message ('" savefile $+ +"' by default)" option-help + "h" "--help" { usage } short-long-option + "Shows a help message" option-help +parse-options + +$# 2 <> ' usage if +2 :$1..n +$1 =: file-base +$2 parse-int dup =: contract-id +32 fits ' usage ifnot +{ contract-id (.) $+ } : +contractid + +."Creating new manual DNS smart contract in workchain " wc . +."with contract id " contract-id . cr + +// Create new manual DNS; source code included from `auto/dns-manual-code.fif` +"auto/dns-manual-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 dns smart-contract creating query to file " type .")" cr diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 0547a57d..9cc820d3 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -140,6 +140,14 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va (cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; (cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; (cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; +(int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) udict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) idict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; (int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; (int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; (int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; diff --git a/crypto/smartcont/wallet-v2.fif b/crypto/smartcont/wallet-v2.fif index 6bf12103..f01ca47e 100755 --- a/crypto/smartcont/wallet-v2.fif +++ b/crypto/smartcont/wallet-v2.fif @@ -55,8 +55,8 @@ dest_addr 2dup bounce 7 + .Addr ." = " .addr ."Body of transfer message is " body-cell dup ."signing message: " dup ."signing message: " dup ."signing message: " > ManualDns::create_set_value_unsigned(td::int16 cat vm::CellBuilder cb; cb.store_long(11, 6); if (name.size() <= 58 - 2) { - cb.store_long(0, 1); cb.store_long(category, 16); + cb.store_long(0, 1); cb.store_long(name.size(), 6); cb.store_bytes(name); } else { - cb.store_long(1, 1); cb.store_long(category, 16); + cb.store_long(1, 1); cb.store_ref(vm::CellBuilder().store_bytes(name).finalize()); } cb.store_maybe_ref(std::move(data)); @@ -220,16 +220,15 @@ td::Result> ManualDns::create_delete_value_unsigned(td::int16 vm::CellBuilder cb; cb.store_long(12, 6); if (name.size() <= 58 - 2) { - cb.store_long(0, 1); cb.store_long(category, 16); + cb.store_long(0, 1); cb.store_long(name.size(), 6); cb.store_bytes(name); } else { - cb.store_long(1, 1); cb.store_long(category, 16); + cb.store_long(1, 1); cb.store_ref(vm::CellBuilder().store_bytes(name).finalize()); } - cb.store_long(0, 1); return cb.finalize(); } @@ -237,7 +236,6 @@ td::Result> ManualDns::create_delete_all_unsigned() const { // 32 TDel: nullify ENTIRE DOMAIN TABLE (x=-) vm::CellBuilder cb; cb.store_long(32, 6); - cb.store_long(0, 1); return cb.finalize(); } @@ -269,7 +267,6 @@ td::Result> ManualDns::create_set_all_unsigned(td::Span> ManualDns::create_delete_name_unsigned(td::Slice n cb.store_long(1, 1); cb.store_ref(vm::CellBuilder().store_bytes(name).finalize()); } - cb.store_long(0, 1); return cb.finalize(); } td::Result> ManualDns::create_set_name_unsigned(td::Slice name, td::Span entries) const { @@ -344,7 +340,6 @@ td::Result> ManualDns::create_init_query(const td::Ed25519::Pr td::uint32 valid_until) const { vm::CellBuilder cb; cb.store_long(0, 6); - cb.store_long(0, 1); TRY_RESULT(prepared, prepare(cb.finalize(), valid_until)); return sign(private_key, std::move(prepared)); diff --git a/crypto/smc-envelope/SmartContractCode.cpp b/crypto/smc-envelope/SmartContractCode.cpp index c97a689c..ebac2166 100644 --- a/crypto/smc-envelope/SmartContractCode.cpp +++ b/crypto/smc-envelope/SmartContractCode.cpp @@ -49,19 +49,19 @@ const auto& get_map() { "0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhghgBD0eG+hb6EgmALTB9QwAfsAkTLiAbPmWwGkyMsfyx/L/" "8ntVAAE0DAAEaCZL9qJoa4WPw=="); with_tvm_code("highload-wallet-r2", - "te6ccgEBCAEAmQABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/" - "0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhghgBD0eG+hb6EgmALTB9QwAfsAkTLiAbPmWwGkyMsfyx/L/" - "8ntVAAE0DACAUgGBwAXuznO1E0NM/MdcL/4ABG4yX7UTQ1wsfg="); + "te6ccgEBCAEAlwABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQC48oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/" + "0VEyuvKhUUS68qIE+QFUEFX5EPKj9ATR+AB/jhYhgBD0eG+lIJgC0wfUMAH7AJEy4gGz5lsBpMjLH8sfy//" + "J7VQABNAwAgFIBgcAF7s5ztRNDTPzHXC/+AARuMl+1E0NcLH4"); with_tvm_code("highload-wallet-v2-r1", "te6ccgEBBwEA1gABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQHu8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//" "QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44YIYAQ9HhvoW+" "hIJgC0wfUMAH7AJEy4gGz5luDJaHIQDSAQPRDiuYxyBLLHxPLP8v/9ADJ7VQGAATQMABBoZfl2omhpj5jpn+n/" "mPoCaKkQQCB6BzfQmMktv8ld0fFADgggED0lm+hb6EyURCUMFMDud4gkzM2AZIyMOKz"); with_tvm_code("highload-wallet-v2-r2", - "te6ccgEBCQEA6QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHu8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//" - "QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44YIYAQ9HhvoW+" - "hIJgC0wfUMAH7AJEy4gGz5luDJaHIQDSAQPRDiuYxyBLLHxPLP8v/9ADJ7VQIAATQMAIBIAYHABe9nOdqJoaa+Y64X/" - "wAQb5fl2omhpj5jpn+n/mPoCaKkQQCB6BzfQmMktv8ld0fFAA4IIBA9JZvoW+hMlEQlDBTA7neIJMzNgGSMjDisw=="); + "te6ccgEBCQEA5QABFP8A9KQT9LzyyAsBAgEgAgMCAUgEBQHq8oMI1xgg0x/TP/gjqh9TILnyY+1E0NMf0z/T//" + "QE0VNggED0Dm+hMfJgUXO68qIH+QFUEIf5EPKjAvQE0fgAf44WIYAQ9HhvpSCYAtMH1DAB+wCRMuIBs+" + "ZbgyWhyEA0gED0Q4rmMcgSyx8Tyz/L//QAye1UCAAE0DACASAGBwAXvZznaiaGmvmOuF/8AEG+X5dqJoaY+Y6Z/p/" + "5j6AmipEEAgegc30JjJLb/JXdHxQANCCAQPSWb6UyURCUMFMDud4gkzM2AZIyMOKz"); with_tvm_code("simple-wallet-r1", "te6ccgEEAQEAAAAAUwAAov8AIN0gggFMl7qXMO1E0NcLH+Ck8mCBAgDXGCDXCx/tRNDTH9P/" "0VESuvKhIvkBVBBE+RDyovgAAdMfMSDXSpbTB9QC+wDe0aTIyx/L/8ntVA=="); diff --git a/crypto/vm/debugops.cpp b/crypto/vm/debugops.cpp index 456338b8..0ec205a3 100644 --- a/crypto/vm/debugops.cpp +++ b/crypto/vm/debugops.cpp @@ -26,7 +26,7 @@ namespace vm { -bool vm_debug_enabled = true; +bool vm_debug_enabled = false; void set_debug_enabled(bool enable_debug) { vm_debug_enabled = enable_debug; diff --git a/doc/TonSites-HOWTO b/doc/TonSites-HOWTO index 9d4005f0..bf0b9714 100644 --- a/doc/TonSites-HOWTO +++ b/doc/TonSites-HOWTO @@ -22,15 +22,15 @@ in the build directory. In order to access existing TON Sites, you need a running instance of RLDP-HTTP Proxy on your computer. It can be invoked as follows: - rldp-http-proxy/rldp-http-proxy -p 8080 -c 3333 -C ton-global.config.json + rldp-http-proxy/rldp-http-proxy -p 8080 -c 3333 -C ton-global-lite-client.config.json or - rldp-http-proxy/rldp-http-proxy -p 8080 -a :3333 -C ton-global.config.json + rldp-http-proxy/rldp-http-proxy -p 8080 -a :3333 -C ton-global-lite-client.config.json -where is your public IPv4 address, provided you have one on your home computer. The TON Network global configuration file `ton-global.config.json` can be downloaded at https://test.ton.org/ton-global.config.json : +where is your public IPv4 address, provided you have one on your home computer. The TON Network global configuration file `ton-global-lite-client.config.json` can be downloaded at https://test.ton.org/ton-global-lite-client.config.json : - wget https://test.ton.org/ton-global.config.json + wget https://test.ton.org/ton-global-lite-client.config.json In the above example, 8080 is the TCP port that will be listened to at localhost for incoming HTTP queries, and 3333 is the UDP port that will be used for all outbound and inbound RLDP and ADNL activity, i.e., for connecting to the TON Sites via the TON Network. @@ -56,9 +56,15 @@ attempts to download the main page of (TON) Site `test.ton` using the proxy at ` because TON Site `test.ton` is currently set up to be a mirror of Web Site https://test.ton.org. +You can also access TON Sites by means of their ADNL addresses by using fake domain `.adnl`: + + curl -x 127.0.0.1:8080 http://untzo7eat2h77xzfugxrfgfy3zbl5txomvetzke6fwr45lehvdkxauy.adnl/ + +currently fetches the same TON Web page. + Alternatively, you can set up `localhost:8080` as a HTTP proxy in your browser. For example, if you use Firefox, visit [Setup] -> General -> Network Settings -> Settings -> Configure Proxy Access -> Manual Proxy configuration, and type "127.0.0.1" into the field "HTTP Proxy", and "8080" into the field "Port". If you don't have Firefox yet, visit https://www.getfirefox.com first. -Once you have set up `localhost:8080` as the HTTP proxy to be used in your browser, you can simply type the required URI, such as `http://test.ton`, in the navigation bar of your browser, and interact with the TON Site in the same way as with the usual Web Sites. +Once you have set up `localhost:8080` as the HTTP proxy to be used in your browser, you can simply type the required URI, such as `http://test.ton` or `http://untzo7eat2h77xzfugxrfgfy3zbl5txomvetzke6fwr45lehvdkxauy.adnl/`, in the navigation bar of your browser, and interact with the TON Site in the same way as with the usual Web Sites. 4. Creating TON Sites ~~~~~~~~~~~~~~~~~~~~~ @@ -67,12 +73,29 @@ Most people will need just to access existing TON Sites, not to create new ones. We suppose that you know already how to set up an ordinary web site, and that you have already configured one on your server, accepting incoming HTTP connections on TCP port :80, and defining the required TON Network domain name, say, `example.ton`, as the main domain name or an alias for your web site in the configuration of your web server. +After that, you first need to generate a persistent ADNL address for your server: + + mkdir keyring + + util/generate-random-id -m adnlid + +You see something like + + 45061C1D4EC44A937D0318589E13C73D151D1CEF5D3C0E53AFBCF56A6C2FE2BD vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3 + +This is your newly-generated persistent ADNL address, in hexadecimal and user-friendly form. The corresponding private key is saved into file 45061...2DB in the current directory. Move it into the keyring directory: + + mv 45061C1* keyring/ + After that, you execute - rldp-http-proxy -a :3333 -L example.ton -C ton-global.config.json + rldp-http-proxy -a :3333 -L '*' -C ton-global.config.json -A -in the background (you can try this in a terminal at first, but if you want your TON Site to run permanently, you'll have to use options `-d` and `-l ` as well). +(with equal to 'vcqm...35f3' in this example) in the background (you can try this in a terminal at first, but if you want your TON Site to run permanently, you'll have to use options `-d` and `-l ` as well). -If all works properly, the RLDP-HTTP proxy will accept incoming HTTP queries from the TON Network via RLDP/ADNL running on UDP port 3333 (of course, you can use any other UDP port if you want to) of IPv4 address (in particular, if you are using a firewall, don't forget to allow `rldp-http-proxy` to receive and send UDP packets from this port), and it will forward these HTTP queries addressed to host `example.ton` to TCP port 80 at 127.0.0.1, i.e., to your ordinary Web server. +If all works properly, the RLDP-HTTP proxy will accept incoming HTTP queries from the TON Network via RLDP/ADNL running on UDP port 3333 (of course, you can use any other UDP port if you want to) of IPv4 address (in particular, if you are using a firewall, don't forget to allow `rldp-http-proxy` to receive and send UDP packets from this port), and it will forward these HTTP queries addressed to all hosts (if you want to forward only specific hosts, change `-L '*'` to `-L `) to TCP port 80 at 127.0.0.1, i.e., to your ordinary Web server. + +You can visit TON Site `http://.adnl` (`http://vcqmha5j3ceve35ammfrhqty46rkhi455otydstv66pk2tmf7rl25f3.adnl` in this example) from a browser running on a client machine as explained in Sections 2 and 3 and check whether your TON Site is actually available to the public. + +If you want to, you can register a TON DNS domain, such as 'example.ton', and create a record for this domain pointing to the persistent ADNL address of your TON Site. Then the RLDP-HTTP proxies running in client mode would resolve 'http://example.ton' as pointing to your ADNL address and will access your TON Site. The process of registration of TON DNS domains is described in a separate document. -You can visit TON Site `http://example.ton` from a browser running on a client machine as explained in Sections 2 and 3 and check whether your TON Site is actually available to the public. diff --git a/http/http-connection.h b/http/http-connection.h index 6d20b8b2..f31af484 100644 --- a/http/http-connection.h +++ b/http/http-connection.h @@ -133,6 +133,7 @@ class HttpConnection : public td::actor::Actor, public td::ObserverBase { // unsubscribe from socket updates // nb: interface will be changed td::actor::SchedulerContext::get()->get_poll().unsubscribe(buffered_fd_.get_poll_info().get_pollable_fd_ref()); + buffered_fd_.close(); } }; diff --git a/http/http-inbound-connection.cpp b/http/http-inbound-connection.cpp index 0180ec1c..ef2f3a58 100644 --- a/http/http-inbound-connection.cpp +++ b/http/http-inbound-connection.cpp @@ -27,25 +27,31 @@ void HttpInboundConnection::send_client_error() { static const auto s = "HTTP/1.0 400 Bad Request\r\n" "Connection: Close\r\n" + "Content-length: 0\r\n" "\r\n"; buffered_fd_.output_buffer().append(td::Slice(s, strlen(s))); close_after_write_ = true; + loop(); } void HttpInboundConnection::send_server_error() { static const auto s = "HTTP/1.1 502 Bad Gateway\r\n" "Connection: keep-alive\r\n" + "Content-length: 0\r\n" "\r\n"; buffered_fd_.output_buffer().append(td::Slice(s, strlen(s))); + loop(); } void HttpInboundConnection::send_proxy_error() { static const auto s = "HTTP/1.1 502 Bad Gateway\r\n" "Connection: keep-alive\r\n" + "Content-length: 0\r\n" "\r\n"; buffered_fd_.output_buffer().append(td::Slice(s, strlen(s))); + loop(); } td::Status HttpInboundConnection::receive(td::ChainBufferReader &input) { diff --git a/http/http-inbound-connection.h b/http/http-inbound-connection.h index 88b7384b..876c3902 100644 --- a/http/http-inbound-connection.h +++ b/http/http-inbound-connection.h @@ -33,6 +33,7 @@ class HttpInboundConnection : public HttpConnection { } td::Status receive_eof() override { + found_eof_ = true; if (reading_payload_) { if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof) { return td::Status::Error("unexpected EOF"); @@ -42,6 +43,10 @@ class HttpInboundConnection : public HttpConnection { return td::Status::OK(); } } else { + if (read_next_request_) { + stop(); + return td::Status::OK(); + } return td::Status::OK(); } } @@ -54,6 +59,10 @@ class HttpInboundConnection : public HttpConnection { writing_payload_ = nullptr; if (!close_after_write_) { read_next_request_ = true; + if (found_eof_) { + stop(); + return; + } } } void payload_read() override { diff --git a/http/http-outbound-connection.h b/http/http-outbound-connection.h index 5a938538..a3f5d513 100644 --- a/http/http-outbound-connection.h +++ b/http/http-outbound-connection.h @@ -42,6 +42,7 @@ class HttpOutboundConnection : public HttpConnection { } td::Status receive_eof() override { + found_eof_ = true; if (reading_payload_) { if (reading_payload_->payload_type() != HttpPayload::PayloadType::pt_eof) { return td::Status::Error("unexpected EOF"); diff --git a/rldp-http-proxy/CMakeLists.txt b/rldp-http-proxy/CMakeLists.txt index cb856ca3..3013ee81 100644 --- a/rldp-http-proxy/CMakeLists.txt +++ b/rldp-http-proxy/CMakeLists.txt @@ -2,4 +2,4 @@ cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR) add_executable(rldp-http-proxy rldp-http-proxy.cpp) target_include_directories(rldp-http-proxy PUBLIC $) -target_link_libraries(rldp-http-proxy PRIVATE tonhttp rldp dht) +target_link_libraries(rldp-http-proxy PRIVATE tonhttp rldp dht tonlib) diff --git a/rldp-http-proxy/rldp-http-proxy.cpp b/rldp-http-proxy/rldp-http-proxy.cpp index 396408f7..16708b8d 100644 --- a/rldp-http-proxy/rldp-http-proxy.cpp +++ b/rldp-http-proxy/rldp-http-proxy.cpp @@ -33,17 +33,24 @@ #include "td/utils/FileLog.h" #include "td/utils/Random.h" #include "td/utils/filesystem.h" +#include "td/utils/overloaded.h" #include "auto/tl/ton_api_json.h" +#include "auto/tl/tonlib_api.hpp" + +#include "td/actor/MultiPromise.h" #include "common/errorcode.h" +#include "tonlib/tonlib/TonlibClient.h" + #include "adnl/adnl.h" #include "rldp/rldp.h" #include "dht/dht.h" #include #include +#include #if TD_DARWIN || TD_LINUX #include @@ -406,6 +413,8 @@ class HttpRldpPayloadSender : public td::actor::Actor { td::Promise cur_query_promise_; }; +class RldpHttpProxy; + class TcpToRldpRequestSender : public td::actor::Actor { public: TcpToRldpRequestSender( @@ -413,7 +422,7 @@ class TcpToRldpRequestSender : public td::actor::Actor { std::shared_ptr request_payload, td::Promise, std::shared_ptr>> promise, td::actor::ActorId adnl, td::actor::ActorId dht, - td::actor::ActorId rldp) + td::actor::ActorId rldp, td::actor::ActorId proxy) : local_id_(local_id) , host_(std::move(host)) , request_(std::move(request)) @@ -421,29 +430,15 @@ class TcpToRldpRequestSender : public td::actor::Actor { , promise_(std::move(promise)) , adnl_(adnl) , dht_(dht) - , rldp_(rldp) { + , rldp_(rldp) + , proxy_(proxy) { } void start_up() override { - auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { - if (R.is_error()) { - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, R.move_as_error()); - return; - } - auto value = R.move_as_ok(); - if (value.value().size() != 32) { - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, td::Status::Error("bad value in dht")); - return; - } - - ton::PublicKeyHash h{value.value().as_slice()}; - td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, ton::adnl::AdnlNodeIdShort{h}); - }); - - ton::PublicKey key = ton::pubkeys::Unenc{"http." + host_}; - ton::dht::DhtKey dht_key{key.compute_short_id(), "http." + host_, 0}; - td::actor::send_closure(dht_, &ton::dht::Dht::get_value, std::move(dht_key), std::move(P)); + resolve(); } + void resolve(); + void resolved(ton::adnl::AdnlNodeIdShort id) { dst_ = id; td::Random::secure_bytes(id_.as_slice()); @@ -533,6 +528,7 @@ class TcpToRldpRequestSender : public td::actor::Actor { td::actor::ActorId adnl_; td::actor::ActorId dht_; td::actor::ActorId rldp_; + td::actor::ActorId proxy_; std::unique_ptr response_; std::shared_ptr response_payload_; @@ -634,6 +630,27 @@ class RldpHttpProxy : public td::actor::Actor { local_hosts_.emplace_back(std::move(name), std::move(remote)); } + void receive_request_result(td::uint64 id, td::Result> R) { + if (id == 0) { + return; + } + auto it = tonlib_requests_.find(id); + CHECK(it != tonlib_requests_.end()); + auto promise = std::move(it->second); + tonlib_requests_.erase(it); + + promise.set_result(std::move(R)); + } + + void send_tonlib_request(tonlib_api::object_ptr obj, + td::Promise> promise) { + auto id = next_tonlib_requests_id_++; + + CHECK(tonlib_requests_.emplace(id, std::move(promise)).second); + + td::actor::send_closure(tonlib_client_, &tonlib::TonlibClient::request, id, std::move(obj)); + } + td::Status load_global_config() { TRY_RESULT_PREFIX(conf_data, td::read_file(global_config_), "failed to read: "); TRY_RESULT_PREFIX(conf_json, td::json_decode(conf_data.as_slice()), "failed to parse json: "); @@ -648,23 +665,45 @@ class RldpHttpProxy : public td::actor::Actor { TRY_RESULT_PREFIX(dht, ton::dht::Dht::create_global_config(std::move(conf.dht_)), "bad [dht] section: "); dht_config_ = std::move(dht); + class Cb : public tonlib::TonlibCallback { + public: + Cb(td::actor::ActorId self_id) : self_id_(self_id) { + } + void on_result(std::uint64_t id, tonlib_api::object_ptr result) override { + td::actor::send_closure(self_id_, &RldpHttpProxy::receive_request_result, id, std::move(result)); + } + void on_error(std::uint64_t id, tonlib_api::object_ptr error) override { + td::actor::send_closure(self_id_, &RldpHttpProxy::receive_request_result, id, + td::Status::Error(error->code_, std::move(error->message_))); + } + + private: + td::actor::ActorId self_id_; + }; + + tonlib_client_ = td::actor::create_actor("tonlibclient", td::make_unique(actor_id(this))); + return td::Status::OK(); } void store_dht() { for (auto &serv : local_hosts_) { - ton::PublicKey key = ton::pubkeys::Unenc{"http." + serv.first}; - ton::dht::DhtKey dht_key{key.compute_short_id(), "http." + serv.first, 0}; - auto dht_update_rule = ton::dht::DhtUpdateRuleAnybody::create().move_as_ok(); - ton::dht::DhtKeyDescription dht_key_description{std::move(dht_key), key, std::move(dht_update_rule), - td::BufferSlice()}; - dht_key_description.check().ensure(); + if (serv.first != "*") { + for (auto &serv_id : server_ids_) { + ton::PublicKey key = ton::pubkeys::Unenc{"http." + serv.first}; + ton::dht::DhtKey dht_key{key.compute_short_id(), "http." + serv.first, 0}; + auto dht_update_rule = ton::dht::DhtUpdateRuleAnybody::create().move_as_ok(); + ton::dht::DhtKeyDescription dht_key_description{std::move(dht_key), key, std::move(dht_update_rule), + td::BufferSlice()}; + dht_key_description.check().ensure(); - auto ttl = static_cast(td::Clocks::system() + 3600); - ton::dht::DhtValue dht_value{std::move(dht_key_description), td::BufferSlice{local_id_.as_slice()}, ttl, - td::BufferSlice("")}; + auto ttl = static_cast(td::Clocks::system() + 3600); + ton::dht::DhtValue dht_value{std::move(dht_key_description), td::BufferSlice{serv_id.as_slice()}, ttl, + td::BufferSlice("")}; - td::actor::send_closure(dht_, &ton::dht::Dht::set_value, std::move(dht_value), [](td::Unit) {}); + td::actor::send_closure(dht_, &ton::dht::Dht::set_value, std::move(dht_value), [](td::Unit) {}); + } + } } alarm_timestamp() = td::Timestamp::in(60.0); } @@ -673,7 +712,58 @@ class RldpHttpProxy : public td::actor::Actor { store_dht(); } + void got_full_id(ton::adnl::AdnlNodeIdShort short_id, ton::adnl::AdnlNodeIdFull full_id) { + server_ids_full_[short_id] = full_id; + } + void run() { + keyring_ = ton::keyring::Keyring::create(is_client_ ? std::string("") : (db_root_ + "/keyring")); + { + auto S = load_global_config(); + if (S.is_error()) { + LOG(INFO) << S; + std::_Exit(2); + } + } + if (is_client_ && server_ids_.size() > 0) { + LOG(ERROR) << "client-only node cannot be server"; + std::_Exit(2); + } + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + R.ensure(); + td::actor::send_closure(SelfId, &RldpHttpProxy::run_cont); + }); + td::MultiPromise mp; + auto ig = mp.init_guard(); + ig.add_promise(std::move(P)); + for (auto &x : server_ids_) { + auto Q = td::PromiseCreator::lambda([promise = ig.get_promise(), SelfId = actor_id(this), + x](td::Result R) mutable { + if (R.is_error()) { + promise.set_error(R.move_as_error()); + } else { + td::actor::send_closure(SelfId, &RldpHttpProxy::got_full_id, x, ton::adnl::AdnlNodeIdFull{R.move_as_ok()}); + promise.set_value(td::Unit()); + } + }); + td::actor::send_closure(keyring_, &ton::keyring::Keyring::get_public_key, x.pubkey_hash(), std::move(Q)); + } + auto Q = td::PromiseCreator::lambda( + [promise = ig.get_promise()](td::Result> R) mutable { + R.ensure(); + promise.set_value(td::Unit()); + }); + + auto conf_dataR = td::read_file(global_config_); + conf_dataR.ensure(); + + auto req = tonlib_api::make_object(tonlib_api::make_object( + tonlib_api::make_object(conf_dataR.move_as_ok().as_slice().str(), "", false, false), + tonlib_api::make_object())); + send_tonlib_request(std::move(req), std::move(Q)); + } + + void run_cont() { if (is_client_ && local_hosts_.size() > 0) { LOG(ERROR) << "client-only node cannot be server"; std::_Exit(2); @@ -682,18 +772,10 @@ class RldpHttpProxy : public td::actor::Actor { LOG(ERROR) << "client-only expects client port"; std::_Exit(2); } - { - auto S = load_global_config(); - if (S.is_error()) { - LOG(INFO) << S; - std::_Exit(2); - } - } - keyring_ = ton::keyring::Keyring::create(""); { adnl_network_manager_ = ton::adnl::AdnlNetworkManager::create(is_client_ ? client_port_ : static_cast(addr_.get_port())); - adnl_ = ton::adnl::Adnl::create("", keyring_.get()); + adnl_ = ton::adnl::Adnl::create(is_client_ ? std::string("") : (db_root_), keyring_.get()); td::actor::send_closure(adnl_, &ton::adnl::Adnl::register_network_manager, adnl_network_manager_.get()); if (is_client_) { td::IPAddress addr; @@ -717,6 +799,10 @@ class RldpHttpProxy : public td::actor::Actor { td::actor::send_closure(keyring_, &ton::keyring::Keyring::add_key, std::move(pk), true, [](td::Unit) {}); local_id_ = ton::adnl::AdnlNodeIdShort{pub.compute_short_id()}; td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, addr_list); + + if (server_ids_.size() == 0 && !is_client_) { + server_ids_.insert(local_id_); + } } { auto pk = ton::PrivateKey{ton::privkeys::Ed25519::random()}; @@ -725,6 +811,9 @@ class RldpHttpProxy : public td::actor::Actor { dht_id_ = ton::adnl::AdnlNodeIdShort{pub.compute_short_id()}; td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, ton::adnl::AdnlNodeIdFull{pub}, addr_list); } + for (auto &serv_id : server_ids_) { + td::actor::send_closure(adnl_, &ton::adnl::Adnl::add_id, server_ids_full_[serv_id], addr_list); + } } { if (is_client_) { @@ -732,7 +821,7 @@ class RldpHttpProxy : public td::actor::Actor { D.ensure(); dht_ = D.move_as_ok(); } else { - auto D = ton::dht::Dht::create(dht_id_, "", dht_config_, keyring_.get(), adnl_.get()); + auto D = ton::dht::Dht::create(dht_id_, db_root_, dht_config_, keyring_.get(), adnl_.get()); D.ensure(); dht_ = D.move_as_ok(); } @@ -758,31 +847,36 @@ class RldpHttpProxy : public td::actor::Actor { server_ = ton::http::HttpServer::create(port_, std::make_shared(actor_id(this))); } - class AdnlCb : public ton::adnl::Adnl::Callback { - public: - AdnlCb(td::actor::ActorId id) : self_id_(id) { - } - void receive_message(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, - td::BufferSlice data) override { - } - void receive_query(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, - td::Promise promise) override { - td::actor::send_closure(self_id_, &RldpHttpProxy::receive_rldp_request, src, std::move(data), - std::move(promise)); - } + for (auto &serv_id : server_ids_) { + class AdnlCb : public ton::adnl::Adnl::Callback { + public: + AdnlCb(td::actor::ActorId id) : self_id_(id) { + } + void receive_message(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, + td::BufferSlice data) override { + } + void receive_query(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, + td::Promise promise) override { + td::actor::send_closure(self_id_, &RldpHttpProxy::receive_rldp_request, src, dst, std::move(data), + std::move(promise)); + } - private: - td::actor::ActorId self_id_; - }; - td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, local_id_, - ton::adnl::Adnl::int_to_bytestring(ton::ton_api::http_request::ID), - std::make_unique(actor_id(this))); + private: + td::actor::ActorId self_id_; + }; + td::actor::send_closure(adnl_, &ton::adnl::Adnl::subscribe, serv_id, + ton::adnl::Adnl::int_to_bytestring(ton::ton_api::http_request::ID), + std::make_unique(actor_id(this))); + } for (auto &serv : local_hosts_) { servers_.emplace(serv.first, td::actor::create_actor("remote", serv.second)); } rldp_ = ton::rldp::Rldp::create(adnl_.get()); td::actor::send_closure(rldp_, &ton::rldp::Rldp::add_id, local_id_); + for (auto &serv_id : server_ids_) { + td::actor::send_closure(rldp_, &ton::rldp::Rldp::add_id, serv_id); + } store_dht(); } @@ -821,18 +915,19 @@ class RldpHttpProxy : public td::actor::Actor { } } std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); }); - if (host.size() < 5 || host.substr(host.size() - 4) != ".ton") { + if (!proxy_all_ && + (host.size() < 5 || (host.substr(host.size() - 4) != ".ton" && host.substr(host.size() - 5) != ".adnl"))) { promise.set_error(td::Status::Error(ton::ErrorCode::error, "bad server name")); return; } td::actor::create_actor("outboundreq", local_id_, host, std::move(request), std::move(payload), std::move(promise), adnl_.get(), dht_.get(), - rldp_.get()) + rldp_.get(), actor_id(this)) .release(); } - void receive_rldp_request(ton::adnl::AdnlNodeIdShort src, td::BufferSlice data, + void receive_rldp_request(ton::adnl::AdnlNodeIdShort src, ton::adnl::AdnlNodeIdShort dst, td::BufferSlice data, td::Promise promise) { LOG(INFO) << "got HTTP request over rldp from " << src; TRY_RESULT_PROMISE(promise, f, ton::fetch_tl_object(std::move(data), true)); @@ -873,26 +968,37 @@ class RldpHttpProxy : public td::actor::Actor { } } std::transform(host.begin(), host.end(), host.begin(), [](unsigned char c) { return std::tolower(c); }); - if (host.size() < 5 || host.substr(host.size() - 4) != ".ton") { - promise.set_error(td::Status::Error(ton::ErrorCode::error, "bad server name")); - return; - } auto it = servers_.find(host); if (it == servers_.end()) { - promise.set_error(td::Status::Error(ton::ErrorCode::error, "unknown server name")); - return; + it = servers_.find("*"); + if (it == servers_.end()) { + promise.set_error(td::Status::Error(ton::ErrorCode::error, "unknown server name")); + return; + } } TRY_RESULT_PROMISE(promise, payload, request->create_empty_payload()); LOG(INFO) << "starting HTTP over RLDP request"; - td::actor::create_actor("inboundreq", f->id_, local_id_, src, std::move(request), + td::actor::create_actor("inboundreq", f->id_, dst, src, std::move(request), std::move(payload), std::move(promise), adnl_.get(), rldp_.get(), it->second.get()) .release(); } + void add_adnl_addr(ton::adnl::AdnlNodeIdShort id) { + server_ids_.insert(id); + } + + void set_db_root(std::string db_root) { + db_root_ = std::move(db_root); + } + + void set_proxy_all(bool value) { + proxy_all_ = value; + } + private: td::uint16 port_; td::IPAddress addr_; @@ -902,6 +1008,8 @@ class RldpHttpProxy : public td::actor::Actor { bool is_client_{false}; td::uint16 client_port_{0}; + std::set server_ids_; + std::map server_ids_full_; ton::adnl::AdnlNodeIdShort local_id_; ton::adnl::AdnlNodeIdShort dht_id_; @@ -916,8 +1024,85 @@ class RldpHttpProxy : public td::actor::Actor { td::actor::ActorOwn rldp_; std::shared_ptr dht_config_; + + std::string db_root_ = "."; + bool proxy_all_ = false; + + td::actor::ActorOwn tonlib_client_; + std::map>> tonlib_requests_; + td::uint64 next_tonlib_requests_id_{1}; }; +void TcpToRldpRequestSender::resolve() { + auto S = td::Slice(host_); + if (S.size() >= 5 && S.substr(S.size() - 5) == ".adnl") { + S.truncate(S.size() - 5); + auto R = ton::adnl::AdnlNodeIdShort::parse(S); + if (R.is_error()) { + abort_query(R.move_as_error_prefix("failed to parse adnl addr: ")); + return; + } + resolved(R.move_as_ok()); + return; + } + if (false) { + auto P = td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, R.move_as_error()); + return; + } + auto value = R.move_as_ok(); + if (value.value().size() != 32) { + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, td::Status::Error("bad value in dht")); + return; + } + + ton::PublicKeyHash h{value.value().as_slice()}; + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, ton::adnl::AdnlNodeIdShort{h}); + }); + + ton::PublicKey key = ton::pubkeys::Unenc{"http." + host_}; + ton::dht::DhtKey dht_key{key.compute_short_id(), "http." + host_, 0}; + td::actor::send_closure(dht_, &ton::dht::Dht::get_value, std::move(dht_key), std::move(P)); + } else { + auto obj = tonlib_api::make_object(nullptr, host_, 0, 16); + + auto P = + td::PromiseCreator::lambda([SelfId = actor_id(this)](td::Result> R) { + if (R.is_error()) { + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, + R.move_as_error_prefix("failed to resolve: ")); + } else { + auto v = R.move_as_ok(); + auto obj = static_cast(v.get()); + ton::adnl::AdnlNodeIdShort id; + td::uint32 cnt = 0; + for (auto &e : obj->entries_) { + tonlib_api::downcast_call( + *e->entry_.get(), td::overloaded( + [&](tonlib_api::dns_entryDataAdnlAddress &x) { + if (td::Random::fast(0, cnt) == 0) { + auto R = ton::adnl::AdnlNodeIdShort::parse(x.adnl_address_->adnl_address_); + if (R.is_ok()) { + id = R.move_as_ok(); + cnt++; + } + } + }, + [&](auto &x) {})); + } + if (cnt == 0) { + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::abort_query, + td::Status::Error(ton::ErrorCode::notready, "failed to resolve")); + } else { + td::actor::send_closure(SelfId, &TcpToRldpRequestSender::resolved, id); + } + } + }); + td::actor::send_closure(proxy_, &RldpHttpProxy::send_tonlib_request, std::move(obj), std::move(P)); + } +} + int main(int argc, char *argv[]) { SET_VERBOSITY_LEVEL(verbosity_WARNING); @@ -961,6 +1146,11 @@ int main(int argc, char *argv[]) { td::actor::send_closure(x, &RldpHttpProxy::set_addr, addr); return td::Status::OK(); }); + p.add_option('A', "adnl", "server ADNL addr", [&](td::Slice arg) -> td::Status { + TRY_RESULT(adnl, ton::adnl::AdnlNodeIdShort::parse(arg)); + td::actor::send_closure(x, &RldpHttpProxy::add_adnl_addr, adnl); + return td::Status::OK(); + }); p.add_option('c', "client-port", "local to use for client adnl queries", [&](td::Slice arg) -> td::Status { TRY_RESULT(port, td::to_integer_safe(arg)); td::actor::send_closure(x, &RldpHttpProxy::set_client_port, port); @@ -977,6 +1167,10 @@ int main(int argc, char *argv[]) { td::actor::send_closure(x, &RldpHttpProxy::set_local_host, arg.str(), addr); return td::Status::OK(); }); + p.add_option('D', "db", "db root", [&](td::Slice arg) -> td::Status { + td::actor::send_closure(x, &RldpHttpProxy::set_db_root, arg.str()); + return td::Status::OK(); + }); p.add_option( 'R', "remote", "@:, indicates a http hostname that will be proxied to remote http server at :", @@ -999,13 +1193,23 @@ int main(int argc, char *argv[]) { }).ensure(); return td::Status::OK(); }); -#if TD_DARWIN || TD_LINUX p.add_option('l', "logname", "log to file", [&](td::Slice fname) { logger_ = td::FileLog::create(fname.str()).move_as_ok(); td::log_interface = logger_.get(); return td::Status::OK(); }); -#endif + p.add_option('P', "proxy-all", "value=[YES|NO]. proxy all HTTP requests (default only *.ton and *.adnl)", + [&](td::Slice value) { + if (value == "YES" || value == "yes") { + td::actor::send_closure(x, &RldpHttpProxy::set_proxy_all, true); + } else if (value == "NO" || value == "no") { + td::actor::send_closure(x, &RldpHttpProxy::set_proxy_all, false); + } else { + return td::Status::Error("--proxy-all expected YES or NO"); + } + + return td::Status::OK(); + }); td::actor::Scheduler scheduler({7}); diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index 88d92696..cbadbb83 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -34,6 +34,7 @@ inputKeyFake = InputKey; exportedKey word_list:vector = ExportedKey; exportedPemKey pem:secureString = ExportedPemKey; exportedEncryptedKey data:secureBytes = ExportedEncryptedKey; +exportedUnencryptedKey data:secureBytes = ExportedUnencryptedKey; bip39Hints words:vector = Bip39Hints; @@ -178,9 +179,11 @@ deleteAllKeys = Ok; exportKey input_key:InputKey = ExportedKey; exportPemKey input_key:InputKey key_password:secureBytes = ExportedPemKey; exportEncryptedKey input_key:InputKey key_password:secureBytes = ExportedEncryptedKey; +exportUnencryptedKey input_key:InputKey = ExportedUnencryptedKey; importKey local_password:secureBytes mnemonic_password:secureBytes exported_key:exportedKey = Key; importPemKey local_password:secureBytes key_password:secureBytes exported_key:exportedPemKey = Key; importEncryptedKey local_password:secureBytes key_password:secureBytes exported_encrypted_key:exportedEncryptedKey = Key; +importUnencryptedKey local_password:secureBytes exported_unencrypted_key:exportedUnencryptedKey = Key; changeLocalPassword input_key:InputKey new_local_password:secureBytes = Key; encrypt decrypted_data:secureBytes secret:secureBytes = Data; @@ -220,7 +223,7 @@ smc.getData id:int53 = tvm.Cell; smc.getState id:int53 = tvm.Cell; smc.runGetMethod id:int53 method:smc.MethodId stack:vector = smc.RunResult; -dns.resolve account_address:accountAddress name:string category:int32 = dns.Resolved; +dns.resolve account_address:accountAddress name:string category:int32 ttl:int32 = dns.Resolved; onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryError id:int64 error:error = Ok; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index d735f3ef..29c41f33 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 897682cc..f058b6d8 100644 --- a/tonlib/test/offline.cpp +++ b/tonlib/test/offline.cpp @@ -143,10 +143,9 @@ TEST(Tonlib, InitClose) { )abc"; sync_send(client, make_object(cfg(bad_config.str()))).ensure_error(); - auto address = - sync_send(client, - make_object(make_object(), 0)) - .move_as_ok(); + auto address = sync_send(client, make_object( + make_object(), 0)) + .move_as_ok(); sync_send(client, make_object(std::move(address))).ensure_error(); sync_send(client, make_object()).ensure(); sync_send(client, make_object()).ensure_error(); @@ -490,6 +489,21 @@ TEST(Tonlib, KeysApi) { .move_as_ok(); CHECK(new_imported_key->public_key_ == key->public_key_); CHECK(new_imported_key->secret_ != key->secret_); + + auto exported_raw_key = + sync_send(client, make_object(make_object( + make_object(key->public_key_, new_imported_key->secret_.copy()), + new_local_password.copy()))) + .move_as_ok(); + sync_send(client, make_object( + make_object(new_imported_key->public_key_, new_imported_key->secret_.copy()))) + .move_as_ok(); + auto raw_imported_key = sync_send(client, make_object(new_local_password.copy(), + std::move(exported_raw_key))) + .move_as_ok(); + + CHECK(raw_imported_key->public_key_ == key->public_key_); + CHECK(raw_imported_key->secret_ != key->secret_); } TEST(Tonlib, ConfigCache) { diff --git a/tonlib/test/online.cpp b/tonlib/test/online.cpp index 9cdee740..a1544888 100644 --- a/tonlib/test/online.cpp +++ b/tonlib/test/online.cpp @@ -552,7 +552,7 @@ void dns_resolve(Client& client, const Wallet& dns, std::string name) { auto address = dns.get_address(); while (true) { auto resolved = - sync_send(client, make_object<::ton::tonlib_api::dns_resolve>(std::move(address), name, 1)).move_as_ok(); + sync_send(client, make_object<::ton::tonlib_api::dns_resolve>(std::move(address), name, 1, 0)).move_as_ok(); CHECK(resolved->entries_.size() == 1); LOG(INFO) << to_string(resolved); if (resolved->entries_[0]->category_ == -1) { diff --git a/tonlib/tonlib/KeyStorage.cpp b/tonlib/tonlib/KeyStorage.cpp index f1ac8271..5bbda8da 100644 --- a/tonlib/tonlib/KeyStorage.cpp +++ b/tonlib/tonlib/KeyStorage.cpp @@ -179,6 +179,11 @@ td::Result KeyStorage::export_encrypted_key(In return ExportedEncryptedKey{std::move(res.encrypted_data)}; } +td::Result KeyStorage::export_unencrypted_key(InputKey input_key) { + TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key))); + return ExportedUnencryptedKey{decrypted_key.private_key.as_octet_string()}; +} + td::Result KeyStorage::import_encrypted_key(td::Slice local_password, td::Slice key_password, ExportedEncryptedKey exported_key) { EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()), @@ -187,6 +192,14 @@ td::Result KeyStorage::import_encrypted_key(td::Slice local_pas return save_key(std::move(decrypted_key), local_password); } +td::Result KeyStorage::import_unencrypted_key(td::Slice local_password, + ExportedUnencryptedKey exported_key) { + RawDecryptedKey raw_key; + raw_key.private_key = std::move(exported_key.data); + DecryptedKey key(std::move(raw_key)); + return save_key(std::move(key), local_password); +} + KeyStorage::PrivateKey KeyStorage::fake_private_key() { return PrivateKey{td::SecureString(32, 0)}; } diff --git a/tonlib/tonlib/KeyStorage.h b/tonlib/tonlib/KeyStorage.h index 38478bf1..8802cc20 100644 --- a/tonlib/tonlib/KeyStorage.h +++ b/tonlib/tonlib/KeyStorage.h @@ -46,6 +46,9 @@ class KeyStorage { struct ExportedEncryptedKey { td::SecureString data; }; + struct ExportedUnencryptedKey { + td::SecureString data; + }; struct PrivateKey { td::SecureString private_key; }; @@ -57,6 +60,7 @@ class KeyStorage { td::Result export_key(InputKey input_key); td::Result export_pem_key(InputKey input_key, td::Slice key_password); td::Result export_encrypted_key(InputKey input_key, td::Slice key_password); + td::Result export_unencrypted_key(InputKey input_key); td::Result change_local_password(InputKey input_key, td::Slice new_local_password); td::Status delete_key(const Key& key); @@ -66,6 +70,7 @@ class KeyStorage { td::Result import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key); td::Result import_encrypted_key(td::Slice local_password, td::Slice key_password, ExportedEncryptedKey exported_key); + td::Result import_unencrypted_key(td::Slice local_password, ExportedUnencryptedKey exported_key); td::Result load_private_key(InputKey input_key); diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 8dcaece8..9d277e23 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -353,6 +353,10 @@ class AccountState { return raw_.info.gen_utime; } + ton::BlockIdExt get_block_id() const { + return raw_.block_id; + } + td::int64 get_balance() const { return raw_.balance; } @@ -2593,14 +2597,22 @@ td::Result> to_tonlib_api( return res; } -void TonlibClient::finish_dns_resolve(std::string name, td::int32 category, td::unique_ptr smc, +void TonlibClient::finish_dns_resolve(std::string name, td::int32 category, td::int32 ttl, + td::optional block_id, td::unique_ptr smc, td::Promise>&& promise) { if (smc->get_wallet_type() != AccountState::WalletType::ManualDns) { return promise.set_error(TonlibError::AccountTypeUnexpected("ManualDns")); } + block_id = smc->get_block_id(); auto dns = ton::ManualDns::create(smc->get_smc_state()); TRY_RESULT_PROMISE(promise, entries, dns->resolve(name, category)); + if (entries.size() == 1 && entries[0].category == -1 && entries[0].name != name && ttl > 0 && + entries[0].data.type == ton::ManualDns::EntryData::Type::NextResolver) { + auto address = entries[0].data.data.get().resolver; + return do_dns_request(name, category, ttl - 1, std::move(block_id), address, std::move(promise)); + } + std::vector> api_entries; for (auto& entry : entries) { TRY_RESULT_PROMISE(promise, entry_data, to_tonlib_api(entry.data)); @@ -2610,21 +2622,27 @@ void TonlibClient::finish_dns_resolve(std::string name, td::int32 category, td:: promise.set_value(tonlib_api::make_object(std::move(api_entries))); } -void TonlibClient::do_dns_request(std::string name, td::int32 category, block::StdAddress address, +void TonlibClient::do_dns_request(std::string name, td::int32 category, td::int32 ttl, + td::optional block_id, block::StdAddress address, td::Promise>&& promise) { - make_request(int_api::GetAccountState{address, query_context_.block_id.copy()}, - promise.send_closure(actor_id(this), &TonlibClient::finish_dns_resolve, name, category)); + auto block_id_copy = block_id.copy(); + make_request(int_api::GetAccountState{address, std::move(block_id_copy)}, + promise.send_closure(actor_id(this), &TonlibClient::finish_dns_resolve, name, category, ttl, + std::move(block_id))); } td::Status TonlibClient::do_request(const tonlib_api::dns_resolve& request, td::Promise>&& promise) { + auto block_id = query_context_.block_id.copy(); if (!request.account_address_) { make_request(int_api::GetDnsResolver{}, - promise.send_closure(actor_id(this), &TonlibClient::do_dns_request, request.name_, request.category_)); + promise.send_closure(actor_id(this), &TonlibClient::do_dns_request, request.name_, request.category_, + request.ttl_, std::move(block_id))); return td::Status::OK(); } TRY_RESULT(account_address, get_account_address(request.account_address_->account_address_)); - do_dns_request(request.name_, request.category_, account_address, std::move(promise)); + do_dns_request(request.name_, request.category_, request.ttl_, std::move(block_id), account_address, + std::move(promise)); return td::Status::OK(); } @@ -2745,6 +2763,28 @@ td::Status TonlibClient::do_request(const tonlib_api::importEncryptedKey& reques promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); return td::Status::OK(); } +td::Status TonlibClient::do_request(const tonlib_api::exportUnencryptedKey& request, + td::Promise>&& promise) { + if (!request.input_key_) { + return TonlibError::EmptyField("input_key"); + } + TRY_RESULT(input_key, from_tonlib(*request.input_key_)); + TRY_RESULT(exported_key, key_storage_.export_unencrypted_key(std::move(input_key))); + promise.set_value(tonlib_api::make_object(std::move(exported_key.data))); + return td::Status::OK(); +} +td::Status TonlibClient::do_request(const tonlib_api::importUnencryptedKey& request, + td::Promise>&& promise) { + if (!request.exported_unencrypted_key_) { + return TonlibError::EmptyField("exported_encrypted_key"); + } + TRY_RESULT(key, key_storage_.import_unencrypted_key( + std::move(request.local_password_), + KeyStorage::ExportedUnencryptedKey{std::move(request.exported_unencrypted_key_->data_)})); + TRY_RESULT(key_bytes, public_key_from_bytes(key.public_key.as_slice())); + promise.set_value(tonlib_api::make_object(key_bytes.serialize(true), std::move(key.secret))); + return td::Status::OK(); +} td::Status TonlibClient::do_request(const tonlib_api::changeLocalPassword& request, td::Promise>&& promise) { diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 363b71a4..b28215e4 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -245,6 +245,11 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::importEncryptedKey& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::exportUnencryptedKey& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::importUnencryptedKey& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::changeLocalPassword& request, td::Promise>&& promise); @@ -290,9 +295,10 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(const tonlib_api::dns_resolve& request, td::Promise>&& promise); - void do_dns_request(std::string name, td::int32 category, block::StdAddress address, - td::Promise>&& promise); - void finish_dns_resolve(std::string name, td::int32 category, td::unique_ptr smc, + void do_dns_request(std::string name, td::int32 category, td::int32 ttl, td::optional block_id, + block::StdAddress address, td::Promise>&& promise); + void finish_dns_resolve(std::string name, td::int32 category, td::int32 ttl, td::optional block_id, + td::unique_ptr smc, td::Promise>&& promise); td::Status do_request(int_api::GetAccountState request, td::Promise>&&); diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 5aecf389..8622770f 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -114,6 +114,7 @@ class TonlibCli : public td::actor::Actor { bool use_callbacks_for_network{false}; td::int32 wallet_version = 2; td::int32 wallet_revision = 0; + td::optional wallet_id; bool ignore_cache{false}; bool one_shot{false}; @@ -223,7 +224,11 @@ class TonlibCli : public td::actor::Actor { LOG_IF(ERROR, r_ok.is_error()) << r_ok.error(); if (r_ok.is_ok()) { if (r_ok.ok()->config_info_) { - wallet_id_ = static_cast(r_ok.ok()->config_info_->default_wallet_id_); + if (options_.wallet_id) { + wallet_id_ = options_.wallet_id.value(); + } else { + wallet_id_ = static_cast(r_ok.ok()->config_info_->default_wallet_id_); + } } td::TerminalIO::out() << "Tonlib is inited\n"; if (options_.one_shot) { @@ -307,12 +312,13 @@ 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() << "getaddress \tget address of wallet with requested key\n"; - td::TerminalIO::out() << "dns resove \n"; + td::TerminalIO::out() << "dns resolve ( | root) \n"; td::TerminalIO::out() << "dns cmd \n"; //td::TerminalIO::out() << "dns cmdlist {\\n} end\n"; td::TerminalIO::out() << "dns cmdfile \n"; td::TerminalIO::out() << "\t = set | delete.name | delete.all\n"; - td::TerminalIO::out() << "\t = DELETED | EMPTY | TEXT:\n"; + td::TerminalIO::out() << "\t = DELETED | EMPTY | TEXT: | NEXT: | SMC: | " + "ADNL:\n"; td::TerminalIO::out() << "blockmode auto|manual\tWith auto mode, all queries will be executed with respect to the latest block. " @@ -331,6 +337,7 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "setbounceble
[] - change bounceble flag in address\n"; td::TerminalIO::out() << "importkey - import key\n"; td::TerminalIO::out() << "importkeypem - import key\n"; + td::TerminalIO::out() << "importkeyraw - import key\n"; td::TerminalIO::out() << "deletekeys - delete ALL PRIVATE KEYS\n"; td::TerminalIO::out() << "exportkey [] - export key\n"; td::TerminalIO::out() << "exportkeypem [] - export key\n"; @@ -403,6 +410,8 @@ class TonlibCli : public td::actor::Actor { get_address(parser.read_word(), std::move(cmd_promise)); } else if (cmd == "importkeypem") { import_key_pem(parser.read_word(), std::move(cmd_promise)); + } else if (cmd == "importkeyraw") { + import_key_raw(parser.read_word(), std::move(cmd_promise)); } else if (cmd == "dns") { run_dns_cmd(parser, std::move(cmd_promise)); } else if (cmd == "gethistory") { @@ -442,16 +451,21 @@ class TonlibCli : public td::actor::Actor { promise.set_value(td::Unit()); return; } - if (resolved->entries_[0]->entry_->get_id() == tonlib_api::dns_entryDataNextResolver::ID) { + if (resolved->entries_[0]->entry_->get_id() == tonlib_api::dns_entryDataNextResolver::ID && ttl != 0) { + td::TerminalIO::out() << "Redirect resolver\n"; auto entry = tonlib_api::move_object_as(resolved->entries_[0]->entry_); - send_query(tonlib_api::make_object(std::move(entry->resolver_), name, category), - promise.send_closure(actor_id(this), &TonlibCli::do_dns_resolve, name, category, ttl)); + send_query(tonlib_api::make_object(std::move(entry->resolver_), name, category, ttl), + promise.send_closure(actor_id(this), &TonlibCli::do_dns_resolve, name, category, 0)); + return; } promise.set_error(td::Status::Error("Failed to resolve")); } void dns_resolve(td::ConstParser& parser, td::Promise promise) { auto key_id = parser.read_word(); + if (key_id == "root") { + key_id = "none"; + } TRY_RESULT_PROMISE(promise, address, to_account_address(key_id, false)); auto name = parser.read_word(); auto category_str = parser.read_word(); @@ -1198,6 +1212,22 @@ class TonlibCli : public td::actor::Actor { return td::Unit(); })); } + void import_key_raw(td::Slice filename, td::Promise promise) { + TRY_RESULT_PROMISE(promise, data, td::read_file_secure(filename.str())); + using tonlib_api::make_object; + send_query(make_object( + td::SecureString(), make_object(std::move(data))), + promise.wrap([&](auto&& key) { + LOG(ERROR) << to_string(key); + KeyInfo info; + info.public_key = key->public_key_; + info.secret = std::move(key->secret_); + keys_.push_back(std::move(info)); + export_key("exportkey", key->public_key_, keys_.size() - 1, td::SecureString()); + store_keys(); + return td::Unit(); + })); + } void export_key(std::string cmd, std::string key, size_t key_i, td::Slice password) { using tonlib_api::make_object; @@ -1485,6 +1515,11 @@ int main(int argc, char* argv[]) { options.use_callbacks_for_network = true; return td::Status::OK(); }); + p.add_option('w', "wallet-id", "do not use this", [&](td::Slice arg) { + TRY_RESULT(wallet_id, td::to_integer_safe((arg))); + options.wallet_id = wallet_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/utils/CMakeLists.txt b/utils/CMakeLists.txt index 82351ae7..32422b3e 100644 --- a/utils/CMakeLists.txt +++ b/utils/CMakeLists.txt @@ -5,7 +5,7 @@ if (NOT OPENSSL_FOUND) endif() add_executable(generate-random-id generate-random-id.cpp ) -target_link_libraries(generate-random-id tl_api ton_crypto keys) +target_link_libraries(generate-random-id tl_api ton_crypto keys adnl) target_include_directories(generate-random-id PUBLIC $/..) diff --git a/utils/generate-random-id.cpp b/utils/generate-random-id.cpp index 177700cc..586b8d69 100644 --- a/utils/generate-random-id.cpp +++ b/utils/generate-random-id.cpp @@ -23,7 +23,7 @@ exception statement from your version. If you delete this exception statement from all source files in the program, then also delete it here. - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include #include @@ -51,11 +51,11 @@ int main(int argc, char *argv[]) { std::string name = "id_ton"; - p.add_option('m', "mode", "sets mode id/adnl/dht/keys", [&](td::Slice key) { + p.add_option('m', "mode", "sets mode (one of id/adnl/dht/keys/adnlid)", [&](td::Slice key) { mode = key.str(); return td::Status::OK(); }); - p.add_option('h', "help", "prints_help", [&]() { + p.add_option('h', "help", "prints this help", [&]() { char b[10240]; td::StringBuilder sb(td::MutableSlice{b, 10000}); sb << p; @@ -63,11 +63,11 @@ int main(int argc, char *argv[]) { std::exit(2); return td::Status::OK(); }); - p.add_option('n', "name", "name to keys", [&](td::Slice arg) { + p.add_option('n', "name", "path to save private keys to", [&](td::Slice arg) { name = arg.str(); return td::Status::OK(); }); - p.add_option('k', "key", "private key to import", [&](td::Slice key) { + p.add_option('k', "key", "path to private key to import", [&](td::Slice key) { if (!pk.empty()) { return td::Status::Error("duplicate '-k' option"); } @@ -96,7 +96,7 @@ int main(int argc, char *argv[]) { } if (mode.size() == 0) { - std::cerr << "'-m' option missing" << std::endl; + std::cerr << "'--mode' option missing" << std::endl; return 2; } @@ -143,6 +143,12 @@ int main(int argc, char *argv[]) { td::write_file(name + ".pub", pub_key.export_as_slice().as_slice()).ensure(); std::cout << short_key.bits256_value().to_hex() << " " << td::base64_encode(short_key.as_slice()) << std::endl; + } else if (mode == "adnlid") { + auto n = pk.compute_short_id(); + name = n.bits256_value().to_hex(); + td::write_file(name, pk.export_as_slice()).ensure(); + + std::cout << name << " " << ton::adnl::AdnlNodeIdShort{n}.serialize() << std::endl; } else { std::cerr << "unknown mode " << mode; return 2;