From 2e2ce00af45a22b729643e3ede77c6ee4c7202a0 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Sun, 14 Jan 2024 07:16:47 +0500 Subject: [PATCH 01/28] add string case transformer to td namespace --- tdutils/td/utils/StringCase.cpp | 69 +++++++++++++++++++++++++++++++++ tdutils/td/utils/StringCase.h | 15 +++++++ 2 files changed, 84 insertions(+) create mode 100644 tdutils/td/utils/StringCase.cpp create mode 100644 tdutils/td/utils/StringCase.h diff --git a/tdutils/td/utils/StringCase.cpp b/tdutils/td/utils/StringCase.cpp new file mode 100644 index 000000000..b4f4b9c4c --- /dev/null +++ b/tdutils/td/utils/StringCase.cpp @@ -0,0 +1,69 @@ +#include "StringCase.h" +#include + +namespace td { + +bool StringCase::is_camel_case(const std::string& input_str) { + if (input_str.empty() || !::islower(input_str[0])) + return false; + + for (char c : input_str) { + if (::isupper(c)) { + return true; + } + } + + return false; +} + +bool StringCase::is_snake_case(const std::string& input_str) { + if (input_str.empty() || !::islower(input_str[0])) + return false; + + for (char c : input_str) { + if (::isupper(c) || c == ' ') { + return false; + } + } + + return true; +} + + +std::string StringCase::camel_to_snake(const std::string& input_str) { + std::string result; + result.reserve(input_str.size() + std::count_if(input_str.begin(), input_str.end(), ::isupper)); + + if (!input_str.empty()) { + result += static_cast(::tolower(input_str[0])); + } + + for (size_t i = 1; i < input_str.size(); ++i) { + if (::isupper(input_str[i])) { + result += "_"; + } + result += static_cast(::tolower(input_str[i])); + } + + return result; +} + + +std::string StringCase::snake_to_camel(const std::string& input_str) { + std::string result; + result.reserve(input_str.size()); + bool capitalizeNext = false; + + for (char c : input_str) { + if (c == '_') { + capitalizeNext = true; + } else { + result += capitalizeNext ? static_cast(::toupper(c)) : c; + capitalizeNext = false; + } + } + + return result; +} + +} diff --git a/tdutils/td/utils/StringCase.h b/tdutils/td/utils/StringCase.h new file mode 100644 index 000000000..ccfe85bbe --- /dev/null +++ b/tdutils/td/utils/StringCase.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace td { + +class StringCase { + public: + static bool is_camel_case(const std::string& input_str); + static bool is_snake_case(const std::string& input_str); + static std::string camel_to_snake(const std::string& input_str); + static std::string snake_to_camel(const std::string& input_str); +}; + +} From 6259202b318cb4d9051d7212088358ae65afc0ac Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Sun, 14 Jan 2024 07:18:40 +0500 Subject: [PATCH 02/28] add camelCase aliases for keywords/builtins in FunC and Asm.fif --- crypto/fift/lib/Asm.fif | 3 +++ crypto/func/builtins.cpp | 20 ++++++++++++++++++++ crypto/func/keywords.cpp | 2 ++ 3 files changed, 25 insertions(+) diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 0a4c7074f..3045fd250 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1580,6 +1580,9 @@ forget @proclist forget @proccnt -3 constant split_prepare -4 constant split_install +recv_internal constant receiveInternalMessage +recv_external constant receiveExternalMessage + { asm-mode 0 3 ~! } : asm-no-remove-unused { asm-mode 1 1 ~! } : asm-remove-unused // enabled by default { asm-mode 3 3 ~! } : asm-warn-remove-unused diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 9de673fd7..bbe9671bd 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -1237,6 +1237,26 @@ void define_builtins() { define_builtin_func("cell_at", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); define_builtin_func("slice_at", TypeExpr::new_map(TupleInt, Slice), compile_tuple_at); define_builtin_func("tuple_at", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at); + define_builtin_func("isNull", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null); + define_builtin_func("throwIf", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); + define_builtin_func("throwUnless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true); + define_builtin_func("throwArg", throw_arg_op, compile_throw_arg, true); + define_builtin_func("throwArgIf", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, true), true); + define_builtin_func("throwArgUnless", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, false), true); + define_builtin_func("loadInt", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0}); + define_builtin_func("loadUint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0}); + define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true)); + define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false)); + define_builtin_func("storeInt", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); + define_builtin_func("storeUint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); + define_builtin_func("~storeInt", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); + define_builtin_func("~storeUint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); + define_builtin_func("loadBits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0}); + define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false)); + define_builtin_func("intAt", TypeExpr::new_map(TupleInt, Int), compile_tuple_at); + define_builtin_func("cellAt", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); + define_builtin_func("sliceAt", TypeExpr::new_map(TupleInt, Slice), compile_tuple_at); + define_builtin_func("tupleAt", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at); define_builtin_func("at", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at); define_builtin_func("touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, X)), AsmOp::Nop()); define_builtin_func("~touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))), diff --git a/crypto/func/keywords.cpp b/crypto/func/keywords.cpp index fedce9db2..3cb45ae09 100644 --- a/crypto/func/keywords.cpp +++ b/crypto/func/keywords.cpp @@ -124,6 +124,8 @@ void define_keywords() { .add_keyword("inline_ref", Kw::_InlineRef) .add_keyword("auto_apply", Kw::_AutoApply) .add_keyword("method_id", Kw::_MethodId) + .add_keyword("inlineRef", Kw::_InlineRef) + .add_keyword("methodId", Kw::_MethodId) .add_keyword("operator", Kw::_Operator) .add_keyword("infix", Kw::_Infix) .add_keyword("infixl", Kw::_Infixl) From 0617f2b25f0071ff96b150d020a161c074eed71f Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Sun, 14 Jan 2024 07:20:21 +0500 Subject: [PATCH 03/28] add method_name transformation to snake_case before calculating crc16 for method_id --- blockchain-explorer/blockchain-explorer-query.cpp | 4 +++- crypto/func/parse-func.cpp | 3 ++- lite-client/lite-client.cpp | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/blockchain-explorer/blockchain-explorer-query.cpp b/blockchain-explorer/blockchain-explorer-query.cpp index b53e79696..5686994db 100644 --- a/blockchain-explorer/blockchain-explorer-query.cpp +++ b/blockchain-explorer/blockchain-explorer-query.cpp @@ -44,6 +44,8 @@ #include "crypto/vm/utils.h" #include "td/utils/crypto.h" +#include "td/utils/StringCase.h" + #include "vm/boc.h" #include "vm/cellops.h" #include "vm/cells/MerkleProof.h" @@ -1418,7 +1420,7 @@ void HttpQueryRunMethod::finish_query() { auto code = state_init.code->prefetch_ref(); auto data = state_init.data->prefetch_ref(); auto stack = td::make_ref(std::move(params_)); - td::int64 method_id = (td::crc16(td::Slice{method_name_}) & 0xffff) | 0x10000; + td::int64 method_id = (td::crc16(td::Slice{td::StringCase::is_camel_case(method_name_) ? td::StringCase::camel_to_snake(method_name_) : method_name_}) & 0xffff) | 0x10000; stack.write().push_smallint(method_id); long long gas_limit = vm::GasLimits::infty; // OstreamLogger ostream_logger(ctx.error_stream); diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index 15794c425..b2bfabe8d 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -18,6 +18,7 @@ */ #include "func.h" #include "td/utils/crypto.h" +#include "td/utils/StringCase.h" #include "common/refint.h" #include "openssl/digest.hpp" #include "block/block.h" @@ -1475,7 +1476,7 @@ void parse_func_def(Lexer& lex) { method_name = func_name.str; } if (method_id.is_null()) { - unsigned crc = td::crc16(method_name); + unsigned crc = td::crc16(td::StringCase::is_camel_case(method_name) ? td::StringCase::camel_to_snake(method_name) : method_name); method_id = td::make_refint((crc & 0xffff) | 0x10000); } } diff --git a/lite-client/lite-client.cpp b/lite-client/lite-client.cpp index dd6df40f7..f31f2f0c7 100644 --- a/lite-client/lite-client.cpp +++ b/lite-client/lite-client.cpp @@ -40,6 +40,7 @@ #include "td/utils/Random.h" #include "td/utils/crypto.h" #include "td/utils/overloaded.h" +#include "td/utils/StringCase.h" #include "td/utils/port/signals.h" #include "td/utils/port/stacktrace.h" #include "td/utils/port/StdStreams.h" @@ -1225,7 +1226,7 @@ bool TestNode::get_account_state(ton::WorkchainId workchain, ton::StdSmcAddress td::int64 TestNode::compute_method_id(std::string method) { td::int64 method_id; if (!convert_int64(method, method_id)) { - method_id = (td::crc16(td::Slice{method}) & 0xffff) | 0x10000; + method_id = (td::crc16(td::Slice{td::StringCase::is_camel_case(method) ? td::StringCase::camel_to_snake(method) : method}) & 0xffff) | 0x10000; } return method_id; } From 8e9dbaeb510971e9dac0e37d6c567742201679e6 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Mon, 15 Jan 2024 09:38:17 +0500 Subject: [PATCH 04/28] add StringCase to tdutils cmake --- tdutils/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tdutils/CMakeLists.txt b/tdutils/CMakeLists.txt index f1e4b1ea5..e44f97899 100644 --- a/tdutils/CMakeLists.txt +++ b/tdutils/CMakeLists.txt @@ -116,6 +116,7 @@ set(TDUTILS_SOURCE td/utils/StackAllocator.cpp td/utils/Status.cpp td/utils/StringBuilder.cpp + td/utils/StringCase.cpp td/utils/Time.cpp td/utils/Timer.cpp td/utils/TsFileLog.cpp @@ -248,6 +249,7 @@ set(TDUTILS_SOURCE td/utils/Storer.h td/utils/StorerBase.h td/utils/StringBuilder.h + td/utils/StringCase.h td/utils/tests.h td/utils/ThreadLocalStorage.h td/utils/ThreadSafeCounter.h From 9471a48b0b39f256fd779918f34bbc2125371d4d Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Mon, 15 Jan 2024 09:38:42 +0500 Subject: [PATCH 05/28] stdlibCamel draft --- crypto/smartcont/stdlibCamel.fc | 639 ++++++++++++++++++++++++++++++++ 1 file changed, 639 insertions(+) create mode 100644 crypto/smartcont/stdlibCamel.fc diff --git a/crypto/smartcont/stdlibCamel.fc b/crypto/smartcont/stdlibCamel.fc new file mode 100644 index 000000000..dba362440 --- /dev/null +++ b/crypto/smartcont/stdlibCamel.fc @@ -0,0 +1,639 @@ +;; Standard library for funC +;; + +{- + This file is part of TON FunC Standard Library. + + FunC Standard Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + FunC Standard Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + +-} + +{- + # Tuple manipulation primitives + The names and the types are mostly self-explaining. + See [polymorhism with forall](https://ton.org/docs/#/func/functions?id=polymorphism-with-forall) + for more info on the polymorphic functions. + + Note that currently values of atomic type `tuple` can't be cast to composite tuple type (e.g. `[int, cell]`) + and vise versa. +-} + +{- + # Lisp-style lists + + Lists can be represented as nested 2-elements tuples. + Empty list is conventionally represented as TVM `null` value (it can be obtained by calling [null()]). + For example, tuple `(1, (2, (3, null)))` represents list `[1, 2, 3]`. Elements of a list can be of different types. +-} + +;;; Adds an element to the beginning of lisp-style list. +forall X -> tuple cons(X head, tuple tail) asm "CONS"; + +;;; Extracts the head and the tail of lisp-style list. +forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; + +;;; Extracts the tail and the head of lisp-style list. +forall X -> (tuple, X) listNext(tuple list) asm( -> 1 0) "UNCONS"; + +;;; Returns the head of lisp-style list. +forall X -> X car(tuple list) asm "CAR"; + +;;; Returns the tail of lisp-style list. +tuple cdr(tuple list) asm "CDR"; + +;;; Creates tuple with zero elements. +tuple emptyTuple() asm "NIL"; + +;;; Appends a value `x` to a `Tuple t = (x1, ..., xn)`, but only if the resulting `Tuple t' = (x1, ..., xn, x)` +;;; is of length at most 255. Otherwise throws a type check exception. +forall X -> tuple tpush(tuple t, X value) asm "TPUSH"; +forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; + +;;; Creates a tuple of length one with given argument as element. +forall X -> [X] single(X x) asm "SINGLE"; + +;;; Unpacks a tuple of length one +forall X -> X unsingle([X] t) asm "UNSINGLE"; + +;;; Creates a tuple of length two with given arguments as elements. +forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; + +;;; Unpacks a tuple of length two +forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; + +;;; Creates a tuple of length three with given arguments as elements. +forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; + +;;; Unpacks a tuple of length three +forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE"; + +;;; Creates a tuple of length four with given arguments as elements. +forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; + +;;; Unpacks a tuple of length four +forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE"; + +;;; Returns the first element of a tuple (with unknown element types). +forall X -> X first(tuple t) asm "FIRST"; + +;;; Returns the second element of a tuple (with unknown element types). +forall X -> X second(tuple t) asm "SECOND"; + +;;; Returns the third element of a tuple (with unknown element types). +forall X -> X third(tuple t) asm "THIRD"; + +;;; Returns the fourth element of a tuple (with unknown element types). +forall X -> X fourth(tuple t) asm "3 INDEX"; + +;;; Returns the first element of a pair tuple. +forall X, Y -> X pairFirst([X, Y] p) asm "FIRST"; + +;;; Returns the second element of a pair tuple. +forall X, Y -> Y pairSecond([X, Y] p) asm "SECOND"; + +;;; Returns the first element of a triple tuple. +forall X, Y, Z -> X tripleFirst([X, Y, Z] p) asm "FIRST"; + +;;; Returns the second element of a triple tuple. +forall X, Y, Z -> Y tripleSecond([X, Y, Z] p) asm "SECOND"; + +;;; Returns the third element of a triple tuple. +forall X, Y, Z -> Z tripleThird([X, Y, Z] p) asm "THIRD"; + + +;;; Push null element (casted to given type) +;;; By the TVM type `Null` FunC represents absence of a value of some atomic type. +;;; So `null` can actually have any atomic type. +forall X -> X null() asm "PUSHNULL"; + +;;; Moves a variable [x] to the top of the stack +forall X -> (X, ()) ~impureTouch(X x) impure asm "NOP"; + + + +;;; Returns the current Unix time as an Integer +int now() asm "NOW"; + +;;; Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`. +;;; If necessary, it can be parsed further using primitives such as [parse_std_addr]. +slice myAddress() asm "MYADDR"; + +;;; Returns the balance of the smart contract as a tuple consisting of an int +;;; (balance in nanotoncoins) and a `cell` +;;; (a dictionary with 32-bit keys representing the balance of "extra currencies") +;;; at the start of Computation Phase. +;;; Note that RAW primitives such as [send_raw_message] do not update this field. +[int, cell] getBalance() asm "BALANCE"; + +;;; Returns the logical time of the current transaction. +int curLt() asm "LTIME"; + +;;; Returns the starting logical time of the current block. +int blockLt() asm "BLOCKLT"; + +;;; Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`. +;;; Useful for signing and checking signatures of arbitrary entities represented by a tree of cells. +int cellHash(cell c) asm "HASHCU"; + +;;; Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`. +;;; The result is the same as if an ordinary cell containing only data and references from `s` had been created +;;; and its hash computed by [cell_hash]. +int sliceHash(slice s) asm "HASHSU"; + +;;; Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight, +;;; throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`. +int stringHash(slice s) asm "SHA256U"; + +{- + # Signature checks +-} + +;;; Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data) +;;; using [public_key] (also represented by a 256-bit unsigned integer). +;;; The signature must contain at least 512 data bits; only the first 512 bits are used. +;;; The result is `−1` if the signature is valid, `0` otherwise. +;;; Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`. +;;; That is, if [hash] is computed as the hash of some data, these data are hashed twice, +;;; the second hashing occurring inside `CHKSIGNS`. +int checkSignature(int hash, slice signature, int publicKey) asm "CHKSIGNU"; + +;;; Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `public_key`, +;;; similarly to [check_signature]. +;;; If the bit length of [data] is not divisible by eight, throws a cell underflow exception. +;;; The verification of Ed25519 signatures is the standard one, +;;; with sha256 used to reduce [data] to the 256-bit number that is actually signed. +int checkDataSignature(slice data, slice signature, int publicKey) asm "CHKSIGNS"; + +{--- + # Computation of boc size + The primitives below may be useful for computing storage fees of user-provided data. +-} + +;;; Returns `(x, y, z, -1)` or `(null, null, null, 0)`. +;;; Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z` +;;; in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account +;;; the identification of equal cells. +;;; The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG, +;;; with a hash table of visited cell hashes used to prevent visits of already-visited cells. +;;; The total count of visited cells `x` cannot exceed non-negative [max_cells]; +;;; otherwise the computation is aborted before visiting the `(max_cells + 1)`-st cell and +;;; a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`. +(int, int, int) computeDataSize(cell c, int maxCells) impure asm "CDATASIZE"; + +;;; Similar to [compute_data_size?], but accepting a `slice` [s] instead of a `cell`. +;;; The returned value of `x` does not take into account the cell that contains the `slice` [s] itself; +;;; however, the data bits and the cell references of [s] are accounted for in `y` and `z`. +(int, int, int) sliceComputeDataSize(slice s, int maxCells) impure asm "SDATASIZE"; + +;;; A non-quiet version of [compute_data_size?] that throws a cell overflow exception (`8`) on failure. +(int, int, int, int) tryComputeDataSize(cell c, int maxCells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; + +;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure. +(int, int, int, int) trySliceComputeDataSize(cell c, int maxCells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; + +;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) +;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; + +{-- + # Debug primitives + Only works for local TVM execution with debug level verbosity +-} +;;; Dumps the stack (at most the top 255 values) and shows the total stack depth. +() dumpStack() impure asm "DUMPSTK"; + +{- + # Persistent storage save and load +-} + +;;; Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. +cell getData() asm "c4 PUSH"; + +;;; Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive. +() setData(cell c) impure asm "c4 POP"; + +{- + # Continuation primitives +-} +;;; Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. +;;; The primitive returns the current value of `c3`. +cont getC3() impure asm "c3 PUSH"; + +;;; Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time. +;;; Note that after execution of this primitive the current code +;;; (and the stack of recursive function calls) won't change, +;;; but any other function call will use a function from the new code. +() setC3(cont c) impure asm "c3 POP"; + +;;; Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist. +cont bless(slice s) impure asm "BLESS"; + +{--- + # Gas related primitives +-} + +;;; Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero, +;;; decreasing the value of `gr` by `gc` in the process. +;;; In other words, the current smart contract agrees to buy some gas to finish the current transaction. +;;; This action is required to process external messages, which bring no value (hence no gas) with themselves. +;;; +;;; For more details check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept). +() acceptMessage() impure asm "ACCEPT"; + +;;; Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero. +;;; If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`, +;;; an (unhandled) out of gas exception is thrown before setting new gas limits. +;;; Notice that [set_gas_limit] with an argument `limit ≥ 2^63 − 1` is equivalent to [accept_message]. +() setGasLimit(int limit) impure asm "SETGASLIMIT"; + +;;; Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”) +;;; so that the current execution is considered “successful” with the saved values even if an exception +;;; in Computation Phase is thrown later. +() commit() impure asm "COMMIT"; + +;;; Not implemented +;;() buy_gas(int gram) impure asm "BUYGAS"; + +;;; Computes the amount of gas that can be bought for `amount` nanoTONs, +;;; and sets `gl` accordingly in the same way as [set_gas_limit]. +() buyGas(int amount) impure asm "BUYGAS"; + +;;; Computes the minimum of two integers [x] and [y]. +int min(int x, int y) asm "MIN"; + +;;; Computes the maximum of two integers [x] and [y]. +int max(int x, int y) asm "MAX"; + +;;; Sorts two integers. +(int, int) minmax(int x, int y) asm "MINMAX"; + +;;; Computes the absolute value of an integer [x]. +int abs(int x) asm "ABS"; + +{- + # Slice primitives + + It is said that a primitive _loads_ some data, + if it returns the data and the remainder of the slice + (so it can also be used as [modifying method](https://ton.org/docs/#/func/statements?id=modifying-methods)). + + It is said that a primitive _preloads_ some data, if it returns only the data + (it can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods)). + + Unless otherwise stated, loading and preloading primitives read the data from a prefix of the slice. +-} + + +;;; Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell, +;;; or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2) +;;; which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards. +slice beginParse(cell c) asm "CTOS"; + +;;; Checks if [s] is empty. If not, throws an exception. +() endParse(slice s) impure asm "ENDS"; + +;;; Loads the first reference from the slice. +(slice, cell) loadRef(slice s) asm( -> 1 0) "LDREF"; + +;;; Preloads the first reference from the slice. +cell preloadRef(slice s) asm "PLDREF"; + +{- Functions below are commented because are implemented on compilator level for optimisation -} + +;;; Loads a signed [len]-bit integer from a slice [s]. +;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; + +;;; Loads an unsigned [len]-bit integer from a slice [s]. +;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; + +;;; Preloads a signed [len]-bit integer from a slice [s]. +;; int preload_int(slice s, int len) asm "PLDIX"; + +;;; Preloads an unsigned [len]-bit integer from a slice [s]. +;; int preload_uint(slice s, int len) asm "PLDUX"; + +;;; Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; + +;;; Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; + +;;; Loads serialized amount of TonCoins (any unsigned integer up to `2^120 - 1`). +(slice, int) loadGrams(slice s) asm( -> 1 0) "LDGRAMS"; +(slice, int) loadCoins(slice s) asm( -> 1 0) "LDGRAMS"; + +;;; Returns all but the first `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice skipBits(slice s, int len) asm "SDSKIPFIRST"; +(slice, ()) ~skipBits(slice s, int len) asm "SDSKIPFIRST"; + +;;; Returns the first `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice firstBits(slice s, int len) asm "SDCUTFIRST"; + +;;; Returns all but the last `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice skipLastBits(slice s, int len) asm "SDSKIPLAST"; +(slice, ()) ~skipLastBits(slice s, int len) asm "SDSKIPLAST"; + +;;; Returns the last `0 ≤ len ≤ 1023` bits of `slice` [s]. +slice sliceLast(slice s, int len) asm "SDCUTLAST"; + +;;; Loads a dictionary `D` (HashMapE) from `slice` [s]. +;;; (returns `null` if `nothing` constructor is used). +(slice, cell) loadDict(slice s) asm( -> 1 0) "LDDICT"; + +;;; Preloads a dictionary `D` from `slice` [s]. +cell preloadDict(slice s) asm "PLDDICT"; + +;;; Loads a dictionary as [load_dict], but returns only the remainder of the slice. +slice skipDict(slice s) asm "SKIPDICT"; + +;;; Loads (Maybe ^Cell) from `slice` [s]. +;;; In other words loads 1 bit and if it is true +;;; loads first ref and return it with slice remainder +;;; otherwise returns `null` and slice remainder +(slice, cell) loadMaybeRef(slice s) asm( -> 1 0) "LDOPTREF"; + +;;; Preloads (Maybe ^Cell) from `slice` [s]. +cell preloadMaybeRef(slice s) asm "PLDOPTREF"; + + +;;; Returns the depth of `cell` [c]. +;;; If [c] has no references, then return `0`; +;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [c]. +;;; If [c] is a `null` instead of a cell, returns zero. +int cellDepth(cell c) asm "CDEPTH"; + + +{- + # Slice size primitives +-} + +;;; Returns the number of references in `slice` [s]. +int sliceRefs(slice s) asm "SREFS"; + +;;; Returns the number of data bits in `slice` [s]. +int sliceBits(slice s) asm "SBITS"; + +;;; Returns both the number of data bits and the number of references in `slice` [s]. +(int, int) sliceBitsRefs(slice s) asm "SBITREFS"; + +;;; Checks whether a `slice` [s] is empty (i.e., contains no bits of data and no cell references). +int isSliceEmpty(slice s) asm "SEMPTY"; + +;;; Checks whether `slice` [s] has no bits of data. +int isSliceDataEmpty(slice s) asm "SDEMPTY"; + +;;; Checks whether `slice` [s] has no references. +int isSliceRefsEmpty(slice s) asm "SREMPTY"; + +;;; Returns the depth of `slice` [s]. +;;; If [s] has no references, then returns `0`; +;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [s]. +int sliceDepth(slice s) asm "SDEPTH"; + +{- + # Builder size primitives +-} + +;;; Returns the number of cell references already stored in `builder` [b] +int builderRefs(builder b) asm "BREFS"; + +;;; Returns the number of data bits already stored in `builder` [b]. +int builderBits(builder b) asm "BBITS"; + +;;; Returns the depth of `builder` [b]. +;;; If no cell references are stored in [b], then returns 0; +;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [b]. +int builderDepth(builder b) asm "BDEPTH"; + +{- + # Builder primitives + It is said that a primitive _stores_ a value `x` into a builder `b` + if it returns a modified version of the builder `b'` with the value `x` stored at the end of it. + It can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods). + + All the primitives below first check whether there is enough space in the `builder`, + and only then check the range of the value being serialized. +-} + +;;; Creates a new empty `builder`. +builder beginCell() asm "NEWC"; + +;;; Converts a `builder` into an ordinary `cell`. +cell endCell(builder b) asm "ENDC"; + +;;; Stores a reference to `cell` [c] into `builder` [b]. +builder storeRef(builder b, cell c) asm(c b) "STREF"; + +;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. +;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; + +;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. +;; builder store_int(builder b, int x, int len) asm(x b len) "STIX"; + + +;;; Stores `slice` [s] into `builder` [b] +builder storeSlice(builder b, slice s) asm "STSLICER"; + +;;; Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. +;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, +;;; which is the smallest integer `l ≥ 0`, such that `x < 2^8l`, +;;; followed by an `8l`-bit unsigned big-endian representation of [x]. +;;; If [x] does not belong to the supported range, a range check exception is thrown. +;;; +;;; Store amounts of TonCoins to the builder as VarUInteger 16 +builder storeGrams(builder b, int x) asm "STGRAMS"; +builder storeCoins(builder b, int x) asm "STGRAMS"; + +;;; Stores dictionary `D` represented by `cell` [c] or `null` into `builder` [b]. +;;; In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise. +builder storeDict(builder b, cell c) asm(c b) "STDICT"; + +;;; Stores (Maybe ^Cell) to builder: +;;; if cell is null store 1 zero bit +;;; otherwise store 1 true bit and ref to cell +builder storeMaybeRef(builder b, cell c) asm(c b) "STOPTREF"; + + +{- + # Address manipulation primitives + The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme: + ```TL-B + addr_none$00 = MsgAddressExt; + addr_extern$01 len:(## 8) external_address:(bits len) + = MsgAddressExt; + anycast_info$_ depth:(#<= 30) { depth >= 1 } + rewrite_pfx:(bits depth) = Anycast; + addr_std$10 anycast:(Maybe Anycast) + workchain_id:int8 address:bits256 = MsgAddressInt; + addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9) + workchain_id:int32 address:(bits addr_len) = MsgAddressInt; + _ _:MsgAddressInt = MsgAddress; + _ _:MsgAddressExt = MsgAddress; + + int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool + src:MsgAddress dest:MsgAddressInt + value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; + ext_out_msg_info$11 src:MsgAddress dest:MsgAddressExt + created_lt:uint64 created_at:uint32 = CommonMsgInfoRelaxed; + ``` + A deserialized `MsgAddress` is represented by a tuple `t` as follows: + + - `addr_none` is represented by `t = (0)`, + i.e., a tuple containing exactly one integer equal to zero. + - `addr_extern` is represented by `t = (1, s)`, + where slice `s` contains the field `external_address`. In other words, ` + t` is a pair (a tuple consisting of two entries), containing an integer equal to one and slice `s`. + - `addr_std` is represented by `t = (2, u, x, s)`, + where `u` is either a `null` (if `anycast` is absent) or a slice `s'` containing `rewrite_pfx` (if anycast is present). + Next, integer `x` is the `workchain_id`, and slice `s` contains the address. + - `addr_var` is represented by `t = (3, u, x, s)`, + where `u`, `x`, and `s` have the same meaning as for `addr_std`. +-} + +;;; Loads from slice [s] the only prefix that is a valid `MsgAddress`, +;;; and returns both this prefix `s'` and the remainder `s''` of [s] as slices. +(slice, slice) loadMsgAddr(slice s) asm( -> 1 0) "LDMSGADDR"; + +;;; Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`. +;;; If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown. +tuple parseAddr(slice s) asm "PARSEMSGADDR"; + +;;; Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`), +;;; applies rewriting from the anycast (if present) to the same-length prefix of the address, +;;; and returns both the workchain and the 256-bit address as integers. +;;; If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`, +;;; throws a cell deserialization exception. +(int, int) parseStdAddr(slice s) asm "REWRITESTDADDR"; + +;;; A variant of [parse_std_addr] that returns the (rewritten) address as a slice [s], +;;; even if it is not exactly 256 bit long (represented by a `msg_addr_var`). +(int, slice) parseVarAddr(slice s) asm "REWRITEVARADDR"; + +{- + # Dictionary primitives +-} + + +;;; Sets the value associated with [key_len]-bit key signed index in dictionary [dict] to [value] (cell), +;;; and returns the resulting dictionary. +cell idictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTISETREF"; +(cell, ()) ~idictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTISETREF"; + +;;; Sets the value associated with [key_len]-bit key unsigned index in dictionary [dict] to [value] (cell), +;;; and returns the resulting dictionary. +cell udictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUSETREF"; +(cell, ()) ~udictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUSETREF"; + +cell idictGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIGETOPTREF"; +(cell, int) tryIdictGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIGETREF" "NULLSWAPIFNOT"; +(cell, int) tryUdictGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUGETREF" "NULLSWAPIFNOT"; +(cell, cell) idictSetGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTISETGETOPTREF"; +(cell, cell) udictSetGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUSETGETOPTREF"; +(cell, int) tryIdictDelete(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIDEL"; +(cell, int) tryUdictDelete(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUDEL"; +(slice, int) tryIdictGet(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIGET" "NULLSWAPIFNOT"; +(slice, int) tryUdictGet(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUGET" "NULLSWAPIFNOT"; +(cell, slice, int) tryIdictDeleteGet(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, slice, int) tryUdictDeleteGet(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryIdictDeleteGet(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryUdictDeleteGet(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUDELGET" "NULLSWAPIFNOT"; +cell udictSet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUSET"; +(cell, ()) ~udictSet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUSET"; +cell idictSet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTISET"; +(cell, ()) ~idictSet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTISET"; +cell dictSet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTSET"; +(cell, ()) ~dictSet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTSET"; +(cell, int) tryUdictAdd(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUADD"; +(cell, int) tryUdictReplace(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUREPLACE"; +(cell, int) tryIdictAdd(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTIADD"; +(cell, int) tryIdictReplace(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTIREPLACE"; +cell udictSetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUSETB"; +(cell, ()) ~udictSetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUSETB"; +cell idictSetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTISETB"; +(cell, ()) ~idictSetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTISETB"; +cell dictSetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTSETB"; +(cell, ()) ~dictSetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTSETB"; +(cell, int) tryUdictAddBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUADDB"; +(cell, int) tryUdictReplaceBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUREPLACEB"; +(cell, int) tryIdictAddBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIADDB"; +(cell, int) tryIdictReplaceBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIREPLACEB"; +(cell, int, slice, int) udictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::deleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::deleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::deleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict::deleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict::deleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict::deleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(int, slice, int) tryUdictGetMin(cell dict, int keyLen) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; +(int, slice, int) tryUdictGetMax(cell dict, int keyLen) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; +(int, cell, int) tryUdictGetMinRef(cell dict, int keyLen) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) tryUdictGetMaxRef(cell dict, int keyLen) asm (-> 1 0 2) "DICTUMAXREF" "NULLSWAPIFNOT2"; +(int, slice, int) tryIdictGetMin(cell dict, int keyLen) asm (-> 1 0 2) "DICTIMIN" "NULLSWAPIFNOT2"; +(int, slice, int) tryIdictGetMax(cell dict, int keyLen) asm (-> 1 0 2) "DICTIMAX" "NULLSWAPIFNOT2"; +(int, cell, int) tryIdictGetMinRef(cell dict, int keyLen) asm (-> 1 0 2) "DICTIMINREF" "NULLSWAPIFNOT2"; +(int, cell, int) tryIdictGetMaxRef(cell dict, int keyLen) asm (-> 1 0 2) "DICTIMAXREF" "NULLSWAPIFNOT2"; +(int, slice, int) tryUdictGetNext(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) tryUdictGetNexteq(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTUGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) tryUdictGetPrev(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) tryUdictGetPreveq(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTUGETPREVEQ" "NULLSWAPIFNOT2"; +(int, slice, int) tryIdictGetNext(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT2"; +(int, slice, int) tryIdictGetNexteq(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTIGETNEXTEQ" "NULLSWAPIFNOT2"; +(int, slice, int) tryIdictGetPrev(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; +(int, slice, int) tryIdictGetPreveq(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; + +;;; Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL +cell newDict() asm "NEWDICT"; +;;; Checks whether a dictionary is empty. Equivalent to cell_null?. +int isDictEmpty(cell c) asm "DICTEMPTY"; + + +{- Prefix dictionary primitives -} +(slice, slice, slice, int) tryPfxdictGet(cell dict, int keyLen, slice key) asm(key dict keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2"; +(cell, int) tryPfxdictSet(cell dict, int keyLen, slice key, slice value) asm(value key dict keyLen) "PFXDICTSET"; +(cell, int) tryPfxdictDelete(cell dict, int keyLen, slice key) asm(key dict keyLen) "PFXDICTDEL"; + +;;; Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value. +cell configParam(int x) asm "CONFIGOPTPARAM"; +;;; Checks whether c is a null. Note, that FunC also has polymorphic null? built-in. +int isCellNull(cell c) asm "ISNULL"; + +;;; Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b − amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15. +() rawReserve(int amount, int mode) impure asm "RAWRESERVE"; +;;; Similar to raw_reserve, but also accepts a dictionary extra_amount (represented by a cell or null) with extra currencies. In this way currencies other than TonCoin can be reserved. +() rawReserveExtra(int amount, cell extraAmount, int mode) impure asm "RAWRESERVEX"; +;;; Sends a raw message contained in msg, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have dummy value addr_none (to be automatically replaced with the current smart contract address), and ihr_fee, fwd_fee, created_lt and created_at fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter mode contains the flags. Currently mode = 0 is used for ordinary messages; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); mode' = mode + 1 means that the sender wants to pay transfer fees separately; mode' = mode + 2 means that any errors arising while processing this message during the action phase should be ignored. Finally, mode' = mode + 32 means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with +128. +() sendRawMessage(cell msg, int mode) impure asm "SENDRAWMSG"; +;;; Creates an output action that would change this smart contract code to that given by cell new_code. Notice that this change will take effect only after the successful termination of the current run of the smart contract +() setCode(cell newCode) impure asm "SETCODE"; + +;;; Generates a new pseudo-random unsigned 256-bit integer x. The algorithm is as follows: if r is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its sha512(r) is computed; the first 32 bytes of this hash are stored as the new value r' of the random seed, and the remaining 32 bytes are returned as the next random value x. +int random() impure asm "RANDU256"; +;;; Generates a new pseudo-random integer z in the range 0..range−1 (or range..−1, if range < 0). More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed. +int rand(int range) impure asm "RAND"; +;;; Returns the current random seed as an unsigned 256-bit Integer. +int getSeed() impure asm "RANDSEED"; +;;; Sets the random seed to unsigned 256-bit seed. +() setSeed(int) impure asm "SETRAND"; +;;; Mixes unsigned 256-bit integer x into the random seed r by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed r, and the second with the big-endian representation of x. +() randomize(int x) impure asm "ADDRAND"; +;;; Equivalent to randomize(cur_lt());. +() randomizeLt() impure asm "LTIME" "ADDRAND"; + +;;; Checks whether the data parts of two slices coinside +int equalSliceBits (slice a, slice b) asm "SDEQ"; + +;;; Concatenates two builders +builder storeBuilder(builder to, builder from) asm "STBR"; From ead4c5e34c76acdb4cad4938def4ddc03ff34d90 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 24 Jan 2024 10:59:09 +0500 Subject: [PATCH 06/28] fix str_const in VarDescr --- crypto/func/abscode.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crypto/func/abscode.cpp b/crypto/func/abscode.cpp index f1ffcfa49..e5813c15f 100644 --- a/crypto/func/abscode.cpp +++ b/crypto/func/abscode.cpp @@ -200,16 +200,19 @@ void VarDescr::operator&=(const VarDescr& y) { void VarDescr::set_value(const VarDescr& y) { val = y.val; int_const = y.int_const; + str_const = y.str_const; } void VarDescr::set_value(VarDescr&& y) { val = y.val; int_const = std::move(y.int_const); + str_const = std::move(y.str_const); } void VarDescr::clear_value() { val = 0; int_const.clear(); + str_const.clear(); } void VarDescrList::show(std::ostream& os) const { From 7db947685544a8159fcae1f7b2605ba9508df43d Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 24 Jan 2024 10:59:26 +0500 Subject: [PATCH 07/28] add more builtin-funcs --- crypto/func/builtins.cpp | 106 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 9de673fd7..711cc4c07 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -17,6 +17,7 @@ Copyright 2017-2020 Telegram Systems LLP */ #include "func.h" +#include namespace funC { using namespace std::literals::string_literals; @@ -1116,6 +1117,103 @@ AsmOp compile_is_null(std::vector& res, std::vector& args, c return exec_op("ISNULL", 1, 1); } +AsmOp compile_slice_begins(std::vector& res, std::vector& args, const SrcLocation&) { + func_assert(args.size() == 2 && res.size() == 2); + auto& slice = args[1]; + if (!slice.is_const()) { + return exec_op("SDBEGINSXQ", 2, 2); + } + std::ostringstream os; + os << "x{" << slice.str_const << "} SDBEGINSQ"; + slice.unused(); + return exec_op(os.str(), 1, 2); +} + +AsmOp compile_store_slice(std::vector& res, std::vector& args, const SrcLocation&) { + func_assert(args.size() == 2 && res.size() == 1); + auto& slice = args[1]; + if (!slice.is_const()) { + return exec_op("STSLICE", 2, 1); + } + + unsigned char buff[128]; + int slice_size = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), slice.str_const.data(), slice.str_const.data() + slice.str_const.size()); + + td::BitString slice_bs(slice_size); + + td::bitstring::bits_memcpy(slice_bs.bits(), buff, slice_size); + + bool all_zeroes = td::bitstring::bits_memscan(slice_bs.bits(), slice_size, false) == (u_long)slice_size; + bool all_ones = td::bitstring::bits_memscan(slice_bs.bits(), slice_size, true) == (u_long)slice_size; + + if (all_zeroes && slice_size == 1) { + slice.unused(); + return exec_op("STZERO", 1, 1); + } + if (all_ones && slice_size == 1) { + slice.unused(); + return exec_op("STONE", 1, 1); + } + + std::ostringstream os; + + if (all_ones) { + slice.unused(); + os << slice_size << " INT STONES"; + return exec_op(os.str(), 1, 1); + } + + if (all_zeroes) { + slice.unused(); + os << slice_size << " INT STZROES"; + return exec_op(os.str(), 1, 1); + } + + if (slice_size <= 57) { + os << "x{" << slice.str_const << "} STSLICECONST"; + slice.unused(); + return exec_op(os.str(), 1, 1); + } + + return exec_op("STSLICE", 2, 1); +} + +AsmOp compile_pack_address(std::vector& res, std::vector& args, const SrcLocation&) { + auto& wc = args[0]; + auto& hash = args[1]; + std::ostringstream os; + + bool const_wc = wc.is_const(); + bool const_hash = hash.is_const(); + + td::BitString address_bb(3 + 8 + 256); + td::bitstring::bits_store_long(address_bb.bits(), 4, 3); // addr_std$10 anycast:(Maybe Anycast) + + if (const_wc) { + td::bitstring::bits_store_long(address_bb.bits() + 3, wc.int_const->to_long(), 8); + os << "NEWC x{" << td::bitstring::bits_to_hex(address_bb.bits(), 3 + 8) << "} STSLICECONST "; + wc.unused(); + } else { + os << "NEWC b{100} STSLICECONST 8 STI "; + } + + if (const_hash) { + hash.int_const->export_bits(address_bb.bits() + 3 + 8, 256, true); + } + + os << "256 STU "; // hash + + if (const_wc && const_hash) { + hash.unused(); + return exec_op("x{" + td::bitstring::bits_to_hex(address_bb.bits(), 267) + "} PUSHSLICE", 0, 1); + } + + os << "ENDC CTOS"; + + return exec_op(os.str(), 2 - (unsigned)const_wc - (unsigned)const_hash, 1); +} + + bool compile_run_method(AsmOpList& code, std::vector& res, std::vector& args, int n, bool has_value) { func_assert(args.size() == (unsigned)n + 1 && res.size() == (unsigned)has_value); @@ -1156,6 +1254,9 @@ void define_builtins() { auto store_int_op = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), Builder); auto store_int_method = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Int, Int}), TypeExpr::new_tensor({Builder, Unit})); + auto store_slice_op = TypeExpr::new_map(TypeExpr::new_tensor({Builder, Slice}), Builder); + auto store_slice_method = + TypeExpr::new_map(TypeExpr::new_tensor({Builder, Slice}), TypeExpr::new_tensor({Builder, Unit})); auto fetch_slice_op = TypeExpr::new_map(SliceInt, TypeExpr::new_tensor({Slice, Slice})); auto prefetch_slice_op = TypeExpr::new_map(SliceInt, Slice); //auto arith_null_op = TypeExpr::new_map(TypeExpr::new_unit(), Int); @@ -1248,6 +1349,11 @@ void define_builtins() { AsmOp::Custom("s0 DUMP", 1, 1), true); define_builtin_func("~strdump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))), AsmOp::Custom("STRDUMP", 1, 1), true); + define_builtin_func("slice_begins_with", TypeExpr::new_map(TypeExpr::new_tensor({Slice, Slice}), SliceInt), + compile_slice_begins, true); + define_builtin_func("store_slice", store_slice_op, compile_store_slice, {1, 0}); + define_builtin_func("~store_slice", store_slice_method, compile_store_slice, {1, 0}); + define_builtin_func("pack_address", TypeExpr::new_map(Int2, Slice), compile_pack_address, {1, 0}); define_builtin_func( "run_method0", TypeExpr::new_map(Int, Unit), [](AsmOpList& a, auto b, auto c) { return compile_run_method(a, b, c, 0, false); }, true); From 26c4c229bb625daa8e58fa7b712fa5ddb4f0f37f Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 24 Jan 2024 10:59:36 +0500 Subject: [PATCH 08/28] stdlib update (draft) --- crypto/smartcont/stdlib.fc | 103 ++++++++++++++++++++++++++++++++++++- 1 file changed, 101 insertions(+), 2 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 978b94738..d2503e13f 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -439,7 +439,7 @@ builder store_ref(builder b, cell c) asm(c b) "STREF"; ;;; Stores `slice` [s] into `builder` [b] -builder store_slice(builder b, slice s) asm "STSLICER"; +;; builder store_slice(builder b, slice s) asm "STSLICER"; ;;; Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. ;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, @@ -636,4 +636,103 @@ int get_seed() impure asm "RANDSEED"; int equal_slice_bits (slice a, slice b) asm "SDEQ"; ;;; Concatenates two builders -builder store_builder(builder to, builder from) asm "STBR"; +builder store_builder(builder to, builder from) asm(from to) "STB"; + +builder store_builder_as_ref(builder to, builder from) asm(from to) "STBREF"; +(slice, slice) load_ref_as_slice(slice s) asm "LDREFRTOS"; +int my_balance_ton() asm "BALANCE FIRST"; + +builder store_op(builder b, int op) asm(op b) "32 STU"; +builder store_query_id(builder b, int query_id) asm(query_id b) "64 STU"; +(slice, int) load_op(slice s) asm( -> 1 0) "32 LDU"; +(slice, int) load_query_id(slice s) asm( -> 1 0) "64 LDU"; + +(int, int) builder_bits_refs(builder b) asm "BBITREFS"; +(int, int) builder_rem_bits_refs(builder b) asm "BREMBITREFS"; + +;;; returns: flags, sender address, forward fees +(int, slice, int) parse_message(cell full_message) asm "CTOS 4 LDU LDMSGADDR LDMSGADDR LDGRAMS SKIPDICT LDGRAMS LDGRAMS DROP 3 1 BLKDROP2"; +int bounceable?(int flags) asm "2 INT AND"; +int bounced?(int flags) asm "ONE AND"; + +builder store_state_init(builder b, cell data, cell code) asm(data code b) "b{00110} STSLICECONST STREF STREF"; + +slice calc_bc_address(cell state_init) inline { + return pack_address(0, cell_hash(state_init)); +} + +slice calc_mc_address(cell state_init) inline { + return pack_address(-1, cell_hash(state_init)); +} + +builder store_number_dec(builder b, int x) asm """ + ZERO // b x i=0 + SWAP // b i=0 x + UNTIL:<{ // b i x + 10 PUSHINT DIVMOD // b i x r + 48 ADDCONST // b i x r + s3 s1 s3 XCHG3 // r b x i + INC // r b x i + s1 s0 XCPU // r b i x x + ISZERO + }> + DROP + REPEAT:<{ 8 STU }> // ..rrr.. b i +"""; + +builder store_number_hex(builder b, int x) asm """ + ZERO // b x i=0 + SWAP // b i=0 x + UNTIL:<{ // b i x + 16 PUSHINT DIVMOD // b i x r + 48 ADDCONST DUP 57 GTINT IF:<{ 7 ADDCONST }> // b i x r + s3 s1 s3 XCHG3 // r b x i + INC // r b x i + s1 s0 XCPU // r b i x x + ISZERO + }> + DROP + REPEAT:<{ 8 STU }> // ..rrr.. b i +"""; + +int validate_addr_bc(slice addr) asm "b{10000000000} PUSHSLICE SDPPFXREV"; +int ext_validate_addr_bc(slice addr) asm """ + DUP + b{10000000000} PUSHSLICE SDPPFXREV SWAP + 267 INT 0 INT SCHKBITREFSQ + AND +"""; + +int workchains_equal?(slice addr1, slice addr2) asm "REWRITESTDADDR DROP SWAP REWRITESTDADDR DROP EQUAL"; +int workchain_match?(slice addr, int wc) asm(wc addr) "REWRITESTDADDR DROP EQUAL"; +int basechain_addr?(slice addr) asm "REWRITESTDADDR DROP 0 EQINT"; +int masterchain_addr?(slice addr) asm "REWRITESTDADDR DROP -1 EQINT"; + +int tuple_length(tuple t) asm "TLEN"; + +const get_c2() asm "c2 PUSH"; +() set_c2(cont c) impure asm "c2 POP"; + +;;; DRAFT, not for use in prod +() send(slice address, int value, builder state_init, builder body, int mode) impure inline { + builder message = begin_cell().store_slice("62_"s).store_slice(address).store_coins(value).store_uint(0, 105); + + if (state_init != null()) { + message = message.store_slice("A_"s).store_builder(state_init); + } else { + message = message.store_slice("4_"s); + } + + if (body != null()) { + (int b1, int r1) = message.builder_rem_bits_refs(); + (int b2, int r2) = body.builder_bits_refs(); + if (b1 >= b2 & r1 >= r2) { + message = message.store_slice("4_"s).store_builder(body); + } else { + message = message.store_slice("C_"s).store_builder_as_ref(body); + } + } else { + message = message.store_slice("4_"s); + } + send_raw_message(message.end_cell(), mode); +} From eedfa8c5d748b757ce0e909bfc54bcecc3f39557 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 24 Jan 2024 11:23:56 +0500 Subject: [PATCH 09/28] add addr_none constant to stdlib --- crypto/smartcont/stdlib.fc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index d2503e13f..f63db4930 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -736,3 +736,5 @@ const get_c2() asm "c2 PUSH"; } send_raw_message(message.end_cell(), mode); } + +const slice addr_none = "2_"s; ;; addr_none$00 = MsgAddressExt; From 6d091842c5d58d2fd37dc4d8aaf5e5c7a18f5383 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 24 Jan 2024 12:36:28 +0500 Subject: [PATCH 10/28] optimize store_slice as zeroes/ones --- crypto/func/builtins.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 711cc4c07..dbc1e3445 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -1157,13 +1157,13 @@ AsmOp compile_store_slice(std::vector& res, std::vector& arg std::ostringstream os; - if (all_ones) { + if (all_ones && (slice_size > 17 || slice_size == 10)) { slice.unused(); os << slice_size << " INT STONES"; return exec_op(os.str(), 1, 1); } - if (all_zeroes) { + if (all_zeroes && (slice_size > 17 || slice_size == 10)) { slice.unused(); os << slice_size << " INT STZROES"; return exec_op(os.str(), 1, 1); From 022c4d6bee0569bbe04bd6173acc9c719ea41a38 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 24 Jan 2024 12:37:31 +0500 Subject: [PATCH 11/28] fix typo STZEROES (store_slice builtin) --- crypto/func/builtins.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index dbc1e3445..063a2119e 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -1165,7 +1165,7 @@ AsmOp compile_store_slice(std::vector& res, std::vector& arg if (all_zeroes && (slice_size > 17 || slice_size == 10)) { slice.unused(); - os << slice_size << " INT STZROES"; + os << slice_size << " INT STZEROES"; return exec_op(os.str(), 1, 1); } From 053b0586144b96dae53045e9228c3211140a50cb Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Mon, 29 Jan 2024 15:30:37 +0500 Subject: [PATCH 12/28] stdlib.fc | cosmetic --- crypto/smartcont/stdlib.fc | 285 +++++++++++++++++++++++++++++++++---- 1 file changed, 255 insertions(+), 30 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index f63db4930..87af03615 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -638,33 +638,233 @@ int equal_slice_bits (slice a, slice b) asm "SDEQ"; ;;; Concatenates two builders builder store_builder(builder to, builder from) asm(from to) "STB"; -builder store_builder_as_ref(builder to, builder from) asm(from to) "STBREF"; -(slice, slice) load_ref_as_slice(slice s) asm "LDREFRTOS"; -int my_balance_ton() asm "BALANCE FIRST"; +;; NEW FUNCS -builder store_op(builder b, int op) asm(op b) "32 STU"; -builder store_query_id(builder b, int query_id) asm(query_id b) "64 STU"; -(slice, int) load_op(slice s) asm( -> 1 0) "32 LDU"; -(slice, int) load_query_id(slice s) asm( -> 1 0) "64 LDU"; +;; ===== TVM UPGRADE ===== -(int, int) builder_bits_refs(builder b) asm "BBITREFS"; -(int, int) builder_rem_bits_refs(builder b) asm "BREMBITREFS"; +;;; Retrieves code of smart-contract from c7 +cell my_code() asm "MYCODE"; -;;; returns: flags, sender address, forward fees -(int, slice, int) parse_message(cell full_message) asm "CTOS 4 LDU LDMSGADDR LDMSGADDR LDGRAMS SKIPDICT LDGRAMS LDGRAMS DROP 3 1 BLKDROP2"; -int bounceable?(int flags) asm "2 INT AND"; -int bounced?(int flags) asm "ONE AND"; +;;; Creates an output action and returns a fee for creating a message. Mode has the same effect as in the case of SENDRAWMSG +int send_message(cell msg, int mode) impure asm "SENDMSG"; + +;; TVM V6 https://github.com/ton-blockchain/ton/blob/testnet/doc/GlobalVersions.md#version-6 + +int get_compute_price(int workchain, int gas_used) asm(gas_used workchain) "GETEXECUTIONPRICE"; +int get_storage_price(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEPRICE"; +int get_forward_price(int workchain, int bits, int cells) asm(cells bits workchain) "GETFORWARDPRICE"; +int get_precompiled_gas_consumption() asm "GETPRECOMPILEDGAS"; + +[slice, slice, slice, slice, slice, slice, slice] get_fee_cofigs() asm "UNPACKEDCONFIGTUPLE"; +;; ===== ADDRESS ===== + +const slice addr_none = "2_"s; ;; addr_none$00 = MsgAddressExt; + +builder store_addr_none(builder b) asm "b{00} STSLICECONST"; + +int addr_none?(slice s) asm "b{00} PUSHSLICE SDEQ"; + +;;; Checking that the address is a standard basechain address and does not have anycast (should be used after load_msg_addr) +int validate_addr_bc(slice addr) asm "b{10000000000} PUSHSLICE SDPPFXREV"; +;;; Checking that the address is a standard masterchain address and does not have anycast (should be used after load_msg_addr) +int validate_addr_mc(slice addr) asm "b{10011111111} PUSHSLICE SDPPFXREV"; + +;;; Checking that the `slice` [addr] is a standard basechain address and does not have anycast (can be used with any `slice`) +int ext_validate_addr_bc(slice addr) asm """ + DUP + b{10000000000} PUSHSLICE SDPPFXREV SWAP + 267 INT 0 INT SCHKBITREFSQ + AND +"""; +;;; Checking that the `slice` [addr] is a standard masterchain address and does not have anycast (can be used with any `slice`) +int ext_validate_addr_mc(slice addr) asm """ + DUP + b{10011111111} PUSHSLICE SDPPFXREV SWAP + 267 INT 0 INT SCHKBITREFSQ + AND +"""; + +;;; Checking that [addr1] and [addr2] have the same workchain +int workchains_equal?(slice addr1, slice addr2) asm "REWRITESTDADDR DROP SWAP REWRITESTDADDR DROP EQUAL"; +;;; Checking that [addr] have the workchain [wc] +int workchain_match?(slice addr, int wc) asm(wc addr) "REWRITESTDADDR DROP EQUAL"; +;;; Checking that [addr] have the workchain 0 +int basechain_addr?(slice addr) asm "REWRITESTDADDR DROP 0 EQINT"; +;;; Checking that [addr] have the workchain -1 +int masterchain_addr?(slice addr) asm "REWRITESTDADDR DROP -1 EQINT"; + +;;; Basic store StateInit construction in `builder` [b]. builder store_state_init(builder b, cell data, cell code) asm(data code b) "b{00110} STSLICECONST STREF STREF"; +;;; Calculate standard basechain address from `state_init` slice calc_bc_address(cell state_init) inline { return pack_address(0, cell_hash(state_init)); } +;;; Calculate standard masterchain address from `state_init` slice calc_mc_address(cell state_init) inline { return pack_address(-1, cell_hash(state_init)); } +;; ===== BOOL ===== + +const int TRUE = -1; +const int FALSE = 0; + +builder store_true(builder b) asm "STONE"; +builder store_false(builder b) asm "STZERO"; +builder store_bool(builder b, int x) asm(x b) "1 STI"; + +(slice, int) ~load_bool(slice s) asm(-> 1 0) "1 LDI"; + +;;; skip (Maybe ^Cell) from `slice` [s]. +(slice, ()) ~skip_maybe_ref(slice s) asm "SKIPOPTREF"; +(slice, ()) ~skip_dict(slice s) asm "SKIPOPTREF"; + +;; ===== MSG FLAGS ===== + +const int BOUNCEABLE = 0x18; ;; 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 +const int NON_BOUNCEABLE = 0x10; ;; 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00 + +builder store_msg_flags(builder b, int flag) asm(flag b) "6 STU"; + +builder store_msg_flags_bounceable(builder b) asm "b{011000} STSLICECONST"; +builder store_msg_flags_non_bounceable(builder b) asm "b{010000} STSLICECONST"; + +;; load msg_flags only +(slice, int) ~load_msg_flags(slice s) asm(-> 1 0) "4 LDU"; + +;;; Basic parse MessageX (full_message), returns: flags, sender, forward fee +(int, slice, int) parse_message(cell full_message) asm "CTOS 4 LDU LDMSGADDR LDMSGADDR LDGRAMS SKIPDICT LDGRAMS LDGRAMS DROP 3 1 BLKDROP2"; + +;;; Checking that the "bounce" bit in the flags is set to "true" +int bounceable?(int flags) asm "2 INT AND"; +;;; Checking that the "bounced" bit in the flags is set to "true" +int bounced?(int flags) asm "ONE AND"; + +(slice, ()) ~skip_bounced_prefix(slice s) asm "x{FFFFFFFF} SDBEGINS"; + +;; ===== MSG BODY ===== + +;;; Store standard uint32 operation code into `builder` [b] +builder store_op(builder b, int op) asm(op b) "32 STU"; +;;; Store standard uint64 query id into `builder` [b] +builder store_query_id(builder b, int query_id) asm(query_id b) "64 STU"; +;;; Load standard uint32 operation code from `slice` [s] +(slice, int) load_op(slice s) asm(-> 1 0) "32 LDU"; +;;; Load standard uint64 query id from `slice` [s] +(slice, int) load_query_id(slice s) asm(-> 1 0) "64 LDU"; + +(slice, (int, int)) ~load_op_and_query_id(slice s) asm(->2 0 1) "32 LDU 64 LDU"; + +;; ===== SEND ===== + +const int MSG_INFO_REST_BITS = 1 + 4 + 4 + 64 + 32; + +;; https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L155 +;; message$_ {X:Type} info:CommonMsgInfo +;; Maybe (Either StateInit ^StateInit) +;; body:(Either X ^X) = Message X; +;; +;;message$_ {X:Type} info:CommonMsgInfoRelaxed +;; init:(Maybe (Either StateInit ^StateInit)) +;; body:(Either X ^X) = MessageRelaxed X; +;; +;;_ (Message Any) = MessageAny; + +;; if have StateInit (always place StateInit in ref): +;; 0b11 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` + +const int MSG_WITH_STATE_INIT_AND_BODY_SIZE = MSG_INFO_REST_BITS + 1 + 1 + 1; +const int MSG_HAVE_STATE_INIT = 4; +const int MSG_STATE_INIT_IN_REF = 2; +const int MSG_BODY_IN_REF = 1; + +;; if no StateInit: +;; 0b0 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` + +const int MSG_ONLY_BODY_SIZE = MSG_INFO_REST_BITS + 1 + 1; + +;; ===== SEND MODES ===== + +;; For `send_raw_message` and `send_message`: + +;;; x = 0 is used for ordinary messages; the gas fees are deducted from the senging amount; action phaes should NOT be ignored. +const int sendmode::REGULAR = 0; +;;; +1 means that the sender wants to pay transfer fees separately. +const int sendmode::PAY_FEES_SEPARATELY = 1; +;;; + 2 means that any errors arising while processing this message during the action phase should be ignored. +const int sendmode::IGNORE_ERRORS = 2; +;;; + 32 means that the current account must be destroyed if its resulting balance is zero. +const int sendmode::DESTROY = 32; +;;; x = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message. +const int sendmode::CARRY_ALL_REMAINING_MESSAGE_VALUE = 64; +;;; x = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message). +const int sendmode::CARRY_ALL_BALANCE = 128; +;;; in the case of action fail - bounce transaction. No effect if sendmode::IGNORE_ERRORS (+2) is used. TVM UPGRADE 2023-07. +const int sendmode::BOUNCE_ON_ACTION_FAIL = 16; + +;; Only for `send_message`: + +;;; do not create an action, only estimate fee +const int sendmode::ESTIMATE_FEE_ONLY = 1024; + +;; Other modes affect the fee calculation as follows: +;; +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account). +;; +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account). + +;; ===== RESERVE MODES ===== + +;;; Creates an output action which would reserve exactly x nanograms (if y = 0). +const int reserve::REGULAR = 0; +;;; Creates an output action which would reserve at most x nanograms (if y = 2). +;;; Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. +const int reserve::AT_MOST = 2; +;;; in the case of action fail - bounce transaction. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. +const int reserve::BOUNCE_ON_ACTION_FAIL = 16; + +;; ===== TOKEN METADATA ===== +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md + +const slice ONCHAIN_CONTENT = "00"s; +const slice OFFCHAIN_CONTENT = "01"s; +const slice SNAKE_FORMAT = "00"s; +const slice CHUNKS_FORMAT = "01"s; + +;; Key is sha256 hash of string. Value is data encoded as described in "Data serialization" paragraph. +;; Snake format - must be prefixed with 0x00 byte +(cell, ()) ~set_token_snake_metadata_entry(cell content_dict, int key, slice value) impure { + content_dict~udict_set_ref(256, key, begin_cell().store_slice(SNAKE_FORMAT).store_slice(value).end_cell()); + return (content_dict, ()); +} + +;; On-chain content layout The first byte is 0x00 and the rest is key/value dictionary. +cell create_token_onchain_metadata(cell content_dict) inline { + return begin_cell().store_slice(ONCHAIN_CONTENT).store_dict(content_dict).end_cell(); +} + +;; ===== BASIC ===== + +;;; Returns the current length of the `tuple` [t] +int tuple_length(tuple t) asm "TLEN"; + +;;; Concatenates two builders, but second builder stores as the reference (end_cell -> store_ref) +builder store_builder_as_ref(builder to, builder from) asm(from to) "STBREF"; + +;;; Loads the reference from the slice and parse (load_ref -> begin_parse) +(slice, slice) load_ref_as_slice(slice s) asm "LDREFRTOS"; + +;;; Returns the TON balance of the smart contract +int get_ton_balance() asm "BALANCE FIRST"; + +;;; Returns the number of data bits and cell references already stored in `builder` [b]. +(int, int) builder_bits_refs(builder b) asm "BBITREFS"; + +;;; Returns the number of data bits and cell references that can be stored in `builder` [b]. +(int, int) builder_rem_bits_refs(builder b) asm "BREMBITREFS"; + +;;; Store `integer` [x] number as string (UTF-8) in decimal form in `builder` [b]. builder store_number_dec(builder b, int x) asm """ ZERO // b x i=0 SWAP // b i=0 x @@ -680,6 +880,7 @@ builder store_number_dec(builder b, int x) asm """ REPEAT:<{ 8 STU }> // ..rrr.. b i """; +;;; Store `int` [x] number as string (UTF-8) in hexadecimal form in `builder` [b]. builder store_number_hex(builder b, int x) asm """ ZERO // b x i=0 SWAP // b i=0 x @@ -695,22 +896,9 @@ builder store_number_hex(builder b, int x) asm """ REPEAT:<{ 8 STU }> // ..rrr.. b i """; -int validate_addr_bc(slice addr) asm "b{10000000000} PUSHSLICE SDPPFXREV"; -int ext_validate_addr_bc(slice addr) asm """ - DUP - b{10000000000} PUSHSLICE SDPPFXREV SWAP - 267 INT 0 INT SCHKBITREFSQ - AND -"""; - -int workchains_equal?(slice addr1, slice addr2) asm "REWRITESTDADDR DROP SWAP REWRITESTDADDR DROP EQUAL"; -int workchain_match?(slice addr, int wc) asm(wc addr) "REWRITESTDADDR DROP EQUAL"; -int basechain_addr?(slice addr) asm "REWRITESTDADDR DROP 0 EQINT"; -int masterchain_addr?(slice addr) asm "REWRITESTDADDR DROP -1 EQINT"; - -int tuple_length(tuple t) asm "TLEN"; - -const get_c2() asm "c2 PUSH"; +;;; Returns the continuation located in register c2 +cont get_c2() asm "c2 PUSH"; +;;; Store `continuation` [c] to register c2 () set_c2(cont c) impure asm "c2 POP"; ;;; DRAFT, not for use in prod @@ -737,4 +925,41 @@ const get_c2() asm "c2 PUSH"; send_raw_message(message.end_cell(), mode); } -const slice addr_none = "2_"s; ;; addr_none$00 = MsgAddressExt; + +;; ===== STANDARD OP's ===== + +;; Common (used in TEP-62,74,85) +const int op::excesses = "d53276db"s; + +;; TEP-62 - NFT +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md +const int op::transfer_nft = "5fcc3d14"s; +const int op::ownership_assigned = "05138d91"s; +const int op::get_static_data = "2fcb26a2"s; +const int op::report_static_data = "8b771735"s; + +;; TEP-66 - NFT Royalty +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md +const int op::get_royalty_params = "693d3950"s; +const int op::report_royalty_params = "a8cb00ad"s; + +;; TEP-74 - Jettons +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md +const int op::transfer_jetton = "0f8a7ea5"s; +const int op::internal_transfer = "178d4519"s; +const int op::transfer_notification = "7362d09c"s; +const int op::burn_notification = "7bdd97de"s; + +;; TEP-85 - SBT +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0085-sbt-standard.md +const int op::prove_ownership = "04ded148"s; +const int op::ownership_proof = "0524c7ae"s; +const int op::request_owner = "d0c3bfea"s; +const int op::owner_info = "0dd607e3"s; +const int op::destroy_sbt = "1f04537a"s; +const int op::revoke_sbt = "6f89f5e3"s; + +;; TEP-89 - Discoverable Jettons Wallets +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md +const int op::provide_wallet_address = "2c76b973"s; +const int op::take_wallet_address = "d1735400"s; From 432a1154e3acde58824ec5f10af99ecae54c1b7b Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Mon, 29 Jan 2024 15:31:59 +0500 Subject: [PATCH 13/28] fix const types --- crypto/smartcont/stdlib.fc | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 87af03615..ed6443972 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -929,37 +929,37 @@ cont get_c2() asm "c2 PUSH"; ;; ===== STANDARD OP's ===== ;; Common (used in TEP-62,74,85) -const int op::excesses = "d53276db"s; +const slice op::excesses = "d53276db"s; ;; TEP-62 - NFT ;; https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md -const int op::transfer_nft = "5fcc3d14"s; -const int op::ownership_assigned = "05138d91"s; -const int op::get_static_data = "2fcb26a2"s; -const int op::report_static_data = "8b771735"s; +const slice op::transfer_nft = "5fcc3d14"s; +const slice op::ownership_assigned = "05138d91"s; +const slice op::get_static_data = "2fcb26a2"s; +const slice op::report_static_data = "8b771735"s; ;; TEP-66 - NFT Royalty ;; https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md -const int op::get_royalty_params = "693d3950"s; -const int op::report_royalty_params = "a8cb00ad"s; +const slice op::get_royalty_params = "693d3950"s; +const slice op::report_royalty_params = "a8cb00ad"s; ;; TEP-74 - Jettons ;; https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md -const int op::transfer_jetton = "0f8a7ea5"s; -const int op::internal_transfer = "178d4519"s; -const int op::transfer_notification = "7362d09c"s; -const int op::burn_notification = "7bdd97de"s; +const slice op::transfer_jetton = "0f8a7ea5"s; +const slice op::internal_transfer = "178d4519"s; +const slice op::transfer_notification = "7362d09c"s; +const slice op::burn_notification = "7bdd97de"s; ;; TEP-85 - SBT ;; https://github.com/ton-blockchain/TEPs/blob/master/text/0085-sbt-standard.md -const int op::prove_ownership = "04ded148"s; -const int op::ownership_proof = "0524c7ae"s; -const int op::request_owner = "d0c3bfea"s; -const int op::owner_info = "0dd607e3"s; -const int op::destroy_sbt = "1f04537a"s; -const int op::revoke_sbt = "6f89f5e3"s; +const slice op::prove_ownership = "04ded148"s; +const slice op::ownership_proof = "0524c7ae"s; +const slice op::request_owner = "d0c3bfea"s; +const slice op::owner_info = "0dd607e3"s; +const slice op::destroy_sbt = "1f04537a"s; +const slice op::revoke_sbt = "6f89f5e3"s; ;; TEP-89 - Discoverable Jettons Wallets ;; https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md -const int op::provide_wallet_address = "2c76b973"s; -const int op::take_wallet_address = "d1735400"s; +const slice op::provide_wallet_address = "2c76b973"s; +const slice op::take_wallet_address = "d1735400"s; From 26424e38b3b8b49292c219277e23c79fcc10346a Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Mon, 29 Jan 2024 20:43:38 +0500 Subject: [PATCH 14/28] stdlib.fc | cosmetic --- crypto/smartcont/stdlib.fc | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index ed6443972..e27acbf34 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -659,6 +659,9 @@ int get_precompiled_gas_consumption() asm "GETPRECOMPILEDGAS"; ;; ===== ADDRESS ===== +const int BASECHAIN = 0; +const int MASTERCHAIN = -1; + const slice addr_none = "2_"s; ;; addr_none$00 = MsgAddressExt; builder store_addr_none(builder b) asm "b{00} STSLICECONST"; @@ -699,12 +702,12 @@ builder store_state_init(builder b, cell data, cell code) asm(data code b) "b{00 ;;; Calculate standard basechain address from `state_init` slice calc_bc_address(cell state_init) inline { - return pack_address(0, cell_hash(state_init)); + return pack_address(BASECHAIN, cell_hash(state_init)); } ;;; Calculate standard masterchain address from `state_init` slice calc_mc_address(cell state_init) inline { - return pack_address(-1, cell_hash(state_init)); + return pack_address(MASTERCHAIN, cell_hash(state_init)); } ;; ===== BOOL ===== @@ -724,8 +727,8 @@ builder store_bool(builder b, int x) asm(x b) "1 STI"; ;; ===== MSG FLAGS ===== -const int BOUNCEABLE = 0x18; ;; 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 -const int NON_BOUNCEABLE = 0x10; ;; 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00 +const slice BOUNCEABLE = "62_"s; ;; 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 +const slice NON_BOUNCEABLE = "42_"s; ;; 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00 builder store_msg_flags(builder b, int flag) asm(flag b) "6 STU"; @@ -849,6 +852,9 @@ cell create_token_onchain_metadata(cell content_dict) inline { ;;; Returns the current length of the `tuple` [t] int tuple_length(tuple t) asm "TLEN"; +builder store_zeroes(builder b, int x) asm "STZEROES"; +builder store_ones(builder b, int x) asm "STONES"; + ;;; Concatenates two builders, but second builder stores as the reference (end_cell -> store_ref) builder store_builder_as_ref(builder to, builder from) asm(from to) "STBREF"; @@ -903,24 +909,24 @@ cont get_c2() asm "c2 PUSH"; ;;; DRAFT, not for use in prod () send(slice address, int value, builder state_init, builder body, int mode) impure inline { - builder message = begin_cell().store_slice("62_"s).store_slice(address).store_coins(value).store_uint(0, 105); + builder message = begin_cell().store_slice(BOUNCEABLE).store_slice(address).store_coins(value).store_zeroes(105); if (state_init != null()) { - message = message.store_slice("A_"s).store_builder(state_init); + message = message.store_slice("A_"s).store_builder(state_init); ;; x{A_} == b{10} } else { - message = message.store_slice("4_"s); + message = message.store_false(); } if (body != null()) { (int b1, int r1) = message.builder_rem_bits_refs(); (int b2, int r2) = body.builder_bits_refs(); if (b1 >= b2 & r1 >= r2) { - message = message.store_slice("4_"s).store_builder(body); + message = message.store_false().store_builder(body); } else { - message = message.store_slice("C_"s).store_builder_as_ref(body); + message = message.store_true().store_builder_as_ref(body); } } else { - message = message.store_slice("4_"s); + message = message.store_false(); } send_raw_message(message.end_cell(), mode); } From 583d657ebceffff59730784d050040bd1e6f7317 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Tue, 30 Jan 2024 16:51:00 +0500 Subject: [PATCH 15/28] new slice methods --- crypto/smartcont/stdlib.fc | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index e27acbf34..b40e23a70 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -664,8 +664,10 @@ const int MASTERCHAIN = -1; const slice addr_none = "2_"s; ;; addr_none$00 = MsgAddressExt; +;;; Store addr_none constuction (b{00}) to `builder` [b] builder store_addr_none(builder b) asm "b{00} STSLICECONST"; +;;; Checking that `slice` [s] is a addr_none constuction; int addr_none?(slice s) asm "b{00} PUSHSLICE SDEQ"; ;;; Checking that the address is a standard basechain address and does not have anycast (should be used after load_msg_addr) @@ -715,16 +717,24 @@ slice calc_mc_address(cell state_init) inline { const int TRUE = -1; const int FALSE = 0; +;;; Store binary true b{1} to `builder` [b] builder store_true(builder b) asm "STONE"; +;;; Store binary false b{0} to `builder` [b] builder store_false(builder b) asm "STZERO"; +;;; Store `int` [x] as bool to `builder` [b] builder store_bool(builder b, int x) asm(x b) "1 STI"; -(slice, int) ~load_bool(slice s) asm(-> 1 0) "1 LDI"; +;;; Loads bool from `slice` [s] +(slice, int) load_bool(slice s) asm(-> 1 0) "1 LDI"; -;;; skip (Maybe ^Cell) from `slice` [s]. +;;; Checks whether `int` [x] is a “boolean value" (i.e., either 0 or -1). +int bool?(int x) asm "CHKBOOL"; + +;;; Skip (Maybe ^Cell) from `slice` [s]. (slice, ()) ~skip_maybe_ref(slice s) asm "SKIPOPTREF"; (slice, ()) ~skip_dict(slice s) asm "SKIPOPTREF"; + ;; ===== MSG FLAGS ===== const slice BOUNCEABLE = "62_"s; ;; 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 @@ -870,6 +880,20 @@ int get_ton_balance() asm "BALANCE FIRST"; ;;; Returns the number of data bits and cell references that can be stored in `builder` [b]. (int, int) builder_rem_bits_refs(builder b) asm "BREMBITREFS"; +;;; Checks whether `slice` [pfx] is a prefix of `slice` [s] +int slice_check_prefix(slice s, slice pfx) asm "SDPFXREV"; +;;; Checks whether `slice` [sfx] is a suffix of `slice` [s] +int slice_check_suffix(slice s, slice sfx) asm "SDSFXREV"; +;;; Checks whether there are at least [l] data bits in `slice` [s]. +int slice_check_bits(slice s, int l) asm "SCHKBITSQ"; +;;; Checks whether there are at least [r] references in `slice` [s]. +int slice_check_refs(slice s, int r) asm "SCHKREFSQ"; +;;; Checks whether there are at least [l] data bits and [r] references in `slice` [s]. +int slice_check_bits_refs(slice s, int l, int r) asm "SCHKBITREFSQ"; + +;;; Checks whether `slice` [s] begins with (the data bits of) [pfx], and removes [pfx] from [s] on success. +;;(slice, int) slice_begins_with(slice s, slice pfx) asm "SDBEGINSXQ"; + ;;; Store `integer` [x] number as string (UTF-8) in decimal form in `builder` [b]. builder store_number_dec(builder b, int x) asm """ ZERO // b x i=0 From a797d1d4dfa8ea68629d88ed9d5726e55765a0a2 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Thu, 1 Feb 2024 22:22:15 +0500 Subject: [PATCH 16/28] stdlib.fc | new funcs (tvm v6 + address) --- crypto/smartcont/stdlib.fc | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index b40e23a70..09f620df8 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -650,12 +650,17 @@ int send_message(cell msg, int mode) impure asm "SENDMSG"; ;; TVM V6 https://github.com/ton-blockchain/ton/blob/testnet/doc/GlobalVersions.md#version-6 -int get_compute_price(int workchain, int gas_used) asm(gas_used workchain) "GETEXECUTIONPRICE"; -int get_storage_price(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEPRICE"; -int get_forward_price(int workchain, int bits, int cells) asm(cells bits workchain) "GETFORWARDPRICE"; +int get_compute_fee(int workchain, int gas_used) asm(gas_used workchain) "GETGASFEE"; +int get_storage_fee(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEFEE"; +int get_forward_fee(int workchain, int bits, int cells) asm(cells bits workchain) "GETFORWARDFEE"; int get_precompiled_gas_consumption() asm "GETPRECOMPILEDGAS"; -[slice, slice, slice, slice, slice, slice, slice] get_fee_cofigs() asm "UNPACKEDCONFIGTUPLE"; +int get_simple_compute_fee(int workchain, int gas_used) asm(gas_used workchain) "GETGASFEESIMPLE"; +int get_simple_forward_fee(int workchain, int bits, int cells) asm(cells bits workchain) "GETFORWARDFEESIMPLE"; +int get_original_fwd_fee(int workchain, int fwd_fee) asm(fwd_fee workchain) "GETORIGINALFWDFEE"; +int my_storage_due() asm "DUEPAYMENT"; + +tuple get_fee_cofigs() asm "UNPACKEDCONFIGTUPLE"; ;; ===== ADDRESS ===== @@ -675,6 +680,9 @@ int validate_addr_bc(slice addr) asm "b{10000000000} PUSHSLICE SDPPFXREV"; ;;; Checking that the address is a standard masterchain address and does not have anycast (should be used after load_msg_addr) int validate_addr_mc(slice addr) asm "b{10011111111} PUSHSLICE SDPPFXREV"; +builder store_bc_address(builder b, cell state_init) asm "HASHCU SWAP b{10000000000} STSLICECONST 256 STU"; +builder store_mc_address(builder b, cell state_init) asm "HASHCU SWAP b{10011111111} STSLICECONST 256 STU"; + ;;; Checking that the `slice` [addr] is a standard basechain address and does not have anycast (can be used with any `slice`) int ext_validate_addr_bc(slice addr) asm """ DUP @@ -769,7 +777,9 @@ builder store_query_id(builder b, int query_id) asm(query_id b) "64 STU"; ;;; Load standard uint64 query id from `slice` [s] (slice, int) load_query_id(slice s) asm(-> 1 0) "64 LDU"; -(slice, (int, int)) ~load_op_and_query_id(slice s) asm(->2 0 1) "32 LDU 64 LDU"; +(slice, (int, int)) ~load_op_and_query_id(slice s) asm(-> 2 0 1) "32 LDU 64 LDU"; + +(slice, ()) ~skip_query_id(slice s) asm "64 INT SDSKIPFIRST"; ;; ===== SEND ===== @@ -935,16 +945,16 @@ cont get_c2() asm "c2 PUSH"; () send(slice address, int value, builder state_init, builder body, int mode) impure inline { builder message = begin_cell().store_slice(BOUNCEABLE).store_slice(address).store_coins(value).store_zeroes(105); - if (state_init != null()) { + ifnot (state_init.null?()) { message = message.store_slice("A_"s).store_builder(state_init); ;; x{A_} == b{10} } else { message = message.store_false(); } - if (body != null()) { + ifnot (body.null?()) { (int b1, int r1) = message.builder_rem_bits_refs(); (int b2, int r2) = body.builder_bits_refs(); - if (b1 >= b2 & r1 >= r2) { + if ((b1 >= b2) & (r1 >= r2)) { message = message.store_false().store_builder(body); } else { message = message.store_true().store_builder_as_ref(body); From 97004fc2bc2f1932ca16d083d636a980b2f97997 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 7 Feb 2024 00:41:04 +0500 Subject: [PATCH 17/28] stdlib.fc | more funcs for dicts --- crypto/smartcont/stdlib.fc | 196 +++++++++++++++++++++++++++++++++---- 1 file changed, 179 insertions(+), 17 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 09f620df8..e833581c4 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -186,18 +186,18 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI ;;; The total count of visited cells `x` cannot exceed non-negative [max_cells]; ;;; otherwise the computation is aborted before visiting the `(max_cells + 1)`-st cell and ;;; a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`. -(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE"; +(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ;;; Similar to [compute_data_size?], but accepting a `slice` [s] instead of a `cell`. ;;; The returned value of `x` does not take into account the cell that contains the `slice` [s] itself; ;;; however, the data bits and the cell references of [s] are accounted for in `y` and `z`. -(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; +(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ;;; A non-quiet version of [compute_data_size?] that throws a cell overflow exception (`8`) on failure. -(int, int, int, int) compute_data_size?(cell c, int max_cells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int) compute_data_size(cell c, int max_cells) impure asm "CDATASIZE"; ;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure. -(int, int, int, int) slice_compute_data_size?(cell c, int max_cells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; ;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) ;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; @@ -565,18 +565,18 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va (cell, int) udict_replace_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEB"; (cell, int) 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" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dict_delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; -(cell, int, slice, int) udict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dict_delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::delete_get_max(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_min?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict_delete_get_min?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_min?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict_delete_get_min?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_min?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict_delete_get_min?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) udict_delete_get_max?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~udict_delete_get_max?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) idict_delete_get_max?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~idict_delete_get_max?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) dict_delete_get_max?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~dict_delete_get_max?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; (int, slice, int) udict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; (int, slice, int) udict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; (int, cell, int) udict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; @@ -648,6 +648,18 @@ cell my_code() asm "MYCODE"; ;;; Creates an output action and returns a fee for creating a message. Mode has the same effect as in the case of SENDRAWMSG int send_message(cell msg, int mode) impure asm "SENDMSG"; +;;; Retrieves value of storage phase fees from c7 +int storage_fees() asm "STORAGEFEES"; + +;;; Retrieves global_id from 19 network config +int global_id() asm "GLOBALID"; + +;;; Returns gas consumed by VM so far (including this instruction). +int gas_consumed() asm "GASCONSUMED"; + +int cell_level(cell c) asm "CLEVEL"; +int cell_level_mask(cell c) asm "CLEVELMASK"; + ;; TVM V6 https://github.com/ton-blockchain/ton/blob/testnet/doc/GlobalVersions.md#version-6 int get_compute_fee(int workchain, int gas_used) asm(gas_used workchain) "GETGASFEE"; @@ -875,6 +887,27 @@ int tuple_length(tuple t) asm "TLEN"; builder store_zeroes(builder b, int x) asm "STZEROES"; builder store_ones(builder b, int x) asm "STONES"; +builder store_varuint16(builder b, int x) asm "STVARUINT16"; +builder store_varint16(builder b, int x) asm "STVARINT16"; +builder store_varuint32(builder b, int x) asm "STVARUINT32"; +builder store_varint32(builder b, int x) asm "STVARINT32"; + +(slice, int) load_varuint16(slice s) asm(-> 1 0) "LDVARUINT16"; +(slice, int) load_varint16(slice s) asm(-> 1 0) "LDVARINT16"; +(slice, int) load_varuint32(slice s) asm(-> 1 0) "LDVARUINT32"; +(slice, int) load_varint32(slice s) asm(-> 1 0) "LDVARINT32"; + +;;; Creates an output action that would modify the collection of this smart contract +;;; libraries by adding or removing library with code given in `Cell` [code]. +;;; Modes: 0 - remove library, 1 - add private library, 2 - add public library +() set_library(cell code, int mode) impure asm "SETLIBCODE"; + +(slice, ()) ~skip_op(slice s) asm "32 LDU NIP"; +(slice, ()) ~skip_query_id(slice s) asm "32 LDU NIP"; +(slice, ()) ~skip_ref(slice s) asm "LDREF NIP"; +(slice, ()) ~skip_coins(slice s) asm "LDGRAMS NIP"; +(slice, ()) ~skip_msg_addr(slice s) asm "LDMSGADDR NIP"; + ;;; Concatenates two builders, but second builder stores as the reference (end_cell -> store_ref) builder store_builder_as_ref(builder to, builder from) asm(from to) "STBREF"; @@ -965,7 +998,6 @@ cont get_c2() asm "c2 PUSH"; send_raw_message(message.end_cell(), mode); } - ;; ===== STANDARD OP's ===== ;; Common (used in TEP-62,74,85) @@ -1003,3 +1035,133 @@ const slice op::revoke_sbt = "6f89f5e3"s; ;; https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md const slice op::provide_wallet_address = "2c76b973"s; const slice op::take_wallet_address = "d1735400"s; + +;; ===== DICTS (missing funcs) ===== + +cell dict_get_ref(cell dict, int key_len, slice index) asm(index dict key_len) "DICTGETOPTREF"; +cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETOPTREF"; + +(cell, cell) dict_set_get_ref(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTSETGETOPTREF"; + +cell dict_set_ref(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTSETREF"; +(cell, ()) ~dict_set_ref(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTSETREF"; + +(cell, int) dict_get_ref?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTGETREF" "NULLSWAPIFNOT"; +(cell, int) dict_delete?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTDEL"; +(slice, int) dict_get?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTGET" "NULLSWAPIFNOT"; + +(cell, slice, int) dict_delete_get?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~dict_delete_get?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTDELGET" "NULLSWAPIFNOT"; + +(cell, int) dict_replace?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTREPLACE"; +(cell, int) dict_add?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTADD"; + +(cell, int) dict_replace_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTREPLACEB"; +(cell, int) dict_add_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTADDB"; + +(slice, slice, int) dict_get_min?(cell dict, int key_len) asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2"; +(slice, slice, int) dict_get_max?(cell dict, int key_len) asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2"; +(slice, cell, int) dict_get_min_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2"; +(slice, cell, int) dict_get_max_ref?(cell dict, int key_len) asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2"; +(slice, slice, int) dict_get_next?(cell dict, int key_len, slice pivot) asm(pivot dict key_len -> 1 0 2) "DICTGETNEXT" "NULLSWAPIFNOT2"; +(slice, slice, int) dict_get_nexteq?(cell dict, int key_len, slice pivot) asm(pivot dict key_len -> 1 0 2) "DICTGETNEXTEQ" "NULLSWAPIFNOT2"; +(slice, slice, int) dict_get_prev?(cell dict, int key_len, slice pivot) asm(pivot dict key_len -> 1 0 2) "DICTGETPREV" "NULLSWAPIFNOT2"; +(slice, slice, int) dict_get_preveq?(cell dict, int key_len, slice pivot) asm(pivot dict key_len -> 1 0 2) "DICTGETPREVEQ" "NULLSWAPIFNOT2"; + +;; ===== DICTS (new funcs) ===== + +(slice, cell, int) load_dict?(slice s) asm(-> 1 0 2) "LDDICTQ" "NULLROTRIFNOT"; +(slice, (cell, int)) ~load_dict?(slice s) asm(-> 1 0 2) "LDDICTQ" "NULLROTRIFNOT"; +(cell, int) preload_dict?(slice s) asm "PLDDICTQ" "NULLSWAPIFNOT"; + +(cell, slice, int) dict_set_get?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSETGET" "NULLSWAPIFNOT"; +(cell, slice, int) udict_set_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSETGET" "NULLSWAPIFNOT"; +(cell, slice, int) idict_set_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISETGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~dict_set_get?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTSETGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_set_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUSETGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_set_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTISETGET" "NULLSWAPIFNOT"; + +(cell, cell, int) dict_set_get_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTSETGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) udict_set_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) idict_set_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~dict_set_get_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTSETGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~udict_set_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~idict_set_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETREF" "NULLSWAPIFNOT"; + +(cell, slice, int) dict_set_get_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETGETB" "NULLSWAPIFNOT"; +(cell, slice, int) udict_set_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETGETB" "NULLSWAPIFNOT"; +(cell, slice, int) idict_set_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~dict_set_get_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTSETGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_set_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUSETGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_set_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTISETGETB" "NULLSWAPIFNOT"; + +(cell, int) dict_replace_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTREPLACEREF"; +(cell, int) udict_replace_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUREPLACEREF"; +(cell, int) idict_replace_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIREPLACEREF"; + +(cell, slice, int) dict_replace_get?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTREPLACEGET" "NULLSWAPIFNOT"; +(cell, slice, int) udict_replace_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACEGET" "NULLSWAPIFNOT"; +(cell, slice, int) idict_replace_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACEGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~dict_replace_get?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTREPLACEGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_replace_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUREPLACEGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_replace_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIREPLACEGET" "NULLSWAPIFNOT"; + +(cell, cell, int) dict_replace_get_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) udict_replace_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) idict_replace_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~dict_replace_get_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~udict_replace_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~idict_replace_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIREPLACEGETREF" "NULLSWAPIFNOT"; + +(cell, slice, int) dict_replace_get_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTREPLACEGETB" "NULLSWAPIFNOT"; +(cell, slice, int) udict_replace_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEGETB" "NULLSWAPIFNOT"; +(cell, slice, int) idict_replace_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~dict_replace_get_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~udict_replace_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~idict_replace_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIREPLACEGETB" "NULLSWAPIFNOT"; + +(cell, int) dict_add_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTADDREF"; +(cell, int) udict_add_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUADDREF"; +(cell, int) idict_add_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIADDREF"; + +(cell, slice, int) dict_add_get?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTADDGET" "NULLSWAPIF"; +(cell, slice, int) udict_add_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADDGET" "NULLSWAPIF"; +(cell, slice, int) idict_add_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADDGET" "NULLSWAPIF"; +(cell, (slice, int)) ~dict_add_get?(cell dict, int key_len, slice index, slice value) asm(value index dict key_len) "DICTADDGET" "NULLSWAPIF"; +(cell, (slice, int)) ~udict_add_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTUADDGET" "NULLSWAPIF"; +(cell, (slice, int)) ~idict_add_get?(cell dict, int key_len, int index, slice value) asm(value index dict key_len) "DICTIADDGET" "NULLSWAPIF"; + +(cell, cell, int) dict_add_get_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTADDGETREF" "NULLSWAPIF"; +(cell, cell, int) udict_add_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUADDGETREF" "NULLSWAPIF"; +(cell, cell, int) idict_add_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIADDGETREF" "NULLSWAPIF"; +(cell, (cell, int)) ~dict_add_get_ref?(cell dict, int key_len, slice index, cell value) asm(value index dict key_len) "DICTADDGETREF" "NULLSWAPIF"; +(cell, (cell, int)) ~udict_add_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUADDGETREF" "NULLSWAPIF"; +(cell, (cell, int)) ~idict_add_get_ref?(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTIADDGETREF" "NULLSWAPIF"; + +(cell, slice, int) dict_add_get_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTADDGETB" "NULLSWAPIF"; +(cell, slice, int) udict_add_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDGETB" "NULLSWAPIF"; +(cell, slice, int) idict_add_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDGETB" "NULLSWAPIF"; +(cell, (slice, int)) ~dict_add_get_builder?(cell dict, int key_len, slice index, builder value) asm(value index dict key_len) "DICTADDGETB" "NULLSWAPIF"; +(cell, (slice, int)) ~udict_add_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTUADDGETB" "NULLSWAPIF"; +(cell, (slice, int)) ~idict_add_get_builder?(cell dict, int key_len, int index, builder value) asm(value index dict key_len) "DICTIADDGETB" "NULLSWAPIF"; + +(cell, cell, int) dict_delete_get_ref?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTDELGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) udict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) idict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~dict_delete_get_ref?(cell dict, int key_len, slice index) asm(index dict key_len) "DICTDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~udict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~idict_delete_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDELGETREF" "NULLSWAPIFNOT"; + +(cell, slice, cell, int) dict_delete_get_min_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMINREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) udict_delete_get_min_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMINREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) idict_delete_get_min_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMINREF" "NULLSWAPIFNOT2"; +(cell, (slice, cell, int)) ~dict_delete_get_min_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMINREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~udict_delete_get_min_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMINREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~idict_delete_get_min_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMINREF" "NULLSWAPIFNOT2"; + +(cell, slice, cell, int) dict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAXREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) udict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAXREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) idict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAXREF" "NULLSWAPIFNOT2"; +(cell, (slice, cell, int)) ~dict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAXREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~udict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAXREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~idict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAXREF" "NULLSWAPIFNOT2"; From 481f5a24bdf9361b30c44e0d4c96b2612f2d62b9 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Thu, 8 Feb 2024 23:49:38 +0500 Subject: [PATCH 18/28] stdlib.fc | hash funcs + signatures --- crypto/smartcont/stdlib.fc | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index e833581c4..4ac94ea8b 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -754,7 +754,6 @@ int bool?(int x) asm "CHKBOOL"; (slice, ()) ~skip_maybe_ref(slice s) asm "SKIPOPTREF"; (slice, ()) ~skip_dict(slice s) asm "SKIPOPTREF"; - ;; ===== MSG FLAGS ===== const slice BOUNCEABLE = "62_"s; ;; 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 @@ -791,7 +790,8 @@ builder store_query_id(builder b, int query_id) asm(query_id b) "64 STU"; (slice, (int, int)) ~load_op_and_query_id(slice s) asm(-> 2 0 1) "32 LDU 64 LDU"; -(slice, ()) ~skip_query_id(slice s) asm "64 INT SDSKIPFIRST"; +(slice, ()) ~skip_op(slice s) asm "32 LDU NIP"; +(slice, ()) ~skip_query_id(slice s) asm "32 LDU NIP"; ;; ===== SEND ===== @@ -902,12 +902,15 @@ builder store_varint32(builder b, int x) asm "STVARINT32"; ;;; Modes: 0 - remove library, 1 - add private library, 2 - add public library () set_library(cell code, int mode) impure asm "SETLIBCODE"; -(slice, ()) ~skip_op(slice s) asm "32 LDU NIP"; -(slice, ()) ~skip_query_id(slice s) asm "32 LDU NIP"; (slice, ()) ~skip_ref(slice s) asm "LDREF NIP"; (slice, ()) ~skip_coins(slice s) asm "LDGRAMS NIP"; (slice, ()) ~skip_msg_addr(slice s) asm "LDMSGADDR NIP"; +cell preload_first_ref(slice s) asm "0 PLDREFIDX"; +cell preload_second_ref(slice s) asm "1 PLDREFIDX"; +cell preload_third_ref(slice s) asm "2 PLDREFIDX"; +cell preload_fourth_ref(slice s) asm "3 PLDREFIDX"; + ;;; Concatenates two builders, but second builder stores as the reference (end_cell -> store_ref) builder store_builder_as_ref(builder to, builder from) asm(from to) "STBREF"; @@ -1165,3 +1168,24 @@ cell dict_set_ref(cell dict, int key_len, slice index, cell value) asm(value ind (cell, (slice, cell, int)) ~dict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMAXREF" "NULLSWAPIFNOT2"; (cell, (int, cell, int)) ~udict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTUREMMAXREF" "NULLSWAPIFNOT2"; (cell, (int, cell, int)) ~idict_delete_get_max_ref?(cell dict, int key_len) asm(-> 0 2 1 3) "DICTIREMMAXREF" "NULLSWAPIFNOT2"; + +;; ===== HASHES ===== + +int sha256_from_snake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_SHA256 }> 1 1 CALLXARGS"; +[int, int] sha512_from_snake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_SHA512 }> 1 1 CALLXARGS"; +[int, int] blake2b_from_snake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_BLAKE2B }> 1 1 CALLXARGS"; +int keccak256_from_snake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_KECCAK256 }> 1 1 CALLXARGS"; +[int, int] keccak512_from_snake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_KECCAK512 }> 1 1 CALLXARGS"; + +builder store_sha256_from_snake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_SHA256 }> 2 1 CALLXARGS"; +builder store_sha512_from_snake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_SHA512 }> 2 1 CALLXARGS"; +builder store_blake2b_from_snake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_BLAKE2B }> 2 1 CALLXARGS"; +builder store_keccak256_from_snake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_KECCAK256 }> 2 1 CALLXARGS"; +builder store_keccak512_from_snake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_KECCAK512 }> 2 1 CALLXARGS"; + +;; ===== SIGNATURES ===== + +int check_secp256r1_signature(int hash, slice signature, int public_key) asm "P256_CHKSIGNU"; +int check_secp256r1_data_signature(slice data, slice signature, int public_key) asm "P256_CHKSIGNS"; +int check_bls_signature(slice data, slice signature, int public_key) asm(public_key data signature) "BLS_VERIFY"; + From 838502dbd5756b1347d8a35a415ac8498ce65c5f Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 14 Feb 2024 01:24:23 +0500 Subject: [PATCH 19/28] four new camelCase aliases for builtin funcs + cosmetic --- crypto/func/builtins.cpp | 47 +++++++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index eeed9e587..308d95fd2 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -1338,26 +1338,6 @@ void define_builtins() { define_builtin_func("cell_at", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); define_builtin_func("slice_at", TypeExpr::new_map(TupleInt, Slice), compile_tuple_at); define_builtin_func("tuple_at", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at); - define_builtin_func("isNull", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null); - define_builtin_func("throwIf", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); - define_builtin_func("throwUnless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true); - define_builtin_func("throwArg", throw_arg_op, compile_throw_arg, true); - define_builtin_func("throwArgIf", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, true), true); - define_builtin_func("throwArgUnless", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, false), true); - define_builtin_func("loadInt", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0}); - define_builtin_func("loadUint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0}); - define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true)); - define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false)); - define_builtin_func("storeInt", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); - define_builtin_func("storeUint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); - define_builtin_func("~storeInt", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); - define_builtin_func("~storeUint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); - define_builtin_func("loadBits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0}); - define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false)); - define_builtin_func("intAt", TypeExpr::new_map(TupleInt, Int), compile_tuple_at); - define_builtin_func("cellAt", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); - define_builtin_func("sliceAt", TypeExpr::new_map(TupleInt, Slice), compile_tuple_at); - define_builtin_func("tupleAt", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at); define_builtin_func("at", TypeExpr::new_forall({X}, TypeExpr::new_map(TupleInt, X)), compile_tuple_at); define_builtin_func("touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, X)), AsmOp::Nop()); define_builtin_func("~touch", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))), @@ -1386,6 +1366,33 @@ void define_builtins() { define_builtin_func( "run_method3", TypeExpr::new_forall({X, Y, Z}, TypeExpr::new_map(TypeExpr::new_tensor({Int, X, Y, Z}), Unit)), [](AsmOpList& a, auto b, auto c) { return compile_run_method(a, b, c, 3, false); }, {1, 2, 3, 0}, {}, true); + + // camelCase aliases + define_builtin_func("isNull", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null); + define_builtin_func("throwIf", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true); + define_builtin_func("throwUnless", impure_bin_op, std::bind(compile_cond_throw, _1, _2, false), true); + define_builtin_func("throwArg", throw_arg_op, compile_throw_arg, true); + define_builtin_func("throwArgIf", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, true), true); + define_builtin_func("throwArgUnless", cond_throw_arg_op, std::bind(compile_cond_throw_arg, _1, _2, false), true); + define_builtin_func("loadInt", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, true), {}, {1, 0}); + define_builtin_func("loadUint", fetch_int_op, std::bind(compile_fetch_int, _1, _2, true, false), {}, {1, 0}); + define_builtin_func("preloadInt", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, true)); + define_builtin_func("preloadUint", prefetch_int_op, std::bind(compile_fetch_int, _1, _2, false, false)); + define_builtin_func("storeInt", store_int_op, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); + define_builtin_func("storeUint", store_int_op, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); + define_builtin_func("~storeInt", store_int_method, std::bind(compile_store_int, _1, _2, true), {1, 0, 2}); + define_builtin_func("~storeUint", store_int_method, std::bind(compile_store_int, _1, _2, false), {1, 0, 2}); + define_builtin_func("loadBits", fetch_slice_op, std::bind(compile_fetch_slice, _1, _2, true), {}, {1, 0}); + define_builtin_func("preloadBits", prefetch_slice_op, std::bind(compile_fetch_slice, _1, _2, false)); + define_builtin_func("intAt", TypeExpr::new_map(TupleInt, Int), compile_tuple_at); + define_builtin_func("cellAt", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); + define_builtin_func("sliceAt", TypeExpr::new_map(TupleInt, Slice), compile_tuple_at); + define_builtin_func("tupleAt", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at); + define_builtin_func("sliceBeginsWith", TypeExpr::new_map(TypeExpr::new_tensor({Slice, Slice}), SliceInt), + compile_slice_begins, true); + define_builtin_func("storeSlice", store_slice_op, compile_store_slice, {1, 0}); + define_builtin_func("~storeSlice", store_slice_method, compile_store_slice, {1, 0}); + define_builtin_func("packAddress", TypeExpr::new_map(Int2, Slice), compile_pack_address, {1, 0}); } } // namespace funC From e3bf85bda2135dad8b674c0c7db428747eb8893f Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 14 Feb 2024 03:25:29 +0500 Subject: [PATCH 20/28] fix "slice_begins_with?" name + alias --- crypto/func/builtins.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crypto/func/builtins.cpp b/crypto/func/builtins.cpp index 308d95fd2..df5014234 100644 --- a/crypto/func/builtins.cpp +++ b/crypto/func/builtins.cpp @@ -1349,7 +1349,7 @@ void define_builtins() { AsmOp::Custom("s0 DUMP", 1, 1), true); define_builtin_func("~strdump", TypeExpr::new_forall({X}, TypeExpr::new_map(X, TypeExpr::new_tensor({X, Unit}))), AsmOp::Custom("STRDUMP", 1, 1), true); - define_builtin_func("slice_begins_with", TypeExpr::new_map(TypeExpr::new_tensor({Slice, Slice}), SliceInt), + define_builtin_func("slice_begins_with?", TypeExpr::new_map(TypeExpr::new_tensor({Slice, Slice}), SliceInt), compile_slice_begins, true); define_builtin_func("store_slice", store_slice_op, compile_store_slice, {1, 0}); define_builtin_func("~store_slice", store_slice_method, compile_store_slice, {1, 0}); @@ -1388,7 +1388,7 @@ void define_builtins() { define_builtin_func("cellAt", TypeExpr::new_map(TupleInt, Cell), compile_tuple_at); define_builtin_func("sliceAt", TypeExpr::new_map(TupleInt, Slice), compile_tuple_at); define_builtin_func("tupleAt", TypeExpr::new_map(TupleInt, Tuple), compile_tuple_at); - define_builtin_func("sliceBeginsWith", TypeExpr::new_map(TypeExpr::new_tensor({Slice, Slice}), SliceInt), + define_builtin_func("isSliceBeginsWith", TypeExpr::new_map(TypeExpr::new_tensor({Slice, Slice}), SliceInt), compile_slice_begins, true); define_builtin_func("storeSlice", store_slice_op, compile_store_slice, {1, 0}); define_builtin_func("~storeSlice", store_slice_method, compile_store_slice, {1, 0}); From b1c3079b9082f372719509777c778640416dbabe Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 14 Feb 2024 03:26:34 +0500 Subject: [PATCH 21/28] stdlib.fc | funcs for c5 register --- crypto/smartcont/stdlib.fc | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index 4ac94ea8b..ecc033913 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -692,8 +692,8 @@ int validate_addr_bc(slice addr) asm "b{10000000000} PUSHSLICE SDPPFXREV"; ;;; Checking that the address is a standard masterchain address and does not have anycast (should be used after load_msg_addr) int validate_addr_mc(slice addr) asm "b{10011111111} PUSHSLICE SDPPFXREV"; -builder store_bc_address(builder b, cell state_init) asm "HASHCU SWAP b{10000000000} STSLICECONST 256 STU"; -builder store_mc_address(builder b, cell state_init) asm "HASHCU SWAP b{10011111111} STSLICECONST 256 STU"; +builder store_bc_address(builder b, int hash) asm(hash b) "b{10000000000} STSLICECONST 256 STU"; +builder store_mc_address(builder b, int hash) asm(hash b) "b{10011111111} STSLICECONST 256 STU"; ;;; Checking that the `slice` [addr] is a standard basechain address and does not have anycast (can be used with any `slice`) int ext_validate_addr_bc(slice addr) asm """ @@ -973,9 +973,13 @@ builder store_number_hex(builder b, int x) asm """ """; ;;; Returns the continuation located in register c2 -cont get_c2() asm "c2 PUSH"; +((int, int) -> ()) get_c2() asm "c2 PUSH"; ;;; Store `continuation` [c] to register c2 -() set_c2(cont c) impure asm "c2 POP"; +() set_c2(((int, int) -> ()) c) impure asm "c2 POP"; +;;; Store `cell` [actions] to register c5 (out actions) +() set_actions(cell actions) impure asm "c5 POP"; +;;; Store empty cell to register c5 (out actions) +() clean_actions() impure asm " PUSHREF c5 POP"; ;;; DRAFT, not for use in prod () send(slice address, int value, builder state_init, builder body, int mode) impure inline { From 30f8388cc24560e641bd2277e88d3f811a08f25d Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Wed, 14 Feb 2024 03:26:43 +0500 Subject: [PATCH 22/28] Update stdlibCamel.fc --- crypto/smartcont/stdlibCamel.fc | 596 ++++++++++++++++++++++++++++++-- 1 file changed, 576 insertions(+), 20 deletions(-) diff --git a/crypto/smartcont/stdlibCamel.fc b/crypto/smartcont/stdlibCamel.fc index dba362440..cd73fe744 100644 --- a/crypto/smartcont/stdlibCamel.fc +++ b/crypto/smartcont/stdlibCamel.fc @@ -186,18 +186,18 @@ int checkDataSignature(slice data, slice signature, int publicKey) asm "CHKSIGNS ;;; The total count of visited cells `x` cannot exceed non-negative [max_cells]; ;;; otherwise the computation is aborted before visiting the `(max_cells + 1)`-st cell and ;;; a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`. -(int, int, int) computeDataSize(cell c, int maxCells) impure asm "CDATASIZE"; +(int, int, int, int) tryComputeDataSize(cell c, int maxCells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ;;; Similar to [compute_data_size?], but accepting a `slice` [s] instead of a `cell`. ;;; The returned value of `x` does not take into account the cell that contains the `slice` [s] itself; ;;; however, the data bits and the cell references of [s] are accounted for in `y` and `z`. -(int, int, int) sliceComputeDataSize(slice s, int maxCells) impure asm "SDATASIZE"; +(int, int, int, int) trySliceComputeDataSize(cell c, int maxCells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; ;;; A non-quiet version of [compute_data_size?] that throws a cell overflow exception (`8`) on failure. -(int, int, int, int) tryComputeDataSize(cell c, int maxCells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int) computeDataSize(cell c, int maxCells) impure asm "CDATASIZE"; ;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure. -(int, int, int, int) trySliceComputeDataSize(cell c, int maxCells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; +(int, int, int) sliceComputeDataSize(slice s, int maxCells) impure asm "SDATASIZE"; ;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) ;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; @@ -432,14 +432,14 @@ cell endCell(builder b) asm "ENDC"; builder storeRef(builder b, cell c) asm(c b) "STREF"; ;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. -;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; +;; builder storeUint(builder b, int x, int len) asm(x b len) "STUX"; ;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. -;; builder store_int(builder b, int x, int len) asm(x b len) "STIX"; +;; builder storeInt(builder b, int x, int len) asm(x b len) "STIX"; ;;; Stores `slice` [s] into `builder` [b] -builder storeSlice(builder b, slice s) asm "STSLICER"; +;; builder storeSlice(builder b, slice s) asm "STSLICER"; ;;; Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. ;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, @@ -565,18 +565,18 @@ cell dictSetBuilder(cell dict, int keyLen, slice index, builder value) asm(value (cell, int) tryUdictReplaceBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUREPLACEB"; (cell, int) tryIdictAddBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIADDB"; (cell, int) tryIdictReplaceBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIREPLACEB"; -(cell, int, slice, int) udictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::deleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::deleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::deleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; -(cell, int, slice, int) udictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~udict::deleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; -(cell, int, slice, int) idictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, (int, slice, int)) ~idict::deleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; -(cell, slice, slice, int) dictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; -(cell, (slice, slice, int)) ~dict::deleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) tryUdictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~tryUdictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) tryIdictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~tryIdictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMIN" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) tryDictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~tryDictDeleteGetMin(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT2"; +(cell, int, slice, int) tryUdictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~tryUdictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAX" "NULLSWAPIFNOT2"; +(cell, int, slice, int) tryIdictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, (int, slice, int)) ~tryIdictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAX" "NULLSWAPIFNOT2"; +(cell, slice, slice, int) tryDictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; +(cell, (slice, slice, int)) ~tryDictDeleteGetMax(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAX" "NULLSWAPIFNOT2"; (int, slice, int) tryUdictGetMin(cell dict, int keyLen) asm (-> 1 0 2) "DICTUMIN" "NULLSWAPIFNOT2"; (int, slice, int) tryUdictGetMax(cell dict, int keyLen) asm (-> 1 0 2) "DICTUMAX" "NULLSWAPIFNOT2"; (int, cell, int) tryUdictGetMinRef(cell dict, int keyLen) asm (-> 1 0 2) "DICTUMINREF" "NULLSWAPIFNOT2"; @@ -636,4 +636,560 @@ int getSeed() impure asm "RANDSEED"; int equalSliceBits (slice a, slice b) asm "SDEQ"; ;;; Concatenates two builders -builder storeBuilder(builder to, builder from) asm "STBR"; +builder storeBuilder(builder to, builder from) asm(from to) "STB"; + +;; NEW FUNCS + +;; ===== TVM UPGRADE ===== + +;;; Retrieves code of smart-contract from c7 +cell myCode() asm "MYCODE"; + +;;; Creates an output action and returns a fee for creating a message. Mode has the same effect as in the case of SENDRAWMSG +int sendMessage(cell msg, int mode) impure asm "SENDMSG"; + +;;; Retrieves value of storage phase fees from c7 +int storageFees() asm "STORAGEFEES"; + +;;; Retrieves global_id from 19 network config +int globalId() asm "GLOBALID"; + +;;; Returns gas consumed by VM so far (including this instruction). +int gasConsumed() asm "GASCONSUMED"; + +int cellLevel(cell c) asm "CLEVEL"; +int cellLevelMask(cell c) asm "CLEVELMASK"; + +;; TVM V6 https://github.com/ton-blockchain/ton/blob/testnet/doc/GlobalVersions.md#version-6 + +int getComputeFee(int workchain, int gasUsed) asm(gasUsed workchain) "GETGASFEE"; +int getStorageFee(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEFEE"; +int getForwardFee(int workchain, int bits, int cells) asm(cells bits workchain) "GETFORWARDFEE"; +int getPrecompiledGasConsumption() asm "GETPRECOMPILEDGAS"; + +int getSimpleComputeFee(int workchain, int gasUsed) asm(gasUsed workchain) "GETGASFEESIMPLE"; +int getSimpleForwardFee(int workchain, int bits, int cells) asm(cells bits workchain) "GETFORWARDFEESIMPLE"; +int getOriginalFwdFee(int workchain, int fwdFee) asm(fwdFee workchain) "GETORIGINALFWDFEE"; +int myStorageDue() asm "DUEPAYMENT"; + +tuple getFeeCofigs() asm "UNPACKEDCONFIGTUPLE"; + +;; ===== ADDRESS ===== + +const int BASECHAIN = 0; +const int MASTERCHAIN = -1; + +const slice addrNone = "2_"s; ;; addr_none$00 = MsgAddressExt; + +;;; Store addr_none constuction (b{00}) to `builder` [b] +builder storeAddrNone(builder b) asm "b{00} STSLICECONST"; + +;;; Checking that `slice` [s] is a addr_none constuction; +int isAddrNone(slice s) asm "b{00} PUSHSLICE SDEQ"; + +;;; Checking that the address is a standard basechain address and does not have anycast (should be used after load_msg_addr) +int validateAddrBc(slice addr) asm "b{10000000000} PUSHSLICE SDPPFXREV"; +;;; Checking that the address is a standard masterchain address and does not have anycast (should be used after load_msg_addr) +int validateAddrMc(slice addr) asm "b{10011111111} PUSHSLICE SDPPFXREV"; + +builder storeBcAddress(builder b, int hash) asm(hash b) "b{10000000000} STSLICECONST 256 STU"; +builder storeMcAddress(builder b, int hash) asm(hash b) "b{10011111111} STSLICECONST 256 STU"; + +;;; Checking that the `slice` [addr] is a standard basechain address and does not have anycast (can be used with any `slice`) +int extValidateAddrBc(slice addr) asm """ + DUP + b{10000000000} PUSHSLICE SDPPFXREV SWAP + 267 INT 0 INT SCHKBITREFSQ + AND +"""; +;;; Checking that the `slice` [addr] is a standard masterchain address and does not have anycast (can be used with any `slice`) +int extValidateAddrMc(slice addr) asm """ + DUP + b{10011111111} PUSHSLICE SDPPFXREV SWAP + 267 INT 0 INT SCHKBITREFSQ + AND +"""; + +;;; Checking that [addr1] and [addr2] have the same workchain +int isWorkchainsEqual(slice addr1, slice addr2) asm "REWRITESTDADDR DROP SWAP REWRITESTDADDR DROP EQUAL"; +;;; Checking that [addr] have the workchain [wc] +int isWorkchainMatch(slice addr, int wc) asm(wc addr) "REWRITESTDADDR DROP EQUAL"; +;;; Checking that [addr] have the workchain 0 +int isBasechainAddr(slice addr) asm "REWRITESTDADDR DROP 0 EQINT"; +;;; Checking that [addr] have the workchain -1 +int isMasterchainAddr(slice addr) asm "REWRITESTDADDR DROP -1 EQINT"; + +;;; Basic store StateInit construction in `builder` [b]. +builder storeStateInit(builder b, cell data, cell code) asm(data code b) "b{00110} STSLICECONST STREF STREF"; + +;;; Calculate standard basechain address from `state_init` +slice calcBcAddress(cell stateInit) inline { + return packAddress(BASECHAIN, cellHash(stateInit)); +} + +;;; Calculate standard masterchain address from `state_init` +slice calcMcAddress(cell stateInit) inline { + return packAddress(MASTERCHAIN, cellHash(stateInit)); +} + +;; ===== BOOL ===== + +const int TRUE = -1; +const int FALSE = 0; + +;;; Store binary true b{1} to `builder` [b] +builder storeTrue(builder b) asm "STONE"; +;;; Store binary false b{0} to `builder` [b] +builder storeFalse(builder b) asm "STZERO"; +;;; Store `int` [x] as bool to `builder` [b] +builder storeBool(builder b, int x) asm(x b) "1 STI"; + +;;; Loads bool from `slice` [s] +(slice, int) loadBool(slice s) asm(-> 1 0) "1 LDI"; + +;;; Checks whether `int` [x] is a “boolean value" (i.e., either 0 or -1). +int isBool(int x) asm "CHKBOOL"; + +;;; Skip (Maybe ^Cell) from `slice` [s]. +(slice, ()) ~skipMaybeRef(slice s) asm "SKIPOPTREF"; +(slice, ()) ~skipDict(slice s) asm "SKIPOPTREF"; + +;; ===== MSG FLAGS ===== + +const slice BOUNCEABLE = "62_"s; ;; 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 +const slice NON_BOUNCEABLE = "42_"s; ;; 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00 + +builder storeMsgFlags(builder b, int flag) asm(flag b) "6 STU"; + +builder storeMsgFlagsBounceable(builder b) asm "b{011000} STSLICECONST"; +builder storeMsgFlagsNonBounceable(builder b) asm "b{010000} STSLICECONST"; + +;; load msg_flags only +(slice, int) ~loadMsgFlags(slice s) asm(-> 1 0) "4 LDU"; + +;;; Basic parse MessageX (full_message), returns: flags, sender, forward fee +(int, slice, int) parseMessage(cell fullMessage) asm "CTOS 4 LDU LDMSGADDR LDMSGADDR LDGRAMS SKIPDICT LDGRAMS LDGRAMS DROP 3 1 BLKDROP2"; + +;;; Checking that the "bounce" bit in the flags is set to "true" +int isBounceable(int flags) asm "2 INT AND"; +;;; Checking that the "bounced" bit in the flags is set to "true" +int isBounced(int flags) asm "ONE AND"; + +(slice, ()) ~skipBouncedPrefix(slice s) asm "x{FFFFFFFF} SDBEGINS"; + +;; ===== MSG BODY ===== + +;;; Store standard uint32 operation code into `builder` [b] +builder storeOp(builder b, int op) asm(op b) "32 STU"; +;;; Store standard uint64 query id into `builder` [b] +builder storeQueryId(builder b, int queryId) asm(queryId b) "64 STU"; +;;; Load standard uint32 operation code from `slice` [s] +(slice, int) loadOp(slice s) asm(-> 1 0) "32 LDU"; +;;; Load standard uint64 query id from `slice` [s] +(slice, int) loadQueryId(slice s) asm(-> 1 0) "64 LDU"; + +(slice, (int, int)) ~loadOpAndQueryId(slice s) asm(-> 2 0 1) "32 LDU 64 LDU"; + +(slice, ()) ~skipOp(slice s) asm "32 LDU NIP"; +(slice, ()) ~skipQueryId(slice s) asm "32 LDU NIP"; + +;; ===== SEND ===== + +const int MSG_INFO_REST_BITS = 1 + 4 + 4 + 64 + 32; + +;; https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L155 +;; message$_ {X:Type} info:CommonMsgInfo +;; Maybe (Either StateInit ^StateInit) +;; body:(Either X ^X) = Message X; +;; +;;message$_ {X:Type} info:CommonMsgInfoRelaxed +;; init:(Maybe (Either StateInit ^StateInit)) +;; body:(Either X ^X) = MessageRelaxed X; +;; +;;_ (Message Any) = MessageAny; + +;; if have StateInit (always place StateInit in ref): +;; 0b11 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` + +const int MSG_WITH_STATE_INIT_AND_BODY_SIZE = MSG_INFO_REST_BITS + 1 + 1 + 1; +const int MSG_HAVE_STATE_INIT = 4; +const int MSG_STATE_INIT_IN_REF = 2; +const int MSG_BODY_IN_REF = 1; + +;; if no StateInit: +;; 0b0 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` + +const int MSG_ONLY_BODY_SIZE = MSG_INFO_REST_BITS + 1 + 1; + +;; ===== SEND MODES ===== + +;; For `send_raw_message` and `send_message`: + +;;; x = 0 is used for ordinary messages; the gas fees are deducted from the senging amount; action phaes should NOT be ignored. +const int sendmode::REGULAR = 0; +;;; +1 means that the sender wants to pay transfer fees separately. +const int sendmode::PAY_FEES_SEPARATELY = 1; +;;; + 2 means that any errors arising while processing this message during the action phase should be ignored. +const int sendmode::IGNORE_ERRORS = 2; +;;; + 32 means that the current account must be destroyed if its resulting balance is zero. +const int sendmode::DESTROY = 32; +;;; x = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message. +const int sendmode::CARRY_ALL_REMAINING_MESSAGE_VALUE = 64; +;;; x = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message). +const int sendmode::CARRY_ALL_BALANCE = 128; +;;; in the case of action fail - bounce transaction. No effect if sendmode::IGNORE_ERRORS (+2) is used. TVM UPGRADE 2023-07. +const int sendmode::BOUNCE_ON_ACTION_FAIL = 16; + +;; Only for `send_message`: + +;;; do not create an action, only estimate fee +const int sendmode::ESTIMATE_FEE_ONLY = 1024; + +;; Other modes affect the fee calculation as follows: +;; +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account). +;; +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account). + +;; ===== RESERVE MODES ===== + +;;; Creates an output action which would reserve exactly x nanograms (if y = 0). +const int reserve::REGULAR = 0; +;;; Creates an output action which would reserve at most x nanograms (if y = 2). +;;; Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. +const int reserve::AT_MOST = 2; +;;; in the case of action fail - bounce transaction. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. +const int reserve::BOUNCE_ON_ACTION_FAIL = 16; + +;; ===== TOKEN METADATA ===== +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md + +const slice ONCHAIN_CONTENT = "00"s; +const slice OFFCHAIN_CONTENT = "01"s; +const slice SNAKE_FORMAT = "00"s; +const slice CHUNKS_FORMAT = "01"s; + +;; Key is sha256 hash of string. Value is data encoded as described in "Data serialization" paragraph. +;; Snake format - must be prefixed with 0x00 byte +(cell, ()) ~setTokenSnakeMetadataEntry(cell contentDict, int key, slice value) impure { + contentDict~udictSetRef(256, key, beginCell().storeSlice(SNAKE_FORMAT).storeSlice(value).endCell()); + return (contentDict, ()); +} + +;; On-chain content layout The first byte is 0x00 and the rest is key/value dictionary. +cell createTokenOnchainMetadata(cell contentDict) inline { + return beginCell().storeSlice(ONCHAIN_CONTENT).storeDict(contentDict).endCell(); +} + +;; ===== BASIC ===== + +;;; Returns the current length of the `tuple` [t] +int tupleLength(tuple t) asm "TLEN"; + +builder storeZeroes(builder b, int x) asm "STZEROES"; +builder storeOnes(builder b, int x) asm "STONES"; + +builder storeVaruint16(builder b, int x) asm "STVARUINT16"; +builder storeVarint16(builder b, int x) asm "STVARINT16"; +builder storeVaruint32(builder b, int x) asm "STVARUINT32"; +builder storeVarint32(builder b, int x) asm "STVARINT32"; + +(slice, int) loadVaruint16(slice s) asm(-> 1 0) "LDVARUINT16"; +(slice, int) loadVarint16(slice s) asm(-> 1 0) "LDVARINT16"; +(slice, int) loadVaruint32(slice s) asm(-> 1 0) "LDVARUINT32"; +(slice, int) loadVarint32(slice s) asm(-> 1 0) "LDVARINT32"; + +;;; Creates an output action that would modify the collection of this smart contract +;;; libraries by adding or removing library with code given in `Cell` [code]. +;;; Modes: 0 - remove library, 1 - add private library, 2 - add public library +() setLibrary(cell code, int mode) impure asm "SETLIBCODE"; + +(slice, ()) ~skipRef(slice s) asm "LDREF NIP"; +(slice, ()) ~skipCoins(slice s) asm "LDGRAMS NIP"; +(slice, ()) ~skipMsgAddr(slice s) asm "LDMSGADDR NIP"; + +cell preloadFirstRef(slice s) asm "0 PLDREFIDX"; +cell preloadSecondRef(slice s) asm "1 PLDREFIDX"; +cell preloadThirdRef(slice s) asm "2 PLDREFIDX"; +cell preloadFourthRef(slice s) asm "3 PLDREFIDX"; + +;;; Concatenates two builders, but second builder stores as the reference (end_cell -> store_ref) +builder storeBuilderAsRef(builder to, builder from) asm(from to) "STBREF"; + +;;; Loads the reference from the slice and parse (load_ref -> begin_parse) +(slice, slice) loadRefAsSlice(slice s) asm "LDREFRTOS"; + +;;; Returns the TON balance of the smart contract +int getTonBalance() asm "BALANCE FIRST"; + +;;; Returns the number of data bits and cell references already stored in `builder` [b]. +(int, int) builderBitsRefs(builder b) asm "BBITREFS"; + +;;; Returns the number of data bits and cell references that can be stored in `builder` [b]. +(int, int) builderRemBitsRefs(builder b) asm "BREMBITREFS"; + +;;; Checks whether `slice` [pfx] is a prefix of `slice` [s] +int sliceCheckPrefix(slice s, slice pfx) asm "SDPFXREV"; +;;; Checks whether `slice` [sfx] is a suffix of `slice` [s] +int sliceCheckSuffix(slice s, slice sfx) asm "SDSFXREV"; +;;; Checks whether there are at least [l] data bits in `slice` [s]. +int sliceCheckBits(slice s, int l) asm "SCHKBITSQ"; +;;; Checks whether there are at least [r] references in `slice` [s]. +int sliceCheckRefs(slice s, int r) asm "SCHKREFSQ"; +;;; Checks whether there are at least [l] data bits and [r] references in `slice` [s]. +int sliceCheckBitsRefs(slice s, int l, int r) asm "SCHKBITREFSQ"; + +;;; Checks whether `slice` [s] begins with (the data bits of) [pfx], and removes [pfx] from [s] on success. +;;(slice, int) slice_begins_with(slice s, slice pfx) asm "SDBEGINSXQ"; + +;;; Store `integer` [x] number as string (UTF-8) in decimal form in `builder` [b]. +builder storeNumberDec(builder b, int x) asm """ + ZERO // b x i=0 + SWAP // b i=0 x + UNTIL:<{ // b i x + 10 PUSHINT DIVMOD // b i x r + 48 ADDCONST // b i x r + s3 s1 s3 XCHG3 // r b x i + INC // r b x i + s1 s0 XCPU // r b i x x + ISZERO + }> + DROP + REPEAT:<{ 8 STU }> // ..rrr.. b i +"""; + +;;; Store `int` [x] number as string (UTF-8) in hexadecimal form in `builder` [b]. +builder storeNumberHex(builder b, int x) asm """ + ZERO // b x i=0 + SWAP // b i=0 x + UNTIL:<{ // b i x + 16 PUSHINT DIVMOD // b i x r + 48 ADDCONST DUP 57 GTINT IF:<{ 7 ADDCONST }> // b i x r + s3 s1 s3 XCHG3 // r b x i + INC // r b x i + s1 s0 XCPU // r b i x x + ISZERO + }> + DROP + REPEAT:<{ 8 STU }> // ..rrr.. b i +"""; + +;;; Returns the continuation located in register c2 +((int, int) -> ()) getC2() asm "c2 PUSH"; +;;; Store `continuation` [c] to register c2 +() setC2(((int, int) -> ()) c) impure asm "c2 POP"; +;;; Store `cell` [actions] to register c5 (out actions) +() setActions(cell actions) impure asm "c5 POP"; +;;; Store empty cell to register c5 (out actions) +() cleanActions() impure asm " PUSHREF c5 POP"; + +;;; DRAFT, not for use in prod +() send(slice address, int value, builder stateInit, builder body, int mode) impure inline { + builder message = beginCell().storeSlice(BOUNCEABLE).storeSlice(address).storeCoins(value).storeZeroes(105); + + ifnot (stateInit.isNull()) { + message = message.storeSlice("A_"s).storeBuilder(stateInit); ;; x{A_} == b{10} + } else { + message = message.storeFalse(); + } + + ifnot (body.isNull()) { + (int b1, int r1) = message.builderRemBitsRefs(); + (int b2, int r2) = body.builderBitsRefs(); + if ((b1 >= b2) & (r1 >= r2)) { + message = message.storeFalse().storeBuilder(body); + } else { + message = message.storeTrue().storeBuilderAsRef(body); + } + } else { + message = message.storeFalse(); + } + sendRawMessage(message.endCell(), mode); +} + +;; ===== STANDARD OP's ===== + +;; Common (used in TEP-62,74,85) +const slice op::excesses = "d53276db"s; + +;; TEP-62 - NFT +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md +const slice op::transferNft = "5fcc3d14"s; +const slice op::ownershipAssigned = "05138d91"s; +const slice op::getStaticData = "2fcb26a2"s; +const slice op::reportStaticData = "8b771735"s; + +;; TEP-66 - NFT Royalty +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md +const slice op::getRoyaltyParams = "693d3950"s; +const slice op::reportRoyaltyParams = "a8cb00ad"s; + +;; TEP-74 - Jettons +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md +const slice op::transferJetton = "0f8a7ea5"s; +const slice op::internalTransfer = "178d4519"s; +const slice op::transferNotification = "7362d09c"s; +const slice op::burnNotification = "7bdd97de"s; + +;; TEP-85 - SBT +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0085-sbt-standard.md +const slice op::proveOwnership = "04ded148"s; +const slice op::ownershipProof = "0524c7ae"s; +const slice op::requestOwner = "d0c3bfea"s; +const slice op::ownerInfo = "0dd607e3"s; +const slice op::destroySbt = "1f04537a"s; +const slice op::revokeSbt = "6f89f5e3"s; + +;; TEP-89 - Discoverable Jettons Wallets +;; https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md +const slice op::provideWalletAddress = "2c76b973"s; +const slice op::takeWalletAddress = "d1735400"s; + +;; ===== DICTS (missing funcs) ===== + +cell dictGetRef(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTGETOPTREF"; +cell udictGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUGETOPTREF"; + +(cell, cell) dictSetGetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTSETGETOPTREF"; + +cell dictSetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTSETREF"; +(cell, ()) ~dictSetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTSETREF"; + +(cell, int) tryDictGetRef(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTGETREF" "NULLSWAPIFNOT"; +(cell, int) tryDictDelete(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTDEL"; +(slice, int) tryDictGet(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTGET" "NULLSWAPIFNOT"; + +(cell, slice, int) tryDictDeleteGet(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTDELGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryDictDeleteGet(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTDELGET" "NULLSWAPIFNOT"; + +(cell, int) tryDictReplace(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTREPLACE"; +(cell, int) tryDictAdd(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTADD"; + +(cell, int) tryDictReplaceBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTREPLACEB"; +(cell, int) tryDictAddBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTADDB"; + +(slice, slice, int) tryDictGetMin(cell dict, int keyLen) asm (-> 1 0 2) "DICTMIN" "NULLSWAPIFNOT2"; +(slice, slice, int) tryDictGetMax(cell dict, int keyLen) asm (-> 1 0 2) "DICTMAX" "NULLSWAPIFNOT2"; +(slice, cell, int) tryDictGetMinRef(cell dict, int keyLen) asm (-> 1 0 2) "DICTMINREF" "NULLSWAPIFNOT2"; +(slice, cell, int) tryDictGetMaxRef(cell dict, int keyLen) asm (-> 1 0 2) "DICTMAXREF" "NULLSWAPIFNOT2"; +(slice, slice, int) tryDictGetNext(cell dict, int keyLen, slice pivot) asm(pivot dict keyLen -> 1 0 2) "DICTGETNEXT" "NULLSWAPIFNOT2"; +(slice, slice, int) tryDictGetNexteq(cell dict, int keyLen, slice pivot) asm(pivot dict keyLen -> 1 0 2) "DICTGETNEXTEQ" "NULLSWAPIFNOT2"; +(slice, slice, int) tryDictGetPrev(cell dict, int keyLen, slice pivot) asm(pivot dict keyLen -> 1 0 2) "DICTGETPREV" "NULLSWAPIFNOT2"; +(slice, slice, int) tryDictGetPreveq(cell dict, int keyLen, slice pivot) asm(pivot dict keyLen -> 1 0 2) "DICTGETPREVEQ" "NULLSWAPIFNOT2"; + +;; ===== DICTS (new funcs) ===== + +(slice, cell, int) tryLoadDict(slice s) asm(-> 1 0 2) "LDDICTQ" "NULLROTRIFNOT"; +(slice, (cell, int)) ~tryLoadDict(slice s) asm(-> 1 0 2) "LDDICTQ" "NULLROTRIFNOT"; +(cell, int) tryPreloadDict(slice s) asm "PLDDICTQ" "NULLSWAPIFNOT"; + +(cell, slice, int) tryDictSetGet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTSETGET" "NULLSWAPIFNOT"; +(cell, slice, int) tryUdictSetGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT"; +(cell, slice, int) tryIdictSetGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTISETGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryDictSetGet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTSETGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryUdictSetGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUSETGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryIdictSetGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTISETGET" "NULLSWAPIFNOT"; + +(cell, cell, int) tryDictSetGetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTSETGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) tryUdictSetGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUSETGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) tryIdictSetGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTISETGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryDictSetGetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTSETGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryUdictSetGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUSETGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryIdictSetGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTISETGETREF" "NULLSWAPIFNOT"; + +(cell, slice, int) tryDictSetGetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTSETGETB" "NULLSWAPIFNOT"; +(cell, slice, int) tryUdictSetGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUSETGETB" "NULLSWAPIFNOT"; +(cell, slice, int) tryIdictSetGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTISETGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryDictSetGetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTSETGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryUdictSetGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUSETGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryIdictSetGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTISETGETB" "NULLSWAPIFNOT"; + +(cell, int) tryDictReplaceRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTREPLACEREF"; +(cell, int) tryUdictReplaceRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUREPLACEREF"; +(cell, int) tryIdictReplaceRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTIREPLACEREF"; + +(cell, slice, int) tryDictReplaceGet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTREPLACEGET" "NULLSWAPIFNOT"; +(cell, slice, int) tryUdictReplaceGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUREPLACEGET" "NULLSWAPIFNOT"; +(cell, slice, int) tryIdictReplaceGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTIREPLACEGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryDictReplaceGet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTREPLACEGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryUdictReplaceGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUREPLACEGET" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryIdictReplaceGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTIREPLACEGET" "NULLSWAPIFNOT"; + +(cell, cell, int) tryDictReplaceGetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) tryUdictReplaceGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) tryIdictReplaceGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTIREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryDictReplaceGetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryUdictReplaceGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUREPLACEGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryIdictReplaceGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTIREPLACEGETREF" "NULLSWAPIFNOT"; + +(cell, slice, int) tryDictReplaceGetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTREPLACEGETB" "NULLSWAPIFNOT"; +(cell, slice, int) tryUdictReplaceGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUREPLACEGETB" "NULLSWAPIFNOT"; +(cell, slice, int) tryIdictReplaceGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryDictReplaceGetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryUdictReplaceGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUREPLACEGETB" "NULLSWAPIFNOT"; +(cell, (slice, int)) ~tryIdictReplaceGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIREPLACEGETB" "NULLSWAPIFNOT"; + +(cell, int) tryDictAddRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTADDREF"; +(cell, int) tryUdictAddRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUADDREF"; +(cell, int) tryIdictAddRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTIADDREF"; + +(cell, slice, int) tryDictAddGet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTADDGET" "NULLSWAPIF"; +(cell, slice, int) tryUdictAddGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUADDGET" "NULLSWAPIF"; +(cell, slice, int) tryIdictAddGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTIADDGET" "NULLSWAPIF"; +(cell, (slice, int)) ~tryDictAddGet(cell dict, int keyLen, slice index, slice value) asm(value index dict keyLen) "DICTADDGET" "NULLSWAPIF"; +(cell, (slice, int)) ~tryUdictAddGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTUADDGET" "NULLSWAPIF"; +(cell, (slice, int)) ~tryIdictAddGet(cell dict, int keyLen, int index, slice value) asm(value index dict keyLen) "DICTIADDGET" "NULLSWAPIF"; + +(cell, cell, int) tryDictAddGetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTADDGETREF" "NULLSWAPIF"; +(cell, cell, int) tryUdictAddGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUADDGETREF" "NULLSWAPIF"; +(cell, cell, int) tryIdictAddGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTIADDGETREF" "NULLSWAPIF"; +(cell, (cell, int)) ~tryDictAddGetRef(cell dict, int keyLen, slice index, cell value) asm(value index dict keyLen) "DICTADDGETREF" "NULLSWAPIF"; +(cell, (cell, int)) ~tryUdictAddGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUADDGETREF" "NULLSWAPIF"; +(cell, (cell, int)) ~tryIdictAddGetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTIADDGETREF" "NULLSWAPIF"; + +(cell, slice, int) tryDictAddGetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTADDGETB" "NULLSWAPIF"; +(cell, slice, int) tryUdictAddGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUADDGETB" "NULLSWAPIF"; +(cell, slice, int) tryIdictAddGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIADDGETB" "NULLSWAPIF"; +(cell, (slice, int)) ~tryDictAddGetBuilder(cell dict, int keyLen, slice index, builder value) asm(value index dict keyLen) "DICTADDGETB" "NULLSWAPIF"; +(cell, (slice, int)) ~tryUdictAddGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTUADDGETB" "NULLSWAPIF"; +(cell, (slice, int)) ~tryIdictAddGetBuilder(cell dict, int keyLen, int index, builder value) asm(value index dict keyLen) "DICTIADDGETB" "NULLSWAPIF"; + +(cell, cell, int) tryDictDeleteGetRef(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTDELGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) tryUdictDeleteGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUDELGETREF" "NULLSWAPIFNOT"; +(cell, cell, int) tryIdictDeleteGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryDictDeleteGetRef(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryUdictDeleteGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUDELGETREF" "NULLSWAPIFNOT"; +(cell, (cell, int)) ~tryIdictDeleteGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTIDELGETREF" "NULLSWAPIFNOT"; + +(cell, slice, cell, int) tryDictDeleteGetMinRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMINREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) tryUdictDeleteGetMinRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMINREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) tryIdictDeleteGetMinRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMINREF" "NULLSWAPIFNOT2"; +(cell, (slice, cell, int)) ~tryDictDeleteGetMinRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMINREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~tryUdictDeleteGetMinRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMINREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~tryIdictDeleteGetMinRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMINREF" "NULLSWAPIFNOT2"; + +(cell, slice, cell, int) tryDictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAXREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) tryUdictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAXREF" "NULLSWAPIFNOT2"; +(cell, int, cell, int) tryIdictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAXREF" "NULLSWAPIFNOT2"; +(cell, (slice, cell, int)) ~tryDictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTREMMAXREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~tryUdictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAXREF" "NULLSWAPIFNOT2"; +(cell, (int, cell, int)) ~tryIdictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAXREF" "NULLSWAPIFNOT2"; + +;; ===== HASHES ===== + +int sha256FromSnake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_SHA256 }> 1 1 CALLXARGS"; +[int, int] sha512FromSnake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_SHA512 }> 1 1 CALLXARGS"; +[int, int] blake2bFromSnake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_BLAKE2B }> 1 1 CALLXARGS"; +int keccak256FromSnake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_KECCAK256 }> 1 1 CALLXARGS"; +[int, int] keccak512FromSnake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_KECCAK512 }> 1 1 CALLXARGS"; + +builder storeSha256FromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_SHA256 }> 2 1 CALLXARGS"; +builder storeSha512FromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_SHA512 }> 2 1 CALLXARGS"; +builder storeBlake2bFromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_BLAKE2B }> 2 1 CALLXARGS"; +builder storeKeccak256FromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_KECCAK256 }> 2 1 CALLXARGS"; +builder storeKeccak512FromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_KECCAK512 }> 2 1 CALLXARGS"; + +;; ===== SIGNATURES ===== + +int checkSecp256r1Signature(int hash, slice signature, int publicKey) asm "P256_CHKSIGNU"; +int checkSecp256r1DataSignature(slice data, slice signature, int publicKey) asm "P256_CHKSIGNS"; +int checkBlsSignature(slice data, slice signature, int publicKey) asm(publicKey data signature) "BLS_VERIFY"; + From b053eb260074ca02ab4dd0eb0a89752378af5588 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Thu, 15 Feb 2024 22:33:28 +0500 Subject: [PATCH 23/28] add // and /* */ comments to FunC --- crypto/parser/lexer.cpp | 19 ++++++++++++------- crypto/parser/lexer.h | 9 +++++---- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/crypto/parser/lexer.cpp b/crypto/parser/lexer.cpp index 117f1df5a..7584e52d4 100644 --- a/crypto/parser/lexer.cpp +++ b/crypto/parser/lexer.cpp @@ -124,8 +124,10 @@ int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) { return classify(); } -Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts, - std::string close_cmts, std::string quote_chars, std::string multiline_quote) +Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, + std::string eol_cmts, std::string open_cmts, std::string close_cmts, + std::string camel_eol_cmts, std::string camel_open_cmts, std::string camel_close_cmts, + std::string quote_chars, std::string multiline_quote) : src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined), multiline_quote(std::move(multiline_quote)) { std::memset(char_class, 0, sizeof(char_class)); @@ -142,6 +144,9 @@ Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::strin set_spec(eol_cmt, eol_cmts); set_spec(cmt_op, open_cmts); set_spec(cmt_cl, close_cmts); + set_spec(c_eol_cmt, camel_eol_cmts); + set_spec(c_cmt_op, camel_open_cmts); + set_spec(c_cmt_cl, camel_close_cmts); for (int c : quote_chars) { if (c > ' ' && c <= 0x7f) { char_class[(unsigned)c] |= cc::quote_char; @@ -206,24 +211,24 @@ const Lexem& Lexer::next() { long long comm = 1; while (!src.seek_eof()) { int cc = src.cur_char(), nc = src.next_char(); - if (cc == eol_cmt[0] || (cc == eol_cmt[1] && nc == eol_cmt[2])) { + if ((cc == eol_cmt[0] || (cc == eol_cmt[1] && nc == eol_cmt[2])) || (cc == c_eol_cmt[0] || (cc == c_eol_cmt[1] && nc == c_eol_cmt[2]))) { src.load_line(); - } else if (cc == cmt_op[1] && nc == cmt_op[2]) { + } else if ((cc == cmt_op[1] && nc == cmt_op[2]) || (cc == c_cmt_op[1] && nc == c_cmt_op[2])) { src.advance(2); comm = comm * 2 + 1; - } else if (cc == cmt_op[0]) { + } else if ((cc == cmt_op[0]) || (cc == c_cmt_op[0])) { src.advance(1); comm *= 2; } else if (comm == 1) { break; - } else if (cc == cmt_cl[1] && nc == cmt_cl[2]) { + } else if ((cc == cmt_cl[1] && nc == cmt_cl[2]) || (cc == c_cmt_cl[1] && nc == c_cmt_cl[2])) { if (!(comm & 1)) { src.error(std::string{"a `"} + (char)cmt_op[0] + "` comment closed by `" + (char)cmt_cl[1] + (char)cmt_cl[2] + "`"); } comm >>= 1; src.advance(2); - } else if (cc == cmt_cl[0]) { + } else if ((cc == cmt_cl[0]) || (cc == c_cmt_cl[0])) { if (!(comm & 1)) { src.error(std::string{"a `"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment closed by `" + (char)cmt_cl[0] + "`"); diff --git a/crypto/parser/lexer.h b/crypto/parser/lexer.h index 686d8eacd..203fff49b 100644 --- a/crypto/parser/lexer.h +++ b/crypto/parser/lexer.h @@ -70,7 +70,7 @@ class Lexer { bool eof; Lexem lexem, peek_lexem; unsigned char char_class[128]; - std::array eol_cmt, cmt_op, cmt_cl; + std::array eol_cmt, cmt_op, cmt_cl, c_eol_cmt, c_cmt_op, c_cmt_cl; std::string multiline_quote; enum cc { left_active = 2, right_active = 1, active = 3, allow_repeat = 4, quote_char = 8 }; @@ -78,9 +78,10 @@ class Lexer { bool eof_found() const { return eof; } - Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;", - std::string open_cmts = "{-", std::string close_cmts = "-}", std::string quote_chars = "\"", - std::string multiline_quote = "\"\"\""); + Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", + std::string eol_cmts = ";;", std::string open_cmts = "{-", std::string close_cmts = "-}", + std::string camel_eol_cmts = "//", std::string camel_open_cmts = "/*", std::string camel_close_cmts = "*/", + std::string quote_chars = "\"", std::string multiline_quote = "\"\"\""); const Lexem& next(); const Lexem& cur() const { return lexem; From 26db91303082a68cf47a482f7c995536105d2a67 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Thu, 15 Feb 2024 22:35:07 +0500 Subject: [PATCH 24/28] stdlib.fc | slice_split + cosmetic --- crypto/smartcont/stdlib.fc | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index ecc033913..bc189dcfd 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -1,5 +1,4 @@ ;; Standard library for funC -;; {- This file is part of TON FunC Standard Library. @@ -13,7 +12,6 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - -} {- @@ -172,7 +170,7 @@ int check_signature(int hash, slice signature, int public_key) asm "CHKSIGNU"; ;;; with sha256 used to reduce [data] to the 256-bit number that is actually signed. int check_data_signature(slice data, slice signature, int public_key) asm "CHKSIGNS"; -{--- +{- # Computation of boc size The primitives below may be useful for computing storage fees of user-provided data. -} @@ -202,7 +200,7 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI ;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) ;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; -{-- +{- # Debug primitives Only works for local TVM execution with debug level verbosity -} @@ -235,7 +233,7 @@ cont get_c3() impure asm "c3 PUSH"; ;;; Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist. cont bless(slice s) impure asm "BLESS"; -{--- +{- # Gas related primitives -} @@ -937,8 +935,11 @@ int slice_check_refs(slice s, int r) asm "SCHKREFSQ"; ;;; Checks whether there are at least [l] data bits and [r] references in `slice` [s]. int slice_check_bits_refs(slice s, int l, int r) asm "SCHKBITREFSQ"; +;;; Splits the first 0 ≤ [bits] ≤ 1023 data bits and first 0 ≤ [refs] ≤ 4 references from `slice` [cs] +(slice, slice) slice_split(slice cs, int bits, int refs) asm(-> 1 0) "SPLIT"; + ;;; Checks whether `slice` [s] begins with (the data bits of) [pfx], and removes [pfx] from [s] on success. -;;(slice, int) slice_begins_with(slice s, slice pfx) asm "SDBEGINSXQ"; +;;(slice, int) slice_begins_with?(slice s, slice pfx) asm "SDBEGINSXQ"; ;;; Store `integer` [x] number as string (UTF-8) in decimal form in `builder` [b]. builder store_number_dec(builder b, int x) asm """ From 661a8d2d074f7691c55b0b95e2948345d38fe371 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Thu, 15 Feb 2024 22:35:15 +0500 Subject: [PATCH 25/28] Update stdlibCamel.fc --- crypto/smartcont/stdlibCamel.fc | 741 ++++++++++++++++---------------- 1 file changed, 371 insertions(+), 370 deletions(-) diff --git a/crypto/smartcont/stdlibCamel.fc b/crypto/smartcont/stdlibCamel.fc index cd73fe744..be69df2ee 100644 --- a/crypto/smartcont/stdlibCamel.fc +++ b/crypto/smartcont/stdlibCamel.fc @@ -1,7 +1,6 @@ -;; Standard library for funC -;; +// Standard library for funC -{- +/* This file is part of TON FunC Standard Library. FunC Standard Library is free software: you can redistribute it and/or modify @@ -13,10 +12,9 @@ but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. +*/ --} - -{- +/* # Tuple manipulation primitives The names and the types are mostly self-explaining. See [polymorhism with forall](https://ton.org/docs/#/func/functions?id=polymorphism-with-forall) @@ -24,260 +22,260 @@ Note that currently values of atomic type `tuple` can't be cast to composite tuple type (e.g. `[int, cell]`) and vise versa. --} +*/ -{- +/* # Lisp-style lists Lists can be represented as nested 2-elements tuples. Empty list is conventionally represented as TVM `null` value (it can be obtained by calling [null()]). For example, tuple `(1, (2, (3, null)))` represents list `[1, 2, 3]`. Elements of a list can be of different types. --} +*/ -;;; Adds an element to the beginning of lisp-style list. +/// Adds an element to the beginning of lisp-style list. forall X -> tuple cons(X head, tuple tail) asm "CONS"; -;;; Extracts the head and the tail of lisp-style list. +/// Extracts the head and the tail of lisp-style list. forall X -> (X, tuple) uncons(tuple list) asm "UNCONS"; -;;; Extracts the tail and the head of lisp-style list. +/// Extracts the tail and the head of lisp-style list. forall X -> (tuple, X) listNext(tuple list) asm( -> 1 0) "UNCONS"; -;;; Returns the head of lisp-style list. +/// Returns the head of lisp-style list. forall X -> X car(tuple list) asm "CAR"; -;;; Returns the tail of lisp-style list. +/// Returns the tail of lisp-style list. tuple cdr(tuple list) asm "CDR"; -;;; Creates tuple with zero elements. +/// Creates tuple with zero elements. tuple emptyTuple() asm "NIL"; -;;; Appends a value `x` to a `Tuple t = (x1, ..., xn)`, but only if the resulting `Tuple t' = (x1, ..., xn, x)` -;;; is of length at most 255. Otherwise throws a type check exception. +/// Appends a value `x` to a `Tuple t = (x1, ..., xn)`, but only if the resulting `Tuple t' = (x1, ..., xn, x)` +/// is of length at most 255. Otherwise throws a type check exception. forall X -> tuple tpush(tuple t, X value) asm "TPUSH"; forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH"; -;;; Creates a tuple of length one with given argument as element. +/// Creates a tuple of length one with given argument as element. forall X -> [X] single(X x) asm "SINGLE"; -;;; Unpacks a tuple of length one +/// Unpacks a tuple of length one forall X -> X unsingle([X] t) asm "UNSINGLE"; -;;; Creates a tuple of length two with given arguments as elements. +/// Creates a tuple of length two with given arguments as elements. forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR"; -;;; Unpacks a tuple of length two +/// Unpacks a tuple of length two forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR"; -;;; Creates a tuple of length three with given arguments as elements. +/// Creates a tuple of length three with given arguments as elements. forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE"; -;;; Unpacks a tuple of length three +/// Unpacks a tuple of length three forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE"; -;;; Creates a tuple of length four with given arguments as elements. +/// Creates a tuple of length four with given arguments as elements. forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE"; -;;; Unpacks a tuple of length four +/// Unpacks a tuple of length four forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE"; -;;; Returns the first element of a tuple (with unknown element types). +/// Returns the first element of a tuple (with unknown element types). forall X -> X first(tuple t) asm "FIRST"; -;;; Returns the second element of a tuple (with unknown element types). +/// Returns the second element of a tuple (with unknown element types). forall X -> X second(tuple t) asm "SECOND"; -;;; Returns the third element of a tuple (with unknown element types). +/// Returns the third element of a tuple (with unknown element types). forall X -> X third(tuple t) asm "THIRD"; -;;; Returns the fourth element of a tuple (with unknown element types). +/// Returns the fourth element of a tuple (with unknown element types). forall X -> X fourth(tuple t) asm "3 INDEX"; -;;; Returns the first element of a pair tuple. +/// Returns the first element of a pair tuple. forall X, Y -> X pairFirst([X, Y] p) asm "FIRST"; -;;; Returns the second element of a pair tuple. +/// Returns the second element of a pair tuple. forall X, Y -> Y pairSecond([X, Y] p) asm "SECOND"; -;;; Returns the first element of a triple tuple. +/// Returns the first element of a triple tuple. forall X, Y, Z -> X tripleFirst([X, Y, Z] p) asm "FIRST"; -;;; Returns the second element of a triple tuple. +/// Returns the second element of a triple tuple. forall X, Y, Z -> Y tripleSecond([X, Y, Z] p) asm "SECOND"; -;;; Returns the third element of a triple tuple. +/// Returns the third element of a triple tuple. forall X, Y, Z -> Z tripleThird([X, Y, Z] p) asm "THIRD"; -;;; Push null element (casted to given type) -;;; By the TVM type `Null` FunC represents absence of a value of some atomic type. -;;; So `null` can actually have any atomic type. +/// Push null element (casted to given type) +/// By the TVM type `Null` FunC represents absence of a value of some atomic type. +/// So `null` can actually have any atomic type. forall X -> X null() asm "PUSHNULL"; -;;; Moves a variable [x] to the top of the stack +/// Moves a variable [x] to the top of the stack forall X -> (X, ()) ~impureTouch(X x) impure asm "NOP"; -;;; Returns the current Unix time as an Integer +/// Returns the current Unix time as an Integer int now() asm "NOW"; -;;; Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`. -;;; If necessary, it can be parsed further using primitives such as [parse_std_addr]. +/// Returns the internal address of the current smart contract as a Slice with a `MsgAddressInt`. +/// If necessary, it can be parsed further using primitives such as [parseStdAddr]. slice myAddress() asm "MYADDR"; -;;; Returns the balance of the smart contract as a tuple consisting of an int -;;; (balance in nanotoncoins) and a `cell` -;;; (a dictionary with 32-bit keys representing the balance of "extra currencies") -;;; at the start of Computation Phase. -;;; Note that RAW primitives such as [send_raw_message] do not update this field. +/// Returns the balance of the smart contract as a tuple consisting of an int +/// (balance in nanotoncoins) and a `cell` +/// (a dictionary with 32-bit keys representing the balance of "extra currencies") +/// at the start of Computation Phase. +/// Note that RAW primitives such as [sendRawMessage] do not update this field. [int, cell] getBalance() asm "BALANCE"; -;;; Returns the logical time of the current transaction. +/// Returns the logical time of the current transaction. int curLt() asm "LTIME"; -;;; Returns the starting logical time of the current block. +/// Returns the starting logical time of the current block. int blockLt() asm "BLOCKLT"; -;;; Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`. -;;; Useful for signing and checking signatures of arbitrary entities represented by a tree of cells. +/// Computes the representation hash of a `cell` [c] and returns it as a 256-bit unsigned integer `x`. +/// Useful for signing and checking signatures of arbitrary entities represented by a tree of cells. int cellHash(cell c) asm "HASHCU"; -;;; Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`. -;;; The result is the same as if an ordinary cell containing only data and references from `s` had been created -;;; and its hash computed by [cell_hash]. +/// Computes the hash of a `slice s` and returns it as a 256-bit unsigned integer `x`. +/// The result is the same as if an ordinary cell containing only data and references from `s` had been created +/// and its hash computed by [cellHash]. int sliceHash(slice s) asm "HASHSU"; -;;; Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight, -;;; throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`. +/// Computes sha256 of the data bits of `slice` [s]. If the bit length of `s` is not divisible by eight, +/// throws a cell underflow exception. The hash value is returned as a 256-bit unsigned integer `x`. int stringHash(slice s) asm "SHA256U"; -{- +/* # Signature checks --} - -;;; Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data) -;;; using [public_key] (also represented by a 256-bit unsigned integer). -;;; The signature must contain at least 512 data bits; only the first 512 bits are used. -;;; The result is `−1` if the signature is valid, `0` otherwise. -;;; Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`. -;;; That is, if [hash] is computed as the hash of some data, these data are hashed twice, -;;; the second hashing occurring inside `CHKSIGNS`. +*/ + +/// Checks the Ed25519-`signature` of a `hash` (a 256-bit unsigned integer, usually computed as the hash of some data) +/// using [publicKey] (also represented by a 256-bit unsigned integer). +/// The signature must contain at least 512 data bits; only the first 512 bits are used. +/// The result is `−1` if the signature is valid, `0` otherwise. +/// Note that `CHKSIGNU` creates a 256-bit slice with the hash and calls `CHKSIGNS`. +/// That is, if [hash] is computed as the hash of some data, these data are hashed twice, +/// the second hashing occurring inside `CHKSIGNS`. int checkSignature(int hash, slice signature, int publicKey) asm "CHKSIGNU"; -;;; Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `public_key`, -;;; similarly to [check_signature]. -;;; If the bit length of [data] is not divisible by eight, throws a cell underflow exception. -;;; The verification of Ed25519 signatures is the standard one, -;;; with sha256 used to reduce [data] to the 256-bit number that is actually signed. +/// Checks whether [signature] is a valid Ed25519-signature of the data portion of `slice data` using `publicKey`, +/// similarly to [checkSignature]. +/// If the bit length of [data] is not divisible by eight, throws a cell underflow exception. +/// The verification of Ed25519 signatures is the standard one, +/// with sha256 used to reduce [data] to the 256-bit number that is actually signed. int checkDataSignature(slice data, slice signature, int publicKey) asm "CHKSIGNS"; -{--- +/* # Computation of boc size The primitives below may be useful for computing storage fees of user-provided data. --} - -;;; Returns `(x, y, z, -1)` or `(null, null, null, 0)`. -;;; Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z` -;;; in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account -;;; the identification of equal cells. -;;; The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG, -;;; with a hash table of visited cell hashes used to prevent visits of already-visited cells. -;;; The total count of visited cells `x` cannot exceed non-negative [max_cells]; -;;; otherwise the computation is aborted before visiting the `(max_cells + 1)`-st cell and -;;; a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`. +*/ + +/// Returns `(x, y, z, -1)` or `(null, null, null, 0)`. +/// Recursively computes the count of distinct cells `x`, data bits `y`, and cell references `z` +/// in the DAG rooted at `cell` [c], effectively returning the total storage used by this DAG taking into account +/// the identification of equal cells. +/// The values of `x`, `y`, and `z` are computed by a depth-first traversal of this DAG, +/// with a hash table of visited cell hashes used to prevent visits of already-visited cells. +/// The total count of visited cells `x` cannot exceed non-negative [maxCells]; +/// otherwise the computation is aborted before visiting the `(maxCells + 1)`-st cell and +/// a zero flag is returned to indicate failure. If [c] is `null`, returns `x = y = z = 0`. (int, int, int, int) tryComputeDataSize(cell c, int maxCells) asm "CDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; -;;; Similar to [compute_data_size?], but accepting a `slice` [s] instead of a `cell`. -;;; The returned value of `x` does not take into account the cell that contains the `slice` [s] itself; -;;; however, the data bits and the cell references of [s] are accounted for in `y` and `z`. +/// Similar to [tryComputeDataSize], but accepting a `slice` [s] instead of a `cell`. +/// The returned value of `x` does not take into account the cell that contains the `slice` [s] itself; +/// however, the data bits and the cell references of [s] are accounted for in `y` and `z`. (int, int, int, int) trySliceComputeDataSize(cell c, int maxCells) asm "SDATASIZEQ NULLSWAPIFNOT2 NULLSWAPIFNOT"; -;;; A non-quiet version of [compute_data_size?] that throws a cell overflow exception (`8`) on failure. +/// A non-quiet version of [tryComputeDataSize] that throws a cell overflow exception (`8`) on failure. (int, int, int) computeDataSize(cell c, int maxCells) impure asm "CDATASIZE"; -;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure. +/// A non-quiet version of [trySliceComputeDataSize] that throws a cell overflow exception (8) on failure. (int, int, int) sliceComputeDataSize(slice s, int maxCells) impure asm "SDATASIZE"; -;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) -;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; +/// Throws an exception with exitCode excno if cond is not 0 (commented since implemented in compilator) +// () throwIf(int excno, int cond) impure asm "THROWARGIF"; -{-- +/* # Debug primitives Only works for local TVM execution with debug level verbosity --} -;;; Dumps the stack (at most the top 255 values) and shows the total stack depth. +*/ +/// Dumps the stack (at most the top 255 values) and shows the total stack depth. () dumpStack() impure asm "DUMPSTK"; -{- +/* # Persistent storage save and load --} +*/ -;;; Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. +/// Returns the persistent contract storage cell. It can be parsed or modified with slice and builder primitives later. cell getData() asm "c4 PUSH"; -;;; Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive. +/// Sets `cell` [c] as persistent contract data. You can update persistent contract storage with this primitive. () setData(cell c) impure asm "c4 POP"; -{- +/* # Continuation primitives --} -;;; Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. -;;; The primitive returns the current value of `c3`. +*/ +/// Usually `c3` has a continuation initialized by the whole code of the contract. It is used for function calls. +/// The primitive returns the current value of `c3`. cont getC3() impure asm "c3 PUSH"; -;;; Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time. -;;; Note that after execution of this primitive the current code -;;; (and the stack of recursive function calls) won't change, -;;; but any other function call will use a function from the new code. +/// Updates the current value of `c3`. Usually, it is used for updating smart contract code in run-time. +/// Note that after execution of this primitive the current code +/// (and the stack of recursive function calls) won't change, +/// but any other function call will use a function from the new code. () setC3(cont c) impure asm "c3 POP"; -;;; Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist. +/// Transforms a `slice` [s] into a simple ordinary continuation `c`, with `c.code = s` and an empty stack and savelist. cont bless(slice s) impure asm "BLESS"; -{--- +/* # Gas related primitives --} - -;;; Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero, -;;; decreasing the value of `gr` by `gc` in the process. -;;; In other words, the current smart contract agrees to buy some gas to finish the current transaction. -;;; This action is required to process external messages, which bring no value (hence no gas) with themselves. -;;; -;;; For more details check [accept_message effects](https://ton.org/docs/#/smart-contracts/accept). +*/ + +/// Sets current gas limit `gl` to its maximal allowed value `gm`, and resets the gas credit `gc` to zero, +/// decreasing the value of `gr` by `gc` in the process. +/// In other words, the current smart contract agrees to buy some gas to finish the current transaction. +/// This action is required to process external messages, which bring no value (hence no gas) with themselves. +/// +/// For more details check [acceptMessage effects](https://ton.org/docs/#/smart-contracts/accept). () acceptMessage() impure asm "ACCEPT"; -;;; Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero. -;;; If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`, -;;; an (unhandled) out of gas exception is thrown before setting new gas limits. -;;; Notice that [set_gas_limit] with an argument `limit ≥ 2^63 − 1` is equivalent to [accept_message]. +/// Sets current gas limit `gl` to the minimum of limit and `gm`, and resets the gas credit `gc` to zero. +/// If the gas consumed so far (including the present instruction) exceeds the resulting value of `gl`, +/// an (unhandled) out of gas exception is thrown before setting new gas limits. +/// Notice that [setGasLimit] with an argument `limit ≥ 2^63 − 1` is equivalent to [acceptMessage]. () setGasLimit(int limit) impure asm "SETGASLIMIT"; -;;; Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”) -;;; so that the current execution is considered “successful” with the saved values even if an exception -;;; in Computation Phase is thrown later. +/// Commits the current state of registers `c4` (“persistent data”) and `c5` (“actions”) +/// so that the current execution is considered “successful” with the saved values even if an exception +/// in Computation Phase is thrown later. () commit() impure asm "COMMIT"; -;;; Not implemented -;;() buy_gas(int gram) impure asm "BUYGAS"; +/// Not implemented +//() buyGas(int gram) impure asm "BUYGAS"; -;;; Computes the amount of gas that can be bought for `amount` nanoTONs, -;;; and sets `gl` accordingly in the same way as [set_gas_limit]. +/// Computes the amount of gas that can be bought for `amount` nanoTONs, +/// and sets `gl` accordingly in the same way as [setGasLimit]. () buyGas(int amount) impure asm "BUYGAS"; -;;; Computes the minimum of two integers [x] and [y]. +/// Computes the minimum of two integers [x] and [y]. int min(int x, int y) asm "MIN"; -;;; Computes the maximum of two integers [x] and [y]. +/// Computes the maximum of two integers [x] and [y]. int max(int x, int y) asm "MAX"; -;;; Sorts two integers. +/// Sorts two integers. (int, int) minmax(int x, int y) asm "MINMAX"; -;;; Computes the absolute value of an integer [x]. +/// Computes the absolute value of an integer [x]. int abs(int x) asm "ABS"; -{- +/* # Slice primitives It is said that a primitive _loads_ some data, @@ -288,131 +286,131 @@ int abs(int x) asm "ABS"; (it can be used as [non-modifying method](https://ton.org/docs/#/func/statements?id=non-modifying-methods)). Unless otherwise stated, loading and preloading primitives read the data from a prefix of the slice. --} +*/ -;;; Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell, -;;; or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2) -;;; which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards. +/// Converts a `cell` [c] into a `slice`. Notice that [c] must be either an ordinary cell, +/// or an exotic cell (see [TVM.pdf](https://ton-blockchain.github.io/docs/tvm.pdf), 3.1.2) +/// which is automatically loaded to yield an ordinary cell `c'`, converted into a `slice` afterwards. slice beginParse(cell c) asm "CTOS"; -;;; Checks if [s] is empty. If not, throws an exception. +/// Checks if [s] is empty. If not, throws an exception. () endParse(slice s) impure asm "ENDS"; -;;; Loads the first reference from the slice. +/// Loads the first reference from the slice. (slice, cell) loadRef(slice s) asm( -> 1 0) "LDREF"; -;;; Preloads the first reference from the slice. +/// Preloads the first reference from the slice. cell preloadRef(slice s) asm "PLDREF"; -{- Functions below are commented because are implemented on compilator level for optimisation -} +/* Functions below are commented because are implemented on compilator level for optimisation */ -;;; Loads a signed [len]-bit integer from a slice [s]. -;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; +/// Loads a signed [len]-bit integer from a slice [s]. +// (slice, int) ~loadInt(slice s, int len) asm(s len -> 1 0) "LDIX"; -;;; Loads an unsigned [len]-bit integer from a slice [s]. -;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; +/// Loads an unsigned [len]-bit integer from a slice [s]. +// (slice, int) ~loadUint(slice s, int len) asm( -> 1 0) "LDUX"; -;;; Preloads a signed [len]-bit integer from a slice [s]. -;; int preload_int(slice s, int len) asm "PLDIX"; +/// Preloads a signed [len]-bit integer from a slice [s]. +// int preloadInt(slice s, int len) asm "PLDIX"; -;;; Preloads an unsigned [len]-bit integer from a slice [s]. -;; int preload_uint(slice s, int len) asm "PLDUX"; +/// Preloads an unsigned [len]-bit integer from a slice [s]. +// int preloadUint(slice s, int len) asm "PLDUX"; -;;; Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. -;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; +/// Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +// (slice, slice) loadBits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; -;;; Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. -;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; +/// Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +// slice preloadBits(slice s, int len) asm "PLDSLICEX"; -;;; Loads serialized amount of TonCoins (any unsigned integer up to `2^120 - 1`). +/// Loads serialized amount of TonCoins (any unsigned integer up to `2^120 - 1`). (slice, int) loadGrams(slice s) asm( -> 1 0) "LDGRAMS"; (slice, int) loadCoins(slice s) asm( -> 1 0) "LDGRAMS"; -;;; Returns all but the first `0 ≤ len ≤ 1023` bits of `slice` [s]. +/// Returns all but the first `0 ≤ len ≤ 1023` bits of `slice` [s]. slice skipBits(slice s, int len) asm "SDSKIPFIRST"; (slice, ()) ~skipBits(slice s, int len) asm "SDSKIPFIRST"; -;;; Returns the first `0 ≤ len ≤ 1023` bits of `slice` [s]. +/// Returns the first `0 ≤ len ≤ 1023` bits of `slice` [s]. slice firstBits(slice s, int len) asm "SDCUTFIRST"; -;;; Returns all but the last `0 ≤ len ≤ 1023` bits of `slice` [s]. +/// Returns all but the last `0 ≤ len ≤ 1023` bits of `slice` [s]. slice skipLastBits(slice s, int len) asm "SDSKIPLAST"; (slice, ()) ~skipLastBits(slice s, int len) asm "SDSKIPLAST"; -;;; Returns the last `0 ≤ len ≤ 1023` bits of `slice` [s]. +/// Returns the last `0 ≤ len ≤ 1023` bits of `slice` [s]. slice sliceLast(slice s, int len) asm "SDCUTLAST"; -;;; Loads a dictionary `D` (HashMapE) from `slice` [s]. -;;; (returns `null` if `nothing` constructor is used). +/// Loads a dictionary `D` (HashMapE) from `slice` [s]. +/// (returns `null` if `nothing` constructor is used). (slice, cell) loadDict(slice s) asm( -> 1 0) "LDDICT"; -;;; Preloads a dictionary `D` from `slice` [s]. +/// Preloads a dictionary `D` from `slice` [s]. cell preloadDict(slice s) asm "PLDDICT"; -;;; Loads a dictionary as [load_dict], but returns only the remainder of the slice. +/// Loads a dictionary as [loadDict], but returns only the remainder of the slice. slice skipDict(slice s) asm "SKIPDICT"; -;;; Loads (Maybe ^Cell) from `slice` [s]. -;;; In other words loads 1 bit and if it is true -;;; loads first ref and return it with slice remainder -;;; otherwise returns `null` and slice remainder +/// Loads (Maybe ^Cell) from `slice` [s]. +/// In other words loads 1 bit and if it is true +/// loads first ref and return it with slice remainder +/// otherwise returns `null` and slice remainder (slice, cell) loadMaybeRef(slice s) asm( -> 1 0) "LDOPTREF"; -;;; Preloads (Maybe ^Cell) from `slice` [s]. +/// Preloads (Maybe ^Cell) from `slice` [s]. cell preloadMaybeRef(slice s) asm "PLDOPTREF"; -;;; Returns the depth of `cell` [c]. -;;; If [c] has no references, then return `0`; -;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [c]. -;;; If [c] is a `null` instead of a cell, returns zero. +/// Returns the depth of `cell` [c]. +/// If [c] has no references, then return `0`; +/// otherwise the returned value is one plus the maximum of depths of cells referred to from [c]. +/// If [c] is a `null` instead of a cell, returns zero. int cellDepth(cell c) asm "CDEPTH"; -{- - # Slice size primitives --} +/* +# Slice size primitives +*/ -;;; Returns the number of references in `slice` [s]. +/// Returns the number of references in `slice` [s]. int sliceRefs(slice s) asm "SREFS"; -;;; Returns the number of data bits in `slice` [s]. +/// Returns the number of data bits in `slice` [s]. int sliceBits(slice s) asm "SBITS"; -;;; Returns both the number of data bits and the number of references in `slice` [s]. +/// Returns both the number of data bits and the number of references in `slice` [s]. (int, int) sliceBitsRefs(slice s) asm "SBITREFS"; -;;; Checks whether a `slice` [s] is empty (i.e., contains no bits of data and no cell references). +/// Checks whether a `slice` [s] is empty (i.e., contains no bits of data and no cell references). int isSliceEmpty(slice s) asm "SEMPTY"; -;;; Checks whether `slice` [s] has no bits of data. +/// Checks whether `slice` [s] has no bits of data. int isSliceDataEmpty(slice s) asm "SDEMPTY"; -;;; Checks whether `slice` [s] has no references. +/// Checks whether `slice` [s] has no references. int isSliceRefsEmpty(slice s) asm "SREMPTY"; -;;; Returns the depth of `slice` [s]. -;;; If [s] has no references, then returns `0`; -;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [s]. +/// Returns the depth of `slice` [s]. +/// If [s] has no references, then returns `0`; +/// otherwise the returned value is one plus the maximum of depths of cells referred to from [s]. int sliceDepth(slice s) asm "SDEPTH"; -{- - # Builder size primitives --} +/* +# Builder size primitives +*/ -;;; Returns the number of cell references already stored in `builder` [b] +/// Returns the number of cell references already stored in `builder` [b] int builderRefs(builder b) asm "BREFS"; -;;; Returns the number of data bits already stored in `builder` [b]. +/// Returns the number of data bits already stored in `builder` [b]. int builderBits(builder b) asm "BBITS"; -;;; Returns the depth of `builder` [b]. -;;; If no cell references are stored in [b], then returns 0; -;;; otherwise the returned value is one plus the maximum of depths of cells referred to from [b]. +/// Returns the depth of `builder` [b]. +/// If no cell references are stored in [b], then returns 0; +/// otherwise the returned value is one plus the maximum of depths of cells referred to from [b]. int builderDepth(builder b) asm "BDEPTH"; -{- +/* # Builder primitives It is said that a primitive _stores_ a value `x` into a builder `b` if it returns a modified version of the builder `b'` with the value `x` stored at the end of it. @@ -420,48 +418,48 @@ int builderDepth(builder b) asm "BDEPTH"; All the primitives below first check whether there is enough space in the `builder`, and only then check the range of the value being serialized. --} +*/ -;;; Creates a new empty `builder`. +/// Creates a new empty `builder`. builder beginCell() asm "NEWC"; -;;; Converts a `builder` into an ordinary `cell`. +/// Converts a `builder` into an ordinary `cell`. cell endCell(builder b) asm "ENDC"; -;;; Stores a reference to `cell` [c] into `builder` [b]. +/// Stores a reference to `cell` [c] into `builder` [b]. builder storeRef(builder b, cell c) asm(c b) "STREF"; -;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. -;; builder storeUint(builder b, int x, int len) asm(x b len) "STUX"; +/// Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. +// builder storeUint(builder b, int x, int len) asm(x b len) "STUX"; -;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. -;; builder storeInt(builder b, int x, int len) asm(x b len) "STIX"; +/// Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. +// builder storeInt(builder b, int x, int len) asm(x b len) "STIX"; -;;; Stores `slice` [s] into `builder` [b] -;; builder storeSlice(builder b, slice s) asm "STSLICER"; +/// Stores `slice` [s] into `builder` [b] +// builder storeSlice(builder b, slice s) asm "STSLICER"; -;;; Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. -;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, -;;; which is the smallest integer `l ≥ 0`, such that `x < 2^8l`, -;;; followed by an `8l`-bit unsigned big-endian representation of [x]. -;;; If [x] does not belong to the supported range, a range check exception is thrown. -;;; -;;; Store amounts of TonCoins to the builder as VarUInteger 16 +/// Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. +/// The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, +/// which is the smallest integer `l ≥ 0`, such that `x < 2^8l`, +/// followed by an `8l`-bit unsigned big-endian representation of [x]. +/// If [x] does not belong to the supported range, a range check exception is thrown. +/// +/// Store amounts of TonCoins to the builder as VarUInteger 16 builder storeGrams(builder b, int x) asm "STGRAMS"; builder storeCoins(builder b, int x) asm "STGRAMS"; -;;; Stores dictionary `D` represented by `cell` [c] or `null` into `builder` [b]. -;;; In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise. +/// Stores dictionary `D` represented by `cell` [c] or `null` into `builder` [b]. +/// In other words, stores a `1`-bit and a reference to [c] if [c] is not `null` and `0`-bit otherwise. builder storeDict(builder b, cell c) asm(c b) "STDICT"; -;;; Stores (Maybe ^Cell) to builder: -;;; if cell is null store 1 zero bit -;;; otherwise store 1 true bit and ref to cell +/// Stores (Maybe ^Cell) to builder: +/// if cell is null store 1 zero bit +/// otherwise store 1 true bit and ref to cell builder storeMaybeRef(builder b, cell c) asm(c b) "STOPTREF"; -{- +/* # Address manipulation primitives The address manipulation primitives listed below serialize and deserialize values according to the following TL-B scheme: ```TL-B @@ -496,39 +494,39 @@ builder storeMaybeRef(builder b, cell c) asm(c b) "STOPTREF"; Next, integer `x` is the `workchain_id`, and slice `s` contains the address. - `addr_var` is represented by `t = (3, u, x, s)`, where `u`, `x`, and `s` have the same meaning as for `addr_std`. --} +*/ -;;; Loads from slice [s] the only prefix that is a valid `MsgAddress`, -;;; and returns both this prefix `s'` and the remainder `s''` of [s] as slices. +/// Loads from slice [s] the only prefix that is a valid `MsgAddress`, +/// and returns both this prefix `s'` and the remainder `s''` of [s] as slices. (slice, slice) loadMsgAddr(slice s) asm( -> 1 0) "LDMSGADDR"; -;;; Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`. -;;; If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown. +/// Decomposes slice [s] containing a valid `MsgAddress` into a `tuple t` with separate fields of this `MsgAddress`. +/// If [s] is not a valid `MsgAddress`, a cell deserialization exception is thrown. tuple parseAddr(slice s) asm "PARSEMSGADDR"; -;;; Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`), -;;; applies rewriting from the anycast (if present) to the same-length prefix of the address, -;;; and returns both the workchain and the 256-bit address as integers. -;;; If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`, -;;; throws a cell deserialization exception. +/// Parses slice [s] containing a valid `MsgAddressInt` (usually a `msg_addr_std`), +/// applies rewriting from the anycast (if present) to the same-length prefix of the address, +/// and returns both the workchain and the 256-bit address as integers. +/// If the address is not 256-bit, or if [s] is not a valid serialization of `MsgAddressInt`, +/// throws a cell deserialization exception. (int, int) parseStdAddr(slice s) asm "REWRITESTDADDR"; -;;; A variant of [parse_std_addr] that returns the (rewritten) address as a slice [s], -;;; even if it is not exactly 256 bit long (represented by a `msg_addr_var`). +/// A variant of [parseStdAddr] that returns the (rewritten) address as a slice [s], +/// even if it is not exactly 256 bit long (represented by a `msg_addr_var`). (int, slice) parseVarAddr(slice s) asm "REWRITEVARADDR"; -{- - # Dictionary primitives --} +/* +# Dictionary primitives +*/ -;;; Sets the value associated with [key_len]-bit key signed index in dictionary [dict] to [value] (cell), -;;; and returns the resulting dictionary. +/// Sets the value associated with [keyLen]-bit key signed index in dictionary [dict] to [value] (cell), +/// and returns the resulting dictionary. cell idictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTISETREF"; (cell, ()) ~idictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTISETREF"; -;;; Sets the value associated with [key_len]-bit key unsigned index in dictionary [dict] to [value] (cell), -;;; and returns the resulting dictionary. +/// Sets the value associated with [keyLen]-bit key unsigned index in dictionary [dict] to [value] (cell), +/// and returns the resulting dictionary. cell udictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUSETREF"; (cell, ()) ~udictSetRef(cell dict, int keyLen, int index, cell value) asm(value index dict keyLen) "DICTUSETREF"; @@ -594,73 +592,73 @@ cell dictSetBuilder(cell dict, int keyLen, slice index, builder value) asm(value (int, slice, int) tryIdictGetPrev(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT2"; (int, slice, int) tryIdictGetPreveq(cell dict, int keyLen, int pivot) asm(pivot dict keyLen -> 1 0 2) "DICTIGETPREVEQ" "NULLSWAPIFNOT2"; -;;; Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL +/// Creates an empty dictionary, which is actually a null value. Equivalent to PUSHNULL cell newDict() asm "NEWDICT"; -;;; Checks whether a dictionary is empty. Equivalent to cell_null?. +/// Checks whether a dictionary is empty. Equivalent to isCellNull. int isDictEmpty(cell c) asm "DICTEMPTY"; -{- Prefix dictionary primitives -} +/* Prefix dictionary primitives */ (slice, slice, slice, int) tryPfxdictGet(cell dict, int keyLen, slice key) asm(key dict keyLen) "PFXDICTGETQ" "NULLSWAPIFNOT2"; (cell, int) tryPfxdictSet(cell dict, int keyLen, slice key, slice value) asm(value key dict keyLen) "PFXDICTSET"; (cell, int) tryPfxdictDelete(cell dict, int keyLen, slice key) asm(key dict keyLen) "PFXDICTDEL"; -;;; Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value. +/// Returns the value of the global configuration parameter with integer index `i` as a `cell` or `null` value. cell configParam(int x) asm "CONFIGOPTPARAM"; -;;; Checks whether c is a null. Note, that FunC also has polymorphic null? built-in. +/// Checks whether c is a null. Note, that FunC also has polymorphic null? built-in. int isCellNull(cell c) asm "ISNULL"; -;;; Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b − amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15. +/// Creates an output action which would reserve exactly amount nanotoncoins (if mode = 0), at most amount nanotoncoins (if mode = 2), or all but amount nanotoncoins (if mode = 1 or mode = 3), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying amount nanotoncoins (or b − amount nanotoncoins, where b is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit +2 in mode means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit +8 in mode means `amount <- -amount` before performing any further actions. Bit +4 in mode means that amount is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently, amount must be a non-negative integer, and mode must be in the range 0..15. () rawReserve(int amount, int mode) impure asm "RAWRESERVE"; -;;; Similar to raw_reserve, but also accepts a dictionary extra_amount (represented by a cell or null) with extra currencies. In this way currencies other than TonCoin can be reserved. +/// Similar to rawReserve, but also accepts a dictionary extraAmount (represented by a cell or null) with extra currencies. In this way currencies other than TonCoin can be reserved. () rawReserveExtra(int amount, cell extraAmount, int mode) impure asm "RAWRESERVEX"; -;;; Sends a raw message contained in msg, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have dummy value addr_none (to be automatically replaced with the current smart contract address), and ihr_fee, fwd_fee, created_lt and created_at fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter mode contains the flags. Currently mode = 0 is used for ordinary messages; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); mode' = mode + 1 means that the sender wants to pay transfer fees separately; mode' = mode + 2 means that any errors arising while processing this message during the action phase should be ignored. Finally, mode' = mode + 32 means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with +128. +/// Sends a raw message contained in msg, which should contain a correctly serialized object Message X, with the only exception that the source address is allowed to have dummy value addr_none (to be automatically replaced with the current smart contract address), and ihr_fee, fwd_fee, created_lt and created_at fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter mode contains the flags. Currently mode = 0 is used for ordinary messages; mode = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); mode = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); mode' = mode + 1 means that the sender wants to pay transfer fees separately; mode' = mode + 2 means that any errors arising while processing this message during the action phase should be ignored. Finally, mode' = mode + 32 means that the current account must be destroyed if its resulting balance is zero. This flag is usually employed together with +128. () sendRawMessage(cell msg, int mode) impure asm "SENDRAWMSG"; -;;; Creates an output action that would change this smart contract code to that given by cell new_code. Notice that this change will take effect only after the successful termination of the current run of the smart contract +/// Creates an output action that would change this smart contract code to that given by cell newCode. Notice that this change will take effect only after the successful termination of the current run of the smart contract () setCode(cell newCode) impure asm "SETCODE"; -;;; Generates a new pseudo-random unsigned 256-bit integer x. The algorithm is as follows: if r is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its sha512(r) is computed; the first 32 bytes of this hash are stored as the new value r' of the random seed, and the remaining 32 bytes are returned as the next random value x. +/// Generates a new pseudo-random unsigned 256-bit integer x. The algorithm is as follows: if r is the old value of the random seed, considered as a 32-byte array (by constructing the big-endian representation of an unsigned 256-bit integer), then its sha512(r) is computed; the first 32 bytes of this hash are stored as the new value r' of the random seed, and the remaining 32 bytes are returned as the next random value x. int random() impure asm "RANDU256"; -;;; Generates a new pseudo-random integer z in the range 0..range−1 (or range..−1, if range < 0). More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed. +/// Generates a new pseudo-random integer z in the range 0..range−1 (or range..−1, if range < 0). More precisely, an unsigned random value x is generated as in random; then z := x * range / 2^256 is computed. int rand(int range) impure asm "RAND"; -;;; Returns the current random seed as an unsigned 256-bit Integer. +/// Returns the current random seed as an unsigned 256-bit Integer. int getSeed() impure asm "RANDSEED"; -;;; Sets the random seed to unsigned 256-bit seed. +/// Sets the random seed to unsigned 256-bit seed. () setSeed(int) impure asm "SETRAND"; -;;; Mixes unsigned 256-bit integer x into the random seed r by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed r, and the second with the big-endian representation of x. +/// Mixes unsigned 256-bit integer x into the random seed r by setting the random seed to sha256 of the concatenation of two 32-byte strings: the first with the big-endian representation of the old seed r, and the second with the big-endian representation of x. () randomize(int x) impure asm "ADDRAND"; -;;; Equivalent to randomize(cur_lt());. +/// Equivalent to randomize(curLt());. () randomizeLt() impure asm "LTIME" "ADDRAND"; -;;; Checks whether the data parts of two slices coinside +/// Checks whether the data parts of two slices coinside int equalSliceBits (slice a, slice b) asm "SDEQ"; -;;; Concatenates two builders +/// Concatenates two builders builder storeBuilder(builder to, builder from) asm(from to) "STB"; -;; NEW FUNCS +// NEW FUNCS -;; ===== TVM UPGRADE ===== +// ===== TVM UPGRADE ===== -;;; Retrieves code of smart-contract from c7 +/// Retrieves code of smart-contract from c7 cell myCode() asm "MYCODE"; -;;; Creates an output action and returns a fee for creating a message. Mode has the same effect as in the case of SENDRAWMSG +/// Creates an output action and returns a fee for creating a message. Mode has the same effect as in the case of SENDRAWMSG int sendMessage(cell msg, int mode) impure asm "SENDMSG"; -;;; Retrieves value of storage phase fees from c7 +/// Retrieves value of storage phase fees from c7 int storageFees() asm "STORAGEFEES"; -;;; Retrieves global_id from 19 network config +/// Retrieves globalId from 19 network config int globalId() asm "GLOBALID"; -;;; Returns gas consumed by VM so far (including this instruction). +/// Returns gas consumed by VM so far (including this instruction). int gasConsumed() asm "GASCONSUMED"; int cellLevel(cell c) asm "CLEVEL"; int cellLevelMask(cell c) asm "CLEVELMASK"; -;; TVM V6 https://github.com/ton-blockchain/ton/blob/testnet/doc/GlobalVersions.md#version-6 +// TVM V6 https://github.com/ton-blockchain/ton/blob/testnet/doc/GlobalVersions.md#version-6 int getComputeFee(int workchain, int gasUsed) asm(gasUsed workchain) "GETGASFEE"; int getStorageFee(int workchain, int seconds, int bits, int cells) asm(cells bits seconds workchain) "GETSTORAGEFEE"; @@ -674,35 +672,35 @@ int myStorageDue() asm "DUEPAYMENT"; tuple getFeeCofigs() asm "UNPACKEDCONFIGTUPLE"; -;; ===== ADDRESS ===== +// ===== ADDRESS ===== const int BASECHAIN = 0; const int MASTERCHAIN = -1; -const slice addrNone = "2_"s; ;; addr_none$00 = MsgAddressExt; +const slice addrNone = "2_"s; // addr_none$00 = MsgAddressExt; -;;; Store addr_none constuction (b{00}) to `builder` [b] +/// Store addr_none constuction (b{00}) to `builder` [b] builder storeAddrNone(builder b) asm "b{00} STSLICECONST"; -;;; Checking that `slice` [s] is a addr_none constuction; +/// Checking that `slice` [s] is a addr_none constuction; int isAddrNone(slice s) asm "b{00} PUSHSLICE SDEQ"; -;;; Checking that the address is a standard basechain address and does not have anycast (should be used after load_msg_addr) +/// Checking that the address is a standard basechain address and does not have anycast (should be used after loadMsgAddr) int validateAddrBc(slice addr) asm "b{10000000000} PUSHSLICE SDPPFXREV"; -;;; Checking that the address is a standard masterchain address and does not have anycast (should be used after load_msg_addr) +/// Checking that the address is a standard masterchain address and does not have anycast (should be used after loadMsgAddr) int validateAddrMc(slice addr) asm "b{10011111111} PUSHSLICE SDPPFXREV"; builder storeBcAddress(builder b, int hash) asm(hash b) "b{10000000000} STSLICECONST 256 STU"; builder storeMcAddress(builder b, int hash) asm(hash b) "b{10011111111} STSLICECONST 256 STU"; -;;; Checking that the `slice` [addr] is a standard basechain address and does not have anycast (can be used with any `slice`) +/// Checking that the `slice` [addr] is a standard basechain address and does not have anycast (can be used with any `slice`) int extValidateAddrBc(slice addr) asm """ DUP b{10000000000} PUSHSLICE SDPPFXREV SWAP 267 INT 0 INT SCHKBITREFSQ AND """; -;;; Checking that the `slice` [addr] is a standard masterchain address and does not have anycast (can be used with any `slice`) +/// Checking that the `slice` [addr] is a standard masterchain address and does not have anycast (can be used with any `slice`) int extValidateAddrMc(slice addr) asm """ DUP b{10011111111} PUSHSLICE SDPPFXREV SWAP @@ -710,82 +708,82 @@ int extValidateAddrMc(slice addr) asm """ AND """; -;;; Checking that [addr1] and [addr2] have the same workchain +/// Checking that [addr1] and [addr2] have the same workchain int isWorkchainsEqual(slice addr1, slice addr2) asm "REWRITESTDADDR DROP SWAP REWRITESTDADDR DROP EQUAL"; -;;; Checking that [addr] have the workchain [wc] +/// Checking that [addr] have the workchain [wc] int isWorkchainMatch(slice addr, int wc) asm(wc addr) "REWRITESTDADDR DROP EQUAL"; -;;; Checking that [addr] have the workchain 0 +/// Checking that [addr] have the workchain 0 int isBasechainAddr(slice addr) asm "REWRITESTDADDR DROP 0 EQINT"; -;;; Checking that [addr] have the workchain -1 +/// Checking that [addr] have the workchain -1 int isMasterchainAddr(slice addr) asm "REWRITESTDADDR DROP -1 EQINT"; -;;; Basic store StateInit construction in `builder` [b]. +/// Basic store StateInit construction in `builder` [b]. builder storeStateInit(builder b, cell data, cell code) asm(data code b) "b{00110} STSLICECONST STREF STREF"; -;;; Calculate standard basechain address from `state_init` +/// Calculate standard basechain address from `stateInit` slice calcBcAddress(cell stateInit) inline { return packAddress(BASECHAIN, cellHash(stateInit)); } -;;; Calculate standard masterchain address from `state_init` +/// Calculate standard masterchain address from `stateInit` slice calcMcAddress(cell stateInit) inline { return packAddress(MASTERCHAIN, cellHash(stateInit)); } -;; ===== BOOL ===== +// ===== BOOL ===== const int TRUE = -1; const int FALSE = 0; -;;; Store binary true b{1} to `builder` [b] +/// Store binary true b{1} to `builder` [b] builder storeTrue(builder b) asm "STONE"; -;;; Store binary false b{0} to `builder` [b] +/// Store binary false b{0} to `builder` [b] builder storeFalse(builder b) asm "STZERO"; -;;; Store `int` [x] as bool to `builder` [b] +/// Store `int` [x] as bool to `builder` [b] builder storeBool(builder b, int x) asm(x b) "1 STI"; -;;; Loads bool from `slice` [s] +/// Loads bool from `slice` [s] (slice, int) loadBool(slice s) asm(-> 1 0) "1 LDI"; -;;; Checks whether `int` [x] is a “boolean value" (i.e., either 0 or -1). +/// Checks whether `int` [x] is a “boolean value" (i.e., either 0 or -1). int isBool(int x) asm "CHKBOOL"; -;;; Skip (Maybe ^Cell) from `slice` [s]. +/// Skip (Maybe ^Cell) from `slice` [s]. (slice, ()) ~skipMaybeRef(slice s) asm "SKIPOPTREF"; (slice, ()) ~skipDict(slice s) asm "SKIPOPTREF"; -;; ===== MSG FLAGS ===== +// ===== MSG FLAGS ===== -const slice BOUNCEABLE = "62_"s; ;; 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 -const slice NON_BOUNCEABLE = "42_"s; ;; 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00 +const slice BOUNCEABLE = "62_"s; // 0b011000 tag - 0, ihr_disabled - 1, bounce - 1, bounced - 0, src = adr_none$00 +const slice NON_BOUNCEABLE = "42_"s; // 0b010000 tag - 0, ihr_disabled - 1, bounce - 0, bounced - 0, src = adr_none$00 builder storeMsgFlags(builder b, int flag) asm(flag b) "6 STU"; builder storeMsgFlagsBounceable(builder b) asm "b{011000} STSLICECONST"; builder storeMsgFlagsNonBounceable(builder b) asm "b{010000} STSLICECONST"; -;; load msg_flags only +// load msgFlags only (slice, int) ~loadMsgFlags(slice s) asm(-> 1 0) "4 LDU"; -;;; Basic parse MessageX (full_message), returns: flags, sender, forward fee +/// Basic parse MessageX (fullMessage), returns: flags, sender, forward fee (int, slice, int) parseMessage(cell fullMessage) asm "CTOS 4 LDU LDMSGADDR LDMSGADDR LDGRAMS SKIPDICT LDGRAMS LDGRAMS DROP 3 1 BLKDROP2"; -;;; Checking that the "bounce" bit in the flags is set to "true" +/// Checking that the "bounce" bit in the flags is set to "true" int isBounceable(int flags) asm "2 INT AND"; -;;; Checking that the "bounced" bit in the flags is set to "true" +/// Checking that the "bounced" bit in the flags is set to "true" int isBounced(int flags) asm "ONE AND"; (slice, ()) ~skipBouncedPrefix(slice s) asm "x{FFFFFFFF} SDBEGINS"; -;; ===== MSG BODY ===== +// ===== MSG BODY ===== -;;; Store standard uint32 operation code into `builder` [b] +/// Store standard uint32 operation code into `builder` [b] builder storeOp(builder b, int op) asm(op b) "32 STU"; -;;; Store standard uint64 query id into `builder` [b] +/// Store standard uint64 query id into `builder` [b] builder storeQueryId(builder b, int queryId) asm(queryId b) "64 STU"; -;;; Load standard uint32 operation code from `slice` [s] +/// Load standard uint32 operation code from `slice` [s] (slice, int) loadOp(slice s) asm(-> 1 0) "32 LDU"; -;;; Load standard uint64 query id from `slice` [s] +/// Load standard uint64 query id from `slice` [s] (slice, int) loadQueryId(slice s) asm(-> 1 0) "64 LDU"; (slice, (int, int)) ~loadOpAndQueryId(slice s) asm(-> 2 0 1) "32 LDU 64 LDU"; @@ -793,95 +791,95 @@ builder storeQueryId(builder b, int queryId) asm(queryId b) "64 STU"; (slice, ()) ~skipOp(slice s) asm "32 LDU NIP"; (slice, ()) ~skipQueryId(slice s) asm "32 LDU NIP"; -;; ===== SEND ===== +// ===== SEND ===== const int MSG_INFO_REST_BITS = 1 + 4 + 4 + 64 + 32; -;; https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L155 -;; message$_ {X:Type} info:CommonMsgInfo -;; Maybe (Either StateInit ^StateInit) -;; body:(Either X ^X) = Message X; -;; -;;message$_ {X:Type} info:CommonMsgInfoRelaxed -;; init:(Maybe (Either StateInit ^StateInit)) -;; body:(Either X ^X) = MessageRelaxed X; -;; -;;_ (Message Any) = MessageAny; +// https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L155 +// message$_ {X:Type} info:CommonMsgInfo +// Maybe (Either StateInit ^StateInit) +// body:(Either X ^X) = Message X; +// +//message$_ {X:Type} info:CommonMsgInfoRelaxed +// init:(Maybe (Either StateInit ^StateInit)) +// body:(Either X ^X) = MessageRelaxed X; +// +//_ (Message Any) = MessageAny; -;; if have StateInit (always place StateInit in ref): -;; 0b11 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` +// if have StateInit (always place StateInit in ref): +// 0b11 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` const int MSG_WITH_STATE_INIT_AND_BODY_SIZE = MSG_INFO_REST_BITS + 1 + 1 + 1; const int MSG_HAVE_STATE_INIT = 4; const int MSG_STATE_INIT_IN_REF = 2; const int MSG_BODY_IN_REF = 1; -;; if no StateInit: -;; 0b0 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` +// if no StateInit: +// 0b0 for `Maybe (Either StateInit ^StateInit)` and 0b1 or 0b0 for `body:(Either X ^X)` const int MSG_ONLY_BODY_SIZE = MSG_INFO_REST_BITS + 1 + 1; -;; ===== SEND MODES ===== +// ===== SEND MODES ===== -;; For `send_raw_message` and `send_message`: +// For `sendRawMessage` and `sendMessage`: -;;; x = 0 is used for ordinary messages; the gas fees are deducted from the senging amount; action phaes should NOT be ignored. +/// x = 0 is used for ordinary messages; the gas fees are deducted from the senging amount; action phaes should NOT be ignored. const int sendmode::REGULAR = 0; -;;; +1 means that the sender wants to pay transfer fees separately. +/// +1 means that the sender wants to pay transfer fees separately. const int sendmode::PAY_FEES_SEPARATELY = 1; -;;; + 2 means that any errors arising while processing this message during the action phase should be ignored. +/// + 2 means that any errors arising while processing this message during the action phase should be ignored. const int sendmode::IGNORE_ERRORS = 2; -;;; + 32 means that the current account must be destroyed if its resulting balance is zero. +/// + 32 means that the current account must be destroyed if its resulting balance is zero. const int sendmode::DESTROY = 32; -;;; x = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message. +/// x = 64 is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message. const int sendmode::CARRY_ALL_REMAINING_MESSAGE_VALUE = 64; -;;; x = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message). +/// x = 128 is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message). const int sendmode::CARRY_ALL_BALANCE = 128; -;;; in the case of action fail - bounce transaction. No effect if sendmode::IGNORE_ERRORS (+2) is used. TVM UPGRADE 2023-07. +/// in the case of action fail - bounce transaction. No effect if sendmode::IGNORE_ERRORS (+2) is used. TVM UPGRADE 2023-07. const int sendmode::BOUNCE_ON_ACTION_FAIL = 16; -;; Only for `send_message`: +// Only for `sendMessage`: -;;; do not create an action, only estimate fee +/// do not create an action, only estimate fee const int sendmode::ESTIMATE_FEE_ONLY = 1024; -;; Other modes affect the fee calculation as follows: -;; +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account). -;; +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account). +// Other modes affect the fee calculation as follows: +// +64 substitutes the entire balance of the incoming message as an outcoming value (slightly inaccurate, gas expenses that cannot be estimated before the computation is completed are not taken into account). +// +128 substitutes the value of the entire balance of the contract before the start of the computation phase (slightly inaccurate, since gas expenses that cannot be estimated before the completion of the computation phase are not taken into account). -;; ===== RESERVE MODES ===== +// ===== RESERVE MODES ===== -;;; Creates an output action which would reserve exactly x nanograms (if y = 0). +/// Creates an output action which would reserve exactly x nanograms (if y = 0). const int reserve::REGULAR = 0; -;;; Creates an output action which would reserve at most x nanograms (if y = 2). -;;; Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. +/// Creates an output action which would reserve at most x nanograms (if y = 2). +/// Bit +2 in y means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. const int reserve::AT_MOST = 2; -;;; in the case of action fail - bounce transaction. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. +/// in the case of action fail - bounce transaction. No effect if RESERVE_AT_MOST (+2) is used. TVM UPGRADE 2023-07. const int reserve::BOUNCE_ON_ACTION_FAIL = 16; -;; ===== TOKEN METADATA ===== -;; https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md +// ===== TOKEN METADATA ===== +// https://github.com/ton-blockchain/TEPs/blob/master/text/0064-token-data-standard.md const slice ONCHAIN_CONTENT = "00"s; const slice OFFCHAIN_CONTENT = "01"s; const slice SNAKE_FORMAT = "00"s; const slice CHUNKS_FORMAT = "01"s; -;; Key is sha256 hash of string. Value is data encoded as described in "Data serialization" paragraph. -;; Snake format - must be prefixed with 0x00 byte +// Key is sha256 hash of string. Value is data encoded as described in "Data serialization" paragraph. +// Snake format - must be prefixed with 0x00 byte (cell, ()) ~setTokenSnakeMetadataEntry(cell contentDict, int key, slice value) impure { contentDict~udictSetRef(256, key, beginCell().storeSlice(SNAKE_FORMAT).storeSlice(value).endCell()); return (contentDict, ()); } -;; On-chain content layout The first byte is 0x00 and the rest is key/value dictionary. +// On-chain content layout The first byte is 0x00 and the rest is key/value dictionary. cell createTokenOnchainMetadata(cell contentDict) inline { return beginCell().storeSlice(ONCHAIN_CONTENT).storeDict(contentDict).endCell(); } -;; ===== BASIC ===== +// ===== BASIC ===== -;;; Returns the current length of the `tuple` [t] +/// Returns the current length of the `tuple` [t] int tupleLength(tuple t) asm "TLEN"; builder storeZeroes(builder b, int x) asm "STZEROES"; @@ -897,9 +895,9 @@ builder storeVarint32(builder b, int x) asm "STVARINT32"; (slice, int) loadVaruint32(slice s) asm(-> 1 0) "LDVARUINT32"; (slice, int) loadVarint32(slice s) asm(-> 1 0) "LDVARINT32"; -;;; Creates an output action that would modify the collection of this smart contract -;;; libraries by adding or removing library with code given in `Cell` [code]. -;;; Modes: 0 - remove library, 1 - add private library, 2 - add public library +/// Creates an output action that would modify the collection of this smart contract +/// libraries by adding or removing library with code given in `Cell` [code]. +/// Modes: 0 - remove library, 1 - add private library, 2 - add public library () setLibrary(cell code, int mode) impure asm "SETLIBCODE"; (slice, ()) ~skipRef(slice s) asm "LDREF NIP"; @@ -911,36 +909,39 @@ cell preloadSecondRef(slice s) asm "1 PLDREFIDX"; cell preloadThirdRef(slice s) asm "2 PLDREFIDX"; cell preloadFourthRef(slice s) asm "3 PLDREFIDX"; -;;; Concatenates two builders, but second builder stores as the reference (end_cell -> store_ref) +/// Concatenates two builders, but second builder stores as the reference (endCell -> storeRef) builder storeBuilderAsRef(builder to, builder from) asm(from to) "STBREF"; -;;; Loads the reference from the slice and parse (load_ref -> begin_parse) +/// Loads the reference from the slice and parse (loadRef -> beginParse) (slice, slice) loadRefAsSlice(slice s) asm "LDREFRTOS"; -;;; Returns the TON balance of the smart contract +/// Returns the TON balance of the smart contract int getTonBalance() asm "BALANCE FIRST"; -;;; Returns the number of data bits and cell references already stored in `builder` [b]. +/// Returns the number of data bits and cell references already stored in `builder` [b]. (int, int) builderBitsRefs(builder b) asm "BBITREFS"; -;;; Returns the number of data bits and cell references that can be stored in `builder` [b]. +/// Returns the number of data bits and cell references that can be stored in `builder` [b]. (int, int) builderRemBitsRefs(builder b) asm "BREMBITREFS"; -;;; Checks whether `slice` [pfx] is a prefix of `slice` [s] +/// Checks whether `slice` [pfx] is a prefix of `slice` [s] int sliceCheckPrefix(slice s, slice pfx) asm "SDPFXREV"; -;;; Checks whether `slice` [sfx] is a suffix of `slice` [s] +/// Checks whether `slice` [sfx] is a suffix of `slice` [s] int sliceCheckSuffix(slice s, slice sfx) asm "SDSFXREV"; -;;; Checks whether there are at least [l] data bits in `slice` [s]. +/// Checks whether there are at least [l] data bits in `slice` [s]. int sliceCheckBits(slice s, int l) asm "SCHKBITSQ"; -;;; Checks whether there are at least [r] references in `slice` [s]. +/// Checks whether there are at least [r] references in `slice` [s]. int sliceCheckRefs(slice s, int r) asm "SCHKREFSQ"; -;;; Checks whether there are at least [l] data bits and [r] references in `slice` [s]. +/// Checks whether there are at least [l] data bits and [r] references in `slice` [s]. int sliceCheckBitsRefs(slice s, int l, int r) asm "SCHKBITREFSQ"; -;;; Checks whether `slice` [s] begins with (the data bits of) [pfx], and removes [pfx] from [s] on success. -;;(slice, int) slice_begins_with(slice s, slice pfx) asm "SDBEGINSXQ"; +/// Splits the first 0 ≤ [bits] ≤ 1023 data bits and first 0 ≤ [refs] ≤ 4 references from `slice` [cs] +(slice, slice) sliceSplit(slice cs, int bits, int refs) asm(-> 1 0) "SPLIT"; + +/// Checks whether `slice` [s] begins with (the data bits of) [pfx], and removes [pfx] from [s] on success. +//(slice, int) sliceBeginsWith?(slice s, slice pfx) asm "SDBEGINSXQ"; -;;; Store `integer` [x] number as string (UTF-8) in decimal form in `builder` [b]. +/// Store `integer` [x] number as string (UTF-8) in decimal form in `builder` [b]. builder storeNumberDec(builder b, int x) asm """ ZERO // b x i=0 SWAP // b i=0 x @@ -956,7 +957,7 @@ builder storeNumberDec(builder b, int x) asm """ REPEAT:<{ 8 STU }> // ..rrr.. b i """; -;;; Store `int` [x] number as string (UTF-8) in hexadecimal form in `builder` [b]. +/// Store `int` [x] number as string (UTF-8) in hexadecimal form in `builder` [b]. builder storeNumberHex(builder b, int x) asm """ ZERO // b x i=0 SWAP // b i=0 x @@ -972,21 +973,21 @@ builder storeNumberHex(builder b, int x) asm """ REPEAT:<{ 8 STU }> // ..rrr.. b i """; -;;; Returns the continuation located in register c2 +/// Returns the continuation located in register c2 ((int, int) -> ()) getC2() asm "c2 PUSH"; -;;; Store `continuation` [c] to register c2 +/// Store `continuation` [c] to register c2 () setC2(((int, int) -> ()) c) impure asm "c2 POP"; -;;; Store `cell` [actions] to register c5 (out actions) +/// Store `cell` [actions] to register c5 (out actions) () setActions(cell actions) impure asm "c5 POP"; -;;; Store empty cell to register c5 (out actions) +/// Store empty cell to register c5 (out actions) () cleanActions() impure asm " PUSHREF c5 POP"; -;;; DRAFT, not for use in prod +/// DRAFT, not for use in prod () send(slice address, int value, builder stateInit, builder body, int mode) impure inline { builder message = beginCell().storeSlice(BOUNCEABLE).storeSlice(address).storeCoins(value).storeZeroes(105); ifnot (stateInit.isNull()) { - message = message.storeSlice("A_"s).storeBuilder(stateInit); ;; x{A_} == b{10} + message = message.storeSlice("A_"s).storeBuilder(stateInit); // x{A_} == b{10} } else { message = message.storeFalse(); } @@ -1005,32 +1006,32 @@ builder storeNumberHex(builder b, int x) asm """ sendRawMessage(message.endCell(), mode); } -;; ===== STANDARD OP's ===== +// ===== STANDARD OP's ===== -;; Common (used in TEP-62,74,85) +// Common (used in TEP-62,74,85) const slice op::excesses = "d53276db"s; -;; TEP-62 - NFT -;; https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md +// TEP-62 - NFT +// https://github.com/ton-blockchain/TEPs/blob/master/text/0062-nft-standard.md const slice op::transferNft = "5fcc3d14"s; const slice op::ownershipAssigned = "05138d91"s; const slice op::getStaticData = "2fcb26a2"s; const slice op::reportStaticData = "8b771735"s; -;; TEP-66 - NFT Royalty -;; https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md +// TEP-66 - NFT Royalty +// https://github.com/ton-blockchain/TEPs/blob/master/text/0066-nft-royalty-standard.md const slice op::getRoyaltyParams = "693d3950"s; const slice op::reportRoyaltyParams = "a8cb00ad"s; -;; TEP-74 - Jettons -;; https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md +// TEP-74 - Jettons +// https://github.com/ton-blockchain/TEPs/blob/master/text/0074-jettons-standard.md const slice op::transferJetton = "0f8a7ea5"s; const slice op::internalTransfer = "178d4519"s; const slice op::transferNotification = "7362d09c"s; const slice op::burnNotification = "7bdd97de"s; -;; TEP-85 - SBT -;; https://github.com/ton-blockchain/TEPs/blob/master/text/0085-sbt-standard.md +// TEP-85 - SBT +// https://github.com/ton-blockchain/TEPs/blob/master/text/0085-sbt-standard.md const slice op::proveOwnership = "04ded148"s; const slice op::ownershipProof = "0524c7ae"s; const slice op::requestOwner = "d0c3bfea"s; @@ -1038,12 +1039,12 @@ const slice op::ownerInfo = "0dd607e3"s; const slice op::destroySbt = "1f04537a"s; const slice op::revokeSbt = "6f89f5e3"s; -;; TEP-89 - Discoverable Jettons Wallets -;; https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md +// TEP-89 - Discoverable Jettons Wallets +// https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md const slice op::provideWalletAddress = "2c76b973"s; const slice op::takeWalletAddress = "d1735400"s; -;; ===== DICTS (missing funcs) ===== +// ===== DICTS (missing funcs) ===== cell dictGetRef(cell dict, int keyLen, slice index) asm(index dict keyLen) "DICTGETOPTREF"; cell udictGetRef(cell dict, int keyLen, int index) asm(index dict keyLen) "DICTUGETOPTREF"; @@ -1075,7 +1076,7 @@ cell dictSetRef(cell dict, int keyLen, slice index, cell value) asm(value index (slice, slice, int) tryDictGetPrev(cell dict, int keyLen, slice pivot) asm(pivot dict keyLen -> 1 0 2) "DICTGETPREV" "NULLSWAPIFNOT2"; (slice, slice, int) tryDictGetPreveq(cell dict, int keyLen, slice pivot) asm(pivot dict keyLen -> 1 0 2) "DICTGETPREVEQ" "NULLSWAPIFNOT2"; -;; ===== DICTS (new funcs) ===== +// ===== DICTS (new funcs) ===== (slice, cell, int) tryLoadDict(slice s) asm(-> 1 0 2) "LDDICTQ" "NULLROTRIFNOT"; (slice, (cell, int)) ~tryLoadDict(slice s) asm(-> 1 0 2) "LDDICTQ" "NULLROTRIFNOT"; @@ -1173,7 +1174,7 @@ cell dictSetRef(cell dict, int keyLen, slice index, cell value) asm(value index (cell, (int, cell, int)) ~tryUdictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTUREMMAXREF" "NULLSWAPIFNOT2"; (cell, (int, cell, int)) ~tryIdictDeleteGetMaxRef(cell dict, int keyLen) asm(-> 0 2 1 3) "DICTIREMMAXREF" "NULLSWAPIFNOT2"; -;; ===== HASHES ===== +// ===== HASHES ===== int sha256FromSnake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_SHA256 }> 1 1 CALLXARGS"; [int, int] sha512FromSnake(slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH HASHEXT_SHA512 }> 1 1 CALLXARGS"; @@ -1187,7 +1188,7 @@ builder storeBlake2bFromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DU builder storeKeccak256FromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_KECCAK256 }> 2 1 CALLXARGS"; builder storeKeccak512FromSnake(builder b, slice snake) asm "CONT:<{ WHILE:<{ DUP SREFS }>DO<{ LDREFRTOS }> DEPTH DEC HASHEXTA_KECCAK512 }> 2 1 CALLXARGS"; -;; ===== SIGNATURES ===== +// ===== SIGNATURES ===== int checkSecp256r1Signature(int hash, slice signature, int publicKey) asm "P256_CHKSIGNU"; int checkSecp256r1DataSignature(slice data, slice signature, int publicKey) asm "P256_CHKSIGNS"; From 25fd989faceac3762cc541f480ef74843ad906b5 Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Mon, 19 Feb 2024 18:08:14 +0500 Subject: [PATCH 26/28] builtin headers support --- crypto/func/parse-func.cpp | 18 +++++- crypto/smartcont/stdlib.fc | 112 +++++++++++++++++++++++++------------ 2 files changed, 91 insertions(+), 39 deletions(-) diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index b2bfabe8d..ead044b08 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -1488,7 +1488,13 @@ void parse_func_def(Lexer& lex) { if (verbosity >= 1) { std::cerr << "function " << func_name.str << " : " << func_type << std::endl; } - SymDef* func_sym = sym::define_global_symbol(func_name.val, 0, loc); + SymDef* defined_func_sym = sym::lookup_symbol(func_name.val, 2); + SymDef* func_sym; + if (defined_func_sym) { + func_sym = defined_func_sym; + } else { + func_sym = sym::define_global_symbol(func_name.val, 0, loc); + } func_assert(func_sym); SymValFunc* func_sym_val = dynamic_cast(func_sym->value); if (func_sym->value) { @@ -1505,8 +1511,14 @@ void parse_func_def(Lexer& lex) { } } if (lex.tp() == ';') { - make_new_glob_func(func_sym, func_type, impure); - lex.next(); + if (!defined_func_sym) { + make_new_glob_func(func_sym, func_type, impure); + lex.next(); + } else { + lex.next(); + sym::close_scope(lex); + return; + } } else if (lex.tp() == '{') { if (dynamic_cast(func_sym_val)) { lex.cur().error("function `"s + func_name.str + "` has been already defined as an assembler built-in"); diff --git a/crypto/smartcont/stdlib.fc b/crypto/smartcont/stdlib.fc index bc189dcfd..3767f1aff 100644 --- a/crypto/smartcont/stdlib.fc +++ b/crypto/smartcont/stdlib.fc @@ -197,9 +197,6 @@ int check_data_signature(slice data, slice signature, int public_key) asm "CHKSI ;;; A non-quiet version of [slice_compute_data_size?] that throws a cell overflow exception (8) on failure. (int, int, int) slice_compute_data_size(slice s, int max_cells) impure asm "SDATASIZE"; -;;; Throws an exception with exit_code excno if cond is not 0 (commented since implemented in compilator) -;; () throw_if(int excno, int cond) impure asm "THROWARGIF"; - {- # Debug primitives Only works for local TVM execution with debug level verbosity @@ -303,26 +300,6 @@ slice begin_parse(cell c) asm "CTOS"; ;;; Preloads the first reference from the slice. cell preload_ref(slice s) asm "PLDREF"; - {- Functions below are commented because are implemented on compilator level for optimisation -} - -;;; Loads a signed [len]-bit integer from a slice [s]. -;; (slice, int) ~load_int(slice s, int len) asm(s len -> 1 0) "LDIX"; - -;;; Loads an unsigned [len]-bit integer from a slice [s]. -;; (slice, int) ~load_uint(slice s, int len) asm( -> 1 0) "LDUX"; - -;;; Preloads a signed [len]-bit integer from a slice [s]. -;; int preload_int(slice s, int len) asm "PLDIX"; - -;;; Preloads an unsigned [len]-bit integer from a slice [s]. -;; int preload_uint(slice s, int len) asm "PLDUX"; - -;;; Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. -;; (slice, slice) load_bits(slice s, int len) asm(s len -> 1 0) "LDSLICEX"; - -;;; Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. -;; slice preload_bits(slice s, int len) asm "PLDSLICEX"; - ;;; Loads serialized amount of TonCoins (any unsigned integer up to `2^120 - 1`). (slice, int) load_grams(slice s) asm( -> 1 0) "LDGRAMS"; (slice, int) load_coins(slice s) asm( -> 1 0) "LDGRAMS"; @@ -429,16 +406,6 @@ cell end_cell(builder b) asm "ENDC"; ;;; Stores a reference to `cell` [c] into `builder` [b]. builder store_ref(builder b, cell c) asm(c b) "STREF"; -;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. -;; builder store_uint(builder b, int x, int len) asm(x b len) "STUX"; - -;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. -;; builder store_int(builder b, int x, int len) asm(x b len) "STIX"; - - -;;; Stores `slice` [s] into `builder` [b] -;; builder store_slice(builder b, slice s) asm "STSLICER"; - ;;; Stores (serializes) an integer [x] in the range `0..2^120 − 1` into `builder` [b]. ;;; The serialization of [x] consists of a 4-bit unsigned big-endian integer `l`, ;;; which is the smallest integer `l ≥ 0`, such that `x < 2^8l`, @@ -672,6 +639,82 @@ int my_storage_due() asm "DUEPAYMENT"; tuple get_fee_cofigs() asm "UNPACKEDCONFIGTUPLE"; +;; ===== BUILTINS ===== + +{- Functions are implemented on compilator level for optimisation -} + +(int, int) divmod(int x, int y); +(int, (int)) ~divmod(int x, int y); +(int, int) moddiv(int x, int y); +(int, (int)) ~moddiv(int x, int y); +int muldiv(int x, int y, int z); +int muldivr(int x, int y, int z); +int muldivc(int x, int y, int z); +(int, int) muldivmod(int x, int y, int z); + +forall X -> int null?(X x); + +;;; Throws an exception with exit_code excno +() throw(int excno) impure; + +;;; Throws an exception with exit_code excno if cond is true (!= 0) +() throw_if(int excno, int cond) impure; + +;;; Throws an exception with exit_code excno if cond is false (== 0) +() throw_unless(int excno, int cond) impure; + +forall X -> () throw_arg(X arg, int excno) impure; +forall X -> () throw_arg_if(X arg, int excno, int cond) impure; +forall X -> () throw_arg_unless(X arg, int excno, int cond) impure; + +;;; Loads a signed [len]-bit integer from a slice [s]. +(slice, int) load_int(slice s, int len); + +;;; Loads an unsigned [len]-bit integer from a slice [s]. +(slice, int) load_uint(slice s, int len); + +;;; Preloads a signed [len]-bit integer from a slice [s]. +int preload_int(slice s, int len); + +;;; Preloads an unsigned [len]-bit integer from a slice [s]. +int preload_uint(slice s, int len); + +;;; Stores a signed [len]-bit integer `x` into `b` for` 0 ≤ len ≤ 257`. +builder store_int(builder b, int x, int len); +(builder, ()) ~store_int(builder b, int x, int len); + +;;; Stores an unsigned [len]-bit integer `x` into `b` for `0 ≤ len ≤ 256`. +builder store_uint(builder b, int x, int len); +(builder, ()) ~store_uint(builder b, int x, int len); + +;;; Loads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +(slice, slice) load_bits(slice s, int len); + +;;; Preloads the first `0 ≤ len ≤ 1023` bits from slice [s] into a separate `slice s''`. +slice preload_bits(slice s, int len); + +int int_at(tuple t, int k); +cell cell_at(tuple t, int k); +slice slice_at(tuple t, int k); +tuple tuple_at(tuple t, int k); +forall X -> X at(tuple t, int k); + +forall X -> X touch(X x) impure; +forall X -> (X, ()) ~touch(X x) impure; +forall X, Y -> (X, Y) touch2(X x, Y y) impure; +forall X, Y -> ((X, Y), ()) ~touch2(X x, Y y) impure; +forall X -> (X, ()) ~dump(X x) impure; +forall X -> (X, ()) ~strdump(X x) impure; + +;;; Checks whether `slice` [s] begins with (the data bits of) [pfx], and removes [pfx] from [s] on success. +(slice, int) slice_begins_with?(slice s, slice pfx); + +;;; Stores `slice` [s] into `builder` [b] +builder store_slice(builder b, slice s); +(builder, ()) ~store_slice(builder b, slice s); + +slice pack_address(int wc, int hash); + ;; ===== ADDRESS ===== const int BASECHAIN = 0; @@ -938,9 +981,6 @@ int slice_check_bits_refs(slice s, int l, int r) asm "SCHKBITREFSQ"; ;;; Splits the first 0 ≤ [bits] ≤ 1023 data bits and first 0 ≤ [refs] ≤ 4 references from `slice` [cs] (slice, slice) slice_split(slice cs, int bits, int refs) asm(-> 1 0) "SPLIT"; -;;; Checks whether `slice` [s] begins with (the data bits of) [pfx], and removes [pfx] from [s] on success. -;;(slice, int) slice_begins_with?(slice s, slice pfx) asm "SDBEGINSXQ"; - ;;; Store `integer` [x] number as string (UTF-8) in decimal form in `builder` [b]. builder store_number_dec(builder b, int x) asm """ ZERO // b x i=0 From 0bdca1d9998958de04ead46e6360eb48ba63100c Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Sat, 24 Feb 2024 02:47:25 +0500 Subject: [PATCH 27/28] pragma camelCase --- crypto/func/func.cpp | 4 +++- crypto/func/func.h | 12 +++++++++++- crypto/func/keywords.cpp | 4 ++-- crypto/func/parse-func.cpp | 34 +++++++++++++++++++++++++++++++--- crypto/parser/lexer.cpp | 24 ++++++++++++------------ crypto/parser/lexer.h | 2 +- 6 files changed, 60 insertions(+), 20 deletions(-) diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index 39648c05a..03f537df3 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -40,6 +40,7 @@ bool stack_layout_comments, op_rewrite_comments, program_envelope, asm_preamble; bool interactive = false; GlobalPragma pragma_allow_post_modification{"allow-post-modification"}; GlobalPragma pragma_compute_asm_ltr{"compute-asm-ltr"}; +GlobalPragma pragma_camel_case{"camelCase"}; std::string generated_from, boc_output_filename; ReadCallback::Callback read_callback; @@ -239,6 +240,7 @@ int func_proceed(const std::vector &sources, std::ostream &outs, st } pragma_allow_post_modification.check_enable_in_libs(); pragma_compute_asm_ltr.check_enable_in_libs(); + pragma_camel_case.check_enable_in_libs(); return funC::generate_output(outs, errs); } catch (src::Fatal& fatal) { errs << "fatal: " << fatal << std::endl; @@ -259,4 +261,4 @@ int func_proceed(const std::vector &sources, std::ostream &outs, st return 0; } -} // namespace funC \ No newline at end of file +} // namespace funC diff --git a/crypto/func/func.h b/crypto/func/func.h index 2b95bcbc5..357618926 100644 --- a/crypto/func/func.h +++ b/crypto/func/func.h @@ -112,8 +112,10 @@ enum Keyword { _Extern, _Inline, _InlineRef, + _InlineRefCamel, _AutoApply, _MethodId, + _MethodIdCamel, _Operator, _Infix, _Infixl, @@ -1754,6 +1756,14 @@ class GlobalPragma { bool enabled() const { return enabled_; } + bool enabled_in_file(const src::FileDescr* fdescr) { + for (const SrcLocation& loc : locs_) { + if (loc.fdescr->filename == fdescr->filename) { + return true; + } + } + return false; + } void enable(SrcLocation loc) { enabled_ = true; locs_.push_back(std::move(loc)); @@ -1777,7 +1787,7 @@ class GlobalPragma { bool enabled_ = false; std::vector locs_; }; -extern GlobalPragma pragma_allow_post_modification, pragma_compute_asm_ltr; +extern GlobalPragma pragma_allow_post_modification, pragma_compute_asm_ltr, pragma_camel_case; /* * diff --git a/crypto/func/keywords.cpp b/crypto/func/keywords.cpp index 3cb45ae09..50b244cfc 100644 --- a/crypto/func/keywords.cpp +++ b/crypto/func/keywords.cpp @@ -124,8 +124,8 @@ void define_keywords() { .add_keyword("inline_ref", Kw::_InlineRef) .add_keyword("auto_apply", Kw::_AutoApply) .add_keyword("method_id", Kw::_MethodId) - .add_keyword("inlineRef", Kw::_InlineRef) - .add_keyword("methodId", Kw::_MethodId) + .add_keyword("inlineRef", Kw::_InlineRefCamel) + .add_keyword("methodId", Kw::_MethodIdCamel) .add_keyword("operator", Kw::_Operator) .add_keyword("infix", Kw::_Infix) .add_keyword("infixl", Kw::_Infixl) diff --git a/crypto/func/parse-func.cpp b/crypto/func/parse-func.cpp index ead044b08..0bf98b8b4 100644 --- a/crypto/func/parse-func.cpp +++ b/crypto/func/parse-func.cpp @@ -404,6 +404,13 @@ bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) { ++undef_func_cnt; make_new_glob_func(def, TypeExpr::new_func()); // was: ... ::new_func() return true; + } else { + auto str_func_name = symbols.get_name(func_name); + if (td::StringCase::is_snake_case(str_func_name) && pragma_camel_case.enabled_in_file(cur.loc.fdescr)) { + cur.loc.show_warning(std::string{"function `"} + str_func_name + "` does not match the current code style (camelCase pragma enabled)"); + } else if (td::StringCase::is_camel_case(str_func_name) && !pragma_camel_case.enabled_in_file(cur.loc.fdescr)) { + cur.loc.show_warning(std::string{"function `"} + str_func_name + "` does not match the current code style (camelCase pragma disabled)"); + } } SymVal* val = dynamic_cast(def->value); if (!val) { @@ -1432,6 +1439,7 @@ TypeExpr* compute_type_closure(TypeExpr* expr, const std::vector& typ void parse_func_def(Lexer& lex) { SrcLocation loc{lex.cur().loc}; + bool camel_case = pragma_camel_case.enabled_in_file(loc.fdescr); sym::open_scope(lex); std::vector type_vars; if (lex.tp() == _Forall) { @@ -1449,13 +1457,20 @@ void parse_func_def(Lexer& lex) { lex.next(); } int f = 0; - if (lex.tp() == _Inline || lex.tp() == _InlineRef) { + if (lex.tp() == _Inline || lex.tp() == _InlineRef || lex.tp() == _InlineRefCamel) { + if ((camel_case && lex.tp() == _InlineRef) || (!camel_case && lex.tp() == _InlineRefCamel)) { + lex.cur().loc.show_warning(std::string{"modifier `"} + lex.cur().str + "` does not match the current code style (camelCase pragma " + (camel_case ? "enabled)" : "disabled)")); + } f = (lex.tp() == _Inline) ? 1 : 2; lex.next(); } td::RefInt256 method_id; std::string method_name; - if (lex.tp() == _MethodId) { + SrcLocation method_name_loc = func_name.loc; + if (lex.tp() == _MethodId || lex.tp() == _MethodIdCamel) { + if ((camel_case && lex.tp() == _MethodId) || (!camel_case && lex.tp() == _MethodIdCamel)) { + lex.cur().loc.show_warning(std::string{"modifier `"} + lex.cur().str + "` does not match the current code style (camelCase pragma " + (camel_case ? "enabled)" : "disabled)")); + } lex.next(); if (lex.tp() == '(') { lex.expect('('); @@ -1470,13 +1485,23 @@ void parse_func_def(Lexer& lex) { } else { throw src::ParseError{lex.cur().loc, "integer or string method identifier expected"}; } + method_name_loc = lex.cur().loc; lex.next(); lex.expect(')'); } else { method_name = func_name.str; } if (method_id.is_null()) { - unsigned crc = td::crc16(td::StringCase::is_camel_case(method_name) ? td::StringCase::camel_to_snake(method_name) : method_name); + unsigned crc; + std::string formatted_method_name = method_name; + if (camel_case) { + if (td::StringCase::is_camel_case(method_name)) { + formatted_method_name = td::StringCase::camel_to_snake(method_name); + } else { + method_name_loc.show_warning(std::string{"method `"} + method_name + "` does not match the current code style (camelCase pragma enabled)"); + } + } + crc = td::crc16(formatted_method_name); method_id = td::make_refint((crc & 0xffff) | 0x10000); } } @@ -1719,6 +1744,9 @@ void parse_pragma(Lexer& lex) { pragma_allow_post_modification.enable(lex.cur().loc); } else if (pragma_name == pragma_compute_asm_ltr.name()) { pragma_compute_asm_ltr.enable(lex.cur().loc); + } else if (pragma_name == pragma_camel_case.name()) { + pragma_camel_case.enable(lex.cur().loc); + lex.set_cmts("//", "/*", "*/"); } else { lex.cur().error(std::string{"unknown pragma `"} + pragma_name + "`"); } diff --git a/crypto/parser/lexer.cpp b/crypto/parser/lexer.cpp index 7584e52d4..cf20d70d7 100644 --- a/crypto/parser/lexer.cpp +++ b/crypto/parser/lexer.cpp @@ -126,7 +126,6 @@ int Lexem::set(std::string _str, const SrcLocation& _loc, int _tp, int _val) { Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, std::string eol_cmts, std::string open_cmts, std::string close_cmts, - std::string camel_eol_cmts, std::string camel_open_cmts, std::string camel_close_cmts, std::string quote_chars, std::string multiline_quote) : src(_src), eof(false), lexem("", src.here(), Lexem::Undefined), peek_lexem("", {}, Lexem::Undefined), multiline_quote(std::move(multiline_quote)) { @@ -141,12 +140,7 @@ Lexer::Lexer(SourceReader& _src, bool init, std::string active_chars, char_class[(unsigned)c] |= activity; } } - set_spec(eol_cmt, eol_cmts); - set_spec(cmt_op, open_cmts); - set_spec(cmt_cl, close_cmts); - set_spec(c_eol_cmt, camel_eol_cmts); - set_spec(c_cmt_op, camel_open_cmts); - set_spec(c_cmt_cl, camel_close_cmts); + set_cmts(eol_cmts, open_cmts, close_cmts); for (int c : quote_chars) { if (c > ' ' && c <= 0x7f) { char_class[(unsigned)c] |= cc::quote_char; @@ -211,24 +205,24 @@ const Lexem& Lexer::next() { long long comm = 1; while (!src.seek_eof()) { int cc = src.cur_char(), nc = src.next_char(); - if ((cc == eol_cmt[0] || (cc == eol_cmt[1] && nc == eol_cmt[2])) || (cc == c_eol_cmt[0] || (cc == c_eol_cmt[1] && nc == c_eol_cmt[2]))) { + if (cc == eol_cmt[0] || (cc == eol_cmt[1] && nc == eol_cmt[2])) { src.load_line(); - } else if ((cc == cmt_op[1] && nc == cmt_op[2]) || (cc == c_cmt_op[1] && nc == c_cmt_op[2])) { + } else if (cc == cmt_op[1] && nc == cmt_op[2]) { src.advance(2); comm = comm * 2 + 1; - } else if ((cc == cmt_op[0]) || (cc == c_cmt_op[0])) { + } else if (cc == cmt_op[0]) { src.advance(1); comm *= 2; } else if (comm == 1) { break; - } else if ((cc == cmt_cl[1] && nc == cmt_cl[2]) || (cc == c_cmt_cl[1] && nc == c_cmt_cl[2])) { + } else if (cc == cmt_cl[1] && nc == cmt_cl[2]) { if (!(comm & 1)) { src.error(std::string{"a `"} + (char)cmt_op[0] + "` comment closed by `" + (char)cmt_cl[1] + (char)cmt_cl[2] + "`"); } comm >>= 1; src.advance(2); - } else if ((cc == cmt_cl[0]) || (cc == c_cmt_cl[0])) { + } else if (cc == cmt_cl[0]) { if (!(comm & 1)) { src.error(std::string{"a `"} + (char)cmt_op[1] + (char)cmt_op[2] + "` comment closed by `" + (char)cmt_cl[0] + "`"); @@ -339,4 +333,10 @@ const Lexem& Lexer::peek() { return peek_lexem; } +void Lexer::set_cmts(std::string eol_cmts, std::string open_cmts, std::string close_cmts) { + set_spec(eol_cmt, eol_cmts); + set_spec(cmt_op, open_cmts); + set_spec(cmt_cl, close_cmts); +} + } // namespace src diff --git a/crypto/parser/lexer.h b/crypto/parser/lexer.h index 203fff49b..052540ce2 100644 --- a/crypto/parser/lexer.h +++ b/crypto/parser/lexer.h @@ -80,7 +80,6 @@ class Lexer { } Lexer(SourceReader& _src, bool init = false, std::string active_chars = ";,() ~.", std::string eol_cmts = ";;", std::string open_cmts = "{-", std::string close_cmts = "-}", - std::string camel_eol_cmts = "//", std::string camel_open_cmts = "/*", std::string camel_close_cmts = "*/", std::string quote_chars = "\"", std::string multiline_quote = "\"\"\""); const Lexem& next(); const Lexem& cur() const { @@ -90,6 +89,7 @@ class Lexer { int tp() const { return lexem.tp; } + void set_cmts(std::string eol_cmts, std::string open_cmts, std::string close_cmts); void expect(int exp_tp, const char* msg = 0); int classify_char(unsigned c) const { return c < 0x80 ? char_class[c] : 0; From 4735e7742984ae83bc785d75de792ef523199d7a Mon Sep 17 00:00:00 2001 From: Andrew Gutarev Date: Sat, 24 Feb 2024 02:49:14 +0500 Subject: [PATCH 28/28] turn off check pragma camelCase in libs --- crypto/func/func.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crypto/func/func.cpp b/crypto/func/func.cpp index 03f537df3..6bec31146 100644 --- a/crypto/func/func.cpp +++ b/crypto/func/func.cpp @@ -240,7 +240,7 @@ int func_proceed(const std::vector &sources, std::ostream &outs, st } pragma_allow_post_modification.check_enable_in_libs(); pragma_compute_asm_ltr.check_enable_in_libs(); - pragma_camel_case.check_enable_in_libs(); + // pragma_camel_case.check_enable_in_libs(); return funC::generate_output(outs, errs); } catch (src::Fatal& fatal) { errs << "fatal: " << fatal << std::endl;