1
0
mirror of https://github.com/danog/ton.git synced 2024-11-30 04:29:19 +01:00

updated fift

updated fift
updated tonlib
This commit is contained in:
ton 2019-10-05 21:21:24 +04:00
parent 7c595294b6
commit 29deff15c3
14 changed files with 137 additions and 49 deletions

View File

@ -421,7 +421,7 @@ block_info#9bc7a987 version:uint32
prev_key_block_seqno:uint32
master_ref:not_master?^BlkMasterInfo
prev_ref:^(BlkPrevInfo after_merge)
prev_vert_ref:vert_seq_no?^(BlkPrevInfo 0)
prev_vert_ref:vert_seqno_incr?^(BlkPrevInfo 0)
= BlockInfo;
prev_blk_info$_ prev:ExtBlkRef = BlkPrevInfo 0;

View File

@ -739,7 +739,8 @@ td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const {
}
td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
return td::rshift(gas_price256 * gas_used, 16, 1);
return gas_used <= flat_gas_limit ? td::make_refint(flat_gas_price)
: td::rshift(gas_price256 * (gas_used - flat_gas_limit), 16, 1) + flat_gas_price;
}
bool Transaction::compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg) {

View File

@ -42,8 +42,8 @@
{ dup 10 < { 48 } { 55 } cond + } : Digit
{ dup 10 < { 48 } { 87 } cond + } : digit
// x s b -- x' s'
{ -rot swap rot /mod Digit rot swap hold } : B#
{ -rot swap rot /mod digit rot swap hold } : b#
{ rot swap /mod Digit rot swap hold } : B#
{ rot swap /mod digit rot swap hold } : b#
{ 16 B# } : X#
{ 16 b# } : x#
// x s b -- 0 s'
@ -110,3 +110,4 @@ variable base
{ null ' cons rot times } : list
{ true (atom) drop } : atom
{ bl word atom 1 'nop } ::_ `
{ hole dup 1 { @ execute } does create } : recursive

View File

@ -123,3 +123,16 @@ forget val, forget val@ forget .val
{ 0 lib+ } : private_lib
// ( D c -- D' ) Add public library c to collection D
{ 1 lib+ } : public_lib
// serialize simple transfers with long comments
// b B n -- b'
recursive append-long-bytes {
over Blen over <= { drop B, } {
B| <b swap 127 append-long-bytes b> -rot B, swap ref,
} cond
} swap !
// b S n -- b'
{ swap $>B swap append-long-bytes } : append-long-string
// S -- c
{ <b over $len { 0 32 u, swap 36 append-long-string } { nip } cond b>
} : simple-transfer-body

View File

@ -2149,7 +2149,23 @@ void interpret_run_vm(IntCtx& ctx, bool with_gas) {
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 3, &data, log, nullptr, &gas);
int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries());
ctx.stack.push_smallint(res);
ctx.stack.push_cell(std::move(data));
if (with_gas) {
ctx.stack.push_smallint(gas.gas_consumed());
}
}
void interpret_run_vm_c7(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
auto c7 = ctx.stack.pop_tuple();
auto data = ctx.stack.pop_cell();
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7));
ctx.stack.push_smallint(res);
ctx.stack.push_cell(std::move(data));
if (with_gas) {
@ -2278,6 +2294,21 @@ void interpret_get_cmdline_arg(IntCtx& ctx) {
}
}
void interpret_getenv(vm::Stack& stack) {
auto str = stack.pop_string();
auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr;
stack.push_string(value ? std::string{value} : "");
}
void interpret_getenv_exists(vm::Stack& stack) {
auto str = stack.pop_string();
auto value = str.size() < 1024 ? getenv(str.c_str()) : nullptr;
if (value) {
stack.push_string(std::string{value});
}
stack.push_bool((bool)value);
}
// x1 .. xn n 'w -->
void interpret_execute_internal(IntCtx& ctx) {
Ref<WordDef> word_def = pop_exec_token(ctx);
@ -2572,6 +2603,8 @@ void init_words_common(Dictionary& d) {
d.def_ctx_word("file-exists? ", interpret_file_exists);
// custom & crypto
d.def_ctx_word("now ", interpret_now);
d.def_stack_word("getenv ", interpret_getenv);
d.def_stack_word("getenv? ", interpret_getenv_exists);
d.def_stack_word("newkeypair ", interpret_new_keypair);
d.def_stack_word("priv>pub ", interpret_priv_key_to_pub);
d.def_stack_word("ed25519_sign ", interpret_ed25519_sign);
@ -2696,6 +2729,8 @@ void init_words_vm(Dictionary& d) {
d.def_ctx_word("gasrunvmdict ", std::bind(interpret_run_vm_dict, _1, true));
d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, false));
d.def_ctx_word("gasrunvm ", std::bind(interpret_run_vm, _1, true));
d.def_ctx_word("runvmctx ", std::bind(interpret_run_vm_c7, _1, false));
d.def_ctx_word("gasrunvmctx ", std::bind(interpret_run_vm_c7, _1, true));
d.def_ctx_word("dbrunvm ", interpret_db_run_vm);
d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
}

View File

@ -173,7 +173,7 @@ config.special!
// gas_price gas_limit special_gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit flat_gas_limit flat_gas_price --
1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 100 100000 config.gas_prices!
10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 0 0 config.mc_gas_prices!
10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 100 1000000 config.mc_gas_prices!
// lump_price bit_price cell_price ihr_factor first_frac next_frac
1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices!
10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices!

View File

@ -63,9 +63,14 @@ tuple parse_addr(slice s) asm "PARSEMSGADDR";
cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
(cell, ()) ~idict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETREF";
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETOPTREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";
(cell, int) udict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDEL";
(slice, int) idict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGET" "NULLSWAPIFNOT";
(slice, int) udict_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGET" "NULLSWAPIFNOT";
(cell, slice, int) idict_delete_get?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGET" "NULLSWAPIFNOT";
@ -80,10 +85,20 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va
(cell, ()) ~dict_set_builder(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETB";
(cell, int) udict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDB";
(cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB";
(cell, int) idict_add_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDB";
(cell, int) idict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEB";
(cell, int, slice, int) udict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";

View File

@ -1,12 +1,17 @@
#!/usr/bin/env fift -s
"TonUtil.fif" include
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-B <body-boc>] [<savefile>]" cr
{ ."usage: " @' $0 type ." <filename-base> <dest-addr> <seqno> <amount> [-B <body-boc>] [-C <transfer-comment>] [<savefile>]" cr
."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file <filename-base>.pk "
."and address from <filename-base>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr 1 halt
} : usage
def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
@' $# 2- =: $# } if } if
"" =: comment // comment for simple transfers
def? $6 { @' $5 dup "-B" $= swap "-C" $= tuck or
{ @' $6 swap { =: comment } { =: body-boc-file } cond [forget] $6
def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond
@' $# 2- =: $#
} if
} if
$# dup 4 < swap 5 > or ' usage if
true constant bounce
@ -17,13 +22,14 @@ $3 parse-int =: seqno
$4 $>GR =: amount
def? $5 { @' $5 } { "wallet-query" } cond constant savefile
3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors
// "" 1 { 69091 * 1+ 65535 and tuck 2521 / 65 + hold swap } 1000 times drop =: comment
file-base +".addr" load-address
2dup 2constant wallet_addr
."Source wallet address = " 2dup .addr cr 6 .Addr cr
file-base +".pk" load-keypair nip constant wallet_pk
def? body-boc-file { @' body-boc-file file>B B>boc } { <b 0 32 u, "TEST" $, b> } cond
def? body-boc-file { @' body-boc-file file>B B>boc } { comment simple-transfer-body } cond
constant body-cell
."Transferring " amount .GR ."to account "

View File

@ -17,6 +17,6 @@ x{917720ED50D8} runvmcode drop .s
20 x{9720C11401C20CB0D8} runvmcode drop .s
x{E3C03077F001A4} x{3020A8} |_ runvmdict drop .s
x{71B0E30277F001A4} x{20A8} |_ runvmdict .s
17 x{30ED44D0D71131A020C8CB3FC9ED54} <b x{0000000012345678} s, b> runvm .s
17 0 x{30ED44D0D71131A020C8CB3FC9ED54} <b x{0000000012345678} s, b> runvm .s
100 19 7 x{92A9069620C004F2F571F321A0} runvmcode .s
19 0 x{92A9069620C004F2F571F321A0} runvmcode .s

View File

@ -349,7 +349,7 @@ VmState::VmState(Ref<CellSlice> _code)
}
VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, int flags, Ref<Cell> _data, VmLog log,
std::vector<Ref<Cell>> _libraries)
std::vector<Ref<Cell>> _libraries, Ref<Tuple> init_c7)
: code(std::move(_code))
, stack(std::move(_stack))
, cp(-1)
@ -360,11 +360,14 @@ VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, int flags, Ref<Cell> _
, libraries(std::move(_libraries)) {
ensure_throw(init_cp(0));
set_c4(std::move(_data));
if (init_c7.not_null()) {
set_c7(std::move(init_c7));
}
init_cregs(flags & 1, flags & 2);
}
VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, const GasLimits& gas, int flags, Ref<Cell> _data, VmLog log,
std::vector<Ref<Cell>> _libraries)
std::vector<Ref<Cell>> _libraries, Ref<Tuple> init_c7)
: code(std::move(_code))
, stack(std::move(_stack))
, cp(-1)
@ -376,6 +379,9 @@ VmState::VmState(Ref<CellSlice> _code, Ref<Stack> _stack, const GasLimits& gas,
, libraries(std::move(_libraries)) {
ensure_throw(init_cp(0));
set_c4(std::move(_data));
if (init_c7.not_null()) {
set_c7(std::move(init_c7));
}
init_cregs(flags & 1, flags & 2);
}
@ -755,10 +761,15 @@ ControlRegs* force_cregs(Ref<Continuation>& cont) {
}
int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* data_ptr, VmLog log, long long* steps,
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries) {
VmState vm{
code, std::move(stack), gas_limits ? *gas_limits : GasLimits{}, flags, data_ptr ? *data_ptr : Ref<Cell>{},
log, std::move(libraries)};
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7) {
VmState vm{code,
std::move(stack),
gas_limits ? *gas_limits : GasLimits{},
flags,
data_ptr ? *data_ptr : Ref<Cell>{},
log,
std::move(libraries),
std::move(init_c7)};
int res = vm.run();
stack = vm.get_stack_ref();
if (res == -1 && data_ptr) {
@ -785,11 +796,11 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
}
int run_vm_code(Ref<CellSlice> code, Stack& stack, int flags, Ref<Cell>* data_ptr, VmLog log, long long* steps,
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries) {
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7) {
Ref<Stack> stk{true};
stk.unique_write().set_contents(std::move(stack));
stack.clear();
int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries));
int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7));
CHECK(stack.is_unique());
if (stk.is_null()) {
stack.clear();

View File

@ -394,9 +394,9 @@ class VmState final : public VmStateInterface {
VmState();
VmState(Ref<CellSlice> _code);
VmState(Ref<CellSlice> _code, Ref<Stack> _stack, int flags = 0, Ref<Cell> _data = {}, VmLog log = {},
std::vector<Ref<Cell>> _libraries = {});
std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {});
VmState(Ref<CellSlice> _code, Ref<Stack> _stack, const GasLimits& _gas, int flags = 0, Ref<Cell> _data = {},
VmLog log = {}, std::vector<Ref<Cell>> _libraries = {});
VmLog log = {}, std::vector<Ref<Cell>> _libraries = {}, Ref<Tuple> init_c7 = {});
template <typename... Args>
VmState(Ref<Cell> code_cell, Args&&... args)
: VmState(convert_code_cell(std::move(code_cell)), std::forward<Args>(args)...) {
@ -573,9 +573,11 @@ class VmState final : public VmStateInterface {
};
int run_vm_code(Ref<CellSlice> _code, Ref<Stack>& _stack, int flags = 0, Ref<Cell>* data_ptr = 0, VmLog log = {},
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {});
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {},
Ref<Tuple> init_c7 = {});
int run_vm_code(Ref<CellSlice> _code, Stack& _stack, int flags = 0, Ref<Cell>* data_ptr = 0, VmLog log = {},
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {});
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {},
Ref<Tuple> init_c7 = {});
ControlData* force_cdata(Ref<Continuation>& cont);
ControlRegs* force_cregs(Ref<Continuation>& cont);

View File

@ -1331,17 +1331,17 @@ rot . swap x. . cr
outputs ``{\tt -1 538fa7\dots 0f7d 0}'', meaning that the specified address is in workchain $-1$ (the masterchain of the TON Blockchain), and that the 256-bit address inside workchain $-1$ is 0x538\dots f7d.
\mysubsection{Dictionary manipulation}\label{p:hashmap.ops}
Fift has several words for {\em hashmap\/} or {\em (TVM) dictionary\/} manipulation, corresponding to values of TL-B type {\tt HashmapE $n$ $X$} as described in~\cite[3.3]{TVM}. These (TVM) dictionaries are not to be confused with the Fift dictionary, which is a completely different thing. A dictionary of TL-B type {\tt HashmapE $n$ $X$} is essentially a key-value collection with distinct $n$-bit keys (where $0\leq n\leq 1023$) and values of an arbitrary TL-B type $X$. Dictionaries are represented by trees of cells (the complete layout may be found in \cite[3.3]{TVM}) and stored as values of type {\em Cell\/} or {\em Slice\/} in the Fift stack.
Fift has several words for {\em hashmap\/} or {\em (TVM) dictionary\/} manipulation, corresponding to values of TL-B type {\tt HashmapE $n$ $X$} as described in~\cite[3.3]{TVM}. These (TVM) dictionaries are not to be confused with the Fift dictionary, which is a completely different thing. A dictionary of TL-B type {\tt HashmapE $n$ $X$} is essentially a key-value collection with distinct $n$-bit keys (where $0\leq n\leq 1023$) and values of an arbitrary TL-B type $X$. Dictionaries are represented by trees of cells (the complete layout may be found in \cite[3.3]{TVM}) and stored as values of type {\em Cell\/} or {\em Slice\/} in the Fift stack. Sometimes empty dictionaries are represented by the {\em Null\/} value.
\begin{itemize}
\item {\tt dictnew} ( -- $s$), pushes a {\em Slice\/} that represents a new empty dictionary.
\item {\tt idict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Slice}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success. Otherwise the unchanged dictionary $s$ and $0$ are returned.
\item {\tt idict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$.
\item {\tt dictnew} ( -- $D$), pushes a {\em Null\/} value that represents a new empty dictionary.
\item {\tt idict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $D$ (represented by a {\em Cell\/} or a {\em Null\/}) with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success. Otherwise the unchanged dictionary $D$ and $0$ are returned.
\item {\tt idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$.
\item {\tt b>idict!}, {\tt b>idict!+}, variants of {\tt idict!} and {\tt idict!+} accepting the new value $v$ in a {\em Builder\/} instead of a {\em Slice}.
\item {\tt udict!}, {\tt udict!+}, {\tt b>udict!}, {\tt b>udict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with an unsigned $n$-bit integer $x$ used as a key.
\item {\tt idict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), looks up the key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Slice\/}~$s$. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt udict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with an {\em un}signed big-endian $n$-bit {\em Integer\/}~$x$ used as a key.
\item {\tt dictmap} ($s$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $s$ with $n$-bit keys. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt dictmerge} ($s$ $s'$ $n$ $e$ -- $s''$), combines two dictionaries $s$ and $s'$ with $n$-bit keys into one dictionary $s''$ with the same keys. If a key is present in only one of the dictionaries $s$ and $s'$, this key and the corresponding value are copied verbatim to the new dictionary $s''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $s$ and $s'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt idict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/}~$D$. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt udict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with an {\em un}signed big-endian $n$-bit {\em Integer\/}~$x$ used as a key.
\item {\tt dictmap} ($D$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $D$ with $n$-bit keys. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt dictmerge} ($D$ $D'$ $n$ $e$ -- $D''$), combines two dictionaries $D$ and $D'$ with $n$-bit keys into one dictionary $D''$ with the same keys. If a key is present in only one of the dictionaries $D$ and $D'$, this key and the corresponding value are copied verbatim to the new dictionary $D''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $D$ and $D'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary.
\end{itemize}
Fift also offers some support for prefix dictionaries:
\begin{itemize}
@ -1355,10 +1355,12 @@ TVM can be linked with the Fift interpreter. In this case, several Fift primitiv
\begin{itemize}
\item {\tt runvmcode} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$, thus executing code~$s$ in TVM. The original Fift stack (without $s$) is passed in its entirety as the initial stack of TVM. When TVM terminates, its resulting stack is used as the new Fift stack, with the exit code $x$ pushed at its top. If $x$ is non-zero, indicating that TVM has been terminated by an unhandled exception, the next stack entry from the top contains the parameter of this exception, and $x$ is the exception code. All other entries are removed from the stack in this case.
\item {\tt runvmdict} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ similarly to {\tt runvmcode}, but also initializes the special register {\tt c3} with the same value, and pushes a zero into the initial TVM stack before the TVM execution begins. In a typical application {\em Slice\/}~$s$ consists of a subroutine selection code that uses the top-of-stack {\em Integer\/} to select the subroutine to be executed, thus enabling the definition and execution of several mutually-recursive subroutines (cf.~\cite[4.6]{TVM} and~\ptref{p:asm.prog}). The selector equal to zero corresponds to the {\tt main()} subroutine in a large TVM program.
\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and pushes a zero into the initial TVM stack similarly to {\tt runvmdict}, but also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage.
\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, similarly to {\tt runvmdict} (without pushing an extra zero to the initial TVM stack; if necessary, it can be pushed explicitly under $s$), and also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage.
\item {\tt runvmctx} (\dots $s$ $c$ $t$ -- \dots $x$ $c'$), a variant of {\tt runvm} that also initializes {\tt c7} (the ``context'') with {\em Tuple\/}~$t$. In this way the execution of a TVM smart contract inside TON Blockchain can be completely emulated, if the correct context is loaded into {\tt c7} (cf.~\cite[4.4.10]{TBC}).
\item {\tt gasrunvmcode} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmcode} that accepts an extra {\em Integer\/} argument $z$ (the original gas limit) at the top of the stack, and returns the gas consumed by this TVM run as a new top-of-stack {\em Integer\/} value~$z'$.
\item {\tt gasrunvmdict} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmdict}.
\item {\tt gasrunvm} (\dots $s$ $c$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvm}.
\item {\tt gasrunvmctx} (\dots $s$ $c$ $t$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvmctx}.
\end{itemize}
For example, one can create an instance of TVM running some simple code as follows:
\begin{verbatim}
@ -2039,9 +2041,9 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
\item {\tt csr.} ($s$ -- ), recursively prints a {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. On the first line, the data bits of $s$ are displayed in hexadecimal form embedded into an {\tt x\{\dots\}} construct similar to the one used for {\em Slice\/} literals (cf.~\ptref{p:slice.lit}). On the next lines, the cells referred to by $s$ are printed with larger indentation.
\item {\tt \underline{def?} $\langle\textit{word-name\/}\rangle$} ( -- $?$), checks whether the word $\langle\textit{word-name\/}\rangle$ is defined at execution time, and returns $-1$ or $0$ accordingly.
\item {\tt depth} ( -- $n$), returns the current depth (the total number of entries) of the Fift stack as an {\em Integer\/} $n\geq0$.
\item {\tt dictmap} ($s$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $s$ with $n$-bit keys, cf.~\ptref{p:hashmap.ops}. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt dictmerge} ($s$ $s'$ $n$ $e$ -- $s''$), combines two dictionaries $s$ and $s'$ with $n$-bit keys into one dictionary $s''$ with the same keys, cf.~\ptref{p:hashmap.ops}. If a key is present in only one of the dictionaries $s$ and $s'$, this key and the corresponding value are copied verbatim to the new dictionary $s''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $s$ and $s'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice\/}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt dictnew} ( -- $s$), pushes a {\em Slice\/} that represents a new empty dictionary, cf.~\ptref{p:hashmap.ops}. Equivalent to {\tt b\{0\}}.
\item {\tt dictmap} ($D$ $n$ $e$ -- $D'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $D$ with $n$-bit keys, cf.~\ptref{p:hashmap.ops}. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt dictmerge} ($D$ $D'$ $n$ $e$ -- $D''$), combines two dictionaries $D$ and $D'$ with $n$-bit keys into one dictionary $D''$ with the same keys, cf.~\ptref{p:hashmap.ops}. If a key is present in only one of the dictionaries $D$ and $D'$, this key and the corresponding value are copied verbatim to the new dictionary $D''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $D$ and $D'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice\/}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt dictnew} ( -- $D$), pushes the {\em Null\/} value that represents a new empty dictionary, cf.~\ptref{p:hashmap.ops}. Equivalent to {\tt null}.
\item {\tt does} ($x_1$ \dots $x_n$ $n$ $e$ -- $e'$), creates a new execution token $e'$ that would push $n$ values $x_1$, \dots, $x_n$ into the stack and then execute $e$ when invoked, cf.~\ptref{p:wordlist.ops}. It is roughly equivalent to a combination of {\tt (\{)}, {\tt (compile)}, and {\tt (\})}.
\item {\tt drop} ($x$ -- ), removes the top-of-stack entry, cf.~\ptref{p:stack.ops}.
\item {\tt dup} ($x$ -- $x$ $x$), duplicates the top-of-stack entry, cf.~\ptref{p:stack.ops}. If the stack is empty, throws an exception.
@ -2062,8 +2064,9 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
\item {\tt first} ($t$ -- $x$), returns the first component of a {\em Tuple}, cf.~\ptref{p:tuples}. Equivalent to {\tt 0 []}.
\item {\tt fits} ($x$ $y$ -- $?$), checks whether {\em Integer\/}~$x$ is a signed $y$-bit integer (i.e., whether $-2^{y-1}\leq x<2^{y-1}$ for $0\leq y\leq 1023$), and returns $-1$ or $0$ accordingly.
\item {\tt forget} ( -- ), forgets (removes from the dictionary) the definition of the next word scanned from the input, cf.~\ptref{p:dict.create}.
\item {\tt gasrunvm} (\dots $s$ $c$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvm}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and pushes a zero into the initial TVM stack similarly to {\tt runvmdict}, but also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. Then starts the new TVM instance with the gas limit set to $z$. The actually consumed gas $z'$ is returned at the top of the final Fift stack, and the final value of {\tt c4} is returned immediately below the top of the final Fift stack as another {\em Cell\/}~$c'$.
\item {\tt gasrunvm} (\dots $s$ $c$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvm}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$. Then starts the new TVM instance with the gas limit set to $z$. The actually consumed gas $z'$ is returned at the top of the final Fift stack, and the final value of {\tt c4} is returned immediately below the top of the final Fift stack as another {\em Cell\/}~$c'$.
\item {\tt gasrunvmcode} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmcode}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ and with the gas limit set to $z$, thus executing code~$s$ in TVM. The original Fift stack (without $s$) is passed in its entirety as the initial stack of the new TVM instance. When TVM terminates, its resulting stack is used as the new Fift stack, with the exit code $x$ and the actually consumed gas $z'$ pushed at its top. If $x$ is non-zero, indicating that TVM has been terminated by an unhandled exception, the next stack entry from the top contains the parameter of this exception, and $x$ is the exception code. All other entries are removed from the stack in this case.
\item {\tt gasrunvmctx} (\dots $s$ $c$ $t$ $z$ -- \dots $x$ $c'$ $z'$), a gas-aware version of {\tt runvmctx}, cf.~\ptref{p:tvm.ops}. Differs from {\tt gasrunmv} in that it initializes {\tt c7} with {\em Tuple\/}~$t$.
\item {\tt gasrunvmdict} (\dots $s$ $z$ -- \dots $x$ $z'$), a gas-aware version of {\tt runvmdict}, cf.~\ptref{p:tvm.ops}: invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ and sets the gas limit to $z$ similarly to {\tt gasrunvmcode}, but also initializes the special register {\tt c3} with the same value, and pushes a zero into the initial TVM stack before the TVM execution begins. The actually consumed gas is returned as an {\em Integer\/} $z'$. In a typical application {\em Slice\/}~$s$ consists of a subroutine selection code that uses the top-of-stack {\em Integer\/} to select the subroutine to be executed, thus enabling the definition and execution of several mutually-recursive subroutines (cf.~\cite[4.6]{TVM} and~\ptref{p:asm.prog}). The selector equal to zero corresponds to the {\tt main()} subroutine in a large TVM program.
\item {\tt halt} ($x$ -- ), quits to the operating system similarly to {\tt bye}, but uses {\em Integer\/} $x$ as the exit code, cf.~\ptref{p:exit.fift}.
\item {\tt hash} ($c$ -- $x$), a deprecated version of {\tt hashu}. Use {\tt hashu} or {\tt hashB} instead.
@ -2077,9 +2080,9 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
\item {\tt i@+} ($s$ $x$ -- $y$ $s'$), fetches a signed big-endian $x$-bit integer from the first $x$ bits of {\em Slice}~$s$ similarly to {\tt i@}, but returns the remainder of $s$ as well, cf.~\ptref{p:slice.ops}.
\item {\tt i@?} ($s$ $x$ -- $y$ $-1$ or $0$), fetches a signed big-endian integer from a {\em Slice\/} similarly to {\tt i@}, but pushes integer $-1$ afterwards on success, cf.~\ptref{p:slice.ops}. If there are less than $x$ bits left in $s$, pushes integer $0$ to indicate failure.
\item {\tt i@?+} ($s$ $x$ -- $y$ $s'$ $-1$ or $s$ $0$), fetches a signed big-endian integer from {\em Slice\/}~$s$ and computes the remainder of this {\em Slice\/} similarly to {\tt i@+}, but pushes $-1$ afterwards to indicate success, cf.~\ptref{p:slice.ops}. On failure, pushes the unchanged {\em Slice\/}~$s$ and $0$ to indicate failure.
\item {\tt idict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned.
\item {\tt idict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt idict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), looks up key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Slice\/}~$s$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt idict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
\item {\tt idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt idict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/} or {\em Null\/}~$D$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt if} ($x$ $e$ -- ), executes execution token (i.e., a {\em WordDef\/}) $e$, but only if {\em Integer\/} $x$ is non-zero, cf.~\ptref{p:cond.ops}.
\item {\tt ifnot} ($x$ $e$ -- ), executes execution token $e$, but only if {\em Integer\/} $x$ is zero, cf.~\ptref{p:cond.ops}.
\item {\tt include} ($S$ -- ), loads and interprets a Fift source file from the path given by {\em String\/}~$S$, cf.~\ptref{p:asm.load}. If the filename $S$ does not begin with a slash, the Fift include search path, typically taken from the {\tt FIFTPATH} environment variable or the {\tt -I} command-line argument of the Fift interpreter (and equal to {\tt /usr/lib/fift} if both are absent), is used to locate~$S$.
@ -2116,9 +2119,10 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
\item {\tt reverse} ($x_1$ \dots $x_n$ $y_1$ \dots $y_m$ $n$ $m$ -- $x_n$ \dots $x_1$ $y_1$ \dots $y_m$), reverses the order of $n$ stack entries located immediately below the topmost $m$ elements, where both $0\leq m,n\leq 255$ are passed in the stack.
\item {\tt roll} ($x_n$ \dots $x_0$ $n$ -- $x_{n-1}$ \dots $x_0$ $x_n$), rotates the top $n$ stack entries, where $n\geq0$ is also passed in the stack, cf.~\ptref{p:stack.ops}. In particular, {\tt 1 roll} is equivalent to {\tt swap}, and {\tt 2 roll} to {\tt rot}.
\item {\tt rot} ($x$ $y$ $z$ -- $y$ $z$ $x$), rotates the three topmost stack entries.
\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$, cf.~\ptref{p:tvm.ops}. In contrast with {\tt runvmdict}, does not push an implicit zero into the initial TVM stack; if necessary, it can be explicitly passed under~$s$. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage.
\item {\tt runvmcode} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$, thus executing code~$s$ in TVM, cf.~\ptref{p:tvm.ops}. The original Fift stack (without $s$) is passed in its entirety as the initial stack of the new TVM instance. When TVM terminates, its resulting stack is used as the new Fift stack, with the exit code $x$ pushed at its top. If $x$ is non-zero, indicating that TVM has been terminated by an unhandled exception, the next stack entry from the top contains the parameter of this exception, and $x$ is the exception code. All other entries are removed from the stack in this case.
\item {\tt runvmctx} (\dots $s$ $c$ $t$ -- \dots $x$ $c'$), a variant of {\tt runvm} that also initializes {\tt c7} (the ``context register'' of TVM) with {\em Tuple\/}~$t$, cf.~\ptref{p:tvm.ops}.
\item {\tt runvmdict} (\dots $s$ -- \dots $x$), invokes a new instance of TVM with the current continuation {\tt cc} initialized from {\em Slice\/} $s$ similarly to {\tt runvmcode}, but also initializes the special register {\tt c3} with the same value, and pushes a zero into the initial TVM stack before start, cf.~\ptref{p:tvm.ops}. In a typical application {\em Slice\/}~$s$ consists of a subroutine selection code that uses the top-of-stack {\em Integer\/} to select the subroutine to be executed, thus enabling the definition and execution of several mutually-recursive subroutines (cf.~\cite[4.6]{TVM} and~\ptref{p:asm.prog}). The selector equal to zero corresponds to the {\tt main()} subroutine in a large TVM program.
\item {\tt runvm} (\dots $s$ $c$ -- \dots $x$ $c'$), invokes a new instance of TVM with both the current continuation {\tt cc} and the special register {\tt c3} initialized from {\em Slice\/}~$s$, and pushes a zero into the initial TVM stack similarly to {\tt runvmdict}, but also initializes special register {\tt c4} (the ``root of persistent data'', cf.~\cite[1.4]{TVM}) with {\em Cell\/}~$c$, cf.~\ptref{p:tvm.ops}. The final value of {\tt c4} is returned at the top of the final Fift stack as another {\em Cell\/}~$c'$. In this way one can emulate the execution of smart contracts that inspect or modify their persistent storage.
\item {\tt s,} ($b$ $s$ -- $b'$), appends data bits and references taken from {\em Slice}~$s$ to {\em Builder}~$b$, cf.~\ptref{p:builder.ops}.
\item {\tt s>} ($s$ -- ), throws an exception if {\em Slice\/}~$s$ is non-empty, cf.~\ptref{p:slice.ops}. It usually marks the end of the deserialization of a cell, checking whether there are any unprocessed data bits or references left.
\item {\tt s>c} ($s$ -- $c$), creates a {\em Cell}~$c$ directly from a {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt <b swap s, b>}.
@ -2150,9 +2154,9 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
\item {\tt u@+} ($s$ $x$ -- $y$ $s'$), fetches an unsigned big-endian $x$-bit integer from the first $x$ bits of {\em Slice}~$s$ similarly to {\tt u@}, but returns the remainder of $s$ as well, cf.~\ptref{p:slice.ops}.
\item {\tt u@?} ($s$ $x$ -- $y$ $-1$ or $0$), fetches an unsigned big-endian integer from a {\em Slice\/} similarly to {\tt u@}, but pushes integer $-1$ afterwards on success, cf.~\ptref{p:slice.ops}. If there are less than $x$ bits left in $s$, pushes integer $0$ to indicate failure.
\item {\tt u@?+} ($s$ $x$ -- $y$ $s'$ $-1$ or $s$ $0$), fetches an unsigned big-endian integer from {\em Slice\/}~$s$ and computes the remainder of this {\em Slice\/} similarly to {\tt u@+}, but pushes $-1$ afterwards to indicate success, cf.~\ptref{p:slice.ops}. On failure, pushes the unchanged {\em Slice\/}~$s$ and $0$ to indicate failure.
\item {\tt udict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by big-endian unsigned $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned.
\item {\tt udict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt udict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt udict@} ($x$ $s$ $n$ -- $v$ $-1$ or $0$), looks up key represented by unsigned big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Slice\/}~$s$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt udict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by big-endian unsigned $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
\item {\tt udict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt udict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt udict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up key represented by unsigned big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/} or {\em Null\/}~$D$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt ufits} ($x$ $y$ -- $?$), checks whether {\em Integer\/}~$x$ is an unsigned $y$-bit integer (i.e., whether $0\leq x<2^y$ for $0\leq y\leq 1023$), and returns $-1$ or $0$ accordingly.
\item {\tt uncons} ($l$ -- $h$ $t$), decomposes a non-empty list into its head and its tail, cf.~\ptref{p:lists}. Equivalent to {\tt unpair}.
\item {\tt \underline{undef?} $\langle\textit{word-name\/}\rangle$} ( -- $?$), checks whether the word $\langle\textit{word-name\/}\rangle$ is undefined at execution time, and returns $-1$ or $0$ accordingly.

View File

@ -6,9 +6,9 @@ Test_Fift_bug_div_default 1ac42861ce96b2896001c587f65e9afe1617db48859f19c2f4e306
Test_Fift_bug_newlize_default e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
Test_Fift_contfrac_default 09ebce5c91bcb70696c6fb6981d82dc3b9e3444dab608a7a1b044c0ddd778a96
Test_Fift_test_default 4e44b3382963ec89f7b5c8f2ebd85da3bc8aebad5b49f5b11b14075061477b4d
Test_Fift_testvm2_default 4dcb0f98fd689cbdd81a3a1f937a497dd64d27ecebdcd67cf28fd6888469d93d
Test_Fift_testvm2_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b
Test_Fift_testvm3_default 3c1b77471c5fd914ed8b5f528b9faed618e278693f5030b953ff150e543864ae
Test_Fift_testvm4_default 4dcb0f98fd689cbdd81a3a1f937a497dd64d27ecebdcd67cf28fd6888469d93d
Test_Fift_testvm4_default 8a6e35fc0224398be9d2de39d31c86ea96965ef1eca2aa9e0af2303150ed4a7b
Test_Fift_testvm4a_default 523b561d6bf2f5ebb26a755e687bfbda8e33462c98e9978119755f79a086cf5e
Test_Fift_testvm4b_default daf8567bd58f05c10bb6596cea33b63e1061fa02dd5560db18ff22f96736f0d5
Test_Fift_testvm4c_default 2bbd67831d90bceaae29546ee3a58c4d376c2e8fb6a5b8ea2eae3ab8787e063e

View File

@ -162,10 +162,10 @@ TEST(Tonlib, TestWallet) {
fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet.fif")).ensure();
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
fift_output =
fift::mem_run_fift(std::move(fift_output.source_lookup),
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"})
.move_as_ok();
fift_output = fift::mem_run_fift(std::move(fift_output.source_lookup),
{"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123",
"321", "-C", "TEST"})
.move_as_ok();
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto gift_message = GenericAccount::create_ext_message(
address, {}, TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", dest));