1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

vm: bugfixes

This commit is contained in:
ton 2020-03-02 17:52:55 +04:00
parent 27aaa11524
commit ba76f1404e
30 changed files with 396 additions and 178 deletions

View file

@ -344,11 +344,11 @@ unsigned long long VarUIntegerPos::as_uint(const vm::CellSlice& cs) const {
bool VarUIntegerPos::store_integer_value(vm::CellBuilder& cb, const td::BigInt256& value) const {
int k = value.bit_size(false);
return k <= (n - 1) * 8 && value.sgn() > 0 && cb.store_long_bool((k + 7) >> 3, ln) &&
return k <= (n - 1) * 8 && value.sgn() >= (int)store_pos_only && cb.store_long_bool((k + 7) >> 3, ln) &&
cb.store_int256_bool(value, (k + 7) & -8, false);
}
const VarUIntegerPos t_VarUIntegerPos_16{16}, t_VarUIntegerPos_32{32};
const VarUIntegerPos t_VarUIntegerPos_16{16}, t_VarUIntegerPos_32{32}, t_VarUIntegerPosRelaxed_32{32, true};
static inline bool redundant_int(const vm::CellSlice& cs) {
int t = (int)cs.prefetch_long(9);

View file

@ -77,7 +77,8 @@ extern const VarUInteger t_VarUInteger_3, t_VarUInteger_7, t_VarUInteger_16, t_V
struct VarUIntegerPos final : TLB_Complex {
int n, ln;
VarUIntegerPos(int _n) : n(_n) {
bool store_pos_only;
VarUIntegerPos(int _n, bool relaxed = false) : n(_n), store_pos_only(!relaxed) {
ln = 32 - td::count_leading_zeroes32(n - 1);
}
bool skip(vm::CellSlice& cs) const override;
@ -90,7 +91,7 @@ struct VarUIntegerPos final : TLB_Complex {
}
};
extern const VarUIntegerPos t_VarUIntegerPos_16, t_VarUIntegerPos_32;
extern const VarUIntegerPos t_VarUIntegerPos_16, t_VarUIntegerPos_32, t_VarUIntegerPosRelaxed_32;
struct VarInteger final : TLB_Complex {
int n, ln;
@ -325,7 +326,7 @@ extern const MsgAddress t_MsgAddress;
struct ExtraCurrencyCollection final : TLB {
HashmapE dict_type, dict_type2;
ExtraCurrencyCollection() : dict_type(32, t_VarUIntegerPos_32), dict_type2(32, t_VarUInteger_32) {
ExtraCurrencyCollection() : dict_type(32, t_VarUIntegerPos_32), dict_type2(32, t_VarUIntegerPosRelaxed_32) {
}
int get_size(const vm::CellSlice& cs) const override {
return dict_type.get_size(cs);

View file

@ -724,7 +724,12 @@ void interpret_tlb_validate_skip(vm::Stack& stack) {
stack.push_bool(ok);
}
void interpret_tlb_type_const(vm::Stack& stack, const tlb::TLB* ptr) {
stack.push_make_object<tlb::TlbTypeHolder>(ptr);
}
void init_words_tlb(fift::Dictionary& d) {
using namespace std::placeholders;
tlb_dict.register_types(block::gen::register_simple_types);
d.def_stack_word("tlb-type-lookup ", interpret_tlb_type_lookup);
d.def_stack_word("tlb-type-name ", interpret_tlb_type_name);
@ -733,6 +738,7 @@ void init_words_tlb(fift::Dictionary& d) {
d.def_stack_word("(tlb-dump-str?) ", interpret_tlb_dump_to_str);
d.def_stack_word("tlb-skip ", interpret_tlb_skip);
d.def_stack_word("tlb-validate-skip ", interpret_tlb_validate_skip);
d.def_stack_word("ExtraCurrencyCollection", std::bind(interpret_tlb_type_const, _1, &block::tlb::t_ExtraCurrencyCollection));
}
void usage(const char* progname) {

View file

@ -111,6 +111,8 @@ variable base
{ cdr cdr } : cddr
{ cdr cdr car } : caddr
{ null ' cons rot times } : list
{ -rot pair swap ! } : 2!
{ @ unpair } : 2@
{ true (atom) drop } : atom
{ bl word atom 1 'nop } ::_ `
{ hole dup 1 { @ execute } does create } : recursive
@ -121,13 +123,18 @@ variable base
{ 0 word -trailing scan-until-word 1 'nop } ::_ $<<
{ 0x40 runvmx } : runvmcode
{ 0x48 runvmx } : gasrunvmcode
{ 0xc8 runvmx } : gas2runvmcode
{ 0x43 runvmx } : runvmdict
{ 0x4b runvmx } : gasrunvmdict
{ 0xcb runvmx } : gas2runvmdict
{ 0x45 runvmx } : runvm
{ 0x4d runvmx } : gasrunvm
{ 0xcd runvmx } : gas2runvm
{ 0x55 runvmx } : runvmctx
{ 0x5d runvmx } : gasrunvmctx
{ 0xdd runvmx } : gas2runvmctx
{ 0x75 runvmx } : runvmctxact
{ 0x7d runvmx } : gasrunvmctxact
{ 0xfd runvmx } : gas2runvmctxact
{ 0x35 runvmx } : runvmctxactq
{ 0x3d runvmx } : gasrunvmctxactq

View file

@ -77,9 +77,10 @@ library TonUtil // TON Blockchain Fift Library
1000000000 constant Gram
{ Gram swap */r } : Gram*/
{ Gram * } : Gram*
{ (number) dup { 1- ' Gram*/ ' Gram* cond true } if
} : $>GR?
// ( S -- nanograms )
{ (number) ?dup 0= abort"not a valid Gram amount"
1- ' Gram*/ ' Gram* cond
{ $>GR? not abort"not a valid Gram amount"
} : $>GR
{ bl word $>GR 1 'nop } ::_ GR$
// ( nanograms -- S )
@ -119,7 +120,7 @@ dictnew constant cc0 // zero currency collection
{ swap cc-key-bits { rot { ."+" } if .val ."*$" ._ true true } idictforeach drop } : (.cc)
{ false (.cc) { ."0" } ifnot } : .cc_
{ .cc_ space } : .cc
{ true (.cc) } : .+cc_
{ true (.cc) drop } : .+cc_
{ .+cc_ space } : .+cc
{ cc-key-bits { rot . ."-> " swap .val .val ."; " true } dictdiff drop cr } : show-cc-diff
{ cc-key-bits { val@ swap val@ + val, true } dictmerge } : cc+
@ -141,13 +142,30 @@ forget val, forget val@ forget .val
} cond } cond } cond } cond
} : cc-key-value?
// ( S -- D -1 or 0 ) Parses an extra currency collection
// e.g. "10000*$3+7777*$-11" means "10000 units of currency #3 and 7777 units of currency #-11"
{ dictnew { // S D
swap dup "+" $pos dup 0< { drop null -rot } { $| 1 $| nip -rot } cond
cc-key-value? { +ccpair over null? dup { rot drop true } if } { 2drop false true } cond
} until
} : $>xcc?
{ $>xcc? not abort"invalid extra currency collection" } : $>xcc
{ char } word dup $len { $>xcc } { drop dictnew } cond 1 'nop } ::_ CX{
// complete currency collections
{ $>xcc? { true } { drop false } cond } : end-parse-cc
// ( S -- x D -1 or 0 ) Parses a currency collection
// e.g. "1.2+300*$2" means "1200000000ng plus 300 units of currency #2"
{ 0 swap dup "+" $pos dup 0< { drop dup
$>GR? { nip nip dictnew true } { end-parse-cc } cond
} { over swap $| swap $>GR? { 2swap 2drop swap 1 $| nip } { drop
} cond end-parse-cc } cond
} : $>cc?
{ $>cc? not abort"invalid extra currency collection" } : $>cc
{ char } word dup $len { $>cc } { drop dictnew } cond 1 'nop } ::_ CX{
{ $>cc? not abort"invalid currency collection" } : $>cc
{ char } word dup $len { $>cc } { drop 0 dictnew } cond 2 'nop } ::_ CC{
// ( x D -- )
{ swap ?dup { .GR_ .+cc_ } { .cc_ } cond } : .GR+cc_
{ .GR+cc_ space } : .GR+cc
{ -rot Gram, swap dict, } : Gram+cc,
// Libraries
// ( -- D ) New empty library collection

View file

@ -2242,6 +2242,7 @@ std::vector<Ref<vm::Cell>> get_vm_libraries() {
// +16 = load c7 (smart-contract context)
// +32 = return c5 (actions)
// +64 = log vm ops to stderr
// +128 = pop hard gas limit (enabled by ACCEPT) from stack as well
void interpret_run_vm(IntCtx& ctx, int mode) {
if (mode < 0) {
mode = ctx.stack.pop_smallint_range(0xff);
@ -2249,7 +2250,13 @@ void interpret_run_vm(IntCtx& ctx, int mode) {
bool with_data = mode & 4;
Ref<vm::Tuple> c7;
Ref<vm::Cell> data, actions;
long long gas_max = (mode & 128) ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
long long gas_limit = (mode & 8) ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
if (!(mode & 128)) {
gas_max = gas_limit;
} else {
gas_max = std::max(gas_max, gas_limit);
}
if (mode & 16) {
c7 = ctx.stack.pop_tuple();
}
@ -2259,7 +2266,7 @@ void interpret_run_vm(IntCtx& ctx, int mode) {
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log((mode & 64) && ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
vm::GasLimits gas{gas_limit, gas_max};
int res =
vm::run_vm_code(cs, ctx.stack, mode & 3, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7), &actions);
ctx.stack.push_smallint(res);

View file

@ -9,9 +9,11 @@ true =: allow-bounce
false =: force-bounce
3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
60 =: timeout // external message expires in 60 seconds
variable extra-currencies
{ extra-currencies @ cc+ extra-currencies ! } : extra-cc+!
begin-options
" <filename-base> <dest-addr> <seqno> <amount> [-n|-b] [-t<timeout>] [-B <body-boc>] [-C <comment>] [<savefile>]" +cr +tab
" <filename-base> <dest-addr> <seqno> <amount> [-x <extra-amount>*<extra-currency-id>] [-n|-b] [-t<timeout>] [-B <body-boc>] [-C <comment>] [<savefile>]" +cr +tab
+"Creates a request to advanced wallet created by new-wallet-v2.fif, with private key loaded from file <filename-base>.pk "
+"and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)"
disable-digit-options generic-help-setopt
@ -19,6 +21,8 @@ begin-options
"Clears bounce flag" option-help
"b" "--force-bounce" { true =: force-bounce } short-long-option
"Forces bounce flag" option-help
"x" "--extra" { $>xcc extra-cc+! } short-long-option-arg
"Indicates the amount of extra currencies to be transfered" option-help
"t" "--timeout" { parse-int =: timeout } short-long-option-arg
"Sets expiration timeout in seconds (" timeout (.) $+ +" by default)" option-help
"B" "--body" { =: body-boc-file } short-long-option-arg
@ -38,7 +42,7 @@ true constant bounce
$1 =: file-base
$2 bounce parse-load-address force-bounce or allow-bounce and =: bounce 2=: dest_addr
$3 parse-int =: seqno
$4 $>GR =: amount
$4 $>cc extra-cc+! extra-currencies @ 2=: amount
$5 "wallet-query" replace-if-null =: savefile
file-base +".addr" load-address
@ -49,13 +53,13 @@ file-base +".pk" load-keypair nip constant wallet_pk
def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond
constant body-cell
."Transferring " amount .GR ."to account "
."Transferring " amount .GR+cc ."to account "
dest_addr 2dup bounce 7 + .Addr ." = " .addr
."seqno=0x" seqno x. ."bounce=" bounce . cr
."Body of transfer message is " body-cell <s csr. cr
// create a message
<b b{01} s, bounce 1 i, b{000} s, dest_addr Addr, amount Gram, 0 9 64 32 + + 1+ u,
<b b{01} s, bounce 1 i, b{000} s, dest_addr Addr, amount Gram+cc, 0 9 64 32 + + u,
body-cell <s 2dup 1 s-fits-with? not rot over 1 i, -rot { drop body-cell ref, } { s, } cond
b>
<b seqno 32 u, now timeout + 32 u, send-mode 8 u, swap ref, b>

View file

@ -9,9 +9,11 @@ true =: allow-bounce
false =: force-bounce
3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
60 =: timeout // external message expires in 60 seconds
variable extra-currencies
{ extra-currencies @ cc+ extra-currencies ! } : extra-cc+!
begin-options
" <filename-base> <dest-addr> <subwallet-id> <seqno> <amount> [-n|-b] [-t<timeout>] [-B <body-boc>] [-C <comment>] [<savefile>]" +cr +tab
" <filename-base> <dest-addr> <subwallet-id> <seqno> <amount> [-x <extra-amount>*<extra-currency-id>] [-n|-b] [-t<timeout>] [-B <body-boc>] [-C <comment>] [<savefile>]" +cr +tab
+"Creates a request to advanced wallet created by new-wallet-v3.fif, with private key loaded from file <filename-base>.pk "
+"and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)"
disable-digit-options generic-help-setopt
@ -19,6 +21,8 @@ begin-options
"Clears bounce flag" option-help
"b" "--force-bounce" { true =: force-bounce } short-long-option
"Forces bounce flag" option-help
"x" "--extra" { $>xcc extra-cc+! } short-long-option-arg
"Indicates the amount of extra currencies to be transfered" option-help
"t" "--timeout" { parse-int =: timeout } short-long-option-arg
"Sets expiration timeout in seconds (" timeout (.) $+ +" by default)" option-help
"B" "--body" { =: body-boc-file } short-long-option-arg
@ -40,7 +44,7 @@ $1 =: file-base
$2 bounce parse-load-address force-bounce or allow-bounce and =: bounce 2=: dest_addr
$3 parse-int =: subwallet_id
$4 parse-int =: seqno
$5 $>GR =: amount
$5 $>cc extra-cc+! extra-currencies @ 2=: amount
$6 "wallet-query" replace-if-null =: savefile
file-base +".addr" load-address
@ -51,14 +55,14 @@ file-base +".pk" load-keypair nip constant wallet_pk
def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond
constant body-cell
."Transferring " amount .GR ."to account "
."Transferring " amount .GR+cc ."to account "
dest_addr 2dup bounce 7 + .Addr ." = " .addr
."subwallet_id=0x" subwallet_id x.
."seqno=0x" seqno x. ."bounce=" bounce . cr
."Body of transfer message is " body-cell <s csr. cr
// create a message
<b b{01} s, bounce 1 i, b{000} s, dest_addr Addr, amount Gram, 0 9 64 32 + + 1+ u,
<b b{01} s, bounce 1 i, b{000} s, dest_addr Addr, amount Gram+cc, 0 9 64 32 + + u,
body-cell <s 2dup 1 s-fits-with? not rot over 1 i, -rot { drop body-cell ref, } { s, } cond
b>
<b subwallet_id 32 u, now timeout + 32 u, seqno 32 u, send-mode 8 u, swap ref, b>

View file

@ -9,6 +9,7 @@ true =: allow-bounce
false =: force-bounce
3 =: send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
variable extra-currencies
{ extra-currencies @ cc+ extra-currencies ! } : extra-cc+!
begin-options
" <filename-base> <dest-addr> <seqno> <amount> [-x <extra-amount>*<extra-currency-id>] [-n|-b] [-B <body-boc>] [-C <comment>] [<savefile>]" +cr +tab
@ -19,7 +20,7 @@ begin-options
"Clears bounce flag" option-help
"b" "--force-bounce" { true =: force-bounce } short-long-option
"Forces bounce flag" option-help
"x" "--extra" { $>cc extra-currencies @ cc+ extra-currencies ! } short-long-option-arg
"x" "--extra" { $>xcc extra-cc+! } short-long-option-arg
"Indicates the amount of extra currencies to be transfered" option-help
"B" "--body" { =: body-boc-file } short-long-option-arg
"Sets the payload of the transfer message" option-help
@ -38,7 +39,7 @@ true =: bounce
$1 =: file-base
$2 bounce parse-load-address allow-bounce and force-bounce or =: bounce 2=: dest_addr
$3 parse-int =: seqno
$4 $>GR =: amount
$4 $>cc extra-cc+! extra-currencies @ 2=: amount
$5 "wallet-query" replace-if-null =: savefile
allow-bounce not force-bounce and abort"cannot have bounce flag both set and cleared"
// "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment
@ -51,13 +52,13 @@ file-base +".pk" load-keypair nip constant wallet_pk
def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond
constant body-cell
."Transferring " amount .GR_ extra-currencies @ .+cc ."to account "
."Transferring " amount .GR+cc ."to account "
dest_addr 2dup bounce 7 + .Addr ." = " .addr
."seqno=0x" seqno x. ."bounce=" bounce . cr
."Body of transfer message is " body-cell <s csr. cr
// create a message
<b b{01} s, bounce 1 i, b{000} s, dest_addr Addr, amount Gram, extra-currencies @ dict, 0 9 64 32 + + u,
<b b{01} s, bounce 1 i, b{000} s, dest_addr Addr, amount Gram+cc, 0 9 64 32 + + u,
body-cell <s 2dup 1 s-fits-with? not rot over 1 i, -rot { drop body-cell ref, } { s, } cond
b>
<b seqno 32 u, send-mode 8 u, swap ref, b>

View file

@ -0,0 +1,20 @@
#!/usr/bin/fift -s
"Asm.fif" include
2500 =: N
{ 5 * } : *K
N 100 / =: N/100
N 10 / =: N/10
{ { EXECUTE } 100 times } : 100EXECUTE
{ DUP { 2DUP } 7 times } : 15DUP
{ { 2DUP } 50 times } : 100DUP
{ { 15 -1 SETCONTARGS 15DUP } 10 times } : 10SET&DUP
<{
CONT:<{ }>
15DUP
N/10 INT REPEAT:<{ 10SET&DUP }>
N/100 *K INT REPEAT:<{ 100DUP }>
N/100 *K INT REPEAT:<{ 100EXECUTE }>
}>s =: Code
Code csr.
Code 1000000 gasrunvmcode

View file

@ -0,0 +1,32 @@
#!/usr/bin/create-state -s
{ dup tlb-type-lookup { nip } { "unknown TLB type " swap $+ abort } cond } : $>tlb
{ bl word $>tlb 1 'nop } ::_ tlb:
{ dup null? { drop true } {
<b true 1 i, swap ref, b> <s ExtraCurrencyCollection
tlb-validate-skip { empty? } { false } cond
} cond
} : cc-valid?
{ cc-valid? { ."(valid)" } { ."(invalid)" } cond } : .cc-valid
{ { dup .cc space .cc-valid } { ."<error>" } cond cr } : cshow
{ ."X = " over dup .cc space .cc-valid cr
."Y = " dup dup .cc space .cc-valid cr
."X + Y = " 2dup CC+? cshow
."X - Y = " 2dup CC-? cshow
."Y - X = " 2dup swap CC-? cshow
."X + X = " over dup CC+? cshow
."Y + Y = " dup dup CC+? cshow
."X - X = " over dup CC-? cshow
."Y - Y = " dup dup CC-? cshow
2drop ."********************" cr
} : one-test
CX{666666666666*$239+1000000000000*$-17} =: X
X CX{666666666666*$239+4444*$-17} one-test
X CX{666666666665*$239+4444*$-17} one-test
X CX{666666666667*$239+4444*$-17} one-test
X CX{666666666666*$239} one-test
X CX{666666666665*$239} one-test
X CX{666666666667*$239} one-test
X CX{1111*$1} one-test
X CX{0*$-17} one-test
X cc0 1 0 +newccpair one-test
X cc0 239 0 +newccpair one-test

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "vm/boc.h"
#include "vm/cellslice.h"
@ -694,6 +694,32 @@ TEST(TonDb, BenchCellBuilder3) {
td::bench(BenchCellBuilder3());
}
TEST(TonDb, BocFuzz) {
vm::std_boc_deserialize(td::base64_decode("te6ccgEBAQEAAgAoAAA=").move_as_ok()).ensure_error();
vm::std_boc_deserialize(td::base64_decode("te6ccgQBQQdQAAAAAAEAte6ccgQBB1BBAAAAAAEAAAAAAP/"
"wAACJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJicmJiYmJiYmJiYmJiQ0NDQ0NDQ0NDQ0NDQ0ND"
"Q0NiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiYmJiQAA//AAAO4=")
.move_as_ok());
vm::std_boc_deserialize(td::base64_decode("SEkh/w==").move_as_ok()).ensure_error();
vm::std_boc_deserialize(
td::base64_decode(
"te6ccqwBMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMAKCEAAAAgAQ==")
.move_as_ok())
.ensure_error();
}
void test_parse_prefix(td::Slice boc) {
for (size_t i = 0; i <= boc.size(); i++) {
auto prefix = boc.substr(0, i);
vm::BagOfCells::Info info;
auto res = info.parse_serialized_header(prefix);
if (res > 0) {
break;
}
CHECK(res != 0);
CHECK(-res > (int)i);
}
}
TEST(TonDb, Boc) {
td::Random::Xorshift128plus rnd{123};
for (int t = 0; t < 1000; t++) {
@ -704,6 +730,8 @@ TEST(TonDb, Boc) {
auto serialized = serialize_boc(std::move(cell), mode);
CHECK(serialized.size() != 0);
test_parse_prefix(serialized);
auto loaded_cell = deserialize_boc(serialized);
ASSERT_EQ(cell_hash, loaded_cell->get_hash());

View file

@ -22,6 +22,7 @@
#include "fift/utils.h"
#include "common/bigint.hpp"
#include "td/utils/base64.h"
#include "td/utils/tests.h"
#include "td/utils/ScopeGuard.h"
#include "td/utils/StringBuilder.h"
@ -53,7 +54,10 @@ std::string run_vm(td::Ref<vm::Cell> cell) {
vm::Stack stack;
try {
vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/, std::move(log) /*VmLog*/);
vm::GasLimits gas_limit(1000, 1000);
vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/, std::move(log) /*VmLog*/,
nullptr, &gas_limit);
} catch (...) {
LOG(FATAL) << "catch unhandled exception";
}
@ -77,6 +81,14 @@ void test_run_vm(td::Slice code_hex) {
test_run_vm(to_cell(buff, bits));
}
void test_run_vm_raw(td::Slice code64) {
auto code = td::base64_decode(code64).move_as_ok();
if (code.size() > 127) {
code.resize(127);
}
test_run_vm(vm::CellBuilder().store_bytes(code).finalize());
}
TEST(VM, simple) {
test_run_vm("ABCBABABABA");
}
@ -126,12 +138,12 @@ TEST(VM, unhandled_exception_1) {
TEST(VM, unhandled_exception_2) {
// infinite loop now
// test_run_vm("EBEDB4");
test_run_vm("EBEDB4");
}
TEST(VM, unhandled_exception_3) {
// infinite loop now
// test_run_vm("EBEDC0");
test_run_vm("EBEDC0");
}
TEST(VM, unhandled_exception_4) {
@ -142,6 +154,13 @@ TEST(VM, unhandled_exception_5) {
test_run_vm("738B04016D21F41476A721F49F");
}
TEST(VM, infinity_loop_1) {
test_run_vm_raw("f3r4AJGQ6rDraIQ=");
}
TEST(VM, infinity_loop_2) {
test_run_vm_raw("kpTt7ZLrig==");
}
TEST(VM, bigint) {
td::StringBuilder sb({}, true);

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include <iostream>
#include <iomanip>
@ -94,10 +94,8 @@ td::Result<Ref<DataCell>> CellSerializationInfo::create_data_cell(td::Slice cell
for (int k = 0; k < refs_cnt; k++) {
cb.store_ref(std::move(refs[k]));
}
auto res = cb.finalize_novm(special);
if (res.is_null()) {
return td::Status::Error("CellBuilder::finalize failed");
}
TRY_RESULT(res, cb.finalize_novm_nothrow(special));
CHECK(!res.is_null());
if (res->is_special() != special) {
return td::Status::Error("is_special mismatch");
}
@ -654,10 +652,10 @@ long long BagOfCells::Info::parse_serialized_header(const td::Slice& slice) {
ptr += 6;
sz -= 6;
if (sz < ref_byte_size) {
return -(int)roots_offset;
return -static_cast<int>(roots_offset);
}
cell_count = (int)read_ref(ptr);
if (cell_count < 0) {
if (cell_count <= 0) {
cell_count = -1;
return 0;
}
@ -671,7 +669,7 @@ long long BagOfCells::Info::parse_serialized_header(const td::Slice& slice) {
}
index_offset = roots_offset;
if (magic == boc_generic) {
index_offset += root_count * ref_byte_size;
index_offset += (long long)root_count * ref_byte_size;
has_roots = true;
} else {
if (root_count != 1) {
@ -690,12 +688,18 @@ long long BagOfCells::Info::parse_serialized_header(const td::Slice& slice) {
return 0;
}
if (sz < 3 * ref_byte_size + offset_byte_size) {
return -(int)roots_offset;
return -static_cast<int>(roots_offset);
}
data_size = read_offset(ptr + 3 * ref_byte_size);
if (data_size > ((unsigned long long)cell_count << 10)) {
return 0;
}
if (data_size > (1ull << 40)) {
return 0; // bag of cells with more than 1TiB data is unlikely
}
if (data_size < cell_count * (2ull + ref_byte_size) - ref_byte_size) {
return 0; // invalid header, too many cells for this amount of data bytes
}
valid = true;
total_size = data_offset + data_size + (has_crc32c ? 4 : 0);
return total_size;
@ -747,14 +751,13 @@ td::Result<td::Ref<vm::DataCell>> BagOfCells::deserialize_cell(int idx, td::Slic
return cell_info.create_data_cell(cell_slice, refs);
}
td::Result<long long> BagOfCells::deserialize(const td::Slice& data) {
td::Result<long long> BagOfCells::deserialize(const td::Slice& data, int max_roots) {
clear();
long long size_est = info.parse_serialized_header(data);
//LOG(INFO) << "estimated size " << size_est << ", true size " << data.size();
if (size_est == 0) {
return td::Status::Error(PSLICE() << "cannot deserialize bag-of-cells: invalid header, error " << size_est);
}
if (size_est < 0) {
//LOG(ERROR) << "cannot deserialize bag-of-cells: not enough bytes (" << data.size() << " present, " << -size_est
//<< " required)";
@ -767,6 +770,9 @@ td::Result<long long> BagOfCells::deserialize(const td::Slice& data) {
return -size_est;
}
//LOG(INFO) << "estimated size " << size_est << ", true size " << data.size();
if (info.root_count > max_roots) {
return td::Status::Error("Bag-of-cells has more root cells than expected");
}
if (info.has_crc32c) {
unsigned crc_computed = td::crc32c(td::Slice{data.ubegin(), data.uend() - 4});
unsigned crc_stored = td::as<unsigned>(data.uend() - 4);
@ -906,7 +912,7 @@ td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
return Ref<Cell>();
}
BagOfCells boc;
auto res = boc.deserialize(data);
auto res = boc.deserialize(data, 1);
if (res.is_error()) {
return res.move_as_error();
}
@ -923,12 +929,12 @@ td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
return std::move(root);
}
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data) {
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data, int max_roots) {
if (data.empty()) {
return std::vector<Ref<Cell>>{};
}
BagOfCells boc;
auto res = boc.deserialize(data);
auto res = boc.deserialize(data, max_roots);
if (res.is_error()) {
return res.move_as_error();
}

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <set>
@ -26,12 +26,6 @@
namespace vm {
using td::Ref;
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false);
td::Result<td::BufferSlice> std_boc_serialize(Ref<Cell> root, int mode = 0);
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data);
td::Result<td::BufferSlice> std_boc_serialize_multi(std::vector<Ref<Cell>> root, int mode = 0);
class NewCellStorageStat {
public:
NewCellStorageStat() {
@ -159,7 +153,7 @@ struct CellSerializationInfo {
class BagOfCells {
public:
enum { hash_bytes = vm::Cell::hash_bytes };
enum { hash_bytes = vm::Cell::hash_bytes, default_max_roots = 16384 };
enum Mode { WithIndex = 1, WithCRC32C = 2, WithTopHash = 4, WithIntHashes = 8, WithCacheBits = 16, max = 31 };
enum { max_cell_whs = 64 };
using Hash = Cell::Hash;
@ -259,9 +253,10 @@ class BagOfCells {
std::size_t serialize_to(unsigned char* buffer, std::size_t buff_size, int mode = 0);
std::string extract_string() const;
td::Result<long long> deserialize(const td::Slice& data);
td::Result<long long> deserialize(const unsigned char* buffer, std::size_t buff_size) {
return deserialize(td::Slice{buffer, buff_size});
td::Result<long long> deserialize(const td::Slice& data, int max_roots = default_max_roots);
td::Result<long long> deserialize(const unsigned char* buffer, std::size_t buff_size,
int max_roots = default_max_roots) {
return deserialize(td::Slice{buffer, buff_size}, max_roots);
}
int get_root_count() const {
return root_count;
@ -311,4 +306,11 @@ class BagOfCells {
std::vector<td::uint8>* cell_should_cache);
};
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false);
td::Result<td::BufferSlice> std_boc_serialize(Ref<Cell> root, int mode = 0);
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data,
int max_roots = BagOfCells::default_max_roots);
td::Result<td::BufferSlice> std_boc_serialize_multi(std::vector<Ref<Cell>> root, int mode = 0);
} // namespace vm

View file

@ -67,9 +67,14 @@ Ref<DataCell> CellBuilder::finalize_copy(bool special) const {
return cell;
}
Ref<DataCell> CellBuilder::finalize_novm(bool special) {
td::Result<Ref<DataCell>> CellBuilder::finalize_novm_nothrow(bool special) {
auto res = DataCell::create(data, size(), td::mutable_span(refs.data(), size_refs()), special);
bits = refs_cnt = 0;
return res;
}
Ref<DataCell> CellBuilder::finalize_novm(bool special) {
auto res = finalize_novm_nothrow(special);
if (res.is_error()) {
LOG(DEBUG) << res.error();
throw CellWriteError{};
@ -570,11 +575,11 @@ CellBuilder* CellBuilder::make_copy() const {
return c;
}
CellSlice CellBuilder::as_cellslice() const & {
CellSlice CellBuilder::as_cellslice() const& {
return CellSlice{finalize_copy()};
}
Ref<CellSlice> CellBuilder::as_cellslice_ref() const & {
Ref<CellSlice> CellBuilder::as_cellslice_ref() const& {
return Ref<CellSlice>{true, finalize_copy()};
}

View file

@ -177,12 +177,13 @@ class CellBuilder : public td::CntObject {
Ref<DataCell> finalize_copy(bool special = false) const;
Ref<DataCell> finalize(bool special = false);
Ref<DataCell> finalize_novm(bool special = false);
td::Result<Ref<DataCell>> finalize_novm_nothrow(bool special = false);
bool finalize_to(Ref<Cell>& res, bool special = false) {
return (res = finalize(special)).not_null();
}
CellSlice as_cellslice() const &;
CellSlice as_cellslice() const&;
CellSlice as_cellslice() &&;
Ref<CellSlice> as_cellslice_ref() const &;
Ref<CellSlice> as_cellslice_ref() const&;
Ref<CellSlice> as_cellslice_ref() &&;
static td::int64 get_total_cell_builders() {
return get_thread_safe_counter().sum();

View file

@ -492,6 +492,7 @@ int exec_setcontargs_common(VmState* st, int copy, int more) {
} else {
cdata->stack.write().move_from_stack(stack, copy);
}
st->consume_stack_gas(cdata->stack);
if (cdata->nargs >= 0) {
cdata->nargs -= copy;
}
@ -557,6 +558,7 @@ int exec_return_args_common(VmState* st, int count) {
cdata->stack.write().move_from_stack(alt_stk.write(), copy);
alt_stk.clear();
}
st->consume_stack_gas(cdata->stack);
if (cdata->nargs >= 0) {
cdata->nargs -= copy;
}
@ -587,6 +589,7 @@ int exec_bless_args_common(VmState* st, int copy, int more) {
stack.check_underflow(copy + 1);
auto cs = stack.pop_cellslice();
auto new_stk = stack.split_top(copy);
st->consume_stack_gas(new_stk);
stack.push_cont(Ref<OrdCont>{true, std::move(cs), st->get_cp(), std::move(new_stk), more});
return 0;
}

View file

@ -176,8 +176,10 @@ int VmState::call(Ref<Continuation> cont, int pass_args, int ret_args) {
if (skip > 0) {
get_stack().pop_many(skip);
}
consume_stack_gas(new_stk);
} else if (copy >= 0) {
new_stk = get_stack().split_top(copy, skip);
consume_stack_gas(new_stk);
} else {
new_stk = std::move(stack);
stack.clear();
@ -196,7 +198,13 @@ int VmState::call(Ref<Continuation> cont, int pass_args, int ret_args) {
throw VmError{Excno::stk_und, "stack underflow while calling a continuation: not enough arguments on stack"};
}
// create new stack from the top `pass_args` elements of the current stack
Ref<Stack> new_stk = (pass_args >= 0 ? get_stack().split_top(pass_args) : std::move(stack));
Ref<Stack> new_stk;
if (pass_args >= 0) {
new_stk = get_stack().split_top(pass_args);
consume_stack_gas(new_stk);
} else {
new_stk = std::move(stack);
}
// create return continuation using the remainder of the current stack
Ref<OrdCont> ret = Ref<OrdCont>{true, std::move(code), cp, std::move(stack), ret_args};
ret.unique_write().get_cdata()->save.set_c0(std::move(cr.c[0]));
@ -251,10 +259,12 @@ int VmState::jump(Ref<Continuation> cont, int pass_args) {
new_stk = cont_data->stack;
}
new_stk.write().move_from_stack(get_stack(), copy);
consume_stack_gas(new_stk);
set_stack(std::move(new_stk));
} else {
if (copy >= 0) {
if (copy >= 0 && copy < stack->depth()) {
get_stack().drop_bottom(stack->depth() - copy);
consume_stack_gas(copy);
}
}
return jump_to(std::move(cont));
@ -264,8 +274,10 @@ int VmState::jump(Ref<Continuation> cont, int pass_args) {
int depth = get_stack().depth();
if (pass_args > depth) {
throw VmError{Excno::stk_und, "stack underflow while jumping to a continuation: not enough arguments on stack"};
} else if (pass_args < depth) {
get_stack().drop_bottom(depth - pass_args);
consume_stack_gas(pass_args);
}
get_stack().drop_bottom(depth - pass_args);
}
return jump_to(std::move(cont));
}
@ -303,6 +315,7 @@ Ref<OrdCont> VmState::extract_cc(int save_cr, int stack_copy, int cc_args) {
} else if (stack_copy > 0) {
stack->check_underflow(stack_copy);
new_stk = get_stack().split_top(stack_copy);
consume_stack_gas(new_stk);
} else {
new_stk = Ref<Stack>{true};
}
@ -332,7 +345,7 @@ int VmState::throw_exception(int excno) {
stack_ref.push_smallint(0);
stack_ref.push_smallint(excno);
code.clear();
consume_gas(exception_gas_price);
gas.consume_chk(exception_gas_price);
return jump(get_c2());
}
@ -342,7 +355,7 @@ int VmState::throw_exception(int excno, StackEntry&& arg) {
stack_ref.push(std::move(arg));
stack_ref.push_smallint(excno);
code.clear();
consume_gas(exception_gas_price);
gas.consume_chk(exception_gas_price);
return jump(get_c2());
}
@ -403,7 +416,6 @@ int VmState::run() {
int res;
Guard guard(this);
do {
// LOG(INFO) << "[BS] data cells: " << DataCell::get_total_data_cells();
try {
try {
try {
@ -419,12 +431,10 @@ int VmState::run() {
} catch (const VmError& vme) {
VM_LOG(this) << "handling exception code " << vme.get_errno() << ": " << vme.get_msg();
try {
// LOG(INFO) << "[EX] data cells: " << DataCell::get_total_data_cells();
++steps;
res = throw_exception(vme.get_errno());
} catch (const VmError& vme2) {
VM_LOG(this) << "exception " << vme2.get_errno() << " while handling exception: " << vme.get_msg();
// LOG(INFO) << "[EXX] data cells: " << DataCell::get_total_data_cells();
return ~vme2.get_errno();
}
}
@ -437,7 +447,6 @@ int VmState::run() {
return vmoog.get_errno(); // no ~ for unhandled exceptions (to make their faking impossible)
}
} while (!res);
// LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells();
if ((res | 1) == -1 && !try_commit()) {
VM_LOG(this) << "automatic commit failed (new data or action cells too deep)";
get_stack().clear();

View file

@ -106,6 +106,8 @@ class VmState final : public VmStateInterface {
tuple_entry_gas_price = 1,
implicit_jmpref_gas_price = 10,
implicit_ret_gas_price = 5,
free_stack_depth = 32,
stack_entry_gas_price = 1,
max_data_depth = 512
};
VmState();
@ -141,11 +143,19 @@ class VmState final : public VmStateInterface {
void consume_tuple_gas(unsigned tuple_len) {
consume_gas(tuple_len * tuple_entry_gas_price);
}
void consume_tuple_gas(const Ref<vm::Tuple>& tup) {
void consume_tuple_gas(const Ref<Tuple>& tup) {
if (tup.not_null()) {
consume_tuple_gas((unsigned)tup->size());
}
}
void consume_stack_gas(unsigned stack_depth) {
consume_gas((std::max(stack_depth, (unsigned)free_stack_depth) - free_stack_depth) * stack_entry_gas_price);
}
void consume_stack_gas(const Ref<Stack>& stk) {
if (stk.not_null()) {
consume_stack_gas((unsigned)stk->depth());
}
}
GasLimits get_gas_limits() const {
return gas;
}