From 1de39f5d7cc1655ae691462370dffec26cd9bc77 Mon Sep 17 00:00:00 2001 From: ton Date: Sat, 8 Feb 2020 23:24:24 +0400 Subject: [PATCH] bugfixes + doc update --- adnl/CMakeLists.txt | 1 + adnl/adnl-node-id.cpp | 35 ++++++ adnl/adnl-node-id.hpp | 6 +- crypto/CMakeLists.txt | 4 +- crypto/common/util.cpp | 66 ++++++---- crypto/common/util.h | 14 ++- crypto/fift/fift-main.cpp | 4 +- crypto/fift/lib/Asm.fif | 67 +++++++--- crypto/fift/lib/TonUtil.fif | 19 +++ crypto/fift/words.cpp | 4 +- crypto/fift/words.h | 4 +- crypto/func/builtins.cpp | 4 +- crypto/smartcont/dns-manual-code.fc | 186 ++++++++++------------------ crypto/smartcont/stdlib.fc | 40 +++--- crypto/smc-envelope/ManualDns.cpp | 35 +++++- crypto/smc-envelope/ManualDns.h | 16 +-- crypto/vm/cells/CellBuilder.cpp | 25 +++- crypto/vm/continuation.cpp | 10 ++ crypto/vm/cp0.cpp | 5 +- crypto/vm/cp0.h | 4 +- crypto/vm/debugops.cpp | 10 ++ crypto/vm/debugops.h | 3 +- crypto/vm/memo.cpp | 33 +++++ crypto/vm/memo.h | 37 ++++++ crypto/vm/stack.cpp | 17 +++ crypto/vm/tupleops.cpp | 21 ++++ crypto/vm/vmstate.h | 6 +- doc/tblkch.tex | 6 +- doc/tvm.tex | 4 + http/http-connection.cpp | 10 +- http/http.h | 7 ++ lite-client/lite-client.cpp | 15 +-- rldp-http-proxy/rldp-http-proxy.cpp | 6 + tdutils/td/utils/base64.cpp | 64 +++++++++- tdutils/td/utils/base64.h | 5 +- tdutils/test/misc.cpp | 20 +++ test/regression-tests.ans | 2 +- test/test-adnl.cpp | 9 +- tl/generate/scheme/tonlib_api.tl | 4 +- tl/generate/scheme/tonlib_api.tlo | Bin 19852 -> 20092 bytes tonlib/tonlib/TonlibClient.cpp | 67 ++++++---- tonlib/tonlib/TonlibClient.h | 6 + tonlib/tonlib/tonlib-cli.cpp | 18 ++- validator/impl/liteserver.cpp | 5 + 44 files changed, 652 insertions(+), 272 deletions(-) create mode 100644 adnl/adnl-node-id.cpp create mode 100644 crypto/vm/memo.cpp create mode 100644 crypto/vm/memo.h diff --git a/adnl/CMakeLists.txt b/adnl/CMakeLists.txt index 04c73044..7bb3bdc5 100644 --- a/adnl/CMakeLists.txt +++ b/adnl/CMakeLists.txt @@ -42,6 +42,7 @@ set(ADNL_SOURCE adnl-message.cpp adnl-network-manager.cpp adnl-node.cpp + adnl-node-id.cpp adnl-packet.cpp adnl-peer-table.cpp adnl-peer.cpp diff --git a/adnl/adnl-node-id.cpp b/adnl/adnl-node-id.cpp new file mode 100644 index 00000000..d3830aea --- /dev/null +++ b/adnl/adnl-node-id.cpp @@ -0,0 +1,35 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2017-2020 Telegram Systems LLP +*/ + +#include "adnl-node-id.hpp" + +#include "common/util.h" + +namespace ton { +namespace adnl { +td::Result AdnlNodeIdShort::parse(td::Slice id) { + TRY_RESULT(str, td::adnl_id_decode(id)); + return AdnlNodeIdShort(str); +} + +std::string AdnlNodeIdShort::serialize() { + return adnl_id_encode(hash_.as_slice()).move_as_ok(); +} +} // namespace adnl +} // namespace ton diff --git a/adnl/adnl-node-id.hpp b/adnl/adnl-node-id.hpp index 84f425f8..2d3ade16 100644 --- a/adnl/adnl-node-id.hpp +++ b/adnl/adnl-node-id.hpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once @@ -75,6 +75,10 @@ class AdnlNodeIdShort { return hash_.is_zero(); } + static td::Result parse(td::Slice key); + + std::string serialize(); + private: PublicKeyHash hash_; }; diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt index 0897166f..e2c4c55b 100644 --- a/crypto/CMakeLists.txt +++ b/crypto/CMakeLists.txt @@ -23,6 +23,7 @@ set(TON_CRYPTO_SOURCE vm/atom.cpp vm/continuation.cpp vm/dict.cpp + vm/memo.cpp vm/dispatch.cpp vm/opctable.cpp vm/cp0.cpp @@ -76,6 +77,7 @@ set(TON_CRYPTO_SOURCE vm/excno.hpp vm/fmt.hpp vm/log.h + vm/memo.h vm/opctable.h vm/stack.hpp vm/stackops.h @@ -354,7 +356,7 @@ if (NOT CMAKE_CROSSCOMPILING) WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMMAND fift -Ifift/lib:smartcont -s asm-to-cpp.fif ${ARG_DEST_FIF} ${ARG_DEST_CPP} ${ARG_NAME} MAIN_DEPENDENCY ${ARG_SOURCE} - DEPENDS fift ${ARG_DEST_FIF} smartcont/asm-to-cpp.fif + DEPENDS fift ${ARG_DEST_FIF} smartcont/asm-to-cpp.fif fift/lib/Asm.fif OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_DEST_CPP} ) add_custom_target(gen_fif_${ID} DEPENDS ${ARG_DEST_FIF} ${ARG_DEST_CPP}) diff --git a/crypto/common/util.cpp b/crypto/common/util.cpp index 72118152..d1b24a76 100644 --- a/crypto/common/util.cpp +++ b/crypto/common/util.cpp @@ -14,12 +14,15 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "util.h" #include +#include "td/utils/crypto.h" +#include "td/utils/base64.h" + namespace td { std::size_t compute_base64_encoded_size(size_t bindata_size) { @@ -73,10 +76,6 @@ std::size_t buff_base64_encode(td::MutableSlice buffer, td::Slice raw, bool base return res_size; } -std::string str_base64_encode(std::string raw, bool base64_url) { - return str_base64_encode(td::Slice{raw}, base64_url); -} - std::string str_base64_encode(td::Slice raw, bool base64_url) { std::size_t res_size = compute_base64_encoded_size(raw.size()); std::string s; @@ -87,10 +86,6 @@ std::string str_base64_encode(td::Slice raw, bool base64_url) { return s; } -bool is_valid_base64(std::string encoded, bool allow_base64_url) { - return is_valid_base64(td::Slice{encoded}, allow_base64_url); -} - bool is_valid_base64(td::Slice encoded, bool allow_base64_url) { const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size(); if (encoded.size() & 3) { @@ -110,10 +105,6 @@ bool is_valid_base64(td::Slice encoded, bool allow_base64_url) { return ptr == end; } -td::int32 decoded_base64_size(std::string encoded, bool allow_base64_url) { - return decoded_base64_size(td::Slice{encoded}, allow_base64_url); -} - td::int32 decoded_base64_size(td::Slice encoded, bool allow_base64_url) { const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size(); if (encoded.size() & 3) { @@ -172,10 +163,6 @@ std::size_t buff_base64_decode(td::MutableSlice buffer, td::Slice encoded, bool return wptr - (unsigned char *)buffer.data(); } -td::BufferSlice base64_decode(std::string encoded, bool allow_base64_url) { - return base64_decode(td::Slice{encoded}, allow_base64_url); -} - td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url) { auto s = decoded_base64_size(encoded, allow_base64_url); if (s <= 0) { @@ -190,10 +177,6 @@ td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url) { return res; } -std::string str_base64_decode(std::string encoded, bool allow_base64_url) { - return str_base64_decode(td::Slice{encoded}, allow_base64_url); -} - std::string str_base64_decode(td::Slice encoded, bool allow_base64_url) { auto s = decoded_base64_size(encoded, allow_base64_url); if (s <= 0) { @@ -209,4 +192,45 @@ std::string str_base64_decode(td::Slice encoded, bool allow_base64_url) { return res; } +td::Result adnl_id_encode(td::Slice id, bool upper_case) { + if (id.size() != 32) { + return td::Status::Error("Wrong andl id size"); + } + td::uint8 buf[35]; + td::MutableSlice buf_slice(buf, 35); + buf_slice[0] = 0x2d; + buf_slice.substr(1).copy_from(id); + auto hash = td::crc16(buf_slice.substr(0, 33)); + buf[33] = static_cast((hash >> 8) & 255); + buf[34] = static_cast(hash & 255); + return td::base32_encode(buf_slice, upper_case).substr(1); +} + +std::string adnl_id_encode(td::Bits256 adnl_addr, bool upper_case) { + return adnl_id_encode(adnl_addr.as_slice(), upper_case).move_as_ok(); +} + +td::Result adnl_id_decode(td::Slice id) { + if (id.size() != 55) { + return td::Status::Error("Wrong length of adnl id"); + } + td::uint8 buf[56]; + buf[0] = 'f'; + td::MutableSlice buf_slice(buf, 56); + buf_slice.substr(1).copy_from(id); + TRY_RESULT(decoded_str, td::base32_decode(buf_slice)); + auto decoded = td::Slice(decoded_str); + if (decoded[0] != 0x2d) { + return td::Status::Error("Invalid first byte"); + } + auto got_hash = (decoded.ubegin()[33] << 8) | decoded.ubegin()[34]; + auto hash = td::crc16(decoded.substr(0, 33)); + if (hash != got_hash) { + return td::Status::Error("Hash mismatch"); + } + Bits256 res; + res.as_slice().copy_from(decoded.substr(1, 32)); + return res; +} + } // namespace td diff --git a/crypto/common/util.h b/crypto/common/util.h index 2a594efc..e6b8ec2e 100644 --- a/crypto/common/util.h +++ b/crypto/common/util.h @@ -14,29 +14,31 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include #include "td/utils/Slice.h" #include "td/utils/buffer.h" +#include "bitstring.h" + namespace td { std::size_t compute_base64_encoded_size(size_t bindata_size); std::size_t buff_base64_encode(td::MutableSlice buffer, td::Slice raw, bool base64_url = false); -std::string str_base64_encode(std::string raw, bool base64_url = false); std::string str_base64_encode(td::Slice raw, bool base64_url = false); -bool is_valid_base64(std::string encoded, bool allow_base64_url = true); bool is_valid_base64(td::Slice encoded, bool allow_base64_url = true); -td::int32 decoded_base64_size(std::string encoded, bool allow_base64_url = true); td::int32 decoded_base64_size(td::Slice encoded, bool allow_base64_url = true); std::size_t buff_base64_decode(td::MutableSlice buffer, td::Slice data, bool allow_base64_url = true); -td::BufferSlice base64_decode(std::string encoded, bool allow_base64_url = true); td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url = true); -std::string str_base64_decode(std::string encoded, bool allow_base64_url = true); std::string str_base64_decode(td::Slice encoded, bool allow_base64_url = true); +//TODO: move it somewhere else +td::Result adnl_id_encode(td::Slice id, bool upper_case = false); +std::string adnl_id_encode(Bits256 adnl_addr, bool upper_case = false); +td::Result adnl_id_decode(td::Slice id); + } // namespace td diff --git a/crypto/fift/fift-main.cpp b/crypto/fift/fift-main.cpp index 8702f13d..077cdecc 100644 --- a/crypto/fift/fift-main.cpp +++ b/crypto/fift/fift-main.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 "vm/stack.hpp" #include @@ -156,7 +156,7 @@ int main(int argc, char* const argv[]) { } fift::init_words_common(config.dictionary); - fift::init_words_vm(config.dictionary); + fift::init_words_vm(config.dictionary, true); // enable vm debug fift::init_words_ton(config.dictionary); if (script_mode) { diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 7c09e82d..ab9f72c2 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -216,6 +216,10 @@ x{6FA0} @Defop NULLSWAPIF x{6FA1} @Defop NULLSWAPIFNOT x{6FA2} @Defop NULLROTRIF x{6FA3} @Defop NULLROTRIFNOT +x{6FA4} @Defop NULLSWAPIF2 +x{6FA5} @Defop NULLSWAPIFNOT2 +x{6FA6} @Defop NULLROTRIF2 +x{6FA7} @Defop NULLROTRIFNOT2 { idict! + not abort"cannot add key to procedure info dictionary" + @procinfo ! +} : @procinfo! +// ( x v1 v2 -- ) +{ not 2 pick @procinfo@ and xor swap @procinfo! } : @procinfo~! +// ( s i f -- ) +{ over @procdictkeylen fits not abort"procedure index out of range" + over swap dup @procinfo~! 2dup @proclistadd + 1 'nop does swap 0 (create) } : @declproc { 1 'nop does swap 0 (create) } : @declglobvar -{ @proccnt @ 1+ dup @proccnt ! @declproc } : @newproc +{ @proccnt @ 1+ dup @proccnt ! 1 @declproc } : @newproc { @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar { 0 =: main @proclist null! @proccnt 0! @gvarcnt 0! { bl word @newproc } : NEWPROC { bl word dup (def?) ' drop ' @newproc cond } : DECLPROC { bl word dup find { nip execute <> abort"method redefined with different id" } - { swap @declproc } + { swap 17 @declproc } cond } : DECLMETHOD { bl word @newglobvar } : DECLGLOBVAR - "main" @proclistadd - dictnew @procdict ! + "main" 0 @proclistadd + dictnew dup @procdict ! + @procinfo ! 16 0 @procinfo! } : PROGRAM{ { over sbits < { s>c B B{3ee6} swap B+ dup crc16 16 u>B B+ B>base64 } : pubkey>$ { pubkey>$ type } : .pubkey + +// adnl address parser +{ 256 u>B B{2D} swap B+ dup crc16 16 u>B B+ } : adnl-preconv +{ swap 32 /mod dup 26 < { 65 } { 24 } cond + rot swap hold } : Base32# +{ <# ' Base32# 8 times #> } : Base32#*8 +{ "" over Blen 5 / { swap 40 B>u@+ Base32#*8 nip rot swap $+ } swap times nip } : B>Base32 + +// ( x -- S ) Converts an adnl-address from a 256-bit integer to a string +{ adnl-preconv B>Base32 1 $| nip } : adnl>$ + +{ 65 - dup 0>= { -33 and dup 26 < } { 41 + dup 25 > over 32 < and } cond ?dup nip } : Base32-digit? +{ Base32-digit? not abort"not a Base32 digit" } : Base32-digit +{ 0 { over $len } { swap 1 $| -rot (char) Base32-digit swap 5 << + } while nip } : Base32-number +{ B{} { over $len } { swap 8 $| -rot Base32-number 40 u>B B+ } while nip } : Base32>B + +// ( S -- x ) Converts an adnl address from a string to 256-bit integer +{ 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 diff --git a/crypto/fift/words.cpp b/crypto/fift/words.cpp index a4067eb9..0e0d65b6 100644 --- a/crypto/fift/words.cpp +++ b/crypto/fift/words.cpp @@ -2868,9 +2868,9 @@ void init_words_ton(Dictionary& d) { d.def_stack_word("base64url>B ", std::bind(interpret_base64_to_bytes, _1, true, false)); } -void init_words_vm(Dictionary& d) { +void init_words_vm(Dictionary& d, bool enable_debug) { using namespace std::placeholders; - vm::init_op_cp0(); + vm::init_op_cp0(enable_debug); // vm run d.def_stack_word("vmlibs ", std::bind(interpret_literal, _1, vm::StackEntry{vm_libraries})); // d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm, _1, 0x40)); diff --git a/crypto/fift/words.h b/crypto/fift/words.h index eac182e4..de5b4389 100644 --- a/crypto/fift/words.h +++ b/crypto/fift/words.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "Dictionary.h" @@ -33,7 +33,7 @@ struct Quit { struct SkipToEof {}; void init_words_common(Dictionary& dictionary); -void init_words_vm(Dictionary& dictionary); +void init_words_vm(Dictionary& dictionary, bool debug_enabled = false); void init_words_ton(Dictionary& dictionary); void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]); diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 41951a53..93f47dec 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "func.h" @@ -765,7 +765,7 @@ AsmOp compile_cond_throw(std::vector& res, std::vector& args x.unused(); return skip_cond ? exec_arg_op("THROW", x.int_const, 0, 0) : exec_arg_op("THROW"s + suff, x.int_const, 1, 0); } else { - return skip_cond ? exec_op("THROWANY", 1, 0) : exec_arg_op("THROWANY"s + suff, 2, 0); + return skip_cond ? exec_op("THROWANY", 1, 0) : exec_op("THROWANY"s + suff, 2, 0); } } diff --git a/crypto/smartcont/dns-manual-code.fc b/crypto/smartcont/dns-manual-code.fc index 7e34a1cc..4a4b82fa 100644 --- a/crypto/smartcont/dns-manual-code.fc +++ b/crypto/smartcont/dns-manual-code.fc @@ -15,7 +15,7 @@ ;; Args: s D n | Success: s' x s'' -1 | Failure: s 0 -> s N N 0 (slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) - asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; + asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2"; ;; Args: x k D n | Success: D' -1 | Failure: D 0 (cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) @@ -36,12 +36,9 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; return (dict, ()); } -(slice, cell, slice, int) pfxdict_get_ref(cell dict, int key_len, slice key) { +(slice, cell, slice, int) pfxdict_get_ref(cell dict, int key_len, slice key) inline_ref { (slice pfx, slice val, slice tail, int succ) = dict.pfxdict_get?(key_len, key); - cell res = null(); - if (succ) { - res = val~load_maybe_ref(); - } + cell res = succ ? val~load_maybe_ref() : null(); return (pfx, res, tail, succ); } @@ -49,16 +46,16 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; ;; Utility functions ;; ;;===========================================================================;; -(int, int, int, cell, cell) load_data() { +(int, int, int, cell, cell) load_data() inline_ref { slice cs = get_data().begin_parse(); var res = (cs~load_uint(32), cs~load_uint(64), cs~load_uint(256), cs~load_dict(), cs~load_dict()); cs.end_parse(); return res; } -() store_data(int subwallet, int last_cleaned, int public_key, cell root, old_queries) impure { +() store_data(int contract_id, int last_cleaned, int public_key, cell root, old_queries) impure { set_data(begin_cell() - .store_uint(subwallet, 32) + .store_uint(contract_id, 32) .store_uint(last_cleaned, 64) .store_uint(public_key, 256) .store_dict(root) @@ -102,7 +99,7 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; [UInt<256b>:new_public_key] -} -(cell, slice) process_op(cell root, slice ops) { +(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); @@ -129,19 +126,13 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; ;; at least one character not counting \0 throw_unless(38, name.slice_bits() >= 16); ;; name shall end with \0 - (_, int name_last_byte) = name.slice_last(8).load_uint(8); - throw_unless(40, name_last_byte == 0); - - ;; Multiple zero characters seem to be allowed as per github issue response - ;; Lets change the tactics! - - int loop = -1; + int name_last_byte = name.slice_last(8).preload_uint(8); + throw_if(40, name_last_byte); + ;; count zero separators slice cname = name; - ;; better safe then sorry, dont want to catch any of loop bugs - while (loop) { - int lval = cname~load_uint(8); - if (lval == 0) { zeros += 1; } - if (cname.slice_bits() == 0) { loop = 0; } + repeat (cname.slice_bits() ^>> 3) { + int c = cname~load_uint(8); + zeros -= (c == 0); } ;; throw_unless(39, zeros == 1); } @@ -163,18 +154,13 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; ;; 11 VSet: set specified value to specified subdomain->category if (op == 11) { cell new_value = ops~load_maybe_ref(); - if (new_value.cell_null?()) { - cat_table~idict_delete?(16, cat); - } else { - cat_table~idict_set_ref(16, cat, new_value); - } + cat_table~idict_set_get_ref(16, cat, new_value); root~pfxdict_set_ref(1023, name, cat_table); return (root, ops); } ;; 12 VDel: delete specified subdomain->category value if (op == 12) { - ifnot (cat_table.dict_empty?()) { - cat_table~idict_delete?(16, cat); + if (cat_table~idict_delete?(16, cat)) { root~pfxdict_set_ref(1023, name, cat_table); } return (root, ops); @@ -198,22 +184,22 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF"; } ;; 32 TDel: nullify ENTIRE DOMAIN TABLE if (op == 32) { - return (null(), ops); + return (null(), ops); } throw(44); ;; invalid operation - return (root, ops); + return (root, ops); } -cell process_ops(cell root, slice ops) { - int f = -1; - while (f) { +cell process_ops(cell root, slice ops) inline_ref { + var stop = false; + do { (root, ops) = process_op(root, ops); if (ops.slice_refs_empty?()) { - f = 0; + stop = true; } else { ops = ops~load_ref().begin_parse(); } - } + } until (stop); return root; } @@ -224,7 +210,7 @@ cell process_ops(cell root, slice ops) { ;; validate signature and seqno slice signature = in_msg~load_bits(512); int shash = slice_hash(in_msg); - var (subwallet_id, query_id) = (in_msg~load_uint(32), in_msg~load_uint(64)); + var (contract_id, query_id) = (in_msg~load_uint(32), in_msg~load_uint(64)); var bound = (now() << 32); throw_if(35, query_id < bound); (_, var found?) = old_queries.udict_get?(64, query_id); @@ -232,17 +218,13 @@ cell process_ops(cell root, slice ops) { throw_unless(34, check_signature(shash, signature, public_key)); accept_message(); ;; message is signed by owner, sanity not guaranteed yet - int op = in_msg.preload_uint(6); - ;; 00 Contract initialization message (only if seqno = 0) - if (op == 0) { - ;; noop - } else { if (op == 51) { + if (op == 51) { in_msg~skip_bits(6); public_key = in_msg~load_uint(256); - } else { + } elseif (op) { ;; 00 Contract initialization message root = process_ops(root, in_msg); - } } + } bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago old_queries~udict_set_builder(64, query_id, begin_cell()); @@ -259,7 +241,7 @@ cell process_ops(cell root, slice ops) { } } until (~ f); - store_data(subwallet_id, last_cleaned, public_key, root, old_queries); + store_data(contract_id, last_cleaned, public_key, root, old_queries); } {- @@ -267,7 +249,6 @@ cell process_ops(cell root, slice ops) { Root cell: [UInt<32b>:seqno] [UInt<256b>:owner_public_key] [OptRef<1b+1r?>:HashmapCatTable>:domains] := HashmapE 16 ^DNSRecord - := arbitary? not defined anywhere in documentation or internet! STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value) #Zeros allows to simultaneously store, for example, com\0 and com\0google\0 @@ -282,100 +263,67 @@ cell process_ops(cell root, slice ops) { ;; Getter methods ;; ;;===========================================================================;; -int get_seqno() method_id { ;; Retrieve sequence number +;; Retrieve contract id (in case several contracts are managed with the same private key) +int get_contract_id() method_id { return get_data().begin_parse().preload_uint(32); } ;;8m dns-record-value (int, cell) dnsresolve(slice subdomain, int category) method_id { - cell Null = null(); ;; pseudo-alias - throw_if(30, subdomain.slice_bits() % 8 != 0); ;; malformed input (~ 8n-bit) - if (subdomain.slice_bits() == 0) { return (0, Null); } ;; zero-length input - {- ;; Logic thrown away: return only first ZB-delimited subdomain, - appends \0 if neccessary (not required for pfx, \0 can be ap more eff) - builder b_name = begin_cell(); - slice remaining = subdomain; - int char = remaining~load_uint(8); ;; seems to be the most optimal way - do { - b_name~store_uint(char, 8); - char = remaining~load_uint(8); - } until ((remaining.slice_bits() == 0) | (char == 0)); - if (char == 0) { category = -1; } - if ((remaining.slice_bits() == 0) & (char != 0)) { - b_name~store_uint(0, 8); ;; string was not terminated with zero byte + int bits = subdomain.slice_bits(); + ifnot (bits) { + return (0, null()); ;; zero-length input } - cell c_name = b_name.end_cell(); - slice s_name = c_name.begin_parse(); - -} - (_, int name_last_byte) = subdomain.slice_last(8).load_uint(8); - if ((name_last_byte == 0) & (subdomain.slice_bits() == 8)) { - return (0, Null); ;; zero-length input, but with zero byte + throw_if(30, bits & 7); ;; malformed input (~ 8n-bit) + + int name_last_byte = subdomain.slice_last(8).preload_uint(8); + if (name_last_byte) { + subdomain = begin_cell().store_slice(subdomain) ;; append zero byte + .store_uint(0, 8).end_cell().begin_parse(); + bits += 8; } - slice s_name = subdomain; - if (name_last_byte != 0) { - s_name = begin_cell().store_slice(subdomain) ;; append zero byte - .store_uint(0, 8).end_cell().begin_parse(); + if (bits == 8) { + return (0, null()); ;; zero-length input, but with zero byte } (_, _, _, cell root, _) = load_data(); - ;; Multiple zero characters seem to be allowed as per github issue response - ;; Lets change the tactics! - + slice cname = subdomain; int zeros = 0; - int loop = -1; - slice cname = s_name; - ;; better safe then sorry, dont want to catch any of loop bugs - while (loop) { - int lval = cname~load_uint(8); - if (lval == 0) { zeros += 1; } - if (cname.slice_bits() == 0) { loop = 0; } + repeat (bits >> 3) { + int c = cname~load_uint(8); + zeros -= (c == 0); } - - ;; can't move below, will cause errors! - slice pfx = cname; cell val = null(); - slice tail = cname; int succ = 0; - - while (zeros > 0) { - slice pfname = begin_cell().store_uint(zeros, 7) - .store_slice(s_name).end_cell().begin_parse(); - (pfx, val, tail, succ) = root.pfxdict_get_ref(1023, pfname); - if (succ) { zeros = 1; } ;; break - zeros -= 1; + + ;; can't move these declarations lower, will cause errors! + slice pfx = cname; + cell val = null(); + slice tail = cname; + + do { + slice pfxname = begin_cell().store_uint(zeros, 7) + .store_slice(subdomain).end_cell().begin_parse(); + (pfx, val, tail, int succ) = root.pfxdict_get_ref(1023, pfxname); + zeros = succ ^ (zeros - 1); ;; break on success + } until (zeros <= 0); + + ifnot (zeros) { + return (0, null()); ;; failed to find entry in prefix dictionary } - - - zeros = pfx.preload_uint(7); - - if (~ succ) { - return (0, Null); ;; failed to find entry in prefix dictionary - } - if (~ tail.slice_empty?()) { ;; if we have tail then len(pfx) < len(subdomain) + + zeros = - zeros; + + ifnot (tail.slice_empty?()) { ;; if we have tail then len(pfx) < len(subdomain) category = -1; ;; incomplete subdomain found, must return next resolver (-1) } int pfx_bits = pfx.slice_bits() - 7; cell cat_table = val; ;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain - ;; COUNTING for the zero byte (if structurally correct: no multiple-ZB keys) - ;; which corresponds to "8m, m=one plus the number of bytes in the subdomain found) + ;; COUNTING the zero byte (if structurally correct: no multiple-ZB keys) + ;; which corresponds to 8m, m=one plus the number of bytes in the subdomain found) if (category == 0) { return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0 } else { cell cat_found = cat_table.idict_get_ref(16, category); - {- it seems that if subdomain is found but cat is not need to return (8m, Null) - if (cat_found.cell_null?()) { - pfx_bits = 0; ;; to return (0, Null) instead of (8m, Null) - ;; my thoughts about this requirement are in next block comment - } -} - return (pfx_bits, cat_found); ;; no need to unslice and cellize the poor cat now - {- Old logic garbage, replaced with ref functions discovered - ;; dictionary category lookup - (slice cat_value, int cat_found) = cat_table.idict_get?(16, category); - if (~ cat_found) { - ;; we have failed to find the cat :( - return (0, Null); - } - ;; cat is found, turn it's slices into cells - return (pfx.slice_bits(), begin_cell().store_slice(cat_value).end_cell()); - -} + return (pfx_bits, cat_found); } } diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 2dc5c137..0547a57d 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -128,26 +128,26 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va (cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; (cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB"; (cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB"; -(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) udict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; -(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; +(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(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_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"; +(int, slice, int) udict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_nexteq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) idict_get_preveq?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; cell new_dict() asm "NEWDICT"; int dict_empty?(cell c) asm "DICTEMPTY"; diff --git a/crypto/smc-envelope/ManualDns.cpp b/crypto/smc-envelope/ManualDns.cpp index c932f49d..d453c778 100644 --- a/crypto/smc-envelope/ManualDns.cpp +++ b/crypto/smc-envelope/ManualDns.cpp @@ -30,7 +30,26 @@ #include "block/block-auto.h" #include "block/block-parse.h" +#include "common/util.h" + namespace ton { +td::StringBuilder& operator<<(td::StringBuilder& sb, const ManualDns::EntryData& data) { + switch (data.type) { + case ManualDns::EntryData::Type::Empty: + return sb << "DELETED"; + case ManualDns::EntryData::Type::Text: + return sb << "TEXT:" << data.data.get().text; + case ManualDns::EntryData::Type::NextResolver: + return sb << "NEXT:" << data.data.get().resolver.rserialize(); + case ManualDns::EntryData::Type::AdnlAddress: + return sb << "ADNL:" + << td::adnl_id_encode(data.data.get().adnl_address.as_slice()) + .move_as_ok(); + case ManualDns::EntryData::Type::SmcAddress: + return sb << "SMC:" << data.data.get().smc_address.rserialize(); + } + return sb << ""; +} //proto_list_nil$0 = ProtoList; //proto_list_next$1 head:Protocol tail:ProtoList = ProtoList; @@ -486,10 +505,19 @@ td::Result> ManualDns::parse_data(td::Slice c td::ConstParser parser(cmd); parser.skip_whitespaces(); auto type = parser.read_till(':'); - parser.advance(1); + parser.skip(':'); if (type == "TEXT") { return ManualDns::EntryData::text(parser.read_all().str()); - } else if (type == "DELETED") { + } else if (type == "ADNL") { + TRY_RESULT(address, td::adnl_id_decode(parser.read_all())); + return ManualDns::EntryData::adnl_address(address); + } else if (type == "SMC") { + TRY_RESULT(address, block::StdAddress::parse(parser.read_all())); + return ManualDns::EntryData::smc_address(address); + } else if (type == "NEXT") { + TRY_RESULT(address, block::StdAddress::parse(parser.read_all())); + return ManualDns::EntryData::next_resolver(address); + } else if (parser.data() == "DELETED") { return {}; } return td::Status::Error(PSLICE() << "Unknown entry type: " << type); @@ -502,6 +530,9 @@ td::Result ManualDns::parse_line(td::Slice cmd) { // delete.all // data = // TEXT: | + // SMC: | + // NEXT: | + // ADNL: // DELETED td::ConstParser parser(cmd); auto type = parser.read_word(); diff --git a/crypto/smc-envelope/ManualDns.h b/crypto/smc-envelope/ManualDns.h index 08db188e..d43750d0 100644 --- a/crypto/smc-envelope/ManualDns.h +++ b/crypto/smc-envelope/ManualDns.h @@ -82,21 +82,7 @@ class DnsInterface { bool operator==(const EntryData& other) const { return data == other.data; } - friend td::StringBuilder& operator<<(td::StringBuilder& sb, const EntryData& data) { - switch (data.type) { - case Type::Empty: - return sb << ""; - case Type::Text: - return sb << "text{" << data.data.get().text << "}"; - case Type::NextResolver: - return sb << "next{" << data.data.get().resolver.rserialize() << "}"; - case Type::AdnlAddress: - return sb << "adnl{" << data.data.get().adnl_address.to_hex() << "}"; - case Type::SmcAddress: - return sb << "smc{" << data.data.get().smc_address.rserialize() << "}"; - } - return sb << ""; - } + friend td::StringBuilder& operator<<(td::StringBuilder& sb, const EntryData& data); td::Result> as_cell() const; static td::Result from_cellslice(vm::CellSlice& cs); diff --git a/crypto/vm/cells/CellBuilder.cpp b/crypto/vm/cells/CellBuilder.cpp index 0515a587..bfd73f8d 100644 --- a/crypto/vm/cells/CellBuilder.cpp +++ b/crypto/vm/cells/CellBuilder.cpp @@ -55,8 +55,16 @@ Ref CellBuilder::finalize_copy(bool special) const { LOG(ERROR) << res.error(); throw CellWriteError{}; } - CHECK(res.ok().not_null()); - return res.move_as_ok(); + auto cell = res.move_as_ok(); + CHECK(cell.not_null()); + if (vm_state_interface) { + vm_state_interface->register_new_cell(cell); + if (cell.is_null()) { + LOG(ERROR) << "cannot register new data cell"; + throw CellWriteError{}; + } + } + return cell; } Ref CellBuilder::finalize_novm(bool special) { @@ -72,10 +80,17 @@ Ref CellBuilder::finalize_novm(bool special) { Ref CellBuilder::finalize(bool special) { auto* vm_state_interface = VmStateInterface::get(); - if (vm_state_interface) { - vm_state_interface->register_cell_create(); + if (!vm_state_interface) { + return finalize_novm(special); } - return finalize_novm(special); + vm_state_interface->register_cell_create(); + auto cell = finalize_novm(special); + vm_state_interface->register_new_cell(cell); + if (cell.is_null()) { + LOG(ERROR) << "cannot register new data cell"; + throw CellWriteError{}; + } + return cell; } Ref CellBuilder::create_pruned_branch(Ref cell, td::uint32 new_level, td::uint32 virt_level) { diff --git a/crypto/vm/continuation.cpp b/crypto/vm/continuation.cpp index fdd27ce6..243dfd5e 100644 --- a/crypto/vm/continuation.cpp +++ b/crypto/vm/continuation.cpp @@ -21,6 +21,7 @@ #include "vm/dict.h" #include "vm/log.h" #include "vm/vm.h" +#include "vm/vmstate.h" namespace vm { @@ -190,6 +191,10 @@ bool ControlData::deserialize(CellSlice& cs, int mode) { } bool Continuation::serialize_ref(CellBuilder& cb) const { + auto* vsi = VmStateInterface::get(); + if (vsi && !vsi->register_op()) { + return false; + } vm::CellBuilder cb2; return serialize(cb2) && cb.store_ref_bool(cb2.finalize()); } @@ -198,6 +203,11 @@ Ref Continuation::deserialize(CellSlice& cs, int mode) { if (mode & 0x1002) { return {}; } + auto* vsi = VmStateInterface::get(); + if (vsi && !vsi->register_op()) { + return {}; + } + mode |= 0x1000; switch (cs.bselect_ext(6, 0x100f011100010001ULL)) { case 0: diff --git a/crypto/vm/cp0.cpp b/crypto/vm/cp0.cpp index 3e062140..fe5dd8ef 100644 --- a/crypto/vm/cp0.cpp +++ b/crypto/vm/cp0.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "cp0.h" #include "opctable.h" @@ -29,7 +29,8 @@ namespace vm { -const OpcodeTable* init_op_cp0() { +const OpcodeTable* init_op_cp0(bool enable_debug) { + set_debug_enabled(enable_debug); static const OpcodeTable* static_op_cp0 = [] { auto op_cp0 = new OpcodeTable("TEST CODEPAGE", Codepage::test_cp); register_stack_ops(*op_cp0); // stackops.cpp diff --git a/crypto/vm/cp0.h b/crypto/vm/cp0.h index 34fc9711..9a657969 100644 --- a/crypto/vm/cp0.h +++ b/crypto/vm/cp0.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once #include "vm/dispatch.h" @@ -23,6 +23,6 @@ namespace vm { class OpcodeTable; -const OpcodeTable* init_op_cp0(); +const OpcodeTable* init_op_cp0(bool debug_enabled = false); } // namespace vm diff --git a/crypto/vm/debugops.cpp b/crypto/vm/debugops.cpp index f8dc4396..456338b8 100644 --- a/crypto/vm/debugops.cpp +++ b/crypto/vm/debugops.cpp @@ -28,6 +28,10 @@ namespace vm { bool vm_debug_enabled = true; +void set_debug_enabled(bool enable_debug) { + vm_debug_enabled = enable_debug; +} + int exec_dummy_debug(VmState* st, int args) { VM_LOG(st) << "execute DEBUG " << (args & 0xff); return 0; @@ -66,6 +70,9 @@ int compute_len_debug_str(const CellSlice& cs, unsigned args, int pfx_bits) { int exec_dump_stack(VmState* st) { VM_LOG(st) << "execute DUMPSTK"; + if (!vm_debug_enabled) { + return 0; + } Stack& stack = st->get_stack(); int d = stack.depth(); std::cerr << "#DEBUG#: stack(" << d << " values) : "; @@ -84,6 +91,9 @@ int exec_dump_stack(VmState* st) { int exec_dump_value(VmState* st, unsigned arg) { arg &= 15; VM_LOG(st) << "execute DUMP s" << arg; + if (!vm_debug_enabled) { + return 0; + } Stack& stack = st->get_stack(); if ((int)arg < stack.depth()) { std::cerr << "#DEBUG#: s" << arg << " = "; diff --git a/crypto/vm/debugops.h b/crypto/vm/debugops.h index 7c7b27d0..ba3d673f 100644 --- a/crypto/vm/debugops.h +++ b/crypto/vm/debugops.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once @@ -25,5 +25,6 @@ class OpcodeTable; extern bool vm_debug_enabled; void register_debug_ops(OpcodeTable& cp0); +void set_debug_enabled(bool enable_debug); } // namespace vm diff --git a/crypto/vm/memo.cpp b/crypto/vm/memo.cpp new file mode 100644 index 00000000..576f28f1 --- /dev/null +++ b/crypto/vm/memo.cpp @@ -0,0 +1,33 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2020 Telegram Systems LLP +*/ +#include "vm/memo.h" +#include "vm/excno.hpp" + +namespace vm { +using td::Ref; + +bool FakeVmStateLimits::register_op(int op_units) { + bool ok = (ops_remaining -= op_units) >= 0; + if (!ok && !quiet) { + throw VmError{Excno::out_of_gas, "too many operations"}; + } + return ok; +} + +} // namespace vm diff --git a/crypto/vm/memo.h b/crypto/vm/memo.h new file mode 100644 index 00000000..3aee39a6 --- /dev/null +++ b/crypto/vm/memo.h @@ -0,0 +1,37 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2020 Telegram Systems LLP +*/ +#pragma once +#include "common/refcnt.hpp" +#include "vm/cells.h" +#include "vm/vmstate.h" + +namespace vm { +using td::Ref; + +class FakeVmStateLimits : public VmStateInterface { + long long ops_remaining; + bool quiet; + + public: + FakeVmStateLimits(long long max_ops = 1LL << 62, bool _quiet = true) : ops_remaining(max_ops), quiet(_quiet) { + } + bool register_op(int op_units = 1) override; +}; + +} // namespace vm diff --git a/crypto/vm/stack.cpp b/crypto/vm/stack.cpp index 92e5174c..84ed4b83 100644 --- a/crypto/vm/stack.cpp +++ b/crypto/vm/stack.cpp @@ -20,6 +20,7 @@ #include "vm/continuation.h" #include "vm/box.hpp" #include "vm/atom.h" +#include "vm/vmstate.h" namespace td { template class td::Cnt; @@ -678,6 +679,10 @@ void Stack::push_maybe_cellslice(Ref cs) { */ bool StackEntry::serialize(vm::CellBuilder& cb, int mode) const { + auto* vsi = VmStateInterface::get(); + if (vsi && !vsi->register_op()) { + return false; + } switch (tp) { case t_null: return cb.store_long_bool(0, 8); // vm_stk_null#00 = VmStackValue; @@ -739,6 +744,10 @@ bool StackEntry::serialize(vm::CellBuilder& cb, int mode) const { } bool StackEntry::deserialize(CellSlice& cs, int mode) { + auto* vsi = VmStateInterface::get(); + if (vsi && !vsi->register_op()) { + return false; + } clear(); int t = (mode & 0xf000) ? ((mode >> 12) & 15) : (int)cs.prefetch_ulong(8); switch (t) { @@ -843,6 +852,10 @@ bool StackEntry::deserialize(Ref cell, int mode) { } bool Stack::serialize(vm::CellBuilder& cb, int mode) const { + auto* vsi = VmStateInterface::get(); + if (vsi && !vsi->register_op()) { + return false; + } // vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack; unsigned n = depth(); if (!cb.store_ulong_rchk_bool(n, 24)) { // vm_stack#_ depth:(## 24) @@ -863,6 +876,10 @@ bool Stack::serialize(vm::CellBuilder& cb, int mode) const { } bool Stack::deserialize(vm::CellSlice& cs, int mode) { + auto* vsi = VmStateInterface::get(); + if (vsi && !vsi->register_op()) { + return false; + } clear(); // vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack; int n; diff --git a/crypto/vm/tupleops.cpp b/crypto/vm/tupleops.cpp index 96f78fe3..ef906f6a 100644 --- a/crypto/vm/tupleops.cpp +++ b/crypto/vm/tupleops.cpp @@ -53,6 +53,23 @@ int exec_null_swap_if(VmState* st, bool cond, int depth) { return 0; } +int exec_null_swap_if_many(VmState* st, bool cond, int depth, int count) { + Stack& stack = st->get_stack(); + VM_LOG(st) << "execute NULL" << (depth ? "ROTR" : "SWAP") << (cond ? "IF" : "IFNOT") << count; + stack.check_underflow(depth + 1); + auto x = stack.pop_int_finite(); + if (!x->sgn() != cond) { + for (int i = 0; i < count; i++) { + stack.push({}); + } + for (int i = 0; i < depth; i++) { + swap(stack[i], stack[i + count]); + } + } + stack.push_int(std::move(x)); + return 0; +} + int exec_mktuple_common(VmState* st, unsigned n) { Stack& stack = st->get_stack(); stack.check_underflow(n); @@ -374,6 +391,10 @@ void register_tuple_ops(OpcodeTable& cp0) { .insert(OpcodeInstr::mksimple(0x6fa1, 16, "NULLSWAPIFNOT", std::bind(exec_null_swap_if, _1, false, 0))) .insert(OpcodeInstr::mksimple(0x6fa2, 16, "NULLROTRIF", std::bind(exec_null_swap_if, _1, true, 1))) .insert(OpcodeInstr::mksimple(0x6fa3, 16, "NULLROTRIFNOT", std::bind(exec_null_swap_if, _1, false, 1))) + .insert(OpcodeInstr::mksimple(0x6fa4, 16, "NULLSWAPIF2", std::bind(exec_null_swap_if_many, _1, true, 0, 2))) + .insert(OpcodeInstr::mksimple(0x6fa5, 16, "NULLSWAPIFNOT2", std::bind(exec_null_swap_if_many, _1, false, 0, 2))) + .insert(OpcodeInstr::mksimple(0x6fa6, 16, "NULLROTRIF2", std::bind(exec_null_swap_if_many, _1, true, 1, 2))) + .insert(OpcodeInstr::mksimple(0x6fa7, 16, "NULLROTRIFNOT2", std::bind(exec_null_swap_if_many, _1, false, 1, 2))) .insert(OpcodeInstr::mkfixed(0x6fb, 12, 4, dump_tuple_index2, exec_tuple_index2)) .insert(OpcodeInstr::mkfixed(0x6fc >> 2, 10, 6, dump_tuple_index3, exec_tuple_index3)); } diff --git a/crypto/vm/vmstate.h b/crypto/vm/vmstate.h index 39e5d48a..a81a4e78 100644 --- a/crypto/vm/vmstate.h +++ b/crypto/vm/vmstate.h @@ -28,12 +28,16 @@ using td::Ref; class VmStateInterface : public td::Context { public: virtual ~VmStateInterface() = default; - virtual Ref load_library( + virtual Ref load_library( td::ConstBitPtr hash) { // may throw a dictionary exception; returns nullptr if library is not found return {}; } virtual void register_cell_load(const CellHash& cell_hash){}; virtual void register_cell_create(){}; + virtual void register_new_cell(Ref& cell){}; + virtual bool register_op(int op_units = 1) { + return true; + }; }; } // namespace vm diff --git a/doc/tblkch.tex b/doc/tblkch.tex index daf50065..e8d7f44f 100644 --- a/doc/tblkch.tex +++ b/doc/tblkch.tex @@ -900,7 +900,7 @@ If one wants to represent $x$ nanograms, one selects an integer $\ell<16$ such t Recall (cf.~\cite[A]{TON}) that the original total supply of Grams is fixed at five billion (i.e., $5\cdot10^{18}<2^{63}$ nanograms), and is expected to grow very slowly. Therefore, all the amounts of Grams encountered in practice will fit in unsigned or even signed 64-bit integers. The validators may use the 64-bit integer representation of Grams in their internal computations; however, the serialization of these values the blockchain is another matter. -\nxsubpoint\emb{Representing collections of arbitrary currencies} +\nxsubpoint\emb{Representing collections of arbitrary currencies}\label{sp:extra.curr} Recall that the TON Blockchain allows its users to define arbitrary cryptocurrencies or tokens apart from the Gram, provided some conditions are met. Such additional cryptocurrencies are identified by 32-bit $\currencyid$s. The list of defined additional cryptocurrencies is a part of the blockchain configuration, stored in the masterchain. When some amounts of one or several such cryptocurrencies need to be represented, a dictionary (cf.~\cite[3.3]{TVM}) with 32-bit $\currencyid$s as keys and \texttt{VarUInteger 32} values is used: @@ -936,7 +936,7 @@ message$_ {X:Type} info:CommonMsgInfo \end{verbatim} The meaning of this scheme is as follows. -Type \texttt{Message $X$} describes a message with the body (or payload) of type $X$. Its serialization starts with \texttt{info} of type \texttt{CommonMsgInfo}, which comes in three flavors: for internal messages, inbound external messages, and outbound external messages, respectively. All of them have a source address \texttt{src} and destination address \texttt{dest}, which are external or internal according to the chosen constructor. Apart from that, an internal message may bear some \texttt{value} in Grams and other defined currencies, and all messages generated inside the TON Blockchain have a logical creation time \texttt{created\_lt} (cf.~\ptref{sp:lt.ton.blkch}) and creation unixtime \texttt{created\_at}, both automatically set by the generating transaction. The creation unixtime equals the creation unixtime of the block containing the generating transaction. +Type \texttt{Message $X$} describes a message with the body (or payload) of type $X$. Its serialization starts with \texttt{info} of type \texttt{CommonMsgInfo}, which comes in three flavors: for internal messages, inbound external messages, and outbound external messages, respectively. All of them have a source address \texttt{src} and destination address \texttt{dest}, which are external or internal according to the chosen constructor. Apart from that, an internal message may bear some \texttt{value} in Grams and other defined currencies (cf.~\ptref{sp:extra.curr}), and all messages generated inside the TON Blockchain have a logical creation time \texttt{created\_lt} (cf.~\ptref{sp:lt.ton.blkch}) and creation unixtime \texttt{created\_at}, both automatically set by the generating transaction. The creation unixtime equals the creation unixtime of the block containing the generating transaction. \nxsubpoint\emb{Forwarding and IHR fees. Total value of an internal message} Internal messages define an \texttt{ihr\_fee} in Grams, which is subtracted from the value attached to the message and awarded to the validators of the destination shardchain if they include the message by the IHR mechanism. The \texttt{fwd\_fee} is the original total forwarding fee paid for using the HR mechanism; it is automatically computed from some configuration parameters and the size of the message at the time the message is generated. @@ -1692,7 +1692,7 @@ The smart-contract information structure {\em SmartContractInfo}, passed in the myself:MsgAddressInt global_config:(Maybe Cell) ] = SmartContractInfo; \end{verbatim} -In other words, the first component of this tuple is an {\em Integer\/} {\tt magic\/} always equal to {\tt 0x076ef1ea}, the second component is an {\em Integer\/} {\tt actions}, originally initialized by zero, but incremented by one whenever an output action is installed by a non-{\tt RAW} output action primitive of the TVM, and so on. The remaining balance is represented by a pair, i.e., a two-component {\em Tuple}: the first component is the nanogram balance, and the second component is a dictionary with 16-bit keys representing all other currencies, if any. +In other words, the first component of this tuple is an {\em Integer\/} {\tt magic\/} always equal to {\tt 0x076ef1ea}, the second component is an {\em Integer\/} {\tt actions}, originally initialized by zero, but incremented by one whenever an output action is installed by a non-{\tt RAW} output action primitive of the TVM, and so on. The remaining balance is represented by a pair, i.e., a two-component {\em Tuple}: the first component is the nanogram balance, and the second component is a dictionary with 32-bit keys representing all other currencies, if any (cf.~\ptref{sp:extra.curr}). The {\tt rand\_seed} field (an unsigned 256-bit integer) here is initialized deterministically starting from the {\tt rand\_seed} of the block, the account address, the hash of the inbound message being processed (if any), and the transaction logical time {\tt trans\_lt}. diff --git a/doc/tvm.tex b/doc/tvm.tex index 61f5a0da..3d7ea5d1 100644 --- a/doc/tvm.tex +++ b/doc/tvm.tex @@ -1437,6 +1437,10 @@ The following primitives work with (the only) value~$\bot$ of type {\em Null}, u \item {\tt 6FA1} --- {\tt NULLSWAPIFNOT} ($x$ -- $x$ or $\bot$ $x$), pushes a {\em Null\/} under the topmost {\em Integer\/}~$x$, but only if $x=0$. May be used for stack alignment after quiet primitives such as {\tt PLDUXQ}. \item {\tt 6FA2} --- {\tt NULLROTRIF} ($x$ $y$ -- $x$ $y$ or $\bot$ $x$ $y$), pushes a {\em Null\/} under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is non-zero. \item {\tt 6FA3} --- {\tt NULLROTRIFNOT} ($x$ $y$ -- $x$ $y$ or $\bot$ $x$ $y$), pushes a {\em Null\/} under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is zero. May be used for stack alignment after quiet primitives such as {\tt LDUXQ}. +\item {\tt 6FA4} --- {\tt NULLSWAPIF2} ($x$ -- $x$ or $\bot$ $\bot$ $x$), pushes two {\em Null\/}s under the topmost {\em Integer\/}~$x$, but only if $x\neq0$. Equivalent to {\tt NULLSWAPIF}; {\tt NULLSWAPIF}. +\item {\tt 6FA5} --- {\tt NULLSWAPIFNOT2} ($x$ -- $x$ or $\bot$ $\bot$ $x$), pushes two {\em Null\/}s under the topmost {\em Integer\/}~$x$, but only if $x=0$. Equivalent to {\tt NULLSWAPIFNOT}; {\tt NULLSWAPIFNOT}. +\item {\tt 6FA6} --- {\tt NULLROTRIF2} ($x$ $y$ -- $x$ $y$ or $\bot$ $\bot$ $x$ $y$), pushes two {\em Null\/}s under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is non-zero. Equivalent to {\tt NULLROTRIF}; {\tt NULLROTRIF}. +\item {\tt 6FA7} --- {\tt NULLROTRIFNOT2} ($x$ $y$ -- $x$ $y$ or $\bot$ $\bot$ $x$ $y$), pushes two {\em Null\/}s under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is zero. Equivalent to {\tt NULLROTRIFNOT}; {\tt NULLROTRIFNOT}. \item {\tt 6FB$ij$} --- {\tt INDEX2 $i$,$j$} ($t$ -- $x$), recovers $x=(t_{i+1})_{j+1}$ for $0\leq i,j\leq 3$. Equivalent to {\tt INDEX $i$}; {\tt INDEX $j$}. \item {\tt 6FB4} --- {\tt CADR} ($t$ -- $x$), recovers $x=(t_2)_1$. \item {\tt 6FB5} --- {\tt CDDR} ($t$ -- $x$), recovers $x=(t_2)_2$. diff --git a/http/http-connection.cpp b/http/http-connection.cpp index cf7b9a66..6ba1a813 100644 --- a/http/http-connection.cpp +++ b/http/http-connection.cpp @@ -149,6 +149,10 @@ void HttpConnection::continue_payload_write() { if (!writing_payload_) { return; } + if (writing_payload_->is_error()) { + stop(); + return; + } auto t = writing_payload_->payload_type(); if (t == HttpPayload::PayloadType::pt_eof) { @@ -233,7 +237,11 @@ td::Status HttpConnection::continue_payload_read(td::ChainBufferReader &input) { return td::Status::OK(); } auto s = input.size(); - TRY_STATUS(reading_payload_->parse(input)); + auto R = reading_payload_->parse(input); + if (R.is_error()) { + reading_payload_->set_error(); + return R.move_as_error(); + } if (input.size() == s) { return td::Status::OK(); } diff --git a/http/http.h b/http/http.h index 4f3b6517..f5e2087a 100644 --- a/http/http.h +++ b/http/http.h @@ -119,6 +119,12 @@ class HttpPayload { bool high_watermark_reached() const { return ready_bytes_ > high_watermark_; } + bool is_error() const { + return error_; + } + void set_error() { + error_ = true; + } PayloadType payload_type() const { return type_; } @@ -152,6 +158,7 @@ class HttpPayload { size_t chunk_size_ = 1 << 14; bool written_zero_chunk_ = false; bool written_trailer_ = false; + bool error_ = false; std::list> callbacks_; diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index 42ce655b..ed1d9782 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -57,6 +57,7 @@ #include "vm/cells/MerkleProof.h" #include "vm/vm.h" #include "vm/cp0.h" +#include "vm/memo.h" #include "ton/ton-shard.h" #include "openssl/rand.hpp" #include "crypto/vm/utils.h" @@ -1065,6 +1066,9 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a }); } else { td::int64 method_id = compute_method_id(method_name); + // set serialization limits + vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls + vm::VmStateInterface::Guard guard(&fstate); // serialize parameters vm::CellBuilder cb; Ref cell; @@ -1298,6 +1302,9 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(os); out << "dump of account state (proof): " << os.str() << std::endl; } + // set deserialization limits + vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls + vm::VmStateInterface::Guard guard(&fstate); if (false && remote_c7.size()) { // DEBUG (dump remote_c7) auto r_c7 = vm::std_boc_deserialize(remote_c7).move_as_ok(); @@ -1392,12 +1399,6 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt out << os.str(); } } - if (0) { // DEBUG - std::ostringstream os; - LOG(DEBUG) << "dumping constructed proof"; - //vm::CellSlice{vm::NoVm(), pb.extract_proof()}.print_rec(os); - out << "constructed state proof: " << os.str(); - } } catch (vm::VmVirtError& err) { out << "virtualization error while parsing runSmcMethod result: " << err.get_msg(); } catch (vm::VmError& err) { @@ -2456,7 +2457,7 @@ int main(int argc, char* argv[]) { }); #endif - vm::init_op_cp0(); + vm::init_op_cp0(true); // enable vm debug td::actor::Scheduler scheduler({2}); diff --git a/rldp-http-proxy/rldp-http-proxy.cpp b/rldp-http-proxy/rldp-http-proxy.cpp index 71ea2213..396408f7 100644 --- a/rldp-http-proxy/rldp-http-proxy.cpp +++ b/rldp-http-proxy/rldp-http-proxy.cpp @@ -210,6 +210,9 @@ class HttpRldpPayloadReceiver : public td::actor::Actor { void abort_query(td::Status error) { LOG(INFO) << "failed to receive HTTP payload: " << error; + if (payload_) { + payload_->set_error(); + } stop(); } @@ -305,6 +308,9 @@ class HttpRldpPayloadSender : public td::actor::Actor { if (!cur_query_promise_) { return; } + if (payload_->is_error()) { + return; + } if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) { answer_query(); } diff --git a/tdutils/td/utils/base64.cpp b/tdutils/td/utils/base64.cpp index 67874d21..356b3af3 100644 --- a/tdutils/td/utils/base64.cpp +++ b/tdutils/td/utils/base64.cpp @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #include "td/utils/base64.h" @@ -30,6 +30,68 @@ namespace td { //TODO: fix copypaste +static const char *const symbols32_lc = "abcdefghijklmnopqrstuvwxyz234567"; +static const char *const symbols32_uc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"; + +string base32_encode(Slice input, bool upper_case) { + auto *symbols32 = (upper_case ? symbols32_uc : symbols32_lc); + string base32; + base32.reserve((input.size() * 8 + 4) / 5); + uint32 c = 0; + uint32 length = 0; + for (size_t i = 0; i < input.size(); i++) { + c = (c << 8) | input.ubegin()[i]; + length += 8; + while (length >= 5) { + length -= 5; + base32.push_back(symbols32[(c >> length) & 31]); + } + } + if (length != 0) { + base32.push_back(symbols32[(c << (5 - length)) & 31]); + } + //TODO: optional padding + return base32; +} + +static unsigned char b32_char_to_value[256]; +static void init_base32_table() { + static bool is_inited = [] { + std::fill(std::begin(b32_char_to_value), std::end(b32_char_to_value), static_cast(32)); + for (unsigned char i = 0; i < 32; i++) { + b32_char_to_value[static_cast(symbols32_lc[i])] = i; + b32_char_to_value[static_cast(symbols32_uc[i])] = i; + } + return true; + }(); + CHECK(is_inited); +} + +Result base32_decode(Slice base32) { + init_base32_table(); + string res; + res.reserve(base32.size() * 5 / 8); + uint32 c = 0; + uint32 length = 0; + for (size_t i = 0; i < base32.size(); i++) { + auto value = b32_char_to_value[base32.ubegin()[i]]; + if (value == 32) { + return Status::Error("Wrong character in the string"); + } + c = (c << 5) | value; + length += 5; + while (length >= 8) { + length -= 8; + res.push_back(td::uint8((c >> length) & 255)); + } + } + if ((c & ((1 << length) - 1)) != 0) { + return Status::Error("Nonzero padding"); + } + //TODO: check padding + return res; +} + static const char *const symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; string base64_encode(Slice input) { diff --git a/tdutils/td/utils/base64.h b/tdutils/td/utils/base64.h index f17cf3eb..bf46a835 100644 --- a/tdutils/td/utils/base64.h +++ b/tdutils/td/utils/base64.h @@ -14,7 +14,7 @@ You should have received a copy of the GNU Lesser General Public License along with TON Blockchain Library. If not, see . - Copyright 2017-2019 Telegram Systems LLP + Copyright 2017-2020 Telegram Systems LLP */ #pragma once @@ -37,4 +37,7 @@ bool is_base64url(Slice input); string base64_filter(Slice input); +string base32_encode(Slice input, bool upper_case = false); +Result base32_decode(Slice base32); + } // namespace td diff --git a/tdutils/test/misc.cpp b/tdutils/test/misc.cpp index c01f397d..f43fcdcc 100644 --- a/tdutils/test/misc.cpp +++ b/tdutils/test/misc.cpp @@ -220,6 +220,26 @@ TEST(Misc, base64) { "Ojo7ISUiOw=="); } +TEST(Misc, base32) { + ASSERT_EQ("", base32_encode("")); + ASSERT_EQ("me", base32_encode("a")); + base32_decode("me").ensure(); + ASSERT_EQ("mfra", base32_encode("ab")); + ASSERT_EQ("mfrgg", base32_encode("abc")); + ASSERT_EQ("mfrggza", base32_encode("abcd")); + ASSERT_EQ("mfrggzdg", base32_encode("abcdf")); + ASSERT_EQ("mfrggzdgm4", base32_encode("abcdfg")); + for (int l = 0; l < 300000; l += l / 20 + l / 1000 * 500 + 1) { + for (int t = 0; t < 10; t++) { + string s = rand_string(std::numeric_limits::min(), std::numeric_limits::max(), l); + auto encoded = base32_encode(s); + auto decoded = base32_decode(encoded); + ASSERT_TRUE(decoded.is_ok()); + ASSERT_TRUE(decoded.ok() == s); + } + } +} + TEST(Misc, to_integer) { ASSERT_EQ(to_integer("-1234567"), -1234567); ASSERT_EQ(to_integer("-1234567"), -1234567); diff --git a/test/regression-tests.ans b/test/regression-tests.ans index 2224b9bb..aaa4b470 100644 --- a/test/regression-tests.ans +++ b/test/regression-tests.ans @@ -23,7 +23,7 @@ Test_Fift_testvm6_default dd6353c8f3f21cf62a4769ee1f3daaec46f43fd633ffb84c5d6535 Test_Fift_testvm7_default 77f54b6c8c9a728d262e912efcc347de7014a37d08793c3adeac8b96fe063342 Test_Fift_testvm8_default 17c9e2205ccecfd8549328b4a501d07dde0336899a7a496e747e1032ad5efff9 Test_Fift_testvm_default ee4cbfec76c050b6de7877cfc39817d594cd1e175b6265b76fb642e30b940437 -Test_Fift_testvmprog_default a6d40d8a7bf0dd3b719c8b1023fef59e01ef094f732951cec4577556c6c68e64 +Test_Fift_testvmprog_default e5d0b2c68ee568280877c8495be558bfd0054ca5d99a99eebb525bbeca8a65af Test_RefInt_main_default 768493e0aef8e09a401a6d369edd1ef503a9215fb09dc460f52b27a8bde767cb Test_VM_assert_code_not_null_default 05bc07e129181c972b976442f200de9487dee8bfb5ac53dd36ff61c5d4d4291d Test_VM_assert_extract_minmax_key_default c352309c61bdf62ba7a0ba7280d303c88b0696fe7efa550c05feb2c662275297 diff --git a/test/test-adnl.cpp b/test/test-adnl.cpp index 1b415644..fd703610 100644 --- a/test/test-adnl.cpp +++ b/test/test-adnl.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 "adnl/adnl-network-manager.h" #include "adnl/adnl.h" @@ -41,6 +41,13 @@ int main() { SET_VERBOSITY_LEVEL(verbosity_INFO); + { + auto id_str = td::Slice("WQUA224U42HFSKN63K6NU23X42VK4IJRLFGG65CU62JAOL6U47HRCHD"); + auto id = ton::adnl::AdnlNodeIdShort::parse(id_str).move_as_ok(); + CHECK(td::hex_decode("a1406b5ca73472c94df6d5e6d35bbf355571098aca637ba2a7b490397ea73e78").ok() == id.as_slice()); + CHECK(id.serialize() == td::to_lower(id_str)); + } + std::string db_root_ = "tmp-ee"; td::rmrf(db_root_).ignore(); td::mkdir(db_root_).ensure(); diff --git a/tl/generate/scheme/tonlib_api.tl b/tl/generate/scheme/tonlib_api.tl index a7385eb3..88d92696 100644 --- a/tl/generate/scheme/tonlib_api.tl +++ b/tl/generate/scheme/tonlib_api.tl @@ -37,6 +37,8 @@ exportedEncryptedKey data:secureBytes = ExportedEncryptedKey; bip39Hints words:vector = Bip39Hints; +adnlAddress adnl_address:string = AdnlAddress; + accountAddress account_address:string = AccountAddress; accountRevisionList revisions:vector = AccountRevisionList; @@ -92,7 +94,7 @@ dns.entryDataUnknown bytes:bytes = dns.EntryData; dns.entryDataText text:string = dns.EntryData; dns.entryDataNextResolver resolver:AccountAddress = dns.EntryData; dns.entryDataSmcAddress smc_address:AccountAddress = dns.EntryData; -//TODO: other entry types +dns.entryDataAdnlAddress adnl_address:AdnlAddress = dns.EntryData; dns.entry name:string category:int32 entry:dns.EntryData = dns.Entry; diff --git a/tl/generate/scheme/tonlib_api.tlo b/tl/generate/scheme/tonlib_api.tlo index 209dc1c2e8a417c4d159ebd753c696b6e8fb0ef3..d735f3efa08a1e8c07d8f7c5c40b02e783688f8a 100644 GIT binary patch delta 354 zcmeB~&G=^yBk!Zx`c@23;5?D{vH;IiLl$nwl)N0rl$4^>;^K)PBsM5^EI$iTolIgwR-^At877RI#A3%U5&biuk45xPPAo5zHF7=czuKfABV z1ConRgcv}*v9jIbe z6A0smss@C?qGnPLG6&|Zt8!d10=s7Hl>oUu73O**F9H>U)Z=s~)F2Mqwu2y-!ptpl TX43(Qq1!c$b^2y+HAi&-k-=+W delta 152 zcmew}hp}fiBk!Zx`c@23;53o<@@5Xk4pvbKQ^V)HDS5?uu6ZRzl`e@Ti3|)ZlLOhr zH}7HNVPQeM(t(pmh S5u>I7VRS%6HtVPdr~?3D3@wWQ diff --git a/tonlib/tonlib/TonlibClient.cpp b/tonlib/tonlib/TonlibClient.cpp index 108bc037..8dcaece8 100644 --- a/tonlib/tonlib/TonlibClient.cpp +++ b/tonlib/tonlib/TonlibClient.cpp @@ -54,6 +54,8 @@ #include "td/utils/tests.h" #include "td/utils/port/path.h" +#include "common/util.h" + namespace tonlib { namespace int_api { struct GetAccountState { @@ -1310,6 +1312,12 @@ td::Result get_account_address(td::Slice account_address) { return address; } +td::Result get_adnl_address(td::Slice adnl_address) { + TRY_RESULT_PREFIX(address, td::adnl_id_decode(adnl_address), + TonlibError::InvalidField("adnl_address", "can't decode")); + return address; +} + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::getAccountAddress& request) { if (!request.initial_account_state_) { @@ -1859,6 +1867,36 @@ td::Status TonlibClient::do_request(const tonlib_api::getAccountState& request, return td::Status::OK(); } +td::Result to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) { + using R = td::Result; + return downcast_call2( + entry_data, + td::overloaded( + [&](tonlib_api::dns_entryDataUnknown& unknown) -> R { return ton::ManualDns::EntryData(); }, + [&](tonlib_api::dns_entryDataNextResolver& next_resolver) -> R { + if (!next_resolver.resolver_) { + return TonlibError::EmptyField("resolver"); + } + TRY_RESULT(resolver, get_account_address(next_resolver.resolver_->account_address_)); + return ton::ManualDns::EntryData::next_resolver(std::move(resolver)); + }, + [&](tonlib_api::dns_entryDataSmcAddress& smc_address) -> R { + if (!smc_address.smc_address_) { + return TonlibError::EmptyField("smc_address"); + } + TRY_RESULT(address, get_account_address(smc_address.smc_address_->account_address_)); + return ton::ManualDns::EntryData::smc_address(std::move(address)); + }, + [&](tonlib_api::dns_entryDataAdnlAddress& adnl_address) -> R { + if (!adnl_address.adnl_address_) { + return TonlibError::EmptyField("adnl_address"); + } + TRY_RESULT(address, get_adnl_address(adnl_address.adnl_address_->adnl_address_)); + return ton::ManualDns::EntryData::adnl_address(std::move(address)); + }, + [&](tonlib_api::dns_entryDataText& text) -> R { return ton::ManualDns::EntryData::text(text.text_); })); +} + class GenericCreateSendGrams : public TonlibQueryActor { public: GenericCreateSendGrams(td::actor::ActorShared client, tonlib_api::createQuery query, @@ -1947,29 +1985,6 @@ class GenericCreateSendGrams : public TonlibQueryActor { return res; } - td::Result to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) { - using R = td::Result; - return downcast_call2( - entry_data, - td::overloaded( - [&](tonlib_api::dns_entryDataUnknown& unknown) -> R { return ton::ManualDns::EntryData(); }, - [&](tonlib_api::dns_entryDataNextResolver& next_resolver) -> R { - if (!next_resolver.resolver_) { - return TonlibError::EmptyField("resolver"); - } - TRY_RESULT(resolver, get_account_address(next_resolver.resolver_->account_address_)); - return ton::ManualDns::EntryData::next_resolver(std::move(resolver)); - }, - [&](tonlib_api::dns_entryDataSmcAddress& smc_address) -> R { - if (!smc_address.smc_address_) { - return TonlibError::EmptyField("smc_address"); - } - TRY_RESULT(address, get_account_address(smc_address.smc_address_->account_address_)); - return ton::ManualDns::EntryData::smc_address(std::move(address)); - }, - [&](tonlib_api::dns_entryDataText& text) -> R { return ton::ManualDns::EntryData::text(text.text_); })); - } - td::Result to_dns_action(tonlib_api::dns_Action& action) { using R = td::Result; return downcast_call2(action, @@ -2566,7 +2581,11 @@ td::Result> to_tonlib_api( res = tonlib_api::make_object( tonlib_api::make_object(resolver.resolver.rserialize(true))); }, - [&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { res = td::Status::Error("TODO"); }, + [&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { + res = tonlib_api::make_object( + tonlib_api::make_object( + td::adnl_id_encode(adnl_address.adnl_address.as_slice()).move_as_ok())); + }, [&](const ton::ManualDns::EntryDataSmcAddress& smc_address) { res = tonlib_api::make_object( tonlib_api::make_object(smc_address.smc_address.rserialize(true))); diff --git a/tonlib/tonlib/TonlibClient.h b/tonlib/tonlib/TonlibClient.h index 36560d38..363b71a4 100644 --- a/tonlib/tonlib/TonlibClient.h +++ b/tonlib/tonlib/TonlibClient.h @@ -32,6 +32,8 @@ #include "td/utils/CancellationToken.h" #include "td/utils/optional.h" +#include "smc-envelope/ManualDns.h" + #include namespace tonlib { @@ -47,6 +49,10 @@ inline std::string to_string(const int_api::SendMessage&) { class AccountState; class Query; +td::Result> to_tonlib_api( + const ton::ManualDns::EntryData& entry_data); +td::Result to_dns_entry_data(tonlib_api::dns_EntryData& entry_data); + class TonlibClient : public td::actor::Actor { public: template diff --git a/tonlib/tonlib/tonlib-cli.cpp b/tonlib/tonlib/tonlib-cli.cpp index 35a0b7e1..5aecf389 100644 --- a/tonlib/tonlib/tonlib-cli.cpp +++ b/tonlib/tonlib/tonlib-cli.cpp @@ -434,7 +434,11 @@ class TonlibCli : public td::actor::Actor { return; } if (resolved->entries_[0]->name_ == name) { - td::TerminalIO::out() << "Done: " << to_string(resolved); + td::TerminalIO::out() << "Done\n"; + for (auto& entry : resolved->entries_) { + td::TerminalIO::out() << " " << entry->name_ << " " << entry->category_ << " " + << tonlib::to_dns_entry_data(*entry->entry_).move_as_ok() << "\n"; + } promise.set_value(td::Unit()); return; } @@ -484,22 +488,14 @@ class TonlibCli : public td::actor::Actor { td::TerminalIO::out() << "Delete all dns enties with name and category: " << action.name << ":" << action.category << "\n"; } else { - tonlib_api::object_ptr data; td::StringBuilder sb; td::Status error; if (action.data.value().data.empty()) { TRY_STATUS_PROMISE(promise, td::Status::Error("Empty entry data is not supported")); } - action.data.value().data.visit(td::overloaded( - [&](const ton::ManualDns::EntryDataText& text) { - data = tonlib_api::make_object(text.text); - sb << "TEXT:" << text.text; - }, - [&](const ton::ManualDns::EntryDataNextResolver& resolver) { error = td::Status::Error("TODO"); }, - [&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { error = td::Status::Error("TODO"); }, - [&](const ton::ManualDns::EntryDataSmcAddress& text) { error = td::Status::Error("TODO"); })); - ; + TRY_RESULT_PROMISE(promise, data, tonlib::to_tonlib_api(action.data.value())); + sb << action.data.value(); TRY_STATUS_PROMISE(promise, std::move(error)); td::TerminalIO::out() << "Set dns entry: " << action.name << ":" << action.category << " " << sb.as_cslice() << "\n"; diff --git a/validator/impl/liteserver.cpp b/validator/impl/liteserver.cpp index fdd91620..c27b67d8 100644 --- a/validator/impl/liteserver.cpp +++ b/validator/impl/liteserver.cpp @@ -35,6 +35,7 @@ #include "vm/dict.h" #include "vm/cells/MerkleProof.h" #include "vm/vm.h" +#include "vm/memo.h" #include "shard.hpp" #include "validator-set.hpp" #include "signature-set.hpp" @@ -686,6 +687,8 @@ void LiteQuery::perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, St fatal_error("cannot deserialize parameter list boc: "s + res.move_as_error().to_string()); return; } + vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls + vm::VmStateInterface::Guard guard(&fstate); auto cs = vm::load_cell_slice(res.move_as_ok()); if (!(vm::Stack::deserialize_to(cs, stack_, 0) && cs.empty_ext())) { fatal_error("parameter list boc cannot be deserialized as a VmStack"); @@ -1076,6 +1079,8 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice stack_ = vm.get_stack_ref(); LOG(INFO) << "runSmcMethod(" << acc_workchain_ << ":" << acc_addr_.to_hex() << ") query completed: exit code is " << exit_code; + vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls + vm::VmStateInterface::Guard guard(&fstate); Ref cell; td::BufferSlice c7_info, result; if (mode & 8) {