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

@ -42,6 +42,7 @@ set(ADNL_SOURCE
adnl-message.cpp adnl-message.cpp
adnl-network-manager.cpp adnl-network-manager.cpp
adnl-node.cpp adnl-node.cpp
adnl-node-id.cpp
adnl-packet.cpp adnl-packet.cpp
adnl-peer-table.cpp adnl-peer-table.cpp
adnl-peer.cpp adnl-peer.cpp

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

@ -0,0 +1,35 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "adnl-node-id.hpp"
#include "common/util.h"
namespace ton {
namespace adnl {
td::Result<AdnlNodeIdShort> AdnlNodeIdShort::parse(td::Slice id) {
TRY_RESULT(str, td::adnl_id_decode(id));
return AdnlNodeIdShort(str);
}
std::string AdnlNodeIdShort::serialize() {
return adnl_id_encode(hash_.as_slice()).move_as_ok();
}
} // namespace adnl
} // namespace ton

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. 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 #pragma once
@ -75,6 +75,10 @@ class AdnlNodeIdShort {
return hash_.is_zero(); return hash_.is_zero();
} }
static td::Result<AdnlNodeIdShort> parse(td::Slice key);
std::string serialize();
private: private:
PublicKeyHash hash_; PublicKeyHash hash_;
}; };

View file

@ -23,6 +23,7 @@ set(TON_CRYPTO_SOURCE
vm/atom.cpp vm/atom.cpp
vm/continuation.cpp vm/continuation.cpp
vm/dict.cpp vm/dict.cpp
vm/memo.cpp
vm/dispatch.cpp vm/dispatch.cpp
vm/opctable.cpp vm/opctable.cpp
vm/cp0.cpp vm/cp0.cpp
@ -76,6 +77,7 @@ set(TON_CRYPTO_SOURCE
vm/excno.hpp vm/excno.hpp
vm/fmt.hpp vm/fmt.hpp
vm/log.h vm/log.h
vm/memo.h
vm/opctable.h vm/opctable.h
vm/stack.hpp vm/stack.hpp
vm/stackops.h vm/stackops.h
@ -354,7 +356,7 @@ if (NOT CMAKE_CROSSCOMPILING)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMAND fift -Ifift/lib:smartcont -s asm-to-cpp.fif ${ARG_DEST_FIF} ${ARG_DEST_CPP} ${ARG_NAME} COMMAND fift -Ifift/lib:smartcont -s asm-to-cpp.fif ${ARG_DEST_FIF} ${ARG_DEST_CPP} ${ARG_NAME}
MAIN_DEPENDENCY ${ARG_SOURCE} 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} OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/${ARG_DEST_CPP}
) )
add_custom_target(gen_fif_${ID} DEPENDS ${ARG_DEST_FIF} ${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 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/>. 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 "util.h"
#include <limits> #include <limits>
#include "td/utils/crypto.h"
#include "td/utils/base64.h"
namespace td { namespace td {
std::size_t compute_base64_encoded_size(size_t bindata_size) { 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; 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::string str_base64_encode(td::Slice raw, bool base64_url) {
std::size_t res_size = compute_base64_encoded_size(raw.size()); std::size_t res_size = compute_base64_encoded_size(raw.size());
std::string s; std::string s;
@ -87,10 +86,6 @@ std::string str_base64_encode(td::Slice raw, bool base64_url) {
return s; 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) { bool is_valid_base64(td::Slice encoded, bool allow_base64_url) {
const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size(); const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
if (encoded.size() & 3) { if (encoded.size() & 3) {
@ -110,10 +105,6 @@ bool is_valid_base64(td::Slice encoded, bool allow_base64_url) {
return ptr == end; 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) { 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(); const unsigned char *ptr = (const unsigned char *)encoded.data(), *end = ptr + encoded.size();
if (encoded.size() & 3) { 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(); 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) { td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url) {
auto s = decoded_base64_size(encoded, allow_base64_url); auto s = decoded_base64_size(encoded, allow_base64_url);
if (s <= 0) { if (s <= 0) {
@ -190,10 +177,6 @@ td::BufferSlice base64_decode(td::Slice encoded, bool allow_base64_url) {
return res; 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) { std::string str_base64_decode(td::Slice encoded, bool allow_base64_url) {
auto s = decoded_base64_size(encoded, allow_base64_url); auto s = decoded_base64_size(encoded, allow_base64_url);
if (s <= 0) { if (s <= 0) {
@ -209,4 +192,45 @@ std::string str_base64_decode(td::Slice encoded, bool allow_base64_url) {
return res; 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 } // namespace td

View file

@ -14,29 +14,31 @@
You should have received a copy of the GNU Lesser General Public License 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/>. 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 #pragma once
#include <string> #include <string>
#include "td/utils/Slice.h" #include "td/utils/Slice.h"
#include "td/utils/buffer.h" #include "td/utils/buffer.h"
#include "bitstring.h"
namespace td { namespace td {
std::size_t compute_base64_encoded_size(size_t bindata_size); 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::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); 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); 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); 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); 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); 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); 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 } // namespace td

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here. 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 "vm/stack.hpp"
#include <cassert> #include <cassert>
@ -156,7 +156,7 @@ int main(int argc, char* const argv[]) {
} }
fift::init_words_common(config.dictionary); 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); fift::init_words_ton(config.dictionary);
if (script_mode) { if (script_mode) {

View file

@ -216,6 +216,10 @@ x{6FA0} @Defop NULLSWAPIF
x{6FA1} @Defop NULLSWAPIFNOT x{6FA1} @Defop NULLSWAPIFNOT
x{6FA2} @Defop NULLROTRIF x{6FA2} @Defop NULLROTRIF
x{6FA3} @Defop NULLROTRIFNOT 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 { <b x{6FB} s, rot 2 u, swap 2 u, @addopb } : INDEX2
x{6FB4} @Defop CADR x{6FB4} @Defop CADR
x{6FB5} @Defop CDDR x{6FB5} @Defop CDDR
@ -305,7 +309,9 @@ x{A90C} @Defop DIVMOD
x{A90D} @Defop DIVMODR x{A90D} @Defop DIVMODR
x{A90E} @Defop DIVMODC x{A90E} @Defop DIVMODC
x{A925} @Defop RSHIFTR x{A925} @Defop RSHIFTR
x{A926} @Defop RSHIFTC
x{A935} @Defop(8u+1) RSHIFTR# x{A935} @Defop(8u+1) RSHIFTR#
x{A936} @Defop(8u+1) RSHIFTC#
x{A938} @Defop(8u+1) MODPOW2# x{A938} @Defop(8u+1) MODPOW2#
x{A984} @Defop MULDIV x{A984} @Defop MULDIV
x{A985} @Defop MULDIVR x{A985} @Defop MULDIVR
@ -1074,56 +1080,83 @@ x{FFF0} @Defop SETCPX
variable @proccnt variable @proccnt
variable @proclist variable @proclist
variable @procdict variable @procdict
variable @procinfo
variable @gvarcnt variable @gvarcnt
19 constant @procdictkeylen 19 constant @procdictkeylen
{ @proclist @ cons @proclist ! } : @proclistadd 32 constant @zcount
{ dup @procdictkeylen fits not abort"procedure index out of range" { pair @proclist @ cons @proclist ! } : @proclistadd
1 'nop does swap dup @proclistadd 0 (create) { @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 } : @declproc
{ 1 'nop does swap 0 (create) } : @declglobvar { 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 { @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
{ 0 =: main @proclist null! @proccnt 0! @gvarcnt 0! { 0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
{ bl word @newproc } : NEWPROC { bl word @newproc } : NEWPROC
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC { bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
{ bl word dup find { bl word dup find
{ nip execute <> abort"method redefined with different id" } { nip execute <> abort"method redefined with different id" }
{ swap @declproc } { swap 17 @declproc }
cond } : DECLMETHOD cond } : DECLMETHOD
{ bl word @newglobvar } : DECLGLOBVAR { bl word @newglobvar } : DECLGLOBVAR
"main" @proclistadd "main" 0 @proclistadd
dictnew @procdict ! dictnew dup @procdict !
@procinfo ! 16 0 @procinfo!
} : PROGRAM{ } : PROGRAM{
{ over sbits < { s>c <b swap ref, b> <s } if } : @adj-long-proc { over sbits < { s>c <b swap ref, b> <s } if } : @adj-long-proc
{ // i s l { // i s l
@adj-long-proc swap @procdict @ @procdictkeylen @adj-long-proc over @procdict @ @procdictkeylen
idict!+ not abort"cannot define procedure, redefined?" idict!+ not abort"cannot define procedure, redefined?"
@procdict ! @procdict ! 2 2 @procinfo~!
} : @def-proc } : @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 { 1000 @def-proc } : PROC
{ 0 @def-proc } : PROCREF { 0 @def-proc } : PROCREF
{ @procdict @ @procdictkeylen idict@ abort"procedure already defined" { @procdict @ @procdictkeylen idict@ abort"procedure already defined"
} : @fail-ifdef } : @fail-ifdef
{ u@?+ { swap abort"first bits are not zeroes" } if } : @cut-zeroes
{ over @fail-ifdef { over @fail-ifdef
2 { rot @normal? rot b> <s swap @def-proc drop } does 2 { rot @normal? rot b> <s @zcount @cut-zeroes swap @def-proc drop } does
null swap @doafter<{ null swap @doafter<{ 0 @zcount u,
} : @PROC:<{ } : @PROC:<{
{ 1000 @PROC:<{ } : PROC:<{ { 1000 @PROC:<{ } : PROC:<{
{ 0 @PROC:<{ } : PROCREF:<{ { 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@ { dup @procdict @ @procdictkeylen idict@
{ nip INLINE } { CALLDICT } cond { swap @proc-inlined INLINE } { CALLDICT } cond
} dup : INLINECALL : INLINECALLDICT } dup : INLINECALL : INLINECALLDICT
{ 0 @procdict @ @procdictkeylen idict@ not abort"`main` procedure not defined" drop { 0 @procdict @ @procdictkeylen idict@ not abort"`main` procedure not defined" drop
} : @chkmaindef } : @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 { @chkmaindef
@proclist @ { dup null? not } { @proclist @ { dup null? not } {
uncons swap dup find not uncons swap unpair over find not
{ +": undefined procedure name in list" abort } if { drop +": undefined procedure name in list" abort } if
execute @procdict @ @procdictkeylen idict@ not drop tuck @procdict @ @procdictkeylen idict@ not
{ +": procedure declared but left undefined" abort } if { +": procedure declared but left undefined" abort } if
drop (forget) drop swap 2dup @procinfo@ @chkprocdef (forget)
} while } while
drop @proclist null! @proccnt 0! drop @proclist null! @procinfo null! @proccnt 0!
@procdict dup @ swap null! @procdict dup @ swap null!
} : }END } : }END
forget @proclist forget @proccnt forget @proclist forget @proccnt

View file

@ -152,3 +152,22 @@ recursive append-long-bytes {
// ( x -- S ) serialize public key // ( x -- S ) serialize public key
{ 256 u>B B{3ee6} swap B+ dup crc16 16 u>B B+ B>base64 } : pubkey>$ { 256 u>B B{3ee6} swap B+ dup crc16 16 u>B B+ B>base64 } : pubkey>$
{ pubkey>$ type } : .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)); 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; using namespace std::placeholders;
vm::init_op_cp0(); vm::init_op_cp0(enable_debug);
// vm run // vm run
d.def_stack_word("vmlibs ", std::bind(interpret_literal, _1, vm::StackEntry{vm_libraries})); 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)); // 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 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/>. 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 #pragma once
#include "Dictionary.h" #include "Dictionary.h"
@ -33,7 +33,7 @@ struct Quit {
struct SkipToEof {}; struct SkipToEof {};
void init_words_common(Dictionary& dictionary); 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 init_words_ton(Dictionary& dictionary);
void import_cmdline_args(Dictionary& d, std::string arg0, int n, const char* const argv[]); 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 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/>. 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" #include "func.h"
@ -765,7 +765,7 @@ AsmOp compile_cond_throw(std::vector<VarDescr>& res, std::vector<VarDescr>& args
x.unused(); 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); return skip_cond ? exec_arg_op("THROW", x.int_const, 0, 0) : exec_arg_op("THROW"s + suff, x.int_const, 1, 0);
} else { } 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 ;; 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) (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 ;; Args: x k D n | Success: D' -1 | Failure: D 0
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) (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, ()); 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); (slice pfx, slice val, slice tail, int succ) = dict.pfxdict_get?(key_len, key);
cell res = null(); cell res = succ ? val~load_maybe_ref() : null();
if (succ) {
res = val~load_maybe_ref();
}
return (pfx, res, tail, succ); return (pfx, res, tail, succ);
} }
@ -49,16 +46,16 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
;; Utility functions ;; ;; Utility functions ;;
;;===========================================================================;; ;;===========================================================================;;
(int, int, int, cell, cell) load_data() { (int, int, int, cell, cell) load_data() inline_ref {
slice cs = get_data().begin_parse(); 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()); var res = (cs~load_uint(32), cs~load_uint(64), cs~load_uint(256), cs~load_dict(), cs~load_dict());
cs.end_parse(); cs.end_parse();
return res; 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() set_data(begin_cell()
.store_uint(subwallet, 32) .store_uint(contract_id, 32)
.store_uint(last_cleaned, 64) .store_uint(last_cleaned, 64)
.store_uint(public_key, 256) .store_uint(public_key, 256)
.store_dict(root) .store_dict(root)
@ -102,7 +99,7 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
[UInt<256b>:new_public_key] [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 op = ops~load_uint(6);
int is_name_ref = (ops~load_uint(1) == 1); 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 ;; at least one character not counting \0
throw_unless(38, name.slice_bits() >= 16); throw_unless(38, name.slice_bits() >= 16);
;; name shall end with \0 ;; name shall end with \0
(_, int name_last_byte) = name.slice_last(8).load_uint(8); int name_last_byte = name.slice_last(8).preload_uint(8);
throw_unless(40, name_last_byte == 0); throw_if(40, name_last_byte);
;; count zero separators
;; Multiple zero characters seem to be allowed as per github issue response
;; Lets change the tactics!
int loop = -1;
slice cname = name; slice cname = name;
;; better safe then sorry, dont want to catch any of loop bugs repeat (cname.slice_bits() ^>> 3) {
while (loop) { int c = cname~load_uint(8);
int lval = cname~load_uint(8); zeros -= (c == 0);
if (lval == 0) { zeros += 1; }
if (cname.slice_bits() == 0) { loop = 0; }
} }
;; throw_unless(39, zeros == 1); ;; 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 ;; 11 VSet: set specified value to specified subdomain->category
if (op == 11) { if (op == 11) {
cell new_value = ops~load_maybe_ref(); cell new_value = ops~load_maybe_ref();
if (new_value.cell_null?()) { cat_table~idict_set_get_ref(16, cat, new_value);
cat_table~idict_delete?(16, cat);
} else {
cat_table~idict_set_ref(16, cat, new_value);
}
root~pfxdict_set_ref(1023, name, cat_table); root~pfxdict_set_ref(1023, name, cat_table);
return (root, ops); return (root, ops);
} }
;; 12 VDel: delete specified subdomain->category value ;; 12 VDel: delete specified subdomain->category value
if (op == 12) { if (op == 12) {
ifnot (cat_table.dict_empty?()) { if (cat_table~idict_delete?(16, cat)) {
cat_table~idict_delete?(16, cat);
root~pfxdict_set_ref(1023, name, cat_table); root~pfxdict_set_ref(1023, name, cat_table);
} }
return (root, ops); return (root, ops);
@ -204,16 +190,16 @@ builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
return (root, ops); return (root, ops);
} }
cell process_ops(cell root, slice ops) { cell process_ops(cell root, slice ops) inline_ref {
int f = -1; var stop = false;
while (f) { do {
(root, ops) = process_op(root, ops); (root, ops) = process_op(root, ops);
if (ops.slice_refs_empty?()) { if (ops.slice_refs_empty?()) {
f = 0; stop = true;
} else { } else {
ops = ops~load_ref().begin_parse(); ops = ops~load_ref().begin_parse();
} }
} } until (stop);
return root; return root;
} }
@ -224,7 +210,7 @@ cell process_ops(cell root, slice ops) {
;; validate signature and seqno ;; validate signature and seqno
slice signature = in_msg~load_bits(512); slice signature = in_msg~load_bits(512);
int shash = slice_hash(in_msg); 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); var bound = (now() << 32);
throw_if(35, query_id < bound); throw_if(35, query_id < bound);
(_, var found?) = old_queries.udict_get?(64, query_id); (_, 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)); throw_unless(34, check_signature(shash, signature, public_key));
accept_message(); ;; message is signed by owner, sanity not guaranteed yet accept_message(); ;; message is signed by owner, sanity not guaranteed yet
int op = in_msg.preload_uint(6); int op = in_msg.preload_uint(6);
;; 00 Contract initialization message (only if seqno = 0) if (op == 51) {
if (op == 0) {
;; noop
} else { if (op == 51) {
in_msg~skip_bits(6); in_msg~skip_bits(6);
public_key = in_msg~load_uint(256); public_key = in_msg~load_uint(256);
} else { } elseif (op) { ;; 00 Contract initialization message
root = process_ops(root, in_msg); root = process_ops(root, in_msg);
} } }
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
old_queries~udict_set_builder(64, query_id, begin_cell()); old_queries~udict_set_builder(64, query_id, begin_cell());
@ -259,7 +241,7 @@ cell process_ops(cell root, slice ops) {
} }
} until (~ f); } 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] Root cell: [UInt<32b>:seqno] [UInt<256b>:owner_public_key]
[OptRef<1b+1r?>:Hashmap<PfxDict:Slice->CatTable>:domains] [OptRef<1b+1r?>:Hashmap<PfxDict:Slice->CatTable>:domains]
<CatTable> := HashmapE 16 ^DNSRecord <CatTable> := HashmapE 16 ^DNSRecord
<DNSRecord> := arbitary? not defined anywhere in documentation or internet!
STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value) STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value)
#Zeros allows to simultaneously store, for example, com\0 and com\0google\0 #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 ;; ;; 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); return get_data().begin_parse().preload_uint(32);
} }
;;8m dns-record-value ;;8m dns-record-value
(int, cell) dnsresolve(slice subdomain, int category) method_id { (int, cell) dnsresolve(slice subdomain, int category) method_id {
cell Null = null(); ;; pseudo-alias int bits = subdomain.slice_bits();
throw_if(30, subdomain.slice_bits() % 8 != 0); ;; malformed input (~ 8n-bit) ifnot (bits) {
if (subdomain.slice_bits() == 0) { return (0, Null); } ;; zero-length input 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
} }
cell c_name = b_name.end_cell(); throw_if(30, bits & 7); ;; malformed input (~ 8n-bit)
slice s_name = c_name.begin_parse();
-} int name_last_byte = subdomain.slice_last(8).preload_uint(8);
(_, int name_last_byte) = subdomain.slice_last(8).load_uint(8); if (name_last_byte) {
if ((name_last_byte == 0) & (subdomain.slice_bits() == 8)) { subdomain = begin_cell().store_slice(subdomain) ;; append zero byte
return (0, Null); ;; zero-length input, but with zero byte
}
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(); .store_uint(0, 8).end_cell().begin_parse();
bits += 8;
}
if (bits == 8) {
return (0, null()); ;; zero-length input, but with zero byte
} }
(_, _, _, cell root, _) = load_data(); (_, _, _, cell root, _) = load_data();
;; Multiple zero characters seem to be allowed as per github issue response slice cname = subdomain;
;; Lets change the tactics!
int zeros = 0; int zeros = 0;
int loop = -1; repeat (bits >> 3) {
slice cname = s_name; int c = cname~load_uint(8);
;; better safe then sorry, dont want to catch any of loop bugs zeros -= (c == 0);
while (loop) {
int lval = cname~load_uint(8);
if (lval == 0) { zeros += 1; }
if (cname.slice_bits() == 0) { loop = 0; }
} }
;; can't move below, will cause errors! ;; can't move these declarations lower, will cause errors!
slice pfx = cname; cell val = null(); slice pfx = cname;
slice tail = cname; int succ = 0; cell val = null();
slice tail = cname;
while (zeros > 0) { do {
slice pfname = begin_cell().store_uint(zeros, 7) slice pfxname = begin_cell().store_uint(zeros, 7)
.store_slice(s_name).end_cell().begin_parse(); .store_slice(subdomain).end_cell().begin_parse();
(pfx, val, tail, succ) = root.pfxdict_get_ref(1023, pfname); (pfx, val, tail, int succ) = root.pfxdict_get_ref(1023, pfxname);
if (succ) { zeros = 1; } ;; break zeros = succ ^ (zeros - 1); ;; break on success
zeros -= 1; } until (zeros <= 0);
ifnot (zeros) {
return (0, null()); ;; failed to find entry in prefix dictionary
} }
zeros = - zeros;
zeros = pfx.preload_uint(7); ifnot (tail.slice_empty?()) { ;; if we have tail then len(pfx) < len(subdomain)
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)
category = -1; ;; incomplete subdomain found, must return next resolver (-1) category = -1; ;; incomplete subdomain found, must return next resolver (-1)
} }
int pfx_bits = pfx.slice_bits() - 7; int pfx_bits = pfx.slice_bits() - 7;
cell cat_table = val; cell cat_table = val;
;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain ;; 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) ;; 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) ;; which corresponds to 8m, m=one plus the number of bytes in the subdomain found)
if (category == 0) { if (category == 0) {
return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0 return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
} else { } else {
cell cat_found = cat_table.idict_get_ref(16, category); 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) return (pfx_bits, cat_found);
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());
-}
} }
} }

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) 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_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) 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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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" "NULLSWAPIFNOT2";
(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_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" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (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" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (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" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (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" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (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" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (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" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (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" "NULLSWAPIFNOT" "NULLSWAPIFNOT"; (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"; cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY"; int dict_empty?(cell c) asm "DICTEMPTY";

View file

@ -30,7 +30,26 @@
#include "block/block-auto.h" #include "block/block-auto.h"
#include "block/block-parse.h" #include "block/block-parse.h"
#include "common/util.h"
namespace ton { 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_nil$0 = ProtoList;
//proto_list_next$1 head:Protocol tail:ProtoList = 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); td::ConstParser parser(cmd);
parser.skip_whitespaces(); parser.skip_whitespaces();
auto type = parser.read_till(':'); auto type = parser.read_till(':');
parser.advance(1); parser.skip(':');
if (type == "TEXT") { if (type == "TEXT") {
return ManualDns::EntryData::text(parser.read_all().str()); 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 {};
} }
return td::Status::Error(PSLICE() << "Unknown entry type: " << type); 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 // delete.all
// data = // data =
// TEXT:<text> | // TEXT:<text> |
// SMC:<smartcontract address> |
// NEXT:<smartcontract address> |
// ADNL:<adnl address>
// DELETED // DELETED
td::ConstParser parser(cmd); td::ConstParser parser(cmd);
auto type = parser.read_word(); auto type = parser.read_word();

View file

@ -82,21 +82,7 @@ class DnsInterface {
bool operator==(const EntryData& other) const { bool operator==(const EntryData& other) const {
return data == other.data; return data == other.data;
} }
friend td::StringBuilder& operator<<(td::StringBuilder& sb, const EntryData& 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>";
}
td::Result<td::Ref<vm::Cell>> as_cell() const; td::Result<td::Ref<vm::Cell>> as_cell() const;
static td::Result<EntryData> from_cellslice(vm::CellSlice& cs); 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(); LOG(ERROR) << res.error();
throw CellWriteError{}; throw CellWriteError{};
} }
CHECK(res.ok().not_null()); auto cell = res.move_as_ok();
return 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) { Ref<DataCell> CellBuilder::finalize_novm(bool special) {
@ -72,11 +80,18 @@ Ref<DataCell> CellBuilder::finalize_novm(bool special) {
Ref<DataCell> CellBuilder::finalize(bool special) { Ref<DataCell> CellBuilder::finalize(bool special) {
auto* vm_state_interface = VmStateInterface::get(); auto* vm_state_interface = VmStateInterface::get();
if (vm_state_interface) { if (!vm_state_interface) {
vm_state_interface->register_cell_create();
}
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) { Ref<Cell> CellBuilder::create_pruned_branch(Ref<Cell> cell, td::uint32 new_level, td::uint32 virt_level) {
if (cell->is_loaded() && cell->get_level() <= virt_level && cell->get_virtualization() == 0) { if (cell->is_loaded() && cell->get_level() <= virt_level && cell->get_virtualization() == 0) {

View file

@ -21,6 +21,7 @@
#include "vm/dict.h" #include "vm/dict.h"
#include "vm/log.h" #include "vm/log.h"
#include "vm/vm.h" #include "vm/vm.h"
#include "vm/vmstate.h"
namespace vm { namespace vm {
@ -190,6 +191,10 @@ bool ControlData::deserialize(CellSlice& cs, int mode) {
} }
bool Continuation::serialize_ref(CellBuilder& cb) const { bool Continuation::serialize_ref(CellBuilder& cb) const {
auto* vsi = VmStateInterface::get();
if (vsi && !vsi->register_op()) {
return false;
}
vm::CellBuilder cb2; vm::CellBuilder cb2;
return serialize(cb2) && cb.store_ref_bool(cb2.finalize()); return serialize(cb2) && cb.store_ref_bool(cb2.finalize());
} }
@ -198,6 +203,11 @@ Ref<Continuation> Continuation::deserialize(CellSlice& cs, int mode) {
if (mode & 0x1002) { if (mode & 0x1002) {
return {}; return {};
} }
auto* vsi = VmStateInterface::get();
if (vsi && !vsi->register_op()) {
return {};
}
mode |= 0x1000; mode |= 0x1000;
switch (cs.bselect_ext(6, 0x100f011100010001ULL)) { switch (cs.bselect_ext(6, 0x100f011100010001ULL)) {
case 0: case 0:

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. 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 "cp0.h"
#include "opctable.h" #include "opctable.h"
@ -29,7 +29,8 @@
namespace vm { 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 = [] { static const OpcodeTable* static_op_cp0 = [] {
auto op_cp0 = new OpcodeTable("TEST CODEPAGE", Codepage::test_cp); auto op_cp0 = new OpcodeTable("TEST CODEPAGE", Codepage::test_cp);
register_stack_ops(*op_cp0); // stackops.cpp 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 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/>. 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 #pragma once
#include "vm/dispatch.h" #include "vm/dispatch.h"
@ -23,6 +23,6 @@ namespace vm {
class OpcodeTable; class OpcodeTable;
const OpcodeTable* init_op_cp0(); const OpcodeTable* init_op_cp0(bool debug_enabled = false);
} // namespace vm } // namespace vm

View file

@ -28,6 +28,10 @@ namespace vm {
bool vm_debug_enabled = true; 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) { int exec_dummy_debug(VmState* st, int args) {
VM_LOG(st) << "execute DEBUG " << (args & 0xff); VM_LOG(st) << "execute DEBUG " << (args & 0xff);
return 0; 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) { int exec_dump_stack(VmState* st) {
VM_LOG(st) << "execute DUMPSTK"; VM_LOG(st) << "execute DUMPSTK";
if (!vm_debug_enabled) {
return 0;
}
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
int d = stack.depth(); int d = stack.depth();
std::cerr << "#DEBUG#: stack(" << d << " values) : "; std::cerr << "#DEBUG#: stack(" << d << " values) : ";
@ -84,6 +91,9 @@ int exec_dump_stack(VmState* st) {
int exec_dump_value(VmState* st, unsigned arg) { int exec_dump_value(VmState* st, unsigned arg) {
arg &= 15; arg &= 15;
VM_LOG(st) << "execute DUMP s" << arg; VM_LOG(st) << "execute DUMP s" << arg;
if (!vm_debug_enabled) {
return 0;
}
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
if ((int)arg < stack.depth()) { if ((int)arg < stack.depth()) {
std::cerr << "#DEBUG#: s" << arg << " = "; std::cerr << "#DEBUG#: s" << arg << " = ";

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. 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 #pragma once
@ -25,5 +25,6 @@ class OpcodeTable;
extern bool vm_debug_enabled; extern bool vm_debug_enabled;
void register_debug_ops(OpcodeTable& cp0); void register_debug_ops(OpcodeTable& cp0);
void set_debug_enabled(bool enable_debug);
} // namespace vm } // 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/continuation.h"
#include "vm/box.hpp" #include "vm/box.hpp"
#include "vm/atom.h" #include "vm/atom.h"
#include "vm/vmstate.h"
namespace td { namespace td {
template class td::Cnt<std::string>; 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 { bool StackEntry::serialize(vm::CellBuilder& cb, int mode) const {
auto* vsi = VmStateInterface::get();
if (vsi && !vsi->register_op()) {
return false;
}
switch (tp) { switch (tp) {
case t_null: case t_null:
return cb.store_long_bool(0, 8); // vm_stk_null#00 = VmStackValue; 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) { bool StackEntry::deserialize(CellSlice& cs, int mode) {
auto* vsi = VmStateInterface::get();
if (vsi && !vsi->register_op()) {
return false;
}
clear(); clear();
int t = (mode & 0xf000) ? ((mode >> 12) & 15) : (int)cs.prefetch_ulong(8); int t = (mode & 0xf000) ? ((mode >> 12) & 15) : (int)cs.prefetch_ulong(8);
switch (t) { switch (t) {
@ -843,6 +852,10 @@ bool StackEntry::deserialize(Ref<Cell> cell, int mode) {
} }
bool Stack::serialize(vm::CellBuilder& cb, int mode) const { 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; // vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack;
unsigned n = depth(); unsigned n = depth();
if (!cb.store_ulong_rchk_bool(n, 24)) { // vm_stack#_ depth:(## 24) 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) { bool Stack::deserialize(vm::CellSlice& cs, int mode) {
auto* vsi = VmStateInterface::get();
if (vsi && !vsi->register_op()) {
return false;
}
clear(); clear();
// vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack; // vm_stack#_ depth:(## 24) stack:(VmStackList depth) = VmStack;
int n; int n;

View file

@ -53,6 +53,23 @@ int exec_null_swap_if(VmState* st, bool cond, int depth) {
return 0; 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) { int exec_mktuple_common(VmState* st, unsigned n) {
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.check_underflow(n); 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(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(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(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(0x6fb, 12, 4, dump_tuple_index2, exec_tuple_index2))
.insert(OpcodeInstr::mkfixed(0x6fc >> 2, 10, 6, dump_tuple_index3, exec_tuple_index3)); .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> { class VmStateInterface : public td::Context<VmStateInterface> {
public: public:
virtual ~VmStateInterface() = default; 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 td::ConstBitPtr hash) { // may throw a dictionary exception; returns nullptr if library is not found
return {}; return {};
} }
virtual void register_cell_load(const CellHash& cell_hash){}; virtual void register_cell_load(const CellHash& cell_hash){};
virtual void register_cell_create(){}; 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 } // namespace vm

View file

@ -900,7 +900,7 @@ If one wants to represent $x$ nanograms, one selects an integer $\ell<16$ such t
Recall (cf.~\cite[A]{TON}) that the original total supply of Grams is fixed at five billion (i.e., $5\cdot10^{18}<2^{63}$ nanograms), and is expected to grow very slowly. Therefore, all the amounts of Grams encountered in practice will fit in unsigned or even signed 64-bit integers. The validators may use the 64-bit integer representation of Grams in their internal computations; however, the serialization of these values the blockchain is another matter. Recall (cf.~\cite[A]{TON}) that the original total supply of Grams is fixed at five billion (i.e., $5\cdot10^{18}<2^{63}$ nanograms), and is expected to grow very slowly. Therefore, all the amounts of Grams encountered in practice will fit in unsigned or even signed 64-bit integers. The validators may use the 64-bit integer representation of Grams in their internal computations; however, the serialization of these values the blockchain is another matter.
\nxsubpoint\emb{Representing collections of arbitrary currencies} \nxsubpoint\emb{Representing collections of arbitrary currencies}\label{sp:extra.curr}
Recall that the TON Blockchain allows its users to define arbitrary cryptocurrencies or tokens apart from the Gram, provided some conditions are met. Such additional cryptocurrencies are identified by 32-bit $\currencyid$s. The list of defined additional cryptocurrencies is a part of the blockchain configuration, stored in the masterchain. Recall that the TON Blockchain allows its users to define arbitrary cryptocurrencies or tokens apart from the Gram, provided some conditions are met. Such additional cryptocurrencies are identified by 32-bit $\currencyid$s. The list of defined additional cryptocurrencies is a part of the blockchain configuration, stored in the masterchain.
When some amounts of one or several such cryptocurrencies need to be represented, a dictionary (cf.~\cite[3.3]{TVM}) with 32-bit $\currencyid$s as keys and \texttt{VarUInteger 32} values is used: When some amounts of one or several such cryptocurrencies need to be represented, a dictionary (cf.~\cite[3.3]{TVM}) with 32-bit $\currencyid$s as keys and \texttt{VarUInteger 32} values is used:
@ -936,7 +936,7 @@ message$_ {X:Type} info:CommonMsgInfo
\end{verbatim} \end{verbatim}
The meaning of this scheme is as follows. The meaning of this scheme is as follows.
Type \texttt{Message $X$} describes a message with the body (or payload) of type $X$. Its serialization starts with \texttt{info} of type \texttt{CommonMsgInfo}, which comes in three flavors: for internal messages, inbound external messages, and outbound external messages, respectively. All of them have a source address \texttt{src} and destination address \texttt{dest}, which are external or internal according to the chosen constructor. Apart from that, an internal message may bear some \texttt{value} in Grams and other defined currencies, and all messages generated inside the TON Blockchain have a logical creation time \texttt{created\_lt} (cf.~\ptref{sp:lt.ton.blkch}) and creation unixtime \texttt{created\_at}, both automatically set by the generating transaction. The creation unixtime equals the creation unixtime of the block containing the generating transaction. Type \texttt{Message $X$} describes a message with the body (or payload) of type $X$. Its serialization starts with \texttt{info} of type \texttt{CommonMsgInfo}, which comes in three flavors: for internal messages, inbound external messages, and outbound external messages, respectively. All of them have a source address \texttt{src} and destination address \texttt{dest}, which are external or internal according to the chosen constructor. Apart from that, an internal message may bear some \texttt{value} in Grams and other defined currencies (cf.~\ptref{sp:extra.curr}), and all messages generated inside the TON Blockchain have a logical creation time \texttt{created\_lt} (cf.~\ptref{sp:lt.ton.blkch}) and creation unixtime \texttt{created\_at}, both automatically set by the generating transaction. The creation unixtime equals the creation unixtime of the block containing the generating transaction.
\nxsubpoint\emb{Forwarding and IHR fees. Total value of an internal message} \nxsubpoint\emb{Forwarding and IHR fees. Total value of an internal message}
Internal messages define an \texttt{ihr\_fee} in Grams, which is subtracted from the value attached to the message and awarded to the validators of the destination shardchain if they include the message by the IHR mechanism. The \texttt{fwd\_fee} is the original total forwarding fee paid for using the HR mechanism; it is automatically computed from some configuration parameters and the size of the message at the time the message is generated. Internal messages define an \texttt{ihr\_fee} in Grams, which is subtracted from the value attached to the message and awarded to the validators of the destination shardchain if they include the message by the IHR mechanism. The \texttt{fwd\_fee} is the original total forwarding fee paid for using the HR mechanism; it is automatically computed from some configuration parameters and the size of the message at the time the message is generated.
@ -1692,7 +1692,7 @@ The smart-contract information structure {\em SmartContractInfo}, passed in the
myself:MsgAddressInt global_config:(Maybe Cell) myself:MsgAddressInt global_config:(Maybe Cell)
] = SmartContractInfo; ] = SmartContractInfo;
\end{verbatim} \end{verbatim}
In other words, the first component of this tuple is an {\em Integer\/} {\tt magic\/} always equal to {\tt 0x076ef1ea}, the second component is an {\em Integer\/} {\tt actions}, originally initialized by zero, but incremented by one whenever an output action is installed by a non-{\tt RAW} output action primitive of the TVM, and so on. The remaining balance is represented by a pair, i.e., a two-component {\em Tuple}: the first component is the nanogram balance, and the second component is a dictionary with 16-bit keys representing all other currencies, if any. In other words, the first component of this tuple is an {\em Integer\/} {\tt magic\/} always equal to {\tt 0x076ef1ea}, the second component is an {\em Integer\/} {\tt actions}, originally initialized by zero, but incremented by one whenever an output action is installed by a non-{\tt RAW} output action primitive of the TVM, and so on. The remaining balance is represented by a pair, i.e., a two-component {\em Tuple}: the first component is the nanogram balance, and the second component is a dictionary with 32-bit keys representing all other currencies, if any (cf.~\ptref{sp:extra.curr}).
The {\tt rand\_seed} field (an unsigned 256-bit integer) here is initialized deterministically starting from the {\tt rand\_seed} of the block, the account address, the hash of the inbound message being processed (if any), and the transaction logical time {\tt trans\_lt}. The {\tt rand\_seed} field (an unsigned 256-bit integer) here is initialized deterministically starting from the {\tt rand\_seed} of the block, the account address, the hash of the inbound message being processed (if any), and the transaction logical time {\tt trans\_lt}.

View file

@ -1437,6 +1437,10 @@ The following primitives work with (the only) value~$\bot$ of type {\em Null}, u
\item {\tt 6FA1} --- {\tt NULLSWAPIFNOT} ($x$ -- $x$ or $\bot$ $x$), pushes a {\em Null\/} under the topmost {\em Integer\/}~$x$, but only if $x=0$. May be used for stack alignment after quiet primitives such as {\tt PLDUXQ}. \item {\tt 6FA1} --- {\tt NULLSWAPIFNOT} ($x$ -- $x$ or $\bot$ $x$), pushes a {\em Null\/} under the topmost {\em Integer\/}~$x$, but only if $x=0$. May be used for stack alignment after quiet primitives such as {\tt PLDUXQ}.
\item {\tt 6FA2} --- {\tt NULLROTRIF} ($x$ $y$ -- $x$ $y$ or $\bot$ $x$ $y$), pushes a {\em Null\/} under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is non-zero. \item {\tt 6FA2} --- {\tt NULLROTRIF} ($x$ $y$ -- $x$ $y$ or $\bot$ $x$ $y$), pushes a {\em Null\/} under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is non-zero.
\item {\tt 6FA3} --- {\tt NULLROTRIFNOT} ($x$ $y$ -- $x$ $y$ or $\bot$ $x$ $y$), pushes a {\em Null\/} under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is zero. May be used for stack alignment after quiet primitives such as {\tt LDUXQ}. \item {\tt 6FA3} --- {\tt NULLROTRIFNOT} ($x$ $y$ -- $x$ $y$ or $\bot$ $x$ $y$), pushes a {\em Null\/} under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is zero. May be used for stack alignment after quiet primitives such as {\tt LDUXQ}.
\item {\tt 6FA4} --- {\tt NULLSWAPIF2} ($x$ -- $x$ or $\bot$ $\bot$ $x$), pushes two {\em Null\/}s under the topmost {\em Integer\/}~$x$, but only if $x\neq0$. Equivalent to {\tt NULLSWAPIF}; {\tt NULLSWAPIF}.
\item {\tt 6FA5} --- {\tt NULLSWAPIFNOT2} ($x$ -- $x$ or $\bot$ $\bot$ $x$), pushes two {\em Null\/}s under the topmost {\em Integer\/}~$x$, but only if $x=0$. Equivalent to {\tt NULLSWAPIFNOT}; {\tt NULLSWAPIFNOT}.
\item {\tt 6FA6} --- {\tt NULLROTRIF2} ($x$ $y$ -- $x$ $y$ or $\bot$ $\bot$ $x$ $y$), pushes two {\em Null\/}s under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is non-zero. Equivalent to {\tt NULLROTRIF}; {\tt NULLROTRIF}.
\item {\tt 6FA7} --- {\tt NULLROTRIFNOT2} ($x$ $y$ -- $x$ $y$ or $\bot$ $\bot$ $x$ $y$), pushes two {\em Null\/}s under the second stack entry from the top, but only if the topmost {\em Integer\/} $y$ is zero. Equivalent to {\tt NULLROTRIFNOT}; {\tt NULLROTRIFNOT}.
\item {\tt 6FB$ij$} --- {\tt INDEX2 $i$,$j$} ($t$ -- $x$), recovers $x=(t_{i+1})_{j+1}$ for $0\leq i,j\leq 3$. Equivalent to {\tt INDEX $i$}; {\tt INDEX $j$}. \item {\tt 6FB$ij$} --- {\tt INDEX2 $i$,$j$} ($t$ -- $x$), recovers $x=(t_{i+1})_{j+1}$ for $0\leq i,j\leq 3$. Equivalent to {\tt INDEX $i$}; {\tt INDEX $j$}.
\item {\tt 6FB4} --- {\tt CADR} ($t$ -- $x$), recovers $x=(t_2)_1$. \item {\tt 6FB4} --- {\tt CADR} ($t$ -- $x$), recovers $x=(t_2)_1$.
\item {\tt 6FB5} --- {\tt CDDR} ($t$ -- $x$), recovers $x=(t_2)_2$. \item {\tt 6FB5} --- {\tt CDDR} ($t$ -- $x$), recovers $x=(t_2)_2$.

View file

@ -149,6 +149,10 @@ void HttpConnection::continue_payload_write() {
if (!writing_payload_) { if (!writing_payload_) {
return; return;
} }
if (writing_payload_->is_error()) {
stop();
return;
}
auto t = writing_payload_->payload_type(); auto t = writing_payload_->payload_type();
if (t == HttpPayload::PayloadType::pt_eof) { if (t == HttpPayload::PayloadType::pt_eof) {
@ -233,7 +237,11 @@ td::Status HttpConnection::continue_payload_read(td::ChainBufferReader &input) {
return td::Status::OK(); return td::Status::OK();
} }
auto s = input.size(); auto s = input.size();
TRY_STATUS(reading_payload_->parse(input)); auto R = reading_payload_->parse(input);
if (R.is_error()) {
reading_payload_->set_error();
return R.move_as_error();
}
if (input.size() == s) { if (input.size() == s) {
return td::Status::OK(); return td::Status::OK();
} }

View file

@ -119,6 +119,12 @@ class HttpPayload {
bool high_watermark_reached() const { bool high_watermark_reached() const {
return ready_bytes_ > high_watermark_; return ready_bytes_ > high_watermark_;
} }
bool is_error() const {
return error_;
}
void set_error() {
error_ = true;
}
PayloadType payload_type() const { PayloadType payload_type() const {
return type_; return type_;
} }
@ -152,6 +158,7 @@ class HttpPayload {
size_t chunk_size_ = 1 << 14; size_t chunk_size_ = 1 << 14;
bool written_zero_chunk_ = false; bool written_zero_chunk_ = false;
bool written_trailer_ = false; bool written_trailer_ = false;
bool error_ = false;
std::list<std::unique_ptr<Callback>> callbacks_; std::list<std::unique_ptr<Callback>> callbacks_;

View file

@ -57,6 +57,7 @@
#include "vm/cells/MerkleProof.h" #include "vm/cells/MerkleProof.h"
#include "vm/vm.h" #include "vm/vm.h"
#include "vm/cp0.h" #include "vm/cp0.h"
#include "vm/memo.h"
#include "ton/ton-shard.h" #include "ton/ton-shard.h"
#include "openssl/rand.hpp" #include "openssl/rand.hpp"
#include "crypto/vm/utils.h" #include "crypto/vm/utils.h"
@ -1065,6 +1066,9 @@ bool TestNode::parse_run_method(ton::WorkchainId workchain, ton::StdSmcAddress a
}); });
} else { } else {
td::int64 method_id = compute_method_id(method_name); td::int64 method_id = compute_method_id(method_name);
// set serialization limits
vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls
vm::VmStateInterface::Guard guard(&fstate);
// serialize parameters // serialize parameters
vm::CellBuilder cb; vm::CellBuilder cb;
Ref<vm::Cell> cell; Ref<vm::Cell> cell;
@ -1298,6 +1302,9 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(os); vm::CellSlice{vm::NoVm(), info.true_root}.print_rec(os);
out << "dump of account state (proof): " << os.str() << std::endl; out << "dump of account state (proof): " << os.str() << std::endl;
} }
// set deserialization limits
vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls
vm::VmStateInterface::Guard guard(&fstate);
if (false && remote_c7.size()) { if (false && remote_c7.size()) {
// DEBUG (dump remote_c7) // DEBUG (dump remote_c7)
auto r_c7 = vm::std_boc_deserialize(remote_c7).move_as_ok(); auto r_c7 = vm::std_boc_deserialize(remote_c7).move_as_ok();
@ -1392,12 +1399,6 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
out << os.str(); out << os.str();
} }
} }
if (0) { // DEBUG
std::ostringstream os;
LOG(DEBUG) << "dumping constructed proof";
//vm::CellSlice{vm::NoVm(), pb.extract_proof()}.print_rec(os);
out << "constructed state proof: " << os.str();
}
} catch (vm::VmVirtError& err) { } catch (vm::VmVirtError& err) {
out << "virtualization error while parsing runSmcMethod result: " << err.get_msg(); out << "virtualization error while parsing runSmcMethod result: " << err.get_msg();
} catch (vm::VmError& err) { } catch (vm::VmError& err) {
@ -2456,7 +2457,7 @@ int main(int argc, char* argv[]) {
}); });
#endif #endif
vm::init_op_cp0(); vm::init_op_cp0(true); // enable vm debug
td::actor::Scheduler scheduler({2}); td::actor::Scheduler scheduler({2});

View file

@ -210,6 +210,9 @@ class HttpRldpPayloadReceiver : public td::actor::Actor {
void abort_query(td::Status error) { void abort_query(td::Status error) {
LOG(INFO) << "failed to receive HTTP payload: " << error; LOG(INFO) << "failed to receive HTTP payload: " << error;
if (payload_) {
payload_->set_error();
}
stop(); stop();
} }
@ -305,6 +308,9 @@ class HttpRldpPayloadSender : public td::actor::Actor {
if (!cur_query_promise_) { if (!cur_query_promise_) {
return; return;
} }
if (payload_->is_error()) {
return;
}
if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) { if (payload_->parse_completed() || payload_->ready_bytes() >= ton::http::HttpRequest::low_watermark()) {
answer_query(); answer_query();
} }

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "td/utils/base64.h" #include "td/utils/base64.h"
@ -30,6 +30,68 @@
namespace td { namespace td {
//TODO: fix copypaste //TODO: fix copypaste
static const char *const symbols32_lc = "abcdefghijklmnopqrstuvwxyz234567";
static const char *const symbols32_uc = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
string base32_encode(Slice input, bool upper_case) {
auto *symbols32 = (upper_case ? symbols32_uc : symbols32_lc);
string base32;
base32.reserve((input.size() * 8 + 4) / 5);
uint32 c = 0;
uint32 length = 0;
for (size_t i = 0; i < input.size(); i++) {
c = (c << 8) | input.ubegin()[i];
length += 8;
while (length >= 5) {
length -= 5;
base32.push_back(symbols32[(c >> length) & 31]);
}
}
if (length != 0) {
base32.push_back(symbols32[(c << (5 - length)) & 31]);
}
//TODO: optional padding
return base32;
}
static unsigned char b32_char_to_value[256];
static void init_base32_table() {
static bool is_inited = [] {
std::fill(std::begin(b32_char_to_value), std::end(b32_char_to_value), static_cast<unsigned char>(32));
for (unsigned char i = 0; i < 32; i++) {
b32_char_to_value[static_cast<size_t>(symbols32_lc[i])] = i;
b32_char_to_value[static_cast<size_t>(symbols32_uc[i])] = i;
}
return true;
}();
CHECK(is_inited);
}
Result<string> base32_decode(Slice base32) {
init_base32_table();
string res;
res.reserve(base32.size() * 5 / 8);
uint32 c = 0;
uint32 length = 0;
for (size_t i = 0; i < base32.size(); i++) {
auto value = b32_char_to_value[base32.ubegin()[i]];
if (value == 32) {
return Status::Error("Wrong character in the string");
}
c = (c << 5) | value;
length += 5;
while (length >= 8) {
length -= 8;
res.push_back(td::uint8((c >> length) & 255));
}
}
if ((c & ((1 << length) - 1)) != 0) {
return Status::Error("Nonzero padding");
}
//TODO: check padding
return res;
}
static const char *const symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; static const char *const symbols64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
string base64_encode(Slice input) { string base64_encode(Slice input) {

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License 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/>. 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 #pragma once
@ -37,4 +37,7 @@ bool is_base64url(Slice input);
string base64_filter(Slice input); string base64_filter(Slice input);
string base32_encode(Slice input, bool upper_case = false);
Result<string> base32_decode(Slice base32);
} // namespace td } // namespace td

View file

@ -220,6 +220,26 @@ TEST(Misc, base64) {
"Ojo7ISUiOw=="); "Ojo7ISUiOw==");
} }
TEST(Misc, base32) {
ASSERT_EQ("", base32_encode(""));
ASSERT_EQ("me", base32_encode("a"));
base32_decode("me").ensure();
ASSERT_EQ("mfra", base32_encode("ab"));
ASSERT_EQ("mfrgg", base32_encode("abc"));
ASSERT_EQ("mfrggza", base32_encode("abcd"));
ASSERT_EQ("mfrggzdg", base32_encode("abcdf"));
ASSERT_EQ("mfrggzdgm4", base32_encode("abcdfg"));
for (int l = 0; l < 300000; l += l / 20 + l / 1000 * 500 + 1) {
for (int t = 0; t < 10; t++) {
string s = rand_string(std::numeric_limits<char>::min(), std::numeric_limits<char>::max(), l);
auto encoded = base32_encode(s);
auto decoded = base32_decode(encoded);
ASSERT_TRUE(decoded.is_ok());
ASSERT_TRUE(decoded.ok() == s);
}
}
}
TEST(Misc, to_integer) { TEST(Misc, to_integer) {
ASSERT_EQ(to_integer<int32>("-1234567"), -1234567); ASSERT_EQ(to_integer<int32>("-1234567"), -1234567);
ASSERT_EQ(to_integer<int64>("-1234567"), -1234567); ASSERT_EQ(to_integer<int64>("-1234567"), -1234567);

View file

@ -23,7 +23,7 @@ Test_Fift_testvm6_default dd6353c8f3f21cf62a4769ee1f3daaec46f43fd633ffb84c5d6535
Test_Fift_testvm7_default 77f54b6c8c9a728d262e912efcc347de7014a37d08793c3adeac8b96fe063342 Test_Fift_testvm7_default 77f54b6c8c9a728d262e912efcc347de7014a37d08793c3adeac8b96fe063342
Test_Fift_testvm8_default 17c9e2205ccecfd8549328b4a501d07dde0336899a7a496e747e1032ad5efff9 Test_Fift_testvm8_default 17c9e2205ccecfd8549328b4a501d07dde0336899a7a496e747e1032ad5efff9
Test_Fift_testvm_default ee4cbfec76c050b6de7877cfc39817d594cd1e175b6265b76fb642e30b940437 Test_Fift_testvm_default ee4cbfec76c050b6de7877cfc39817d594cd1e175b6265b76fb642e30b940437
Test_Fift_testvmprog_default a6d40d8a7bf0dd3b719c8b1023fef59e01ef094f732951cec4577556c6c68e64 Test_Fift_testvmprog_default e5d0b2c68ee568280877c8495be558bfd0054ca5d99a99eebb525bbeca8a65af
Test_RefInt_main_default 768493e0aef8e09a401a6d369edd1ef503a9215fb09dc460f52b27a8bde767cb Test_RefInt_main_default 768493e0aef8e09a401a6d369edd1ef503a9215fb09dc460f52b27a8bde767cb
Test_VM_assert_code_not_null_default 05bc07e129181c972b976442f200de9487dee8bfb5ac53dd36ff61c5d4d4291d Test_VM_assert_code_not_null_default 05bc07e129181c972b976442f200de9487dee8bfb5ac53dd36ff61c5d4d4291d
Test_VM_assert_extract_minmax_key_default c352309c61bdf62ba7a0ba7280d303c88b0696fe7efa550c05feb2c662275297 Test_VM_assert_extract_minmax_key_default c352309c61bdf62ba7a0ba7280d303c88b0696fe7efa550c05feb2c662275297

View file

@ -23,7 +23,7 @@
exception statement from your version. If you delete this exception statement exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here. from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#include "adnl/adnl-network-manager.h" #include "adnl/adnl-network-manager.h"
#include "adnl/adnl.h" #include "adnl/adnl.h"
@ -41,6 +41,13 @@
int main() { int main() {
SET_VERBOSITY_LEVEL(verbosity_INFO); SET_VERBOSITY_LEVEL(verbosity_INFO);
{
auto id_str = td::Slice("WQUA224U42HFSKN63K6NU23X42VK4IJRLFGG65CU62JAOL6U47HRCHD");
auto id = ton::adnl::AdnlNodeIdShort::parse(id_str).move_as_ok();
CHECK(td::hex_decode("a1406b5ca73472c94df6d5e6d35bbf355571098aca637ba2a7b490397ea73e78").ok() == id.as_slice());
CHECK(id.serialize() == td::to_lower(id_str));
}
std::string db_root_ = "tmp-ee"; std::string db_root_ = "tmp-ee";
td::rmrf(db_root_).ignore(); td::rmrf(db_root_).ignore();
td::mkdir(db_root_).ensure(); td::mkdir(db_root_).ensure();

View file

@ -37,6 +37,8 @@ exportedEncryptedKey data:secureBytes = ExportedEncryptedKey;
bip39Hints words:vector<string> = Bip39Hints; bip39Hints words:vector<string> = Bip39Hints;
adnlAddress adnl_address:string = AdnlAddress;
accountAddress account_address:string = AccountAddress; accountAddress account_address:string = AccountAddress;
accountRevisionList revisions:vector<int32> = AccountRevisionList; accountRevisionList revisions:vector<int32> = AccountRevisionList;
@ -92,7 +94,7 @@ dns.entryDataUnknown bytes:bytes = dns.EntryData;
dns.entryDataText text:string = dns.EntryData; dns.entryDataText text:string = dns.EntryData;
dns.entryDataNextResolver resolver:AccountAddress = dns.EntryData; dns.entryDataNextResolver resolver:AccountAddress = dns.EntryData;
dns.entryDataSmcAddress smc_address:AccountAddress = dns.EntryData; dns.entryDataSmcAddress smc_address:AccountAddress = dns.EntryData;
//TODO: other entry types dns.entryDataAdnlAddress adnl_address:AdnlAddress = dns.EntryData;
dns.entry name:string category:int32 entry:dns.EntryData = dns.Entry; dns.entry name:string category:int32 entry:dns.EntryData = dns.Entry;

Binary file not shown.

View file

@ -54,6 +54,8 @@
#include "td/utils/tests.h" #include "td/utils/tests.h"
#include "td/utils/port/path.h" #include "td/utils/port/path.h"
#include "common/util.h"
namespace tonlib { namespace tonlib {
namespace int_api { namespace int_api {
struct GetAccountState { struct GetAccountState {
@ -1310,6 +1312,12 @@ td::Result<block::StdAddress> get_account_address(td::Slice account_address) {
return address; return address;
} }
td::Result<td::Bits256> get_adnl_address(td::Slice adnl_address) {
TRY_RESULT_PREFIX(address, td::adnl_id_decode(adnl_address),
TonlibError::InvalidField("adnl_address", "can't decode"));
return address;
}
tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request( tonlib_api::object_ptr<tonlib_api::Object> TonlibClient::do_static_request(
const tonlib_api::getAccountAddress& request) { const tonlib_api::getAccountAddress& request) {
if (!request.initial_account_state_) { if (!request.initial_account_state_) {
@ -1859,6 +1867,36 @@ td::Status TonlibClient::do_request(const tonlib_api::getAccountState& request,
return td::Status::OK(); return td::Status::OK();
} }
td::Result<ton::ManualDns::EntryData> to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) {
using R = td::Result<ton::ManualDns::EntryData>;
return downcast_call2<R>(
entry_data,
td::overloaded(
[&](tonlib_api::dns_entryDataUnknown& unknown) -> R { return ton::ManualDns::EntryData(); },
[&](tonlib_api::dns_entryDataNextResolver& next_resolver) -> R {
if (!next_resolver.resolver_) {
return TonlibError::EmptyField("resolver");
}
TRY_RESULT(resolver, get_account_address(next_resolver.resolver_->account_address_));
return ton::ManualDns::EntryData::next_resolver(std::move(resolver));
},
[&](tonlib_api::dns_entryDataSmcAddress& smc_address) -> R {
if (!smc_address.smc_address_) {
return TonlibError::EmptyField("smc_address");
}
TRY_RESULT(address, get_account_address(smc_address.smc_address_->account_address_));
return ton::ManualDns::EntryData::smc_address(std::move(address));
},
[&](tonlib_api::dns_entryDataAdnlAddress& adnl_address) -> R {
if (!adnl_address.adnl_address_) {
return TonlibError::EmptyField("adnl_address");
}
TRY_RESULT(address, get_adnl_address(adnl_address.adnl_address_->adnl_address_));
return ton::ManualDns::EntryData::adnl_address(std::move(address));
},
[&](tonlib_api::dns_entryDataText& text) -> R { return ton::ManualDns::EntryData::text(text.text_); }));
}
class GenericCreateSendGrams : public TonlibQueryActor { class GenericCreateSendGrams : public TonlibQueryActor {
public: public:
GenericCreateSendGrams(td::actor::ActorShared<TonlibClient> client, tonlib_api::createQuery query, GenericCreateSendGrams(td::actor::ActorShared<TonlibClient> client, tonlib_api::createQuery query,
@ -1947,29 +1985,6 @@ class GenericCreateSendGrams : public TonlibQueryActor {
return res; return res;
} }
td::Result<ton::ManualDns::EntryData> to_dns_entry_data(tonlib_api::dns_EntryData& entry_data) {
using R = td::Result<ton::ManualDns::EntryData>;
return downcast_call2<R>(
entry_data,
td::overloaded(
[&](tonlib_api::dns_entryDataUnknown& unknown) -> R { return ton::ManualDns::EntryData(); },
[&](tonlib_api::dns_entryDataNextResolver& next_resolver) -> R {
if (!next_resolver.resolver_) {
return TonlibError::EmptyField("resolver");
}
TRY_RESULT(resolver, get_account_address(next_resolver.resolver_->account_address_));
return ton::ManualDns::EntryData::next_resolver(std::move(resolver));
},
[&](tonlib_api::dns_entryDataSmcAddress& smc_address) -> R {
if (!smc_address.smc_address_) {
return TonlibError::EmptyField("smc_address");
}
TRY_RESULT(address, get_account_address(smc_address.smc_address_->account_address_));
return ton::ManualDns::EntryData::smc_address(std::move(address));
},
[&](tonlib_api::dns_entryDataText& text) -> R { return ton::ManualDns::EntryData::text(text.text_); }));
}
td::Result<ton::ManualDns::Action> to_dns_action(tonlib_api::dns_Action& action) { td::Result<ton::ManualDns::Action> to_dns_action(tonlib_api::dns_Action& action) {
using R = td::Result<ton::ManualDns::Action>; using R = td::Result<ton::ManualDns::Action>;
return downcast_call2<R>(action, return downcast_call2<R>(action,
@ -2566,7 +2581,11 @@ td::Result<tonlib_api::object_ptr<tonlib_api::dns_EntryData>> to_tonlib_api(
res = tonlib_api::make_object<tonlib_api::dns_entryDataNextResolver>( res = tonlib_api::make_object<tonlib_api::dns_entryDataNextResolver>(
tonlib_api::make_object<tonlib_api::accountAddress>(resolver.resolver.rserialize(true))); tonlib_api::make_object<tonlib_api::accountAddress>(resolver.resolver.rserialize(true)));
}, },
[&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { res = td::Status::Error("TODO"); }, [&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) {
res = tonlib_api::make_object<tonlib_api::dns_entryDataAdnlAddress>(
tonlib_api::make_object<tonlib_api::adnlAddress>(
td::adnl_id_encode(adnl_address.adnl_address.as_slice()).move_as_ok()));
},
[&](const ton::ManualDns::EntryDataSmcAddress& smc_address) { [&](const ton::ManualDns::EntryDataSmcAddress& smc_address) {
res = tonlib_api::make_object<tonlib_api::dns_entryDataSmcAddress>( res = tonlib_api::make_object<tonlib_api::dns_entryDataSmcAddress>(
tonlib_api::make_object<tonlib_api::accountAddress>(smc_address.smc_address.rserialize(true))); tonlib_api::make_object<tonlib_api::accountAddress>(smc_address.smc_address.rserialize(true)));

View file

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

View file

@ -434,7 +434,11 @@ class TonlibCli : public td::actor::Actor {
return; return;
} }
if (resolved->entries_[0]->name_ == name) { if (resolved->entries_[0]->name_ == name) {
td::TerminalIO::out() << "Done: " << to_string(resolved); td::TerminalIO::out() << "Done\n";
for (auto& entry : resolved->entries_) {
td::TerminalIO::out() << " " << entry->name_ << " " << entry->category_ << " "
<< tonlib::to_dns_entry_data(*entry->entry_).move_as_ok() << "\n";
}
promise.set_value(td::Unit()); promise.set_value(td::Unit());
return; return;
} }
@ -484,22 +488,14 @@ class TonlibCli : public td::actor::Actor {
td::TerminalIO::out() << "Delete all dns enties with name and category: " << action.name << ":" td::TerminalIO::out() << "Delete all dns enties with name and category: " << action.name << ":"
<< action.category << "\n"; << action.category << "\n";
} else { } else {
tonlib_api::object_ptr<tonlib_api::dns_EntryData> data;
td::StringBuilder sb; td::StringBuilder sb;
td::Status error; td::Status error;
if (action.data.value().data.empty()) { if (action.data.value().data.empty()) {
TRY_STATUS_PROMISE(promise, td::Status::Error("Empty entry data is not supported")); TRY_STATUS_PROMISE(promise, td::Status::Error("Empty entry data is not supported"));
} }
action.data.value().data.visit(td::overloaded( TRY_RESULT_PROMISE(promise, data, tonlib::to_tonlib_api(action.data.value()));
[&](const ton::ManualDns::EntryDataText& text) { sb << action.data.value();
data = tonlib_api::make_object<tonlib_api::dns_entryDataText>(text.text);
sb << "TEXT:" << text.text;
},
[&](const ton::ManualDns::EntryDataNextResolver& resolver) { error = td::Status::Error("TODO"); },
[&](const ton::ManualDns::EntryDataAdnlAddress& adnl_address) { error = td::Status::Error("TODO"); },
[&](const ton::ManualDns::EntryDataSmcAddress& text) { error = td::Status::Error("TODO"); }));
;
TRY_STATUS_PROMISE(promise, std::move(error)); TRY_STATUS_PROMISE(promise, std::move(error));
td::TerminalIO::out() << "Set dns entry: " << action.name << ":" << action.category << " " << sb.as_cslice() td::TerminalIO::out() << "Set dns entry: " << action.name << ":" << action.category << " " << sb.as_cslice()
<< "\n"; << "\n";

View file

@ -35,6 +35,7 @@
#include "vm/dict.h" #include "vm/dict.h"
#include "vm/cells/MerkleProof.h" #include "vm/cells/MerkleProof.h"
#include "vm/vm.h" #include "vm/vm.h"
#include "vm/memo.h"
#include "shard.hpp" #include "shard.hpp"
#include "validator-set.hpp" #include "validator-set.hpp"
#include "signature-set.hpp" #include "signature-set.hpp"
@ -686,6 +687,8 @@ void LiteQuery::perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, St
fatal_error("cannot deserialize parameter list boc: "s + res.move_as_error().to_string()); fatal_error("cannot deserialize parameter list boc: "s + res.move_as_error().to_string());
return; return;
} }
vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls
vm::VmStateInterface::Guard guard(&fstate);
auto cs = vm::load_cell_slice(res.move_as_ok()); auto cs = vm::load_cell_slice(res.move_as_ok());
if (!(vm::Stack::deserialize_to(cs, stack_, 0) && cs.empty_ext())) { if (!(vm::Stack::deserialize_to(cs, stack_, 0) && cs.empty_ext())) {
fatal_error("parameter list boc cannot be deserialized as a VmStack"); fatal_error("parameter list boc cannot be deserialized as a VmStack");
@ -1076,6 +1079,8 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice
stack_ = vm.get_stack_ref(); stack_ = vm.get_stack_ref();
LOG(INFO) << "runSmcMethod(" << acc_workchain_ << ":" << acc_addr_.to_hex() << ") query completed: exit code is " LOG(INFO) << "runSmcMethod(" << acc_workchain_ << ":" << acc_addr_.to_hex() << ") query completed: exit code is "
<< exit_code; << exit_code;
vm::FakeVmStateLimits fstate(1000); // limit recursive (de)serialization calls
vm::VmStateInterface::Guard guard(&fstate);
Ref<vm::Cell> cell; Ref<vm::Cell> cell;
td::BufferSlice c7_info, result; td::BufferSlice c7_info, result;
if (mode & 8) { if (mode & 8) {