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

updated func and tonlib

This commit is contained in:
ton 2020-02-15 20:03:17 +04:00
parent 493ae2410c
commit a73d202ba2
50 changed files with 1340 additions and 271 deletions

View file

@ -0,0 +1,474 @@
{-
Adapted from original version written by:
/------------------------------------------------------------------------\
| Created for: Telegram (Open Network) Blockchain Contest |
| Task 2: DNS Resolver (Automatically registering) |
>------------------------------------------------------------------------<
| Author: Oleksandr Murzin (tg: @skydev / em: alexhacker64@gmail.com) |
| October 2019 |
\------------------------------------------------------------------------/
-}
;;===========================================================================;;
;; Utility functions ;;
;;===========================================================================;;
{-
Data structure:
Root cell: [OptRef<1b+1r?>:Hashmap<PfxDict:Slice->UInt<32b>,CatTable>:domains]
[OptRef<1b+1r?>:Hashmap<UInt<64b>(Time|Hash32)->Slice(DomName)>:gc]
[UInt<32b>:stdperiod] [Gram:PPReg] [Gram:PPCell] [Gram:PPBit]
[UInt<32b>:lasthousekeeping]
<CatTable> := HashmapE 16 ^DNSRecord
STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value)
#Zeros allows to simultaneously store, for example, com\0 and com\0google\0
That will be stored as \1com\0 and \2com\0google\0 (pfx tree has restricitons)
This will allow to resolve more specific requests to subdomains, and resort
to parent domain next resolver lookup if subdomain is not found
com\0goo\0 lookup will, for example look up \2com\0goo\0 and then
\1com\0goo\0 which will return \1com\0 (as per pfx tree) with -1 cat
-}
(cell, cell, [int, int, int, int], int, int) load_data() inline_ref {
slice cs = get_data().begin_parse();
return (
cs~load_dict(), ;; pfx tree: domains data and exp
cs~load_dict(), ;; gc auxillary with expiry and 32 bit hash slice
[ cs~load_uint(32), ;; length of this period of time in seconds
cs~load_grams(), ;; standard payment for registering a new subdomain
cs~load_grams(), ;; price paid for each cell (PPC)
cs~load_grams() ], ;; and bit (PPB)
cs~load_uint(32), ;; next housekeeping to be done at
cs~load_uint(32) ;; last housekeeping done at
);
}
(int, int, int, int) load_prices() inline_ref {
slice cs = get_data().begin_parse();
(cs~load_dict(), cs~load_dict());
return (cs~load_uint(32), cs~load_grams(), cs~load_grams(), cs~load_grams());
}
() store_data(cell dd, cell gc, prices, int nhk, int lhk) impure {
var [sp, ppr, ppc, ppb] = prices;
set_data(begin_cell()
.store_dict(dd) ;; domains data and exp
.store_dict(gc) ;; keyed expiration time and 32 bit hash slice
.store_int(sp, 32) ;; standard period
.store_grams(ppr) ;; price per registration
.store_grams(ppc) ;; price per cell
.store_grams(ppb) ;; price per bit
.store_uint(nhk, 32) ;; next housekeeping
.store_uint(lhk, 32) ;; last housekeeping
.end_cell());
}
global var query_info;
() send_message(slice addr, int tag, int query_id,
int body, int grams, int mode) impure {
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
;; src:MsgAddress -> 011000 0x18
var msg = begin_cell()
.store_uint (0x18, 6)
.store_slice(addr)
.store_grams(grams)
.store_uint (0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint (tag, 32)
.store_uint (query_id, 64);
if (body >= 0) {
msg~store_uint(body, 32);
}
send_raw_message(msg.end_cell(), mode);
}
() send_error(int error_code) impure {
var (addr, query_id, op) = query_info;
return send_message(addr, error_code, query_id, op, 0, 64);
}
() send_ok(int price) impure {
raw_reserve(price, 4);
var (addr, query_id, op) = query_info;
return send_message(addr, 0xef6b6179, query_id, op, 0, 128);
}
() housekeeping(cell dd, cell gc, prices, int nhk, int lhk) impure {
int n = now();
if (n < max(nhk, lhk + 60)) { ;; housekeeping cooldown: 1 minute
;; if housekeeping was done recently, or if next housekeeping is in the future, just save
return store_data(dd, gc, prices, nhk, lhk);
}
;; need to do some housekeeping - maybe remove entry with
;; least expiration but only if it is already expired
;; no iterating and deleting all to not put too much gas gc
;; burden on any random specific user request
;; over time it will do the garbage collection required
(int mkey, cell name, int found?) = gc.udict_get_min_ref?(64);
if (found?) { ;; no short circuit optimization, two nested ifs
nhk = (mkey >> 32);
if (nhk < n) {
slice sname = name.begin_parse();
(_, slice val, _, found?) = dd.pfxdict_get?(1023, sname);
if (found?) {
int exp = val.preload_uint(32);
if (exp <= n) {
dd~pfxdict_delete?(1023, sname);
}
}
gc~udict_delete?(64, mkey);
(mkey, _, found?) = gc.udict_get_min_ref?(64);
nhk = (found? ? mkey >> 32 : 0xffffffff);
}
}
store_data(dd, gc, prices, nhk, n);
}
int _calcprice(cell data, ppc, ppb) inline_ref { ;; only for internal calcs
var (_, bits, refs) = compute_data_size(data, 100); ;; 100 cells max
return ppc * refs + ppb * bits;
}
int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
if (cat_table.null?()) { ;; domain not found: return notf | 2^31
return 0xee6f7466;
}
cell cown = cat_table.idict_get_ref(16, -2);
if (cown.null?()) { ;; no owner on this domain: no-2
return 0xee6f2d32;
}
var ERR_BAD2 = 0xe2616432;
slice sown = cown.begin_parse();
if (sown.slice_bits() < 16 + 3 + 8 + 256) { ;; bad owner record: bad2
return ERR_BAD2;
}
if (sown~load_uint(16 + 3) != 0x9fd3 * 8 + 4) {
return ERR_BAD2;
}
(int owner_wc, int owner_addr) = (sown~load_int(8), sown.preload_uint(256));
if ((owner_wc != src_wc) | (owner_addr != src_addr)) { ;; not owner: nown
return 0xee6f776e;
}
return 0; ;; ok
}
;;===========================================================================;;
;; Internal message handler (Code 0) ;;
;;===========================================================================;;
{-
Internal message cell structure:
8 4 2 1
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
src:MsgAddressInt dest:MsgAddressInt
value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
created_lt:uint64 created_at:uint32
Internal message data structure:
[UInt<32b>:op] [UInt<64b>:query_id] [Ref<1r>:name]
(if not prolong: [Ref<1r>:value->CatTable])
-}
;; Must send at least GR$1 more for possible gas fees!
() recv_internal(int ct_bal, int msg_value, cell in_msg_cell, slice in_msg) impure {
;; this time very interested in internal messages
if (in_msg.slice_bits() < 32) {
return (); ;; simple transfer or short
}
slice cs = in_msg_cell.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) {
return (); ;; bounced messages
}
slice s_addr = cs~load_msg_addr();
(int src_wc, int src_addr) = s_addr.parse_std_addr();
int op = in_msg~load_uint(32);
ifnot (op) {
return (); ;; simple transfer with comment
}
int query_id = 0;
if (in_msg.slice_bits() >= 64) {
query_id = in_msg~load_uint(64);
}
query_info = (s_addr, query_id, op);
if (op & (1 << 31)) {
return (); ;; an answer to our query
}
int qt = (op == 0x72656764) * 1 + (op == 0x70726f6c) * 2 + (op == 0x75706464) * 4 + (op == 0x676f6763) * 8;
ifnot (qt) { ;; unknown query, return error
return send_error(0xffffffff);
}
qt = - qt;
(cell domdata, cell gc, [int, int, int, int] prices, int nhk, int lhk) = load_data();
if (qt == 8) { ;; 0x676f6763 -> GO, GC! go!!!
;; Manual garbage collection iteration
housekeeping(domdata, gc, prices, nhk, 1); ;; forced
return send_error(0xef6b6179);
}
slice name = null();
cell name_cell = in_msg~load_maybe_ref();
if (name_cell.null?()) {
int bytes = in_msg~load_uint(6);
name = in_msg~load_bits(bytes * 8);
} else {
name = name_cell.begin_parse();
}
(_, int name_last_byte) = name.slice_last(8).load_uint(8);
if (name_last_byte != 0) { ;; name must end with \0! no\0 error
return send_error(0xee6f5c30);
}
int zeros = 0;
slice cname = name;
repeat (cname.slice_bits() ^>> 3) {
int c = cname~load_uint(8);
zeros -= (c == 0);
}
;; if (zeros != 1) { ;; too much zero chars (overflow): ov\0
;; return send_error(0xef765c30); }
name = begin_cell().store_uint(zeros, 7).store_slice(name).end_cell().begin_parse();
(slice pfx, slice val, slice tail, int found?) = domdata.pfxdict_get?(1023, name);
int n = now();
cell cat_table = null();
int exp = 0;
if (found?) {
exp = val~load_uint(32);
if (n > exp) { ;; expired domains behave as not registered
found? = false;
} else {
cat_table = val.preload_ref();
}
}
;; ##########################################################################
int err = 0;
if (qt != 1) { ;; not a "register", check that domain exists and is controlled by correct smc
err = check_owner(cat_table, src_wc, src_addr);
}
if (err) {
return send_error(err);
}
;; ##########################################################################
;; load desired data (reuse old for a "prolong" operation)
cell data = null();
if (qt != 2) { ;; not a "prolong", load data dictionary
data = in_msg~load_ref();
;; basic integrity check of (client-provided) dictionary
ifnot (data.dict_empty?()) { ;; 1000 gas!
(int dmin, _, int minok) = idict_get_min?(data, 16);
(int dmax, _, int maxok) = idict_get_max?(data, 16);
throw_unless(31, minok & maxok & (dmin <= dmax));
}
} else {
data = cat_table;
}
;; compute action price
var [stdper, ppr, ppc, ppb] = prices;
int price = _calcprice(data, ppc, ppb) + (ppr & (qt != 4));
if (msg_value - (1 << 30) < price) { ;; gr<p: grams - GR$1 < price
return send_error(0xe7723c70);
}
;; load desired expiration unixtime
int req_expires_at = in_msg~load_uint(32);
;; ##########################################################################
if (qt == 2) { ;; 0x70726f6c -> prol | prolong domain
slice value = begin_cell().store_uint(exp + stdper, 32).store_ref(data).end_cell().begin_parse();
ifnot (domdata~pfxdict_set?(1023, name, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
int sh_low = name.slice_hash() & ((1 << 32) - 1);
int gckeyO = (exp << 32) + sh_low;
int gckeyN = gckeyO + (stdper << 32);
gc~udict_delete?(64, gckeyO); ;; delete old gc entry, add new
gc~udict_set_ref(64, gckeyN, begin_cell().store_slice(name).end_cell());
housekeeping(domdata, gc, prices, nhk, lhk);
return send_ok(price);
}
;; ##########################################################################
if (qt == 1) { ;; 0x72656764 -> regd | register domain
if (found?) { ;; domain already exists: return alre | 2^31
return send_error(0xe16c7265);
}
int expires_at = n + stdper;
slice value = begin_cell().store_uint(expires_at, 32).store_ref(data).end_cell().begin_parse();
ifnot (domdata~pfxdict_set?(1023, name, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
int gckey = (expires_at << 32) | (name.slice_hash() & ((1 << 32) - 1));
gc~udict_set_ref(64, gckey, begin_cell().store_slice(name).end_cell());
;; using ref requires additional cell, but using value (DICTUSET) may
;; cause problems with very long names or complex dictionaries
housekeeping(domdata, gc, prices, min(nhk, expires_at), lhk);
return send_ok(price);
}
;; ##########################################################################
if (qt == 4) { ;; 0x75706464 -> updd | update domain (data)
slice value = begin_cell().store_uint(exp, 32).store_ref(data).end_cell().begin_parse();
ifnot (domdata~pfxdict_set?(1023, name, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
;; no need to update gc here
housekeeping(domdata, gc, prices, nhk, lhk);
return send_ok(price);
}
;; ##########################################################################
return (); ;; should NEVER reach this part of code!
}
;;===========================================================================;;
;; External message handler (Code -1) ;;
;;===========================================================================;;
() recv_external(slice in_msg) impure {
;; not interested at all! but need to init!
(cell dd, cell gc, var prices, int nhk, int lhk) = load_data();
ifnot (lhk) {
accept_message();
store_data(dd, gc, prices, 0, now());
}
}
;;===========================================================================;;
;; Getter methods ;;
;;===========================================================================;;
(int, cell, int, slice) dnsdictlookup(slice subdomain, int nowtime) inline_ref {
int bits = subdomain.slice_bits();
ifnot (bits) {
return (0, null(), 0, null()); ;; zero-length input
}
throw_if(30, bits & 7); ;; malformed input (~ 8n-bit)
int name_last_byte = subdomain.slice_last(8).preload_uint(8);
if (name_last_byte) {
subdomain = begin_cell().store_slice(subdomain) ;; append zero byte
.store_uint(0, 8).end_cell().begin_parse();
bits += 8;
}
if (bits == 8) {
return (0, null(), 0, null()); ;; zero-length input, but with zero byte
}
(_, cell root) = get_data().begin_parse().load_dict();
slice cname = subdomain;
int zeros = 0;
repeat (bits >> 3) {
int c = cname~load_uint(8);
zeros -= (c == 0);
}
;; can't move these declarations lower, will cause errors!
slice pfx = cname;
slice val = null();
slice tail = cname;
int exp = 0;
do {
slice pfxname = begin_cell().store_uint(zeros, 7)
.store_slice(subdomain).end_cell().begin_parse();
(pfx, val, tail, int succ) = root.pfxdict_get?(1023, pfxname);
if (succ) {
int exp = val~load_uint(32);
if (nowtime > exp) { ;; entry expired, skip
succ = false;
}
}
zeros = succ ^ (zeros - 1); ;; break on success
} until (zeros <= 0);
ifnot (zeros) {
return (0, null(), 0, null()); ;; failed to find entry in prefix dictionary
}
zeros = - zeros;
return (exp, val.preload_ref(), tail.slice_empty?(), pfx);
}
;;8m dns-record-value
(int, cell) dnsresolve(slice subdomain, int category) method_id {
(int exp, cell cat_table, int exact?, slice pfx) = dnsdictlookup(subdomain, now());
ifnot (exp) {
return (0, null());
}
ifnot (exact?) { ;; incomplete subdomain found, must return next resolver (-1)
category = -1;
}
int pfx_bits = pfx.slice_bits() - 7;
;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain
;; COUNTING the zero byte (if structurally correct: no multiple-ZB keys)
;; which corresponds to 8m, m=one plus the number of bytes in the subdomain found)
if (category == 0) {
return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
} else {
cell cat_found = cat_table.idict_get_ref(16, category);
return (pfx_bits, cat_found);
}
}
;; getexpiration needs to know the current time to skip any possible expired
;; subdomains in the chain. it will return 0 if not found or expired.
int getexpirationx(slice subdomain, int nowtime) inline method_id {
(int exp, _, _, _) = dnsdictlookup(subdomain, nowtime);
return exp;
}
int getexpiration(slice subdomain) method_id {
return getexpirationx(subdomain, now());
}
int getstdperiod() method_id {
(int stdper, _, _, _) = load_prices();
return stdper;
}
int getppr() method_id {
(_, int ppr, _, _) = load_prices();
return ppr;
}
int getppc() method_id {
(_, _, int ppc, _) = load_prices();
return ppc;
}
int getppb() method_id {
( _, _, _, int ppb) = load_prices();
return ppb;
}
int calcprice(cell val) method_id { ;; only for external gets (not efficient)
(_, _, int ppc, int ppb) = load_prices();
return _calcprice(val, ppc, ppb);
}
int calcregprice(cell val) method_id { ;; only for external gets (not efficient)
(_, int ppr, int ppc, int ppb) = load_prices();
return ppr + _calcprice(val, ppc, ppb);
}

View file

@ -13,25 +13,7 @@
;; Custom ASM instructions ;;
;;===========================================================================;;
;; Args: s D n | Success: s' x s'' -1 | Failure: s 0 -> s N N 0
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key)
asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
;; Args: x k D n | Success: D' -1 | Failure: D 0
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value)
asm(value key dict key_len) "PFXDICTSET";
;; Args: k D n | Success: D' -1 | Failure: D 0
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key)
asm(key dict key_len) "PFXDICTDEL";
slice slice_last(slice s, int len) asm "SDCUTLAST";
;; Actually, equivalent to dictionaries, provided for clarity
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
(cell, ()) pfxdict_set_ref(cell dict, int key_len, slice key, cell value) {
(cell, ()) pfxdict_set_ref(cell dict, int key_len, slice key, cell value) {
throw_unless(33, dict~pfxdict_set?(key_len, key, begin_cell().store_maybe_ref(value).end_cell().begin_parse()));
return (dict, ());
}

View file

@ -123,7 +123,7 @@ _ ~credit_to(credits, addr, amount) {
}
;; parse current election data
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
elect_at~dump();
;; elect_at~dump();
msg_value -= 1000000000; ;; deduct GR$1 for sending confirmation
if ((msg_value << 12) < total_stake) {
;; stake smaller than 1/4096 of the total accumulated stakes, return
@ -443,7 +443,7 @@ _ compute_total_stake(l, n, m_stake) {
if (f) {
var (stake, _, pubkey) = (min(key~load_uint(128), max_stake), key~load_uint(32), key.preload_uint(256));
var (max_f, _, adnl_addr) = (cs~load_uint(32), cs~load_uint(256), cs.preload_uint(256));
l = cons(tuple4(stake, max_f, pubkey, adnl_addr), l);
l = cons([stake, max_f, pubkey, adnl_addr], l);
}
} until (~ f);
;; l is the list of all stakes in decreasing order
@ -468,7 +468,7 @@ _ compute_total_stake(l, n, m_stake) {
}
;; we have to select first m validators from list l
l1 = touch(l);
l1~dump(); ;; DEBUG
;; l1~dump(); ;; DEBUG
repeat (m - 1) {
l1 = cdr(l1);
}
@ -480,7 +480,7 @@ _ compute_total_stake(l, n, m_stake) {
var vset = new_dict();
var frozen = new_dict();
do {
var (stake, max_f, pubkey, adnl_addr) = l~list_next().untuple4();
var [stake, max_f, pubkey, adnl_addr] = l~list_next();
;; lookup source address first
var (val, f) = members.udict_get?(256, pubkey);
throw_unless(61, f);
@ -733,7 +733,7 @@ int announce_new_elections(ds, elect, credits) {
(_, var min_stake) = config_param(17).begin_parse().load_grams();
;; announce new elections
var elect_at = t + elect_begin_before;
elect_at~dump();
;; elect_at~dump();
var elect_close = elect_at - elect_end_before;
elect = pack_elect(elect_at, elect_close, min_stake, 0, new_dict(), false, false);
set_data(begin_cell().store_dict(elect).store_dict(credits).store_slice(ds).end_cell());
@ -786,7 +786,7 @@ _ participant_list() method_id {
do {
(id, var fs, var f) = members.udict_get_prev?(256, id);
if (f) {
l = cons(pair(id, fs~load_grams()), l);
l = cons([id, fs~load_grams()], l);
}
} until (~ f);
return l;
@ -806,7 +806,7 @@ _ participant_list_extended() method_id {
if (f) {
var (stake, time, max_factor, addr, adnl_addr) = (cs~load_grams(), cs~load_uint(32), cs~load_uint(32), cs~load_uint(256), cs~load_uint(256));
cs.end_parse();
l = cons(pair(id, tuple4(stake, max_factor, addr, adnl_addr)), l);
l = cons([id, [stake, max_factor, addr, adnl_addr]], l);
}
} until (~ f);
return (elect_at, elect_close, min_stake, total_stake, l, failed, finished);

View file

@ -93,8 +93,8 @@ file-base +"-dns" +contractid +".addr" load-address
} : subdomain>s
// ( b V -- b' )
{ dup first
dup `smc eq? { drop untriple rot nip rot x{9fd3} s, -rot Addr, 0 8 u, } {
dup `next eq? { drop untriple rot nip rot x{ba93} s, -rot Addr, 0 8 u, } {
dup `smc eq? { drop untriple 2swap drop x{9fd3} s, -rot Addr, 0 8 u, } {
dup `next eq? { drop untriple 2swap drop x{ba93} s, -rot Addr, 0 8 u, } {
dup `adnl eq? { drop second swap x{ad01} s, swap 256 u, } {
dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } {
abort"unknown value type"

View file

@ -69,5 +69,5 @@ int get_public_key() method_id {
}
int balance() method_id {
return restricted?() ? 0 : get_balance().first();
return restricted?() ? 0 : get_balance().pair_first();
}

View file

@ -66,7 +66,7 @@ int balance() method_id {
var rdict = ds~load_dict();
ds.end_parse();
var ts = days_passed();
var balance = get_balance().first();
var balance = get_balance().pair_first();
var (_, value, found) = rdict.idict_get_preveq?(16, ts);
if (found) {
balance = max(balance - value~load_grams(), 0);

View file

@ -6,21 +6,26 @@ forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
forall X -> X car(tuple list) asm "CAR";
tuple cdr(tuple list) asm "CDR";
forall X, Y -> tuple pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair(tuple t) asm "UNPAIR";
forall X, Y, Z -> tuple triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple(tuple t) asm "UNTRIPLE";
forall X, Y, Z, W -> tuple tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4(tuple t) asm "4 UNTUPLE";
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
forall X -> X first(tuple t) asm "FIRST";
forall X -> X second(tuple t) asm "SECOND";
forall X -> X third(tuple t) asm "THIRD";
forall X -> X fourth(tuple t) asm "3 INDEX";
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
forall X -> X null() asm "PUSHNULL";
int now() asm "NOW";
slice my_address() asm "MYADDR";
tuple get_balance() asm "BALANCE";
[int, cell] get_balance() asm "BALANCE";
int cur_lt() asm "LTIME";
int block_lt() asm "BLOCKLT";
@ -47,6 +52,7 @@ cont bless(slice s) impure asm "BLESS";
() accept_message() impure asm "ACCEPT";
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
() commit() impure asm "COMMIT";
() buy_gas(int gram) impure asm "BUYGAS";
int min(int x, int y) asm "MIN";
int max(int x, int y) asm "MAX";
@ -67,10 +73,16 @@ slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
slice first_bits(slice s, int len) asm "SDCUTFIRST";
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
slice slice_last(slice s, int len) asm "SDCUTLAST";
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
cell preload_dict(slice s) asm "PLDDICT";
slice skip_dict(slice s) asm "SKIPDICT";
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
@ -78,6 +90,9 @@ int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";
builder store_ref(builder b, cell c) asm(c b) "STREF";
@ -159,11 +174,15 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
cell config_param(int x) asm "CONFIGOPTPARAM";
int cell_null?(cell c) asm "ISNULL";
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
() raw_reserve_extra(slice currencies, int mode) impure asm "RAWRESERVEX";
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
() set_code(cell new_code) impure asm "SETCODE";