1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +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

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