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

updated tonlib + fixes in vm

This commit is contained in:
ton 2020-02-20 19:56:18 +04:00
parent 28735ddc9e
commit efd47af432
42 changed files with 750 additions and 307 deletions

View file

@ -16,7 +16,7 @@
{-
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]
[OptRef<1b+1r?>:Hashmap<UInt<160b>(Time|Hash128)->Slice(DomName)>:gc]
[UInt<32b>:stdperiod] [Gram:PPReg] [Gram:PPCell] [Gram:PPBit]
[UInt<32b>:lasthousekeeping]
<CatTable> := HashmapE 16 ^DNSRecord
@ -30,12 +30,13 @@
\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 {
(cell, cell, cell, [int, int, int, int], int, int) load_data() inline_ref {
slice cs = get_data().begin_parse();
return (
cs~load_ref(), ;; control data
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_dict(), ;; gc auxillary with expiration and 128-bit hash slice
[ cs~load_uint(30), ;; 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)
@ -46,16 +47,17 @@
(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());
(cs~load_ref(), cs~load_dict(), cs~load_dict());
return (cs~load_uint(30), cs~load_grams(), cs~load_grams(), cs~load_grams());
}
() store_data(cell dd, cell gc, prices, int nhk, int lhk) impure {
() store_data(cell ctl, cell dd, cell gc, prices, int nhk, int lhk) impure {
var [sp, ppr, ppc, ppb] = prices;
set_data(begin_cell()
.store_ref(ctl) ;; control data
.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_dict(gc) ;; keyed expiration time and 128-bit hash slice
.store_uint(sp, 30) ;; standard period
.store_grams(ppr) ;; price per registration
.store_grams(ppc) ;; price per cell
.store_grams(ppb) ;; price per bit
@ -94,40 +96,42 @@ global var query_info;
return send_message(addr, 0xef6b6179, query_id, op, 0, 128);
}
() housekeeping(cell dd, cell gc, prices, int nhk, int lhk) impure {
() housekeeping(cell ctl, cell dd, cell gc, prices, int nhk, int lhk, int max_steps) 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);
return store_data(ctl, 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);
(int mkey, cell domain, int found?) = gc.udict_get_min_ref?(32 + 128);
while (found? & max_steps) { ;; no short circuit optimization, two nested ifs
nhk = (mkey >> 128);
if (nhk < n) {
slice sname = name.begin_parse();
(_, slice val, _, found?) = dd.pfxdict_get?(1023, sname);
slice sdomain = domain.begin_parse();
(_, slice val, _, found?) = dd.pfxdict_get?(1023, sdomain);
if (found?) {
int exp = val.preload_uint(32);
if (exp <= n) {
dd~pfxdict_delete?(1023, sname);
dd~pfxdict_delete?(1023, sdomain);
}
}
gc~udict_delete?(64, mkey);
(mkey, _, found?) = gc.udict_get_min_ref?(64);
gc~udict_delete?(32 + 128, mkey);
(mkey, domain, found?) = gc.udict_get_min_ref?(32 + 128);
nhk = (found? ? mkey >> 32 : 0xffffffff);
max_steps -= 1;
}
}
store_data(dd, gc, prices, nhk, n);
store_data(ctl, dd, gc, prices, nhk, n);
}
int _calcprice(cell data, ppc, ppb) inline_ref { ;; only for internal calcs
int calcprice_internal(slice domain, 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;
bits += slice_bits(domain) * 2 + (128 + 32 + 32);
return ppc * (refs + 2) + ppb * bits;
}
int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
@ -165,13 +169,43 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
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]
[UInt<32b>:op] [UInt<64b>:query_id] [Ref<1r>:domain]
(if not prolong: [Ref<1r>:value->CatTable])
-}
;; Control operations: permitted only to the owner of this smartcontract
() perform_ctl_op(int op, int src_wc, int src_addr, slice in_msg) impure inline_ref {
var (ctl, domdata, gc, prices, nhk, lhk) = load_data();
var cs = ctl.begin_parse();
if ((cs~load_int(8) != src_wc) | (cs~load_uint(256) != src_addr)) {
return send_error(0xee6f776e);
}
if (op == 0x43685072) { ;; ChPr = Change Prices
var (stdper, ppr, ppc, ppb) = (in_msg~load_uint(32), in_msg~load_grams(), in_msg~load_grams(), in_msg~load_grams());
in_msg.end_parse();
;; NB: stdper == 0 -> disable new actions
store_data(ctl, domdata, gc, [stdper, ppr, ppc, ppb], nhk, lhk);
return send_ok(0);
}
if (op == 0x4344656c) { ;; CDel = destroy smart contract
ifnot (domdata.null?()) {
;; domain dictionary not empty, force gc
housekeeping(ctl, domdata, gc, prices, nhk, 1, -1);
}
(ctl, domdata, gc, prices, nhk, lhk) = load_data();
ifnot (domdata.null?()) {
;; domain dictionary still not empty, error
return send_error(0xee74656d);
}
var (addr, query_id, op) = query_info;
return send_message(addr, 0xef6b6179, query_id, op, 0, 128 + 32);
}
return send_error(0xffffffff);
}
;; 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 {
() recv_internal(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
@ -197,47 +231,60 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
if (op & (1 << 31)) {
return (); ;; an answer to our query
}
if ((op >> 24) == 0x43) {
;; Control operations
return perform_ctl_op(op, src_wc, src_addr, in_msg);
}
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();
(cell ctl, 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
int max_steps = in_msg~load_int(32); ;; -1 = infty
housekeeping(ctl, domdata, gc, prices, nhk, 1, max_steps); ;; forced
return send_error(0xef6b6179);
}
slice name = null();
cell name_cell = in_msg~load_maybe_ref();
if (name_cell.null?()) {
slice domain = null();
cell domain_cell = in_msg~load_maybe_ref();
int fail = 0;
if (domain_cell.null?()) {
int bytes = in_msg~load_uint(6);
name = in_msg~load_bits(bytes * 8);
fail = (bytes == 0);
domain = in_msg~load_bits(bytes * 8);
} else {
name = name_cell.begin_parse();
domain = domain_cell.begin_parse();
var (bits, refs) = slice_bits_refs(domain);
fail = (refs | ((bits - 8) & (7 - 128)));
}
(_, int name_last_byte) = name.slice_last(8).load_uint(8);
if (name_last_byte != 0) { ;; name must end with \0! no\0 error
ifnot (fail) {
;; domain must end with \0! no\0 error
fail = domain.slice_last(8).preload_uint(8);
}
if (fail) {
return send_error(0xee6f5c30);
}
int zeros = 0;
slice cname = name;
repeat (cname.slice_bits() ^>> 3) {
int c = cname~load_uint(8);
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); }
name = begin_cell().store_uint(zeros, 7).store_slice(name).end_cell().begin_parse();
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, name);
(slice pfx, slice val, slice tail, int found?) = domdata.pfxdict_get?(1023, domain);
int n = now();
cell cat_table = null();
int exp = 0;
@ -270,17 +317,22 @@ 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 dmin, _, int minok) = idict_get_min?(data, 16);
(int dmax, _, int maxok) = idict_get_max?(data, 16);
throw_unless(31, minok & maxok & (dmin <= dmax));
(_, _, int minok) = idict_get_min?(data, 16);
(_, _, int maxok) = idict_get_max?(data, 16);
throw_unless(31, minok & maxok);
}
} else {
data = cat_table;
}
;; compute action price
;; load prices
var [stdper, ppr, ppc, ppb] = prices;
int price = _calcprice(data, ppc, ppb) + (ppr & (qt != 4));
ifnot (stdper) { ;; smart contract disabled by owner, no new actions
return send_error(0xd34f4646);
}
;; compute action price
int price = calcprice_internal(domain, data, ppc, ppb) + (ppr & (qt != 4));
if (msg_value - (1 << 30) < price) { ;; gr<p: grams - GR$1 < price
return send_error(0xe7723c70);
}
@ -290,19 +342,22 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;; ##########################################################################
if (qt == 2) { ;; 0x70726f6c -> prol | prolong domain
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, name, value)) { ;; Set ERR | 2^31
ifnot (domdata~pfxdict_set?(1023, domain, 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());
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());
housekeeping(domdata, gc, prices, nhk, lhk);
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
return send_ok(price);
}
@ -313,14 +368,14 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
}
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
ifnot (domdata~pfxdict_set?(1023, domain, 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());
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 names or complex dictionaries
housekeeping(domdata, gc, prices, min(nhk, expires_at), lhk);
;; cause problems with very long domains or complex dictionaries
housekeeping(ctl, domdata, gc, prices, min(nhk, expires_at), lhk, 1);
return send_ok(price);
}
@ -328,11 +383,10 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
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
ifnot (domdata~pfxdict_set?(1023, domain, value)) { ;; Set ERR | 2^31
return send_error(0xf3657272);
}
;; no need to update gc here
housekeeping(domdata, gc, prices, nhk, lhk);
housekeeping(ctl, domdata, gc, prices, nhk, lhk, 1);
return send_ok(price);
}
;; ##########################################################################
@ -345,11 +399,11 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;;===========================================================================;;
() 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();
;; only for initialization
(cell ctl, cell dd, cell gc, var prices, int nhk, int lhk) = load_data();
ifnot (lhk) {
accept_message();
store_data(dd, gc, prices, 0, now());
return store_data(ctl, dd, gc, prices, 0xffffffff, now());
}
}
@ -357,16 +411,16 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;; Getter methods ;;
;;===========================================================================;;
(int, cell, int, slice) dnsdictlookup(slice subdomain, int nowtime) inline_ref {
int bits = subdomain.slice_bits();
(int, cell, int, slice) dnsdictlookup(slice domain, int nowtime) inline_ref {
int bits = domain.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
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();
bits += 8;
}
@ -375,22 +429,21 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
}
(_, cell root) = get_data().begin_parse().load_dict();
slice cname = subdomain;
slice sd_tail = domain;
int zeros = 0;
repeat (bits >> 3) {
int c = cname~load_uint(8);
int c = sd_tail~load_uint(8);
zeros -= (c == 0);
}
;; can't move these declarations lower, will cause errors!
slice pfx = cname;
slice tail = slice pfx = sd_tail;
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();
.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);
@ -410,8 +463,8 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
}
;;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());
(int, cell) dnsresolve(slice domain, int category) method_id {
(int exp, cell cat_table, int exact?, slice pfx) = dnsdictlookup(domain, now());
ifnot (exp) {
return (0, null());
}
@ -434,13 +487,13 @@ int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
;; 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);
int getexpirationx(slice domain, int nowtime) inline method_id {
(int exp, _, _, _) = dnsdictlookup(domain, nowtime);
return exp;
}
int getexpiration(slice subdomain) method_id {
return getexpirationx(subdomain, now());
int getexpiration(slice domain) method_id {
return getexpirationx(domain, now());
}
int getstdperiod() method_id {
@ -463,12 +516,12 @@ int getppb() method_id {
return ppb;
}
int calcprice(cell val) method_id { ;; only for external gets (not efficient)
int calcprice(slice domain, cell val) method_id { ;; only for external gets (not efficient)
(_, _, int ppc, int ppb) = load_prices();
return _calcprice(val, ppc, ppb);
return calcprice_internal(domain, val, ppc, ppb);
}
int calcregprice(cell val) method_id { ;; only for external gets (not efficient)
int calcregprice(slice domain, cell val) method_id { ;; only for external gets (not efficient)
(_, int ppr, int ppc, int ppb) = load_prices();
return ppr + _calcprice(val, ppc, ppb);
return ppr + calcprice_internal(domain, val, ppc, ppb);
}