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

emergency update

This commit is contained in:
ton 2020-03-24 03:32:16 +04:00
parent 5d846e0aaf
commit 9f351fc29f
87 changed files with 2486 additions and 655 deletions

View file

@ -391,7 +391,7 @@ endif()
add_executable(create-state block/create-state.cpp)
target_include_directories(create-state PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block)
target_link_libraries(create-state PUBLIC ton_crypto fift-lib ton_block tonlib)
if (WINGETOPT_FOUND)
target_link_libraries_system(create-state wingetopt)
endif()

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 "crypto/Ed25519.h"
@ -310,6 +310,10 @@ Result<SecureString> Ed25519::compute_shared_secret(const PublicKey &public_key,
return std::move(result);
}
int Ed25519::version() {
return OPENSSL_VERSION_NUMBER;
}
#else
Result<Ed25519::PrivateKey> Ed25519::generate_private_key() {
@ -387,6 +391,10 @@ Result<SecureString> Ed25519::compute_shared_secret(const PublicKey &public_key,
return std::move(shared_secret);
}
int Ed25519::version() {
return 0;
}
#endif
} // namespace td

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
@ -65,6 +65,8 @@ class Ed25519 {
static Result<PrivateKey> generate_private_key();
static Result<SecureString> compute_shared_secret(const PublicKey &public_key, const PrivateKey &private_key);
static int version();
};
} // namespace td

View file

@ -1633,15 +1633,19 @@ bool valid_config_data(Ref<vm::Cell> cell, const td::BitArray<256>& addr, bool c
bool config_params_present(vm::Dictionary& dict, Ref<vm::Cell> param_dict_root) {
auto res = block::Config::unpack_param_dict(std::move(param_dict_root));
if (res.is_error()) {
LOG(ERROR)
<< "invalid mandatory parameters dictionary while checking existence of all mandatory configuration parameters";
return false;
}
for (int x : res.move_as_ok()) {
// LOG(DEBUG) << "checking whether mandatory configuration parameter #" << x << " exists";
if (!dict.int_key_exists(x)) {
LOG(ERROR) << "configuration parameter #" << x
<< " (declared as mandatory in configuration parameter #9) is missing";
return false;
}
}
// LOG(DEBUG) << "all mandatory configuration parameters present";
return true;
}

View file

@ -414,7 +414,7 @@ block_info#9bc7a987 version:uint32
after_split:(## 1)
want_split:Bool want_merge:Bool
key_block:Bool vert_seqno_incr:(## 1)
flags:(## 8)
flags:(## 8) { flags <= 1 }
seq_no:# vert_seq_no:# { vert_seq_no >= vert_seqno_incr }
{ prev_seq_no:# } { ~prev_seq_no + 1 = seq_no }
shard:ShardIdent gen_utime:uint32
@ -423,6 +423,7 @@ block_info#9bc7a987 version:uint32
gen_catchain_seqno:uint32
min_ref_mc_seqno:uint32
prev_key_block_seqno:uint32
gen_software:flags . 0?GlobalVersion
master_ref:not_master?^BlkMasterInfo
prev_ref:^(BlkPrevInfo after_merge)
prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0)

View file

@ -55,6 +55,8 @@
#include "td/utils/port/path.h"
#include "td/utils/port/signals.h"
#include "tonlib/keys/Mnemonic.h"
#include "block.h"
#include "block-parse.h"
#include "block-auto.h"
@ -634,7 +636,25 @@ void interpret_sub_extra_currencies(vm::Stack& stack) {
stack.push_bool(ok);
}
void interpret_mnemonic_to_privkey(vm::Stack& stack, int mode) {
td::SecureString str{td::Slice{stack.pop_string()}};
auto res = tonlib::Mnemonic::create(std::move(str), td::SecureString());
if (res.is_error()) {
throw fift::IntError{res.move_as_error().to_string()};
}
auto privkey = res.move_as_ok().to_private_key();
td::SecureString key;
if (mode & 1) {
auto pub = privkey.get_public_key();
key = pub.move_as_ok().as_octet_string();
} else {
key = privkey.as_octet_string();
}
stack.push_bytes(key.as_slice());
}
void init_words_custom(fift::Dictionary& d) {
using namespace std::placeholders;
d.def_stack_word("verb@ ", interpret_get_verbosity);
d.def_stack_word("verb! ", interpret_set_verbosity);
d.def_stack_word("wcid@ ", interpret_get_workchain);
@ -651,6 +671,8 @@ void init_words_custom(fift::Dictionary& d) {
d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr);
d.def_stack_word("CC+? ", interpret_add_extra_currencies);
d.def_stack_word("CC-? ", interpret_sub_extra_currencies);
d.def_stack_word("mnemo>priv ", std::bind(interpret_mnemonic_to_privkey, _1, 0));
d.def_stack_word("mnemo>pub ", std::bind(interpret_mnemonic_to_privkey, _1, 1));
}
tlb::TypenameLookup tlb_dict;
@ -738,7 +760,8 @@ 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));
d.def_stack_word("ExtraCurrencyCollection",
std::bind(interpret_tlb_type_const, _1, &block::tlb::t_ExtraCurrencyCollection));
}
void usage(const char* progname) {

View file

@ -1561,10 +1561,10 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
return skip_invalid ? 0 : 38; // not enough (extra) funds
}
if (ap.remaining_balance.extra.not_null() || req.extra.not_null()) {
LOG(WARNING) << "subtracting extra currencies: "
<< block::CurrencyCollection{0, ap.remaining_balance.extra}.to_str() << " minus "
<< block::CurrencyCollection{0, req.extra}.to_str() << " equals "
<< block::CurrencyCollection{0, new_extra}.to_str();
LOG(DEBUG) << "subtracting extra currencies: "
<< block::CurrencyCollection{0, ap.remaining_balance.extra}.to_str() << " minus "
<< block::CurrencyCollection{0, req.extra}.to_str() << " equals "
<< block::CurrencyCollection{0, new_extra}.to_str();
}
auto fwd_fee_mine = msg_prices.get_first_part(fwd_fee);
@ -1730,11 +1730,17 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
}
bounce_phase = std::make_unique<BouncePhase>();
BouncePhase& bp = *bounce_phase;
block::gen::Message::Record msg;
block::gen::CommonMsgInfo::Record_int_msg_info info;
if (!tlb::unpack_cell_inexact(in_msg, info)) {
auto cs = vm::load_cell_slice(in_msg);
if (!(tlb::unpack(cs, info) && gen::t_Maybe_Either_StateInit_Ref_StateInit.skip(cs) && cs.have(1) &&
cs.have_refs((int)cs.prefetch_ulong(1)))) {
bounce_phase.reset();
return false;
}
if (cs.fetch_ulong(1)) {
cs = vm::load_cell_slice(cs.prefetch_ref());
}
info.ihr_disabled = true;
info.bounce = false;
info.bounced = true;
@ -1786,10 +1792,26 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
&& block::tlb::t_Grams.store_long(cb, bp.fwd_fees) // fwd_fee:Grams
&& cb.store_long_bool(info.created_lt, 64) // created_lt:uint64
&& cb.store_long_bool(info.created_at, 32) // created_at:uint32
&& cb.store_long_bool(0, 2) // init:(Maybe ...) state:(Either ..)
&& cb.finalize_to(bp.out_msg));
&& cb.store_bool_bool(false)); // init:(Maybe ...)
if (cfg.bounce_msg_body) {
int body_bits = std::min((int)cs.size(), cfg.bounce_msg_body);
if (cb.remaining_bits() >= body_bits + 33u) {
CHECK(cb.store_bool_bool(false) // body:(Either X ^X) -> left X
&& cb.store_long_bool(-1, 32) // int = -1 ("message type")
&& cb.append_bitslice(cs.prefetch_bits(body_bits))); // truncated message body
} else {
vm::CellBuilder cb2;
CHECK(cb.store_bool_bool(true) // body:(Either X ^X) -> right ^X
&& cb2.store_long_bool(-1, 32) // int = -1 ("message type")
&& cb2.append_bitslice(cs.prefetch_bits(body_bits)) // truncated message body
&& cb.store_builder_ref_bool(std::move(cb2))); // ^X
}
} else {
CHECK(cb.store_bool_bool(false)); // body:(Either ..)
}
CHECK(cb.finalize_to(bp.out_msg));
if (verbosity > 2) {
std::cerr << "generated bounced message: ";
LOG(INFO) << "generated bounced message: ";
block::gen::t_Message_Any.print_ref(std::cerr, bp.out_msg);
}
out_msgs.push_back(bp.out_msg);

View file

@ -139,6 +139,7 @@ struct ComputePhaseConfig {
struct ActionPhaseConfig {
int max_actions{255};
int bounce_msg_body{0}; // usually 0 or 256 bits
MsgPrices fwd_std;
MsgPrices fwd_mc; // from/to masterchain
const WorkchainSet* workchains{nullptr};

View file

@ -118,6 +118,24 @@ std::pair<RefInt256, RefInt256> divmod(RefInt256 x, RefInt256 y, int round_mode)
return std::make_pair(std::move(quot), std::move(x));
}
RefInt256 muldiv(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode) {
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
RefInt256 quot{true};
tmp.mod_div(*z, quot.unique_write(), round_mode);
quot.write().normalize();
return quot;
}
std::pair<RefInt256, RefInt256> muldivmod(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode) {
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
RefInt256 quot{true};
tmp.mod_div(*z, quot.unique_write(), round_mode);
quot.write().normalize();
return std::make_pair(std::move(quot), td::make_refint(tmp));
}
RefInt256 operator&(RefInt256 x, RefInt256 y) {
x.write() &= *y;
return x;

View file

@ -45,6 +45,8 @@ extern RefInt256 operator%(RefInt256 x, RefInt256 y);
extern RefInt256 div(RefInt256 x, RefInt256 y, int round_mode = -1);
extern RefInt256 mod(RefInt256 x, RefInt256 y, int round_mode = -1);
extern std::pair<RefInt256, RefInt256> divmod(RefInt256 x, RefInt256 y, int round_mode = -1);
extern RefInt256 muldiv(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode = -1);
extern std::pair<RefInt256, RefInt256> muldivmod(RefInt256 x, RefInt256 y, RefInt256 z, int round_mode = -1);
extern RefInt256 operator-(RefInt256 x);
extern RefInt256 operator&(RefInt256 x, RefInt256 y);
extern RefInt256 operator|(RefInt256 x, RefInt256 y);

View file

@ -1,7 +1,7 @@
library TVM_Asm
// simple TVM Assembler
variable @atend
'nop @atend !
{ "not in asm context" abort } @atend !
{ `normal eq? not abort"must be terminated by }>" } : @normal?
{ @atend @ 1 { @atend ! @normal? } does @atend ! } : @pushatend
{ @pushatend <b } : <{
@ -12,10 +12,11 @@ variable @atend
{ @atend @ 2 { @atend ! rot b> ref, swap @endblk } does @atend ! <b } : @|
{ @atend @ 3 { @atend ! 2swap rot execute } does @atend ! <b } : @doafter<{
{ over brembits <= } : @havebits
{ rot + -rot + swap } : pair+
{ rot >= -rot <= and } : 2x<=
{ 2 pick brembitrefs 1- 2x<= } : @havebitrefs
{ @havebits not ' @| if } : @ensurebits
{ @havebitrefs not ' @| if } : @ensurebitrefs
{ @havebits ' @| ifnot } : @ensurebits
{ @havebitrefs ' @| ifnot } : @ensurebitrefs
{ rot over @ensurebits -rot u, } : @simpleuop
{ tuck sbitrefs @ensurebitrefs swap s, } : @addop
{ tuck bbitrefs @ensurebitrefs swap b+ } : @addopb
@ -27,6 +28,7 @@ variable @atend
{ 1 { <b swap s, swap 4 u, @addopb } does create } : @Defop(4u)
{ 1 { <b swap s, rot 4 u, swap 4 u, @addopb } does create } : @Defop(4u,4u)
{ 1 { <b swap s, swap ref, @addopb } does create } : @Defop(ref)
{ 1 { <b swap s, rot ref, swap ref, @addopb } does create } : @Defop(ref*2)
{ <b 0xef 8 u, swap 12 i, b> } : si()
// x mi ma -- ?
{ rot tuck >= -rot <= and } : @range
@ -272,10 +274,27 @@ x{8A} @Defop(ref) PUSHREFCONT
} cond
} cond
} dup : PUSHSLICE : SLICE
{ tuck bbitrefs swap 16 + dup 7 and 3 -roll swap @havebitrefs
not rot or
{ swap b> PUSHREFCONT }
{ over bbitrefs 2dup 120 0 2x<=
// ( b' -- ? )
{ bbitrefs or 0= } : @cont-empty?
{ bbits 7 and 0= } : @cont-aligned?
// ( b b' -- ? )
{ bbitrefs over 7 and { 2drop drop false } {
swap 16 + swap @havebitrefs nip
} cond
} : @cont-fits?
// ( b b' -- ? )
{ bbitrefs over 7 and { 2drop drop false } {
32 1 pair+ @havebitrefs nip
} cond
} : @cont-ref-fit?
// ( b b' b'' -- ? )
{ over @cont-aligned? over @cont-aligned? and not { 2drop drop false } {
bbitrefs rot bbitrefs pair+ swap 32 + swap @havebitrefs nip
} cond
} : @two-cont-fit?
{ 2dup @cont-fits? not
{ b> PUSHREFCONT }
{ swap over bbitrefs 2dup 120 0 2x<=
{ drop swap x{9} s, swap 3 >> 4 u, swap b+ }
{ rot x{8F_} s, swap 2 u, swap 3 >> 7 u, swap b+ } cond
} cond
@ -320,12 +339,16 @@ x{A985} @Defop MULDIVR
x{A98C} @Defop MULDIVMOD
x{A9A4} @Defop MULRSHIFT
x{A9A5} @Defop MULRSHIFTR
x{A9A6} @Defop MULRSHIFTC
x{A9B4} @Defop(8u+1) MULRSHIFT#
x{A9B5} @Defop(8u+1) MULRSHIFTR#
x{A9B6} @Defop(8u+1) MULRSHIFTC#
x{A9C4} @Defop LSHIFTDIV
x{A9C5} @Defop LSHIFTDIVR
x{A9C6} @Defop LSHIFTDIVC
x{A9D4} @Defop(8u+1) LSHIFT#DIV
x{A9D5} @Defop(8u+1) LSHIFT#DIVR
x{A9D6} @Defop(8u+1) LSHIFT#DIVC
x{AA} @Defop(8u+1) LSHIFT#
x{AB} @Defop(8u+1) RSHIFT#
x{AC} @Defop LSHIFT
@ -628,46 +651,80 @@ x{DB3F} @Defop RETDATA
x{DC} @Defop IFRET
x{DD} @Defop IFNOTRET
x{DE} @Defop IF
{ }> PUSHCONT IF } : }>IF
x{DF} @Defop IFNOT
{ }> PUSHCONT IFNOT } : }>IFNOT
' IFNOTRET : IF:
' IFRET : IFNOT:
x{E0} @Defop IFJMP
{ }> PUSHCONT IFJMP } : }>IFJMP
{ { @normal? PUSHCONT IFJMP } @doafter<{ } : IFJMP:<{
x{E1} @Defop IFNOTJMP
{ }> PUSHCONT IFNOTJMP } : }>IFNOTJMP
{ { @normal? PUSHCONT IFNOTJMP } @doafter<{ } : IFNOTJMP:<{
x{E2} @Defop IFELSE
{ `else @endblk } : }>ELSE<{
{ `else: @endblk } : }>ELSE:
{ PUSHCONT { @normal? PUSHCONT IFELSE } @doafter<{ } : @doifelse
{ 1 { swap @normal? -rot PUSHCONT swap PUSHCONT IFELSE } does @doafter<{ } : @doifnotelse
{
{ dup `else eq?
{ drop @doifelse }
{ dup `else: eq?
{ drop PUSHCONT IFJMP }
{ @normal? PUSHCONT IF
} cond
} cond
} @doafter<{
} : IF:<{
{
{ dup `else eq?
{ drop @doifnotelse }
{ dup `else: eq?
{ drop PUSHCONT IFNOTJMP }
{ @normal? PUSHCONT IFNOT
} cond
} cond
} @doafter<{
} : IFNOT:<{
x{E300} @Defop(ref) IFREF
x{E301} @Defop(ref) IFNOTREF
x{E302} @Defop(ref) IFJMPREF
x{E303} @Defop(ref) IFNOTJMPREF
x{E30D} @Defop(ref) IFREFELSE
x{E30E} @Defop(ref) IFELSEREF
x{E30F} @Defop(ref*2) IFREFELSEREF
{ 16 1 @havebitrefs nip } : @refop-fits?
// b b1 [e0 e1 e2] -- b'
{ -rot dup @cont-empty? { drop swap 0 } {
2dup @cont-fits? { rot 1 } {
over @refop-fits? { b> rot 2 } {
swap @| swap 2dup @cont-fits? { rot 1 } {
b> rot 2
} cond } cond } cond } cond
[] execute
} : @run-cont-op
{ triple 1 ' @run-cont-op does create } : @def-cont-op
{ } { PUSHCONT IF } { IFREF } @def-cont-op IF-cont
{ IFRET } { PUSHCONT IFJMP } { IFJMPREF } @def-cont-op IFJMP-cont
{ } { PUSHCONT IFNOT } { IFNOTREF } @def-cont-op IFNOT-cont
{ IFNOTRET } { PUSHCONT IFNOTJMP } { IFNOTJMPREF } @def-cont-op IFNOTJMP-cont
{ dup 2over rot } : 3dup
recursive IFELSE-cont2 {
dup @cont-empty? { drop IF-cont } {
over @cont-empty? { nip IFNOT-cont } {
3dup @two-cont-fit? { -rot PUSHCONT swap PUSHCONT IFELSE } {
3dup nip @cont-ref-fit? { rot swap PUSHCONT swap b> IFREFELSE } {
3dup drop @cont-ref-fit? { -rot PUSHCONT swap b> IFELSEREF } {
rot 32 2 @havebitrefs { rot b> rot b> IFREFELSEREF } {
@| -rot IFELSE-cont2
} cond } cond } cond } cond } cond } cond
} swap !
{ }> IF-cont } : }>IF
{ }> IFNOT-cont } : }>IFNOT
{ }> IFJMP-cont } : }>IFJMP
{ }> IFNOTJMP-cont } : }>IFNOTJMP
{ { @normal? IFJMP-cont } @doafter<{ } : IFJMP:<{
{ { @normal? IFNOTJMP-cont } @doafter<{ } : IFNOTJMP:<{
{ `else @endblk } : }>ELSE<{
{ `else: @endblk } : }>ELSE:
{ 1 { swap @normal? swap IFELSE-cont2 } does @doafter<{ } : @doifelse
{ 1 { swap @normal? IFELSE-cont2 } does @doafter<{ } : @doifnotelse
{
{ dup `else eq?
{ drop @doifelse }
{ dup `else: eq?
{ drop IFJMP-cont }
{ @normal? IF-cont
} cond
} cond
} @doafter<{
} : IF:<{
{
{ dup `else eq?
{ drop @doifnotelse }
{ dup `else: eq?
{ drop IFNOTJMP-cont }
{ @normal? IFNOT-cont
} cond
} cond
} @doafter<{
} : IFNOT:<{
x{E304} @Defop CONDSEL
x{E305} @Defop CONDSELCHK
x{E308} @Defop IFRETALT
@ -676,18 +733,22 @@ x{E309} @Defop IFNOTRETALT
{ <b x{E3B_} swap 5 u, @addopb } : IFNBITJMP
{ <b x{E3D_} swap 5 u, swap ref, @addopb } : IFBITJMPREF
{ <b x{E3F_} swap 5 u, swap ref, @addopb } : IFNBITJMPREF
x{E4} @Defop REPEAT
{ }> PUSHCONT REPEAT } : }>REPEAT
{ { @normal? PUSHCONT REPEAT } @doafter<{ } : REPEAT:<{
x{E5} dup @Defop REPEATEND @Defop REPEAT:
x{E6} @Defop UNTIL
{ }> PUSHCONT UNTIL } : }>UNTIL
{ { @normal? PUSHCONT UNTIL } @doafter<{ } : UNTIL:<{
x{E7} dup @Defop UNTILEND @Defop UNTIL:
x{E8} @Defop WHILE
x{E9} @Defop WHILEEND
x{EA} @Defop AGAIN
x{EB} dup @Defop AGAINEND @Defop AGAIN:
{ `do @endblk } : }>DO<{
{ `do: @endblk } : }>DO:
{ }> PUSHCONT REPEAT } : }>REPEAT
{ { @normal? PUSHCONT REPEAT } @doafter<{ } : REPEAT:<{
{ }> PUSHCONT UNTIL } : }>UNTIL
{ { @normal? PUSHCONT UNTIL } @doafter<{ } : UNTIL:<{
{ PUSHCONT { @normal? PUSHCONT WHILE } @doafter<{ } : @dowhile
{
{ dup `do eq?
@ -696,10 +757,34 @@ x{E9} @Defop WHILEEND
} cond
} @doafter<{
} : WHILE:<{
x{EA} @Defop AGAIN
{ }> PUSHCONT AGAIN } : }>AGAIN
{ { @normal? PUSHCONT AGAIN } @doafter<{ } : AGAIN:<{
x{EB} dup @Defop AGAINEND @Defop AGAIN:
x{E314} @Defop REPEATBRK
x{E315} @Defop REPEATENDBRK
x{E316} @Defop UNTILBRK
x{E317} dup @Defop UNTILENDBRK @Defop UNTILBRK:
x{E318} @Defop WHILEBRK
x{E319} @Defop WHILEENDBRK
x{E31A} @Defop AGAINBRK
x{E31B} dup @Defop AGAINENDBRK @Defop AGAINBRK:
{ }> PUSHCONT REPEATBRK } : }>REPEATBRK
{ { @normal? PUSHCONT REPEATBRK } @doafter<{ } : REPEATBRK:<{
{ }> PUSHCONT UNTILBRK } : }>UNTILBRK
{ { @normal? PUSHCONT UNTILBRK } @doafter<{ } : UNTILBRK:<{
{ PUSHCONT { @normal? PUSHCONT WHILEBRK } @doafter<{ } : @dowhile
{
{ dup `do eq?
{ drop @dowhile }
{ `do: eq? not abort"`}>DO<{` expected" PUSHCONT WHILEENDBRK
} cond
} @doafter<{
} : WHILEBRK:<{
{ }> PUSHCONT AGAINBRK } : }>AGAINBRK
{ { @normal? PUSHCONT AGAINBRK } @doafter<{ } : AGAINBRK:<{
//
// continuation stack manipulation and continuation creation
//
@ -745,6 +830,8 @@ x{EDF6} @Defop THENRET
x{EDF7} @Defop THENRETALT
x{EDF8} @Defop INVERT
x{EDF9} @Defop BOOLEVAL
x{EDFA} @Defop SAMEALT
x{EDFB} @Defop SAMEALTSAVE
// x{EE} is BLESSARGS
//
// dictionary subroutine call/jump primitives

View file

@ -111,6 +111,42 @@ recursive list-map {
swap uncons -rot over execute -rot list-map cons
} cond
} swap !
variable ctxdump variable curctx
// (a1 .. an) e -- executes e for a1, ..., an
{ ctxdump @ curctx @ ctxdump 2! curctx 2!
{ curctx 2@ over null? not } { swap uncons rot tuck curctx 2! execute }
while 2drop ctxdump 2@ curctx ! ctxdump !
} : list-foreach
forget ctxdump forget curctx
//
// Experimental implementation of `for` loops with index
//
variable loopdump variable curloop
{ curloop @ loopdump @ loopdump 2! } : push-loop-ctx
{ loopdump 2@ loopdump ! curloop ! } : pop-loop-ctx
// ilast i0 e -- executes e for i=i0,i0+1,...,ilast-1
{ -rot 2dup > {
push-loop-ctx {
triple dup curloop ! first execute curloop @ untriple 1+ 2dup <=
} until pop-loop-ctx
} if 2drop drop
} : for
// ilast i0 e -- same as 'for', but pushes current index i before executing e
{ -rot 2dup > {
push-loop-ctx {
triple dup curloop ! untriple nip swap execute curloop @ untriple 1+ 2dup <=
} until pop-loop-ctx
} if 2drop drop
} : for-i
// ( -- i ) Returns innermost loop index
{ curloop @ third } : i
// ( -- j ) Returns outer loop index
{ loopdump @ car third } : j
{ loopdump @ cadr third } : k
forget curloop forget loopdump
//
// create Lisp-style lists using words "(" and ")"
//

View file

@ -177,28 +177,15 @@ void interpret_divmod(vm::Stack& stack, int round_mode) {
}
void interpret_times_div(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int();
auto y = stack.pop_int();
auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
stack.push_int(std::move(q));
auto z = stack.pop_int(), y = stack.pop_int(), x = stack.pop_int();
stack.push_int(muldiv(std::move(x), std::move(y), std::move(z), round_mode));
}
void interpret_times_divmod(vm::Stack& stack, int round_mode) {
auto z = stack.pop_int();
auto y = stack.pop_int();
auto x = stack.pop_int();
typename td::BigInt256::DoubleInt tmp{0};
tmp.add_mul(*x, *y);
auto q = td::make_refint();
tmp.mod_div(*z, q.unique_write(), round_mode);
q.unique_write().normalize();
stack.push_int(std::move(q));
stack.push_int(td::make_refint(tmp));
auto z = stack.pop_int(), y = stack.pop_int(), x = stack.pop_int();
auto dm = muldivmod(std::move(x), std::move(y), std::move(z));
stack.push_int(std::move(dm.first));
stack.push_int(std::move(dm.second));
}
void interpret_times_mod(vm::Stack& stack, int round_mode) {

View file

@ -427,9 +427,7 @@ AsmOp compile_negate(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
return exec_op("NEGATE", 1);
}
AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
AsmOp compile_mul_internal(VarDescr& r, VarDescr& x, VarDescr& y) {
if (x.is_int_const() && y.is_int_const()) {
r.set_const(x.int_const * y.int_const);
x.unused();
@ -492,6 +490,11 @@ AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
return exec_op("MUL", 2);
}
AsmOp compile_mul(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
return compile_mul_internal(res[0], args[0], args[1]);
}
AsmOp compile_lshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
@ -566,9 +569,7 @@ AsmOp compile_rshift(std::vector<VarDescr>& res, std::vector<VarDescr>& args, in
return exec_op(rshift, 2);
}
AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
AsmOp compile_div_internal(VarDescr& r, VarDescr& x, VarDescr& y, int round_mode) {
if (x.is_int_const() && y.is_int_const()) {
r.set_const(div(x.int_const, y.int_const, round_mode));
x.unused();
@ -608,6 +609,11 @@ AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int r
return exec_op(op, 2);
}
AsmOp compile_div(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
return compile_div_internal(res[0], args[0], args[1], round_mode);
}
AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 2);
VarDescr &r = res[0], &x = args[0], &y = args[1];
@ -648,6 +654,87 @@ AsmOp compile_mod(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int r
return exec_op(op, 2);
}
AsmOp compile_muldiv(std::vector<VarDescr>& res, std::vector<VarDescr>& args, int round_mode) {
assert(res.size() == 1 && args.size() == 3);
VarDescr &r = res[0], &x = args[0], &y = args[1], &z = args[2];
if (x.is_int_const() && y.is_int_const() && z.is_int_const()) {
r.set_const(muldiv(x.int_const, y.int_const, z.int_const, round_mode));
x.unused();
y.unused();
z.unused();
return push_const(r.int_const);
}
if (x.always_zero() || y.always_zero()) {
// dubious optimization for z=0...
x.unused();
y.unused();
z.unused();
r.set_const(td::make_refint(0));
return push_const(r.int_const);
}
char c = (round_mode < 0) ? 0 : (round_mode > 0 ? 'C' : 'R');
r.val = emulate_div(emulate_mul(x.val, y.val), z.val);
if (z.is_int_const()) {
if (*z.int_const == 0) {
x.unused();
y.unused();
z.unused();
r.set_const(div(z.int_const, z.int_const));
return push_const(r.int_const);
}
if (*z.int_const == 1) {
z.unused();
return compile_mul_internal(r, x, y);
}
}
if (y.is_int_const() && *y.int_const == 1) {
y.unused();
return compile_div_internal(r, x, z, round_mode);
}
if (x.is_int_const() && *x.int_const == 1) {
x.unused();
return compile_div_internal(r, y, z, round_mode);
}
if (z.is_int_const()) {
int k = is_pos_pow2(z.int_const);
if (k > 0) {
z.unused();
std::string op = "MULRSHIFT";
if (c) {
op += c;
}
return exec_arg_op(op + '#', k, 2);
}
}
if (y.is_int_const()) {
int k = is_pos_pow2(y.int_const);
if (k > 0) {
y.unused();
std::string op = "LSHIFT#DIV";
if (c) {
op += c;
}
return exec_arg_op(op, k, 2);
}
}
if (x.is_int_const()) {
int k = is_pos_pow2(x.int_const);
if (k > 0) {
x.unused();
std::string op = "LSHIFT#DIV";
if (c) {
op += c;
}
return exec_arg_op(op, k, 2);
}
}
std::string op = "MULDIV";
if (c) {
op += c;
}
return exec_op(op, 3);
}
int compute_compare(td::RefInt256 x, td::RefInt256 y, int mode) {
int s = td::cmp(x, y);
if (mode == 7) {
@ -933,8 +1020,9 @@ void define_builtins() {
define_builtin_func("^_&=_", arith_bin_op, AsmOp::Custom("AND", 2));
define_builtin_func("^_|=_", arith_bin_op, AsmOp::Custom("OR", 2));
define_builtin_func("^_^=_", arith_bin_op, AsmOp::Custom("XOR", 2));
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIVR", 3));
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), AsmOp::Custom("MULDIV", 3));
define_builtin_func("muldiv", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, -1));
define_builtin_func("muldivr", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 0));
define_builtin_func("muldivc", TypeExpr::new_map(Int3, Int), std::bind(compile_muldiv, _1, _2, 1));
define_builtin_func("muldivmod", TypeExpr::new_map(Int3, Int2), AsmOp::Custom("MULDIVMOD", 3, 2));
define_builtin_func("_==_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 2));
define_builtin_func("_!=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 5));

View file

@ -575,9 +575,9 @@ bool Optimizer::find_at_least(int pb) {
(is_push_rotrev(&i) && rewrite(AsmOp::Push(i), AsmOp::Custom("-ROT"))) ||
(is_push_xchg(&i, &j, &k) && rewrite(AsmOp::Push(i), AsmOp::Xchg(j, k))) ||
(is_reverse(&i, &j) && rewrite(AsmOp::BlkReverse(i, j))) ||
(is_blkdrop2(&i, &j) && rewrite(AsmOp::BlkDrop2(i, j))) ||
(is_nip_seq(&i, &j) && rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
(is_pop_blkdrop(&i, &k) && rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
(is_blkdrop2(&i, &j) && rewrite(AsmOp::BlkDrop2(i, j))) ||
(is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
? rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
: rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||

View file

@ -23,6 +23,25 @@
.end_cell());
}
;; [min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price]
_ parse_vote_config(cell c) inline {
var cs = c.begin_parse();
throw_unless(44, cs~load_uint(8) == 0x36);
var res = [cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(8), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32), cs~load_uint(32)];
cs.end_parse();
return res;
}
;; cfg_vote_setup#91 normal_params:^ConfigProposalSetup critical_params:^ConfigProposalSetup = ConfigVotingSetup;
_ get_vote_config(int critical?) inline_ref {
var cs = config_param(11).begin_parse();
throw_unless(44, cs~load_uint(8) == 0x91);
if (critical?) {
cs~load_ref();
}
return parse_vote_config(cs.preload_ref());
}
(int, int) check_validator_set(cell vset) {
var cs = vset.begin_parse();
throw_unless(9, cs~load_uint(8) == 0x12); ;; validators_ext#12 only
@ -46,53 +65,14 @@
.end_cell(), mode);
}
() send_confirmation(addr, query_id, ans_tag) impure {
() send_confirmation(addr, query_id, ans_tag) impure inline {
return send_answer(addr, query_id, ans_tag, 64);
}
() send_error(addr, query_id, ans_tag) impure {
() send_error(addr, query_id, ans_tag) impure inline {
return send_answer(addr, query_id, ans_tag, 64);
}
() recv_internal(cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
var s_addr = cs~load_msg_addr();
(int src_wc, int src_addr) = s_addr.parse_std_addr();
if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) {
;; source not in masterchain, or a bounced message, or a simple transfer
return ();
}
int tag = in_msg~load_uint(32);
int query_id = in_msg~load_uint(64);
if (tag == 0x4e565354) {
;; set next validator set
var vset = in_msg~load_ref();
in_msg.end_parse();
var elector_param = config_param(1);
var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256);
var ok = false;
if (src_addr == elector_addr) {
;; message from elector smart contract
;; set next validator set
(var t_since, var t_until) = check_validator_set(vset);
var t = now();
ok = (t_since > t) & (t_until > t_since);
}
if (ok) {
set_conf_param(36, vset);
;; send confirmation
return send_confirmation(s_addr, query_id, 0xee764f4b);
} else {
return send_error(s_addr, query_id, 0xee764f6f);
}
}
;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
;; to bounce message back to sender
throw_unless(37, (tag == 0) | (tag & (1 << 31)));
;; do nothing for other internal messages
}
;; forward a message to elector smart contract to make it upgrade its code
() change_elector_code(slice cs) impure {
var dest_addr = config_param(1).begin_parse().preload_uint(256);
@ -114,7 +94,7 @@
_ perform_action(cfg_dict, public_key, action, cs) {
if (action == 0x43665021) {
;; change one configuration parameter
var param_index = cs~load_uint(32);
var param_index = cs~load_int(32);
var param_value = cs~load_ref();
cs.end_parse();
cfg_dict~idict_set_ref(32, param_index, param_value);
@ -143,11 +123,8 @@ _ perform_action(cfg_dict, public_key, action, cs) {
}
}
(slice, int) get_validator_descr(int idx) inline_ref {
(cell, int, slice) get_current_vset() inline_ref {
var vset = config_param(34);
if (vset.null?()) {
return (null(), 0);
}
var cs = begin_parse(vset);
;; validators_ext#12 utime_since:uint32 utime_until:uint32
;; total:(## 16) main:(## 16) { main <= total } { main >= 1 }
@ -155,6 +132,11 @@ _ perform_action(cfg_dict, public_key, action, cs) {
throw_unless(40, cs~load_uint(8) == 0x12);
cs~skip_bits(32 + 32 + 16 + 16);
int total_weight = cs~load_uint(64);
return (vset, total_weight, cs);
}
(slice, int) get_validator_descr(int idx) inline_ref {
var (vset, total_weight, cs) = get_current_vset();
var dict = begin_cell().store_slice(cs).end_cell();
var (value, _) = dict.udict_get?(16, idx);
return (value, total_weight);
@ -169,6 +151,40 @@ _ perform_action(cfg_dict, public_key, action, cs) {
return (cs~load_uint(256), cs~load_uint(64));
}
;; cfg_proposal#f3 param_id:int32 param_value:(Maybe ^Cell) if_hash_equal:(Maybe uint256)
;; c -> (param-id param-cell maybe-hash)
(int, cell, int) parse_config_proposal(cell c) inline_ref {
var cs = c.begin_parse();
throw_unless(44, cs~load_int(8) == 0xf3 - 0x100);
var (id, val, hash) = (cs~load_int(32), cs~load_maybe_ref(), cs~load_int(1));
if (hash) {
hash = cs~load_uint(256);
} else {
hash = -1;
}
cs.end_parse();
return (id, val, hash);
}
;; cfg_proposal_status#ce expires:uint32 proposal:^ConfigProposal is_critical:Bool
;; voters:(HashmapE 16 True) remaining_weight:int64 validator_set_id:uint256
;; rounds_remaining:uint8 wins:uint8 losses:uint8 = ConfigProposalStatus;
(int, cell, int, cell, int, int, slice) unpack_proposal_status(slice cs) inline_ref {
throw_unless(44, cs~load_int(8) == 0xce - 0x100);
return (cs~load_uint(32), cs~load_ref(), cs~load_int(1), cs~load_dict(), cs~load_int(64), cs~load_uint(256), cs);
}
builder begin_pack_proposal_status(int expires, cell proposal, int critical?, cell voters, int weight_remaining, int vset_id) inline {
return begin_cell()
.store_int(0xce - 0x100, 8)
.store_uint(expires, 32)
.store_ref(proposal)
.store_int(critical?, 1)
.store_dict(voters)
.store_int(weight_remaining, 64)
.store_uint(vset_id, 256);
}
(cell, int, int, slice) new_proposal(cs) inline {
return (null(), 0, 0, cs);
}
@ -216,6 +232,141 @@ builder pack_proposal(cell voters, int sum_weight, int vset_id, slice body) inli
}
}
int register_voting_proposal(slice cs, int msg_value) inline_ref {
var (expire_at, proposal, critical?) = (cs~load_uint(32), cs~load_ref(), cs~load_int(1));
if (expire_at >> 30) {
expire_at -= now();
}
var (param_id, param_val, hash) = parse_config_proposal(proposal);
if (hash >= 0) {
cell cur_val = config_param(param_id);
int cur_hash = null?(cur_val) ? 0 : cell_hash(cur_val);
if (cur_hash != hash) {
hash = -0xe2646356; ;; bad current value
}
} else {
var m_params = config_param(9);
var (_, found?) = m_params.idict_get?(32, param_id);
if (found?) {
hash = -0xcd506e6c; ;; cannot set mandatory parameter to null
}
}
if (param_val.cell_depth() >= 256) {
hash = -0xc2616456; ;; bad value
}
if (hash < -1) {
return hash; ;; return error if any
}
ifnot (critical?) {
var crit_params = config_param(10);
var (_, found?) = crit_params.idict_get?(32, param_id);
if (found?) {
hash = -0xc3726954; ;; trying to set a critical parameter without critical flag
}
}
if (hash < -1) {
return hash;
}
;; obtain vote proposal configuration
var vote_cfg = get_vote_config(critical?);
var [min_tot_rounds, max_tot_rounds, min_wins, max_losses, min_store_sec, max_store_sec, bit_price, cell_price] = vote_cfg;
if (expire_at < min_store_sec) {
return -0xc5787069; ;; expired
}
expire_at = min(expire_at, max_store_sec);
;; compute price
var (_, bits, refs) = compute_data_size(param_val, 1024);
var pps = bit_price * (bits + 1024) + cell_price * (refs + 2);
var price = pps * expire_at;
expire_at += now();
var (cfg_dict, stored_seqno, public_key, vote_dict) = load_data();
int phash = proposal.cell_hash();
var (pstatus, found?) = vote_dict.udict_get?(256, phash);
if (found?) {
;; proposal already exists; we can only extend it
var (expires, r_proposal, r_critical?, voters, weight_remaining, vset_id, rest) = unpack_proposal_status(pstatus);
if (r_critical? != critical?) {
return -0xc3726955; ;; cannot upgrade critical parameter to non-critical...
}
if (expires >= expire_at) {
return -0xc16c7245; ;; proposal already exists
}
;; recompute price
price = pps * (expire_at - expires + 16384);
if (msg_value - price < (1 << 30)) {
return -0xf0617924; ;; need more money
}
;; update expiration time
vote_dict~udict_set_builder(256, phash, begin_pack_proposal_status(expire_at, r_proposal, r_critical?, voters, weight_remaining, vset_id).store_slice(rest));
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
return price;
}
if (msg_value - price < (1 << 30)) {
return -0xf0617924; ;; need more money
}
;; obtain current validator set data
var (vset, total_weight, _) = get_current_vset();
int weight_remaining = muldiv(total_weight, 2, 3) + 1;
;; create new proposal
vote_dict~udict_set_builder(256, phash,
begin_pack_proposal_status(expire_at, proposal, critical?, null(), weight_remaining, vset.cell_hash())
.store_uint(max_tot_rounds, 8).store_uint(0, 16));
store_data(cfg_dict, stored_seqno, public_key, vote_dict);
return price;
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
var cs = in_msg_cell.begin_parse();
var flags = cs~load_uint(4); ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
var s_addr = cs~load_msg_addr();
(int src_wc, int src_addr) = s_addr.parse_std_addr();
if ((src_wc + 1) | (flags & 1) | in_msg.slice_empty?()) {
;; source not in masterchain, or a bounced message, or a simple transfer
return ();
}
int tag = in_msg~load_uint(32);
int query_id = in_msg~load_uint(64);
if (tag == 0x4e565354) {
;; set next validator set
var vset = in_msg~load_ref();
in_msg.end_parse();
var elector_param = config_param(1);
var elector_addr = cell_null?(elector_param) ? -1 : elector_param.begin_parse().preload_uint(256);
var ok = false;
if (src_addr == elector_addr) {
;; message from elector smart contract
;; set next validator set
(var t_since, var t_until) = check_validator_set(vset);
var t = now();
ok = (t_since > t) & (t_until > t_since);
}
if (ok) {
set_conf_param(36, vset);
;; send confirmation
return send_confirmation(s_addr, query_id, 0xee764f4b);
} else {
return send_error(s_addr, query_id, 0xee764f6f);
}
}
if (tag == 0x6e565052) {
;; new voting proposal
var price = register_voting_proposal(cs, msg_value);
int mode = 64;
int ans_tag = - price;
if (price >= 0) {
;; ok, debit price
raw_reserve(price, 4);
ans_tag = 0xee565052;
mode = 128;
}
return send_answer(s_addr, query_id, ans_tag, mode);
}
;; if tag is non-zero and its higher bit is zero, throw an exception (the message is an unsupported query)
;; to bounce message back to sender
throw_unless(37, (tag == 0) | (tag & (1 << 31)));
;; do nothing for other internal messages
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;

View file

@ -201,8 +201,8 @@ smc1_addr config.minter_smc!
1000000000000 -17 of-cc 666666666666 239 of-cc cc+ config.to_mint!
( 9 10 18 20 21 22 23 24 25 28 34 ) config.mandatory_params!
( -1000 -1001 9 10 32 34 36 ) config.critical_params!
( 0 1 9 10 12 14 15 16 17 18 20 21 22 23 24 25 28 34 ) config.mandatory_params!
( -1000 -1001 0 1 9 10 12 14 15 16 17 32 34 36 ) config.critical_params!
// [ min_tot_rounds max_tot_rounds min_wins max_losses min_store_sec max_store_sec bit_price cell_price ]
// first for ordinary proposals, then for critical proposals

View file

@ -66,6 +66,13 @@ _ unpack_owner_info(slice cs) inline_ref {
return (root_i, 0, 0, in_msg);
}
(cell, ()) dec_flood(cell owner_infos, int creator_i) {
(slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
(int public_key, int flood) = unpack_owner_info(owner_info);
owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
return (owner_infos, ());
}
() try_init() impure inline_ref {
;; first query without signatures is always accepted
(int wallet_id, int n, int k, int last_cleaned, cell owner_infos, cell pending_queries) = unpack_state();
@ -82,10 +89,7 @@ _ unpack_owner_info(slice cs) inline_ref {
send_raw_message(msg~load_ref(), mode);
}
pending_queries~udict_set_builder(64, query_id, begin_cell().store_int(0, 1));
(slice owner_info, var found?) = owner_infos.udict_get?(8, creator_i);
(int public_key, int flood) = unpack_owner_info(owner_info);
owner_infos~udict_set_builder(8, creator_i, pack_owner_info(public_key, flood - 1));
owner_infos~dec_flood(creator_i);
} else {
pending_queries~udict_set_builder(64, query_id, begin_cell()
.store_uint(1, 1)
@ -123,8 +127,8 @@ _ unpack_owner_info(slice cs) inline_ref {
last_cleaned -= last_cleaned == 0;
(slice owner_info, var found?) = owner_infos.udict_get?(8, root_i);
(int public_key, int flood) = unpack_owner_info(owner_info);
throw_unless(31, found?);
(int public_key, int flood) = unpack_owner_info(owner_info);
throw_unless(32, check_signature(root_hash, root_signature, public_key));
cell signatures = in_msg~load_dict();
@ -154,11 +158,12 @@ _ unpack_owner_info(slice cs) inline_ref {
cnt_bits |= mask;
cnt += 1;
throw_if(41, ~ found? & (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
set_gas_limit(100000);
ifnot (found?) {
owner_infos~udict_set_builder(8, root_i, pack_owner_info(public_key, flood));
throw_if(41, (cnt < k) & (bound + ((60 * 60) << 32) > query_id));
}
(pending_queries, owner_infos) = update_pending_queries(pending_queries, owner_infos, msg, query_id, creator_i, cnt, cnt_bits, n, k);
@ -178,12 +183,15 @@ _ unpack_owner_info(slice cs) inline_ref {
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
int old_last_cleaned = last_cleaned;
do {
var (pending_queries', i, _, f) = pending_queries.udict_delete_get_min(64);
var (pending_queries', i, query, f) = pending_queries.udict_delete_get_min(64);
f~touch();
if (f) {
f = (i < bound);
}
if (f) {
if (query~load_int(1)) {
owner_infos~dec_flood(query~load_uint(8));
}
pending_queries = pending_queries';
last_cleaned = i;
need_save = -1;

View file

@ -64,6 +64,8 @@ cont bless(slice s) impure asm "BLESS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
(int, int) minmax(int x, int y) asm "MINMAX";
int abs(int x) asm "ABS";
slice begin_parse(cell c) asm "CTOS";
() end_parse(slice s) impure asm "ENDS";

View file

@ -44,7 +44,7 @@ td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
return stack_ref;
}
td::Ref<vm::Tuple> prepare_vm_c7() {
td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
// TODO: fix initialization of c7
td::BitArray<256> rand_seed;
rand_seed.as_slice().fill(0);
@ -54,7 +54,7 @@ td::Ref<vm::Tuple> prepare_vm_c7() {
td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::make_refint(0), // unixtime:Integer
td::make_refint(now), // unixtime:Integer
td::make_refint(0), // block_lt:Integer
td::make_refint(0), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
@ -166,8 +166,12 @@ td::Ref<vm::Cell> SmartContract::get_init_state() const {
}
SmartContract::Answer SmartContract::run_method(Args args) {
td::uint32 now = 0;
if (args.now) {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7();
args.c7 = prepare_vm_c7(now);
}
if (!args.limits) {
args.limits = vm::GasLimits{(long long)0, (long long)1000000, (long long)10000};
@ -182,8 +186,12 @@ SmartContract::Answer SmartContract::run_method(Args args) {
}
SmartContract::Answer SmartContract::run_get_method(Args args) const {
td::uint32 now = 0;
if (args.now) {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7();
args.c7 = prepare_vm_c7(now);
}
if (!args.limits) {
args.limits = vm::GasLimits{1000000};

View file

@ -55,6 +55,7 @@ class SmartContract : public td::CntObject {
td::optional<vm::GasLimits> limits;
td::optional<td::Ref<vm::Tuple>> c7;
td::optional<td::Ref<vm::Stack>> stack;
td::optional<td::int32> now;
bool ignore_chksig{false};
Args() {
@ -62,6 +63,10 @@ class SmartContract : public td::CntObject {
Args(std::initializer_list<vm::StackEntry> stack)
: stack(td::Ref<vm::Stack>(true, std::vector<vm::StackEntry>(std::move(stack)))) {
}
Args&& set_now(int now) {
this->now = now;
return std::move(*this);
}
Args&& set_method_id(td::Slice method_name) {
unsigned crc = td::crc16(method_name);
return set_method_id((crc & 0xffff) | 0x10000);

View file

@ -583,16 +583,58 @@ TEST(Smartcon, Multisig) {
wallet_id, td::transform(keys, [](auto& key) { return key.get_public_key().ok().as_octet_string(); }), k);
auto ms = ton::MultisigWallet::create(init_state);
td::uint64 query_id = 123 | ((100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
// first empty query (init)
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code == 0);
// first empty query
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize()).code > 0);
td::uint32 now = 0;
auto args = [&now]() -> ton::SmartContract::Args { return ton::SmartContract::Args().set_now(now); };
// first empty query (init)
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize(), args()).code == 0);
// first empty query
CHECK(ms.write().send_external_message(vm::CellBuilder().finalize(), args()).code > 0);
{
td::uint64 query_id = 123 | ((now + 10 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(0, keys[0]);
auto res = ms.write().send_external_message(query, args());
CHECK(!res.accepted);
CHECK(res.code == 41);
}
{
for (int i = 1; i <= 11; i++) {
td::uint64 query_id = i | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(5, keys[5]);
auto res = ms.write().send_external_message(query, args());
if (i <= 10) {
CHECK(res.accepted);
} else {
CHECK(!res.accepted);
}
}
now += 100 * 60 + 100;
{
td::uint64 query_id = 200 | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(6, keys[6]);
auto res = ms.write().send_external_message(query, args());
CHECK(res.accepted);
}
{
td::uint64 query_id = 300 | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
auto query = qb.create(5, keys[5]);
auto res = ms.write().send_external_message(query, args());
CHECK(res.accepted);
}
}
td::uint64 query_id = 123 | ((now + 100 * 60ull) << 32);
ton::MultisigWallet::QueryBuilder qb(wallet_id, query_id, vm::CellBuilder().finalize());
for (int i = 0; i < 10; i++) {
auto query = qb.create(i, keys[i]);
auto ans = ms.write().send_external_message(query);
auto ans = ms.write().send_external_message(query, args());
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
}
@ -602,12 +644,12 @@ TEST(Smartcon, Multisig) {
auto query = qb.create(49, keys[49]);
CHECK(ms->get_n_k() == std::make_pair(n, k));
auto ans = ms.write().send_external_message(query);
auto ans = ms.write().send_external_message(query, args());
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
CHECK(ans.success);
ASSERT_EQ(0, ms->processed(query_id));
CHECK(ms.write().send_external_message(query).code > 0);
CHECK(ms.write().send_external_message(query, args()).code > 0);
ASSERT_EQ(0, ms->processed(query_id));
{
@ -618,7 +660,7 @@ TEST(Smartcon, Multisig) {
query = qb.create(99, keys[99]);
}
ans = ms.write().send_external_message(query);
ans = ms.write().send_external_message(query, args());
LOG(INFO) << "CODE: " << ans.code;
LOG(INFO) << "GAS: " << ans.gas_used;
ASSERT_EQ(-1, ms->processed(query_id));

View file

@ -789,7 +789,7 @@ void register_shift_logic_ops(OpcodeTable& cp0) {
int exec_minmax(VmState* st, int mode) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute MINMAXOP " << mode;
VM_LOG(st) << "execute " << (mode & 1 ? "Q" : "") << (mode & 2 ? "MIN" : "") << (mode & 4 ? "MAX" : "");
stack.check_underflow(2);
auto x = stack.pop_int();
auto y = stack.pop_int();
@ -811,7 +811,7 @@ int exec_minmax(VmState* st, int mode) {
int exec_abs(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute ABS";
VM_LOG(st) << "execute " << (quiet ? "QABS" : "ABS");
stack.check_underflow(1);
auto x = stack.pop_int();
if (x->is_valid() && x->sgn() < 0) {

View file

@ -58,14 +58,27 @@ std::string dump_push_ref(CellSlice& cs, unsigned args, int pfx_bits, std::strin
return "";
}
cs.advance(pfx_bits);
cs.advance_refs(1);
return name;
auto cell = cs.fetch_ref();
return name + " (" + cell->get_hash().to_hex() + ")";
}
int compute_len_push_ref(const CellSlice& cs, unsigned args, int pfx_bits) {
return cs.have_refs(1) ? (0x10000 + pfx_bits) : 0;
}
std::string dump_push_ref2(CellSlice& cs, unsigned args, int pfx_bits, std::string name) {
if (!cs.have_refs(2)) {
return "";
}
cs.advance(pfx_bits);
auto cell1 = cs.fetch_ref(), cell2 = cs.fetch_ref();
return name + " (" + cell1->get_hash().to_hex() + ") (" + cell2->get_hash().to_hex() + ")";
}
int compute_len_push_ref2(const CellSlice& cs, unsigned args, int pfx_bits) {
return cs.have_refs(2) ? (0x20000 + pfx_bits) : 0;
}
int exec_push_slice_common(VmState* st, CellSlice& cs, unsigned data_bits, unsigned refs, int pfx_bits) {
if (!cs.have(pfx_bits + data_bits)) {
throw VmError{Excno::inv_opcode, "not enough data bits for a PUSHSLICE instruction"};

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "vm/cellslice.h"
@ -28,4 +28,7 @@ void register_cell_ops(OpcodeTable &cp0);
std::string dump_push_ref(CellSlice &cs, unsigned args, int pfx_bits, std::string name);
int compute_len_push_ref(const CellSlice &cs, unsigned args, int pfx_bits);
std::string dump_push_ref2(CellSlice &cs, unsigned args, int pfx_bits, std::string name);
int compute_len_push_ref2(const CellSlice &cs, unsigned args, int pfx_bits);
} // namespace vm

View file

@ -26,10 +26,12 @@
#include "vm/excno.hpp"
#include "vm/vm.h"
using namespace std::literals::string_literals;
namespace vm {
int exec_execute(VmState* st) {
VM_LOG(st) << "execute EXECUTE\n";
VM_LOG(st) << "execute EXECUTE";
auto cont = st->get_stack().pop_cont();
return st->call(std::move(cont));
}
@ -150,12 +152,58 @@ int exec_callcc_varargs(VmState* st) {
int exec_do_with_ref(VmState* st, CellSlice& cs, int pfx_bits, const std::function<int(VmState*, Ref<OrdCont>)>& func,
const char* name) {
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "no references left for a CALLREF instruction"};
throw VmError{Excno::inv_opcode, "no references left for a "s + name + " instruction"};
}
cs.advance(pfx_bits);
auto cell = cs.fetch_ref();
VM_LOG(st) << "execute " << name << " (" << cell->get_hash().to_hex() << ")";
return func(st, Ref<OrdCont>{true, load_cell_slice_ref(std::move(cell)), st->get_cp()});
return func(st, st->ref_to_cont(std::move(cell)));
}
int exec_do_with_cell(VmState* st, CellSlice& cs, int pfx_bits, const std::function<int(VmState*, Ref<Cell>)>& func,
const char* name) {
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "no references left for a "s + name + " instruction"};
}
cs.advance(pfx_bits);
auto cell = cs.fetch_ref();
VM_LOG(st) << "execute " << name << " (" << cell->get_hash().to_hex() << ")";
return func(st, std::move(cell));
}
int exec_ifelse_ref(VmState* st, CellSlice& cs, int pfx_bits, bool mode) {
const char* name = mode ? "IFREFELSE" : "IFELSEREF";
if (!cs.have_refs(1)) {
throw VmError{Excno::inv_opcode, "no references left for a "s + name + " instruction"};
}
cs.advance(pfx_bits);
auto cell = cs.fetch_ref();
Stack& stack = st->get_stack();
VM_LOG(st) << "execute " << name << " (" << cell->get_hash().to_hex() << ")";
stack.check_underflow(2);
auto cont = stack.pop_cont();
if (stack.pop_bool() == mode) {
cont = st->ref_to_cont(std::move(cell));
} else {
cell.clear();
}
return st->call(std::move(cont));
}
int exec_ifref_elseref(VmState* st, CellSlice& cs, unsigned args, int pfx_bits) {
if (!cs.have_refs(2)) {
throw VmError{Excno::inv_opcode, "no references left for a IFREFELSEREF instruction"};
}
cs.advance(pfx_bits);
auto cell1 = cs.fetch_ref(), cell2 = cs.fetch_ref();
Stack& stack = st->get_stack();
VM_LOG(st) << "execute IFREFELSEREF (" << cell1->get_hash().to_hex() << ") (" << cell2->get_hash().to_hex() << ")";
if (!stack.pop_bool()) {
cell1 = std::move(cell2);
} else {
cell2.clear();
}
return st->call(st->ref_to_cont(std::move(cell1)));
}
int exec_ret_data(VmState* st) {
@ -349,7 +397,7 @@ int exec_if_bit_jmpref(VmState* st, CellSlice& cs, unsigned args, int pfx_bits)
bool val = x->get_bit(bit);
stack.push_int(std::move(x));
if (val ^ negate) {
return st->jump(Ref<OrdCont>{true, load_cell_slice_ref(std::move(cell)), st->get_cp()});
return st->jump(st->ref_to_cont(std::move(cell)));
}
return 0;
}
@ -365,66 +413,72 @@ std::string dump_if_bit_jmpref(CellSlice& cs, unsigned args, int pfx_bits) {
return os.str();
}
int exec_repeat(VmState* st) {
int exec_repeat(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute REPEAT\n";
VM_LOG(st) << "execute REPEAT" << (brk ? "BRK" : "");
stack.check_underflow(2);
auto cont = stack.pop_cont();
int c = stack.pop_smallint_range(0x7fffffff, 0x80000000);
if (c <= 0) {
return 0;
}
return st->repeat(std::move(cont), st->extract_cc(1), c);
return st->repeat(std::move(cont), st->c1_envelope_if(brk, st->extract_cc(1)), c);
}
int exec_repeat_end(VmState* st) {
int exec_repeat_end(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute REPEATEND\n";
VM_LOG(st) << "execute REPEATEND" << (brk ? "BRK" : "");
stack.check_underflow(1);
int c = stack.pop_smallint_range(0x7fffffff, 0x80000000);
if (c <= 0) {
return st->ret();
}
auto cont = st->extract_cc(0);
return st->repeat(std::move(cont), st->get_c0(), c);
return st->repeat(std::move(cont), st->c1_envelope_if(brk, st->get_c0()), c);
}
int exec_until(VmState* st) {
int exec_until(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute UNTIL\n";
VM_LOG(st) << "execute UNTIL" << (brk ? "BRK" : "");
auto cont = stack.pop_cont();
return st->until(std::move(cont), st->extract_cc(1));
return st->until(std::move(cont), st->c1_envelope_if(brk, st->extract_cc(1)));
}
int exec_until_end(VmState* st) {
VM_LOG(st) << "execute UNTILEND\n";
int exec_until_end(VmState* st, bool brk) {
VM_LOG(st) << "execute UNTILEND" << (brk ? "BRK" : "");
auto cont = st->extract_cc(0);
return st->until(std::move(cont), st->get_c0());
return st->until(std::move(cont), st->c1_envelope_if(brk, st->get_c0()));
}
int exec_while(VmState* st) {
int exec_while(VmState* st, bool brk) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute WHILE\n";
VM_LOG(st) << "execute WHILE" << (brk ? "BRK" : "");
stack.check_underflow(2);
auto body = stack.pop_cont();
auto cond = stack.pop_cont();
return st->loop_while(std::move(cond), std::move(body), st->extract_cc(1));
return st->loop_while(std::move(cond), std::move(body), st->c1_envelope_if(brk, st->extract_cc(1)));
}
int exec_while_end(VmState* st) {
VM_LOG(st) << "execute WHILEEND\n";
int exec_while_end(VmState* st, bool brk) {
VM_LOG(st) << "execute WHILEEND" << (brk ? "BRK" : "");
auto cond = st->get_stack().pop_cont();
auto body = st->extract_cc(0);
return st->loop_while(std::move(cond), std::move(body), st->get_c0());
return st->loop_while(std::move(cond), std::move(body), st->c1_envelope_if(brk, st->get_c0()));
}
int exec_again(VmState* st) {
VM_LOG(st) << "execute AGAIN\n";
int exec_again(VmState* st, bool brk) {
VM_LOG(st) << "execute AGAIN" << (brk ? "BRK" : "");
if (brk) {
st->set_c1(st->extract_cc(3));
}
return st->again(st->get_stack().pop_cont());
}
int exec_again_end(VmState* st) {
VM_LOG(st) << "execute AGAINEND\n";
int exec_again_end(VmState* st, bool brk) {
VM_LOG(st) << "execute AGAINEND" << (brk ? "BRK" : "");
if (brk) {
st->c1_save_set();
}
return st->again(st->extract_cc(0));
}
@ -437,44 +491,70 @@ void register_continuation_cond_loop_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mksimple(0xe0, 8, "IFJMP", exec_if_jmp))
.insert(OpcodeInstr::mksimple(0xe1, 8, "IFNOTJMP", exec_ifnot_jmp))
.insert(OpcodeInstr::mksimple(0xe2, 8, "IFELSE", exec_if_else))
.insert(OpcodeInstr::mkext(
0xe300, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? st->call(std::move(cont)) : 0; },
"IFREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(
0xe301, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? 0 : st->call(std::move(cont)); },
"IFNOTREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(
0xe302, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFJMPREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? st->jump(std::move(cont)) : 0; },
"IFJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(
0xe303, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTJMPREF"),
std::bind(exec_do_with_ref, _1, _2, _4,
[](auto st, auto cont) { return st->get_stack().pop_bool() ? 0 : st->jump(std::move(cont)); },
"IFNOTJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe300, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? st->call(st->ref_to_cont(std::move(cell)))
: 0;
},
"IFREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe301, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? 0
: st->call(st->ref_to_cont(std::move(cell)));
},
"IFNOTREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe302, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFJMPREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? st->jump(st->ref_to_cont(std::move(cell)))
: 0;
},
"IFJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe303, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFNOTJMPREF"),
std::bind(exec_do_with_cell, _1, _2, _4,
[](auto st, auto cell) {
return st->get_stack().pop_bool()
? 0
: st->jump(st->ref_to_cont(std::move(cell)));
},
"IFNOTJMPREF"),
compute_len_push_ref))
.insert(OpcodeInstr::mksimple(0xe304, 16, "CONDSEL", exec_condsel))
.insert(OpcodeInstr::mksimple(0xe305, 16, "CONDSELCHK", exec_condsel_chk))
.insert(OpcodeInstr::mksimple(0xe308, 16, "IFRETALT", exec_ifretalt))
.insert(OpcodeInstr::mksimple(0xe309, 16, "IFNOTRETALT", exec_ifnotretalt))
.insert(OpcodeInstr::mkext(0xe30d, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFREFELSE"),
std::bind(exec_ifelse_ref, _1, _2, _4, true), compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe30e, 16, 0, std::bind(dump_push_ref, _1, _2, _3, "IFELSEREF"),
std::bind(exec_ifelse_ref, _1, _2, _4, false), compute_len_push_ref))
.insert(OpcodeInstr::mkext(0xe30f, 16, 0, std::bind(dump_push_ref2, _1, _2, _3, "IFREFELSEREF"),
exec_ifref_elseref, compute_len_push_ref2))
.insert(OpcodeInstr::mkfixed(0xe380 >> 6, 10, 6, dump_if_bit_jmp, exec_if_bit_jmp))
.insert(OpcodeInstr::mkext(0xe3c0 >> 6, 10, 6, dump_if_bit_jmpref, exec_if_bit_jmpref, compute_len_push_ref))
.insert(OpcodeInstr::mksimple(0xe4, 8, "REPEAT", exec_repeat))
.insert(OpcodeInstr::mksimple(0xe5, 8, "REPEATEND", exec_repeat_end))
.insert(OpcodeInstr::mksimple(0xe6, 8, "UNTIL", exec_until))
.insert(OpcodeInstr::mksimple(0xe7, 8, "UNTILEND", exec_until_end))
.insert(OpcodeInstr::mksimple(0xe8, 8, "WHILE", exec_while))
.insert(OpcodeInstr::mksimple(0xe9, 8, "WHILEEND", exec_while_end))
.insert(OpcodeInstr::mksimple(0xea, 8, "AGAIN", exec_again))
.insert(OpcodeInstr::mksimple(0xeb, 8, "AGAINEND", exec_again_end));
.insert(OpcodeInstr::mksimple(0xe4, 8, "REPEAT", std::bind(exec_repeat, _1, false)))
.insert(OpcodeInstr::mksimple(0xe5, 8, "REPEATEND", std::bind(exec_repeat_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xe6, 8, "UNTIL", std::bind(exec_until, _1, false)))
.insert(OpcodeInstr::mksimple(0xe7, 8, "UNTILEND", std::bind(exec_until_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xe8, 8, "WHILE", std::bind(exec_while, _1, false)))
.insert(OpcodeInstr::mksimple(0xe9, 8, "WHILEEND", std::bind(exec_while_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xea, 8, "AGAIN", std::bind(exec_again, _1, false)))
.insert(OpcodeInstr::mksimple(0xeb, 8, "AGAINEND", std::bind(exec_again_end, _1, false)))
.insert(OpcodeInstr::mksimple(0xe314, 16, "REPEATBRK", std::bind(exec_repeat, _1, true)))
.insert(OpcodeInstr::mksimple(0xe315, 16, "REPEATENDBRK", std::bind(exec_repeat_end, _1, true)))
.insert(OpcodeInstr::mksimple(0xe316, 16, "UNTILBRK", std::bind(exec_until, _1, true)))
.insert(OpcodeInstr::mksimple(0xe317, 16, "UNTILENDBRK", std::bind(exec_until_end, _1, true)))
.insert(OpcodeInstr::mksimple(0xe318, 16, "WHILEBRK", std::bind(exec_while, _1, true)))
.insert(OpcodeInstr::mksimple(0xe319, 16, "WHILEENDBRK", std::bind(exec_while_end, _1, true)))
.insert(OpcodeInstr::mksimple(0xe31a, 16, "AGAINBRK", std::bind(exec_again, _1, true)))
.insert(OpcodeInstr::mksimple(0xe31b, 16, "AGAINENDBRK", std::bind(exec_again_end, _1, true)));
}
int exec_setcontargs_common(VmState* st, int copy, int more) {
@ -706,6 +786,17 @@ int exec_save_ctr(VmState* st, unsigned args) {
return 0;
}
int exec_samealt(VmState* st, bool save) {
VM_LOG(st) << "execute SAMEALT" << (save ? "SAVE" : "");
auto c0 = st->get_c0();
if (save) {
force_cregs(c0)->define_c1(st->get_c1());
st->set_c0(c0);
}
st->set_c1(std::move(c0));
return 0;
}
int exec_savealt_ctr(VmState* st, unsigned args) {
unsigned idx = args & 15;
VM_LOG(st) << "execute SAVEALTCTR c" << idx;
@ -883,6 +974,8 @@ void register_continuation_change_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mksimple(0xedf7, 16, "THENRETALT", exec_thenret_alt))
.insert(OpcodeInstr::mksimple(0xedf8, 16, "INVERT", exec_invert))
.insert(OpcodeInstr::mksimple(0xedf9, 16, "BOOLEVAL", exec_booleval))
.insert(OpcodeInstr::mksimple(0xedfa, 16, "SAMEALT", std::bind(exec_samealt, _1, false)))
.insert(OpcodeInstr::mksimple(0xedfb, 16, "SAMEALTSAVE", std::bind(exec_samealt, _1, true)))
.insert(OpcodeInstr::mkfixed(0xee, 8, 8, std::bind(dump_setcontargs, _1, _2, "BLESSARGS"), exec_bless_args));
}

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
@ -45,6 +45,7 @@ const char* get_exception_msg(Excno exc_no);
class VmError {
Excno exc_no;
bool msg_alloc = false;
const char* msg;
long long arg;
@ -55,6 +56,18 @@ class VmError {
}
VmError(Excno _excno, const char* _msg, long long _arg) : exc_no(_excno), msg(_msg), arg(_arg) {
}
VmError(Excno _excno, std::string _msg, long long _arg = 0) : exc_no(_excno), msg_alloc(true), arg(_arg) {
msg_alloc = true;
char* p = (char*)malloc(_msg.size() + 1);
memcpy(p, _msg.data(), _msg.size());
p[_msg.size()] = 0;
msg = p;
}
~VmError() {
if (msg_alloc) {
free(const_cast<char*>(msg));
}
}
int get_errno() const {
return static_cast<int>(exc_no);
}

View file

@ -333,6 +333,22 @@ int VmState::ret_alt(int ret_args) {
return jump(std::move(cont), ret_args);
}
Ref<Continuation> VmState::c1_envelope(Ref<Continuation> cont, bool save) {
if (save) {
force_cregs(cont)->define_c1(cr.c[1]);
force_cregs(cont)->define_c0(cr.c[0]);
}
set_c1(cont);
return cont;
}
void VmState::c1_save_set(bool save) {
if (save) {
force_cregs(cr.c[0])->define_c1(cr.c[1]);
}
set_c1(cr.c[0]);
}
Ref<OrdCont> VmState::extract_cc(int save_cr, int stack_copy, int cc_args) {
Ref<Stack> new_stk;
if (stack_copy < 0 || stack_copy == stack->depth()) {

View file

@ -297,6 +297,11 @@ class VmState final : public VmStateInterface {
int throw_exception(int excno, StackEntry&& arg);
int throw_exception(int excno);
Ref<OrdCont> extract_cc(int save_cr = 1, int stack_copy = -1, int cc_args = -1);
Ref<Continuation> c1_envelope(Ref<Continuation> cont, bool save = true);
Ref<Continuation> c1_envelope_if(bool cond, Ref<Continuation> cont, bool save = true) {
return cond ? c1_envelope(std::move(cont), save) : std::move(cont);
}
void c1_save_set(bool save = true);
void fatal(void) const {
throw VmFatal{};
}
@ -313,6 +318,9 @@ class VmState final : public VmStateInterface {
bool get_chksig_always_succeed() const {
return chksig_always_succeed;
}
Ref<OrdCont> ref_to_cont(Ref<Cell> cell) const {
return td::make_ref<OrdCont>(load_cell_slice_ref(std::move(cell)), get_cp());
}
private:
void init_cregs(bool same_c3 = false, bool push_0 = true);