mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
emergency update
This commit is contained in:
parent
5d846e0aaf
commit
9f351fc29f
87 changed files with 2486 additions and 655 deletions
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 ")"
|
||||
//
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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)))) ||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue