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:
parent
77842f9b63
commit
1de39f5d7c
44 changed files with 652 additions and 272 deletions
|
@ -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})
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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[]);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 << " = ";
|
||||
|
|
|
@ -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
33
crypto/vm/memo.cpp
Normal 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
37
crypto/vm/memo.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue