1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +00:00

bugfixes + doc update

This commit is contained in:
ton 2020-02-08 23:24:24 +04:00
parent 77842f9b63
commit 1de39f5d7c
44 changed files with 652 additions and 272 deletions

View file

@ -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

35
adnl/adnl-node-id.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-node-id.hpp"
#include "common/util.h"
namespace ton {
namespace adnl {
td::Result<AdnlNodeIdShort> 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

View file

@ -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 <http://www.gnu.org/licenses/>.
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<AdnlNodeIdShort> parse(td::Slice key);
std::string serialize();
private:
PublicKeyHash hash_;
};

View file

@ -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})

View file

@ -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 <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "util.h"
#include <limits>
#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<std::string> 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<td::uint8>((hash >> 8) & 255);
buf[34] = static_cast<td::uint8>(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<Bits256> 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

View file

@ -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 <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <string>
#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<std::string> adnl_id_encode(td::Slice id, bool upper_case = false);
std::string adnl_id_encode(Bits256 adnl_addr, bool upper_case = false);
td::Result<Bits256> adnl_id_decode(td::Slice id);
} // namespace td

View file

@ -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 <cassert>
@ -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) {

View file

@ -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
{ <b x{6FB} s, rot 2 u, swap 2 u, @addopb } : INDEX2
x{6FB4} @Defop CADR
x{6FB5} @Defop CDDR
@ -305,7 +309,9 @@ x{A90C} @Defop DIVMOD
x{A90D} @Defop DIVMODR
x{A90E} @Defop DIVMODC
x{A925} @Defop RSHIFTR
x{A926} @Defop RSHIFTC
x{A935} @Defop(8u+1) RSHIFTR#
x{A936} @Defop(8u+1) RSHIFTC#
x{A938} @Defop(8u+1) MODPOW2#
x{A984} @Defop MULDIV
x{A985} @Defop MULDIVR
@ -1074,56 +1080,83 @@ x{FFF0} @Defop SETCPX
variable @proccnt
variable @proclist
variable @procdict
variable @procinfo
variable @gvarcnt
19 constant @procdictkeylen
{ @proclist @ cons @proclist ! } : @proclistadd
{ dup @procdictkeylen fits not abort"procedure index out of range"
1 'nop does swap dup @proclistadd 0 (create)
32 constant @zcount
{ pair @proclist @ cons @proclist ! } : @proclistadd
{ @procinfo @ @procdictkeylen idict@ { 16 i@ } { 0 } cond } : @procinfo@
{ <b rot 16 i, swap @procinfo @ @procdictkeylen b>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 swap ref, b> <s } if } : @adj-long-proc
{ // i s l
@adj-long-proc swap @procdict @ @procdictkeylen
@adj-long-proc over @procdict @ @procdictkeylen
idict!+ not abort"cannot define procedure, redefined?"
@procdict !
@procdict ! 2 2 @procinfo~!
} : @def-proc
{ @procinfo @ null? not } : @have-procinfo?
{ @have-procinfo? { 4 4 @procinfo~! } { drop } cond } : @proc-inlined
{ @have-procinfo? { 8 8 @procinfo~! } { drop } cond } : @proc-called
{ 1000 @def-proc } : PROC
{ 0 @def-proc } : PROCREF
{ @procdict @ @procdictkeylen idict@ abort"procedure already defined"
} : @fail-ifdef
{ u@?+ { swap abort"first bits are not zeroes" } if } : @cut-zeroes
{ over @fail-ifdef
2 { rot @normal? rot b> <s swap @def-proc drop } does
null swap @doafter<{
2 { rot @normal? rot b> <s @zcount @cut-zeroes swap @def-proc drop } does
null swap @doafter<{ 0 @zcount u,
} : @PROC:<{
{ 1000 @PROC:<{ } : PROC:<{
{ 0 @PROC:<{ } : PROCREF:<{
{ dup @proc-called CALLDICT } dup : CALL : CALLDICT
{ dup @proc-called JMPDICT } dup : JMP : JMPDICT
{ dup @proc-called PREPAREDICT } dup : PREPARE : PREPAREDICT
{ dup @procdict @ @procdictkeylen idict@
{ nip INLINE } { CALLDICT } cond
{ swap @proc-inlined INLINE } { CALLDICT } cond
} dup : INLINECALL : INLINECALLDICT
{ 0 @procdict @ @procdictkeylen idict@ not abort"`main` procedure not defined" drop
} : @chkmaindef
{ @procdict @ @procdictkeylen idict- drop @procdict ! } : @remove-proc
{ ."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
} if // remove unused procs
drop 2drop
} : @chkprocdef
{ @chkmaindef
@proclist @ { dup null? not } {
uncons swap dup find not
{ +": undefined procedure name in list" abort } if
execute @procdict @ @procdictkeylen idict@ not
uncons swap unpair over find not
{ drop +": undefined procedure name in list" abort } if
drop tuck @procdict @ @procdictkeylen idict@ not
{ +": procedure declared but left undefined" abort } if
drop (forget)
drop swap 2dup @procinfo@ @chkprocdef (forget)
} while
drop @proclist null! @proccnt 0!
drop @proclist null! @procinfo null! @proccnt 0!
@procdict dup @ swap null!
} : }END
forget @proclist forget @proccnt

View file

@ -152,3 +152,22 @@ recursive append-long-bytes {
// ( x -- S ) serialize public key
{ 256 u>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

View file

@ -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));

View file

@ -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 <http://www.gnu.org/licenses/>.
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[]);

View file

@ -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 <http://www.gnu.org/licenses/>.
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<VarDescr>& res, std::vector<VarDescr>& 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);
}
}

View file

@ -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?>:Hashmap<PfxDict:Slice->CatTable>:domains]
<CatTable> := HashmapE 16 ^DNSRecord
<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);
}
}

View file

@ -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";

View file

@ -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<ManualDns::EntryDataText>().text;
case ManualDns::EntryData::Type::NextResolver:
return sb << "NEXT:" << data.data.get<ManualDns::EntryDataNextResolver>().resolver.rserialize();
case ManualDns::EntryData::Type::AdnlAddress:
return sb << "ADNL:"
<< td::adnl_id_encode(data.data.get<ManualDns::EntryDataAdnlAddress>().adnl_address.as_slice())
.move_as_ok();
case ManualDns::EntryData::Type::SmcAddress:
return sb << "SMC:" << data.data.get<ManualDns::EntryDataSmcAddress>().smc_address.rserialize();
}
return sb << "<unknown>";
}
//proto_list_nil$0 = ProtoList;
//proto_list_next$1 head:Protocol tail:ProtoList = ProtoList;
@ -486,10 +505,19 @@ td::Result<td::optional<ManualDns::EntryData>> 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::ActionExt> ManualDns::parse_line(td::Slice cmd) {
// delete.all
// data =
// TEXT:<text> |
// SMC:<smartcontract address> |
// NEXT:<smartcontract address> |
// ADNL:<adnl address>
// DELETED
td::ConstParser parser(cmd);
auto type = parser.read_word();

View file

@ -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 << "<empty>";
case Type::Text:
return sb << "text{" << data.data.get<EntryDataText>().text << "}";
case Type::NextResolver:
return sb << "next{" << data.data.get<EntryDataNextResolver>().resolver.rserialize() << "}";
case Type::AdnlAddress:
return sb << "adnl{" << data.data.get<EntryDataAdnlAddress>().adnl_address.to_hex() << "}";
case Type::SmcAddress:
return sb << "smc{" << data.data.get<EntryDataSmcAddress>().smc_address.rserialize() << "}";
}
return sb << "<unknown>";
}
friend td::StringBuilder& operator<<(td::StringBuilder& sb, const EntryData& data);
td::Result<td::Ref<vm::Cell>> as_cell() const;
static td::Result<EntryData> from_cellslice(vm::CellSlice& cs);

View file

@ -55,8 +55,16 @@ Ref<DataCell> 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<DataCell> CellBuilder::finalize_novm(bool special) {
@ -72,10 +80,17 @@ Ref<DataCell> CellBuilder::finalize_novm(bool special) {
Ref<DataCell> 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<Cell> CellBuilder::create_pruned_branch(Ref<Cell> cell, td::uint32 new_level, td::uint32 virt_level) {

View file

@ -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> 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:

View file

@ -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 <http://www.gnu.org/licenses/>.
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

View file

@ -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 <http://www.gnu.org/licenses/>.
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

View file

@ -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 << " = ";

View file

@ -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 <http://www.gnu.org/licenses/>.
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

33
crypto/vm/memo.cpp Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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

37
crypto/vm/memo.h Normal file
View file

@ -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 <http://www.gnu.org/licenses/>.
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

View file

@ -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<std::string>;
@ -678,6 +679,10 @@ void Stack::push_maybe_cellslice(Ref<CellSlice> 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> 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;

View file

@ -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));
}

View file

@ -28,12 +28,16 @@ using td::Ref;
class VmStateInterface : public td::Context<VmStateInterface> {
public:
virtual ~VmStateInterface() = default;
virtual Ref<vm::Cell> load_library(
virtual Ref<Cell> 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<DataCell>& cell){};
virtual bool register_op(int op_units = 1) {
return true;
};
};
} // namespace vm

View file

@ -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}.

View file

@ -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$.

View file

@ -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();
}

View file

@ -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<std::unique_ptr<Callback>> callbacks_;

View file

@ -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<vm::Cell> 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});

View file

@ -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();
}

View file

@ -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 <http://www.gnu.org/licenses/>.
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<unsigned char>(32));
for (unsigned char i = 0; i < 32; i++) {
b32_char_to_value[static_cast<size_t>(symbols32_lc[i])] = i;
b32_char_to_value[static_cast<size_t>(symbols32_uc[i])] = i;
}
return true;
}();
CHECK(is_inited);
}
Result<string> 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) {

View file

@ -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 <http://www.gnu.org/licenses/>.
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<string> base32_decode(Slice base32);
} // namespace td

View file

@ -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<char>::min(), std::numeric_limits<char>::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<int32>("-1234567"), -1234567);
ASSERT_EQ(to_integer<int64>("-1234567"), -1234567);

View file

@ -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

View file

@ -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();

View file

@ -37,6 +37,8 @@ exportedEncryptedKey data:secureBytes = ExportedEncryptedKey;
bip39Hints words:vector<string> = Bip39Hints;
adnlAddress adnl_address:string = AdnlAddress;
accountAddress account_address:string = AccountAddress;
accountRevisionList revisions:vector<int32> = 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;

Binary file not shown.

View file

@ -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<block::StdAddress> get_account_address(td::Slice account_address) {
return address;
}
td::Result<td::Bits256> 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<tonlib_api::Object> 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<ton::ManualDns::EntryData> to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) {
using R = td::Result<ton::ManualDns::EntryData>;
return downcast_call2<R>(
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<TonlibClient> client, tonlib_api::createQuery query,
@ -1947,29 +1985,6 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return res;
}
td::Result<ton::ManualDns::EntryData> to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) {
using R = td::Result<ton::ManualDns::EntryData>;
return downcast_call2<R>(
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<ton::ManualDns::Action> to_dns_action(tonlib_api::dns_Action& action) {
using R = td::Result<ton::ManualDns::Action>;
return downcast_call2<R>(action,
@ -2566,7 +2581,11 @@ td::Result<tonlib_api::object_ptr<tonlib_api::dns_EntryData>> to_tonlib_api(
res = tonlib_api::make_object<tonlib_api::dns_entryDataNextResolver>(
tonlib_api::make_object<tonlib_api::accountAddress>(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::dns_entryDataAdnlAddress>(
tonlib_api::make_object<tonlib_api::adnlAddress>(
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::dns_entryDataSmcAddress>(
tonlib_api::make_object<tonlib_api::accountAddress>(smc_address.smc_address.rserialize(true)));

View file

@ -32,6 +32,8 @@
#include "td/utils/CancellationToken.h"
#include "td/utils/optional.h"
#include "smc-envelope/ManualDns.h"
#include <map>
namespace tonlib {
@ -47,6 +49,10 @@ inline std::string to_string(const int_api::SendMessage&) {
class AccountState;
class Query;
td::Result<tonlib_api::object_ptr<tonlib_api::dns_EntryData>> to_tonlib_api(
const ton::ManualDns::EntryData& entry_data);
td::Result<ton::ManualDns::EntryData> to_dns_entry_data(tonlib_api::dns_EntryData& entry_data);
class TonlibClient : public td::actor::Actor {
public:
template <class T>

View file

@ -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<tonlib_api::dns_EntryData> 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<tonlib_api::dns_entryDataText>(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";

View file

@ -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<vm::Cell> cell;
td::BufferSlice c7_info, result;
if (mode & 8) {