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

updated vm (breaking compatibility)

- updated vm
- new actor scheduler
- updated tonlib
- updated DNS smartcontract
This commit is contained in:
ton 2020-02-28 14:28:47 +04:00
parent 9e4816e7f6
commit e27fb1e09c
100 changed files with 3692 additions and 1299 deletions

View file

@ -0,0 +1,115 @@
#!/usr/bin/fift -s
"TonUtil.fif" include
"GetOpt.fif" include
{ show-options-help 1 halt } : usage
"dns-msg-body.boc" =: savefile
begin-options
" <auto-dns-addr> [-o<savefile-boc>] (add|update|prolong) <subdomain> <expire-in-sec> ... " +cr +tab
+"Creates the internal message body containing a request to automatic DNS smart contract <auto-dns-addr> created by new-auto-dns.fif, "
+"to be sent later with a suitable payment from a wallet to <auto-dns-addr>, and saves it into <savefile-boc> ('" savefile $+ +"' by default). "
+"The operation to be performed is one of" +cr +tab
+"add <subdomain> <expire-in-sec> { owner <smc-addr> | cat <cat-id> (smc <smc-addr> | next <next-resolver-smc-addr> | adnl <adnl-addr> | text <string>) }" +cr +tab
+"update <subdomain> <expire-in-sec> { owner <smc-addr> | cat <cat-id> (smc <smc-addr> | next <next-resolver-smc-addr> | adnl <adnl-addr> | text <string>) }" +cr +tab
+"prolong <subdomain> <expire-in-sec>"
disable-digit-options generic-help-setopt
"o" "--output" { =: savefile } short-long-option-arg
"Sets output file for generated initialization message ('" savefile $+ +"' by default)" option-help
"h" "--help" { usage } short-long-option
"Shows a help message" option-help
parse-options
$# 4 < ' usage if
4 :$1..n
$1 true parse-load-address =: bounce 2=: dest-addr
$2 dup =: main-op-name atom =: main-op
$3 dup =: subdomain $len 127 > abort"subdomain name too long"
$4 parse-int dup 30 1<< < { now + } if =: expire-at
{ $* @ dup null? { second $@ ! } { drop } cond } : @skip
{ $* @ null? } : @end?
{ $* @ uncons $* ! } : @next
{ @next drop } 4 times
main-op dup `add eq? over `update eq? or swap `prolong eq? or
{ "unknown main operation '" main-op-name $+ +"'; one of 'add', 'update' or 'prolong' expected" abort } ifnot
main-op `prolong eq? not =: need-params
$# 4 > need-params <> abort"extra parameters, or no parameters for chosen main operation"
variable Values dictnew Values !
// ( i c -- )
{ over 0= abort"category cannot be zero"
<b swap ref, swap Values @ 16 b>idict!+ not abort"duplicate category id"
Values !
} : register-value
{ @end? abort"category number expected" @next (number) 1 <> abort"category must be integer"
dup 16 fits not abort"category does not fit into 16 bit integer"
dup 0= abort"category must be non-zero"
} : parse-cat-num
{ @end? abort"smart contract address expected"
@next false parse-load-address drop
} : cl-parse-smc-addr
{ @end? abort"adnl address expected"
@next parse-adnl-addr
} : cl-parse-adnl-addr
{ <b x{9fd3} s, -rot Addr, 0 8 u, b> } : serialize-smc-addr
{ <b x{ba93} s, -rot Addr, b> } : serialize-next-resolver
{ <b x{ad01} s, swap 256 u, 0 8 u, b> } : serialize-adnl-addr
{ <b x{1eda01} s, over $len 8 u, swap $, b> } : serialize-text
{ @end? abort"subdomain record value expected" @next
dup "smc" $= { drop cl-parse-smc-addr serialize-smc-addr } {
dup "next" $= { drop cl-parse-smc-addr serialize-next-resolver } {
dup "adnl" $= { drop cl-parse-adnl-addr serialize-adnl-addr } {
dup "text" $= { drop @next serialize-text } {
"unknown record type "' swap $+ +"'" abort
} cond } cond } cond } cond
} : parse-value
{ @next dup "owner" $= { drop -2 cl-parse-smc-addr serialize-smc-addr } {
dup "cat" $= { drop parse-cat-num parse-value } {
"unknown action '" swap $+ +"'" abort
} cond } cond
register-value
} : parse-action
{ { @end? not } { parse-action } while } : parse-actions
parse-actions
// ( S -- S1 .. Sn n )
{ 1 swap { dup "." $pos dup 0>= } { $| 1 $| nip rot 1+ swap } while drop swap
} : split-by-dots
// ( S -- s )
{ dup $len dup 0= abort"subdomain cannot be empty" 126 > abort"subdomain too long"
dup 0 chr $pos 1+ abort"subdomain contains null characters"
split-by-dots <b { // ... S b
swap dup $len 0= abort"empty subdomain component" $, 0 8 u,
} rot times b> <s
} : subdomain>s
main-op ( _( `add 0x72656764 ) _( `update 0x75706464 ) _( `prolong 0x70726f6c ) )
assq-val not abort"unknown main operation"
=: op-id
."Automatic DNS smart contract address = " dest-addr 2dup .addr cr 6 .Addr cr
."Action: " main-op .l subdomain type space expire-at . cr
."Operation code: 0x" op-id 8 0X. cr
."Value: "
Values @ dup null? { drop ."(none)" } { <s csr. } cond cr
<b op-id 32 u, expire-at 32 u, Values @ dict, b> =: actions-builder
// create an internal message
now 32 << actions-builder hashu 32 1<<1- and + =: query_id
<b op-id 32 i, query_id 64 u,
subdomain subdomain>s tuck sbits 8 / 7 i, swap s,
main-op `prolong eq? { Values @ ref, } ifnot
expire-at 32 u, b>
dup ."Internal message body is: " <s csr. cr
2 boc+>B dup Bx. cr
."Query_id is " query_id dup . ."= 0x" X. cr
savefile tuck B>file
."(Saved to file " type .")" cr

View file

@ -107,22 +107,24 @@ global var query_info;
;; 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 domain, int found?) = gc.udict_get_min_ref?(32 + 128);
(int mkey, _, int found?) = gc.udict_get_min?(256);
while (found? & max_steps) { ;; no short circuit optimization, two nested ifs
nhk = (mkey >> 128);
nhk = (mkey >> (256 - 32));
if (nhk < n) {
slice sdomain = domain.begin_parse();
(_, slice val, _, found?) = dd.pfxdict_get?(1023, sdomain);
int key = mkey % (1 << (256 - 32));
(slice val, found?) = dd.udict_get?(256 - 32, key);
if (found?) {
int exp = val.preload_uint(32);
if (exp <= n) {
dd~pfxdict_delete?(1023, sdomain);
dd~udict_delete?(256 - 32, key);
}
}
gc~udict_delete?(32 + 128, mkey);
(mkey, domain, found?) = gc.udict_get_min_ref?(32 + 128);
nhk = (found? ? mkey >> 32 : 0xffffffff);
gc~udict_delete?(256, mkey);
(mkey, _, found?) = gc.udict_get_min?(256);
nhk = (found? ? mkey >> (256 - 32) : 0xffffffff);
max_steps -= 1;
} else {
found? = false;
}
}
store_data(ctl, dd, gc, prices, nhk, n);
@ -134,16 +136,15 @@ int calcprice_internal(slice domain, cell data, ppc, ppb) inline_ref { ;; only f
return ppc * (refs + 2) + 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
int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int strict) inline_ref {
if (strict & 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;
if (owner_info.null?()) { ;; no owner on this domain: no-2 (in strict mode), ok else
return strict & 0xee6f2d32;
}
var ERR_BAD2 = 0xe2616432;
slice sown = cown.begin_parse();
slice sown = owner_info.begin_parse();
if (sown.slice_bits() < 16 + 3 + 8 + 256) { ;; bad owner record: bad2
return ERR_BAD2;
}
@ -272,38 +273,38 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
return send_error(0xee6f5c30);
}
int zeros = 0;
slice cdomain = domain;
repeat (cdomain.slice_bits() ^>> 3) {
int c = cdomain~load_uint(8);
zeros -= (c == 0);
}
;; if (zeros != 1) { ;; too much zero chars (overflow): ov\0
;; return send_error(0xef765c30); }
domain = begin_cell().store_uint(zeros, 7).store_slice(domain).end_cell().begin_parse();
(slice pfx, slice val, slice tail, int found?) = domdata.pfxdict_get?(1023, domain);
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();
cell cat_table = cell owner_info = null();
int key = int exp = int zeros = 0;
slice tail = domain;
repeat (tail.slice_bits() ^>> 3) {
cat_table = null();
int z = (tail~load_uint(8) == 0);
zeros -= z;
if (z) {
key = (string_hash(domain.skip_last_bits(tail.slice_bits())) >> 32);
var (val, found?) = domdata.udict_get?(256 - 32, key);
if (found?) {
exp = val~load_uint(32);
if (exp >= n) { ;; entry not expired
cell cat_table = val~load_ref();
val.end_parse();
var (cown, ok) = cat_table.idict_get_ref?(16, -2);
if (ok) {
owner_info = cown;
}
}
}
}
}
if (zeros > 4) { ;; too much zero chars (overflow): ov\0
return send_error(0xef765c30);
}
;; ##########################################################################
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);
}
int err = check_owner(cat_table, owner_info, src_wc, src_addr, qt != 1);
if (err) {
return send_error(err);
}
@ -317,8 +318,14 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
data = in_msg~load_ref();
;; basic integrity check of (client-provided) dictionary
ifnot (data.dict_empty?()) { ;; 1000 gas!
(_, _, int minok) = idict_get_min?(data, 16);
(_, _, int maxok) = idict_get_max?(data, 16);
var (oinfo, ok) = data.idict_get_ref?(16, -2);
if (ok) {
var cs = oinfo.begin_parse();
throw_unless(31, cs.slice_bits() >= 16 + 3 + 8 + 256);
throw_unless(31, cs.preload_uint(19) == 0x9fd3 * 8 + 4);
}
(_, _, int minok) = data.idict_get_min?(16);
(_, _, int maxok) = data.idict_get_max?(16);
throw_unless(31, minok & maxok);
}
} else {
@ -345,17 +352,12 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
if (exp > n + stdper) { ;; does not expire soon, cannot prolong
return send_error(0xf365726f);
}
slice value = begin_cell().store_uint(exp + stdper, 32).store_ref(data).end_cell().begin_parse();
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
int sh_low = domain.slice_hash() & ((1 << 128) - 1);
int gckeyO = (exp << 128) + sh_low;
int gckeyN = gckeyO + (stdper << 128);
gc~udict_delete?(32 + 128, gckeyO); ;; delete old gc entry, add new
gc~udict_set_ref(32 + 128, gckeyN, begin_cell().store_slice(domain).end_cell());
domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(exp + stdper, 32).store_ref(data));
int gckeyO = (exp << (256 - 32)) + key;
int gckeyN = gckeyO + (stdper << (256 - 32));
gc~udict_delete?(256, gckeyO); ;; delete old gc entry, add new
gc~udict_set_builder(256, gckeyN, begin_cell());
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
return send_ok(price);
@ -363,29 +365,22 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;; ##########################################################################
if (qt == 1) { ;; 0x72656764 -> regd | register domain
if (found?) { ;; domain already exists: return alre | 2^31
ifnot (cat_table.null?()) { ;; 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, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
int gckey = (expires_at << 128) | (domain.slice_hash() & ((1 << 128) - 1));
gc~udict_set_ref(32 + 128, gckey, begin_cell().store_slice(domain).end_cell());
;; using ref requires additional cell, but using value (DICTUSET) may
;; cause problems with very long domains or complex dictionaries
domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(expires_at, 32).store_ref(data));
int gckey = (expires_at << (256 - 32)) | key;
gc~udict_set_builder(256, gckey, begin_cell());
housekeeping(ctl, domdata, gc, prices, min(nhk, expires_at), lhk, 1);
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, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
domdata~udict_set_builder(256 - 32, key, begin_cell().store_uint(exp, 32).store_ref(data));
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
return send_ok(price);
}
@ -412,54 +407,46 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;;===========================================================================;;
(int, cell, int, slice) dnsdictlookup(slice domain, int nowtime) inline_ref {
int bits = domain.slice_bits();
(int bits, int refs) = domain.slice_bits_refs();
throw_if(30, refs | (bits & 7)); ;; malformed input (~ 8n-bit)
ifnot (bits) {
return (0, null(), 0, null()); ;; zero-length input
}
throw_if(30, bits & 7); ;; malformed input (~ 8n-bit)
int domain_last_byte = domain.slice_last(8).preload_uint(8);
if (domain_last_byte) {
domain = begin_cell().store_slice(domain) ;; append zero byte
.store_uint(0, 8).end_cell().begin_parse();
.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();
var ds = get_data().begin_parse();
(_, cell root) = (ds~load_ref(), ds~load_dict());
slice sd_tail = domain;
int zeros = 0;
repeat (bits >> 3) {
int c = sd_tail~load_uint(8);
zeros -= (c == 0);
}
;; can't move these declarations lower, will cause errors!
slice tail = slice pfx = sd_tail;
slice val = null();
int exp = 0;
int tail_bits = -1;
slice tail = domain;
do {
slice pfxname = begin_cell().store_uint(zeros, 7)
.store_slice(domain).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;
repeat (bits >> 3) {
if (tail~load_uint(8) == 0) {
var key = (string_hash(domain.skip_last_bits(tail.slice_bits())) >> 32);
var (v, found?) = root.udict_get?(256 - 32, key);
if (found?) {
if (v.preload_uint(32) >= nowtime) { ;; entry not expired
val = v;
tail_bits = tail.slice_bits();
}
}
}
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);
if (val.null?()) {
return (0, null(), 0, null()); ;; failed to find entry in subdomain dictionary
}
return (val~load_uint(32), val~load_ref(), tail_bits == 0, domain.skip_last_bits(tail_bits));
}
;;8m dns-record-value
@ -472,12 +459,12 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
category = -1;
}
int pfx_bits = pfx.slice_bits() - 7;
int pfx_bits = pfx.slice_bits();
;; 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) {
ifnot (category) {
return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
} else {
cell cat_found = cat_table.idict_get_ref(16, category);

View file

@ -94,8 +94,8 @@ file-base +"-dns" +contractid +".addr" load-address
// ( b V -- b' )
{ dup first
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 `next eq? { drop untriple 2swap drop x{ba93} s, -rot Addr, } {
dup `adnl eq? { drop second swap x{ad01} s, swap 256 u, 0 8 u, } {
dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } {
abort"unknown value type"
} cond } cond } cond } cond

View file

@ -6,6 +6,11 @@ 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";
tuple empty_tuple() asm "NIL";
forall X -> tuple tpush(tuple t, X value) asm "TPUSH";
forall X -> (tuple, ()) ~tpush(tuple t, X value) asm "TPUSH";
forall X -> [X] single(X x) asm "SINGLE";
forall X -> X unsingle([X] t) asm "UNSINGLE";
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";
@ -22,6 +27,7 @@ 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";
forall X -> (X, ()) ~impure_touch(X x) impure asm "NOP";
int now() asm "NOW";
slice my_address() asm "MYADDR";
@ -115,7 +121,8 @@ cell idict_set_ref(cell dict, int key_len, int index, cell value) asm(value inde
cell udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
(cell, ()) ~udict_set_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETREF";
cell idict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETOPTREF";
cell udict_get_ref(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETOPTREF";
(cell, int) idict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIGETREF";
(cell, int) udict_get_ref?(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETREF";
(cell, cell) idict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTISETGETOPTREF";
(cell, cell) udict_set_get_ref(cell dict, int key_len, int index, cell value) asm(value index dict key_len) "DICTUSETGETOPTREF";
(cell, int) idict_delete?(cell dict, int key_len, int index) asm(index dict key_len) "DICTIDEL";