mirror of
				https://github.com/ton-blockchain/ton
				synced 2025-03-09 15:40:10 +00:00 
			
		
		
		
	Merge pull request #478 from ton-blockchain/upd-dns-smartcontracts
auto-dns & manual-dns smartcontracts updated to actual DNS standard
This commit is contained in:
		
						commit
						ac9fc3a515
					
				
					 4 changed files with 61 additions and 26 deletions
				
			
		| 
						 | 
				
			
			@ -43,12 +43,12 @@ $# 4 > need-params <> abort"extra parameters, or no parameters for chosen main o
 | 
			
		|||
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"
 | 
			
		||||
  <b swap ref, swap Values @ 256 b>udict!+ 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 256 fits not abort"category does not fit into 256 bit integer"
 | 
			
		||||
  dup 0= abort"category must be non-zero"
 | 
			
		||||
} : parse-cat-num
 | 
			
		||||
{ @end? abort"smart contract address expected" 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,15 @@
 | 
			
		|||
  | Author: Oleksandr Murzin (tg: @skydev / em: alexhacker64@gmail.com)    |
 | 
			
		||||
  |         October 2019                                                   |
 | 
			
		||||
  \------------------------------------------------------------------------/
 | 
			
		||||
  Updated to actual DNS standard version by starlightduck in 2022
 | 
			
		||||
-}
 | 
			
		||||
 | 
			
		||||
;;===========================================================================;;
 | 
			
		||||
;; Custom ASM instructions                                                   ;;
 | 
			
		||||
;;===========================================================================;;
 | 
			
		||||
 | 
			
		||||
cell udict_get_ref_(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETOPTREF";
 | 
			
		||||
 | 
			
		||||
;;===========================================================================;;
 | 
			
		||||
;; Utility functions                                                         ;;
 | 
			
		||||
;;===========================================================================;;
 | 
			
		||||
| 
						 | 
				
			
			@ -19,7 +26,7 @@
 | 
			
		|||
         [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
 | 
			
		||||
  <CatTable> := HashmapE 256 (~~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
 | 
			
		||||
| 
						 | 
				
			
			@ -189,6 +196,7 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
    store_data(ctl, domdata, gc, [stdper, ppr, ppc, ppb], nhk, lhk);
 | 
			
		||||
    return send_ok(0);
 | 
			
		||||
  }
 | 
			
		||||
  var (addr, query_id, op) = query_info;
 | 
			
		||||
  if (op == 0x4344656c) {  ;; CDel = destroy smart contract
 | 
			
		||||
    ifnot (domdata.null?()) {
 | 
			
		||||
      ;; domain dictionary not empty, force gc
 | 
			
		||||
| 
						 | 
				
			
			@ -199,9 +207,12 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
      ;; 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);
 | 
			
		||||
  }
 | 
			
		||||
  if (op == 0x54616b65) { ;; Take = take grams from the contract
 | 
			
		||||
    var amount = in_msg~load_grams();
 | 
			
		||||
    return send_message(addr, 0xef6b6179, query_id, op, amount, 64);
 | 
			
		||||
  }
 | 
			
		||||
  return send_error(0xffffffff);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -289,7 +300,8 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
        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);
 | 
			
		||||
          ;; update: category length now u256 instead of i16, owner index is now 0 instead of -2
 | 
			
		||||
          var (cown, ok) = cat_table.udict_get_ref?(256, 0);
 | 
			
		||||
          if (ok) {
 | 
			
		||||
            owner_info = cown;
 | 
			
		||||
          }
 | 
			
		||||
| 
						 | 
				
			
			@ -318,14 +330,15 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
    data = in_msg~load_ref();
 | 
			
		||||
    ;; basic integrity check of (client-provided) dictionary
 | 
			
		||||
    ifnot (data.dict_empty?()) { ;; 1000 gas!
 | 
			
		||||
      var (oinfo, ok) = data.idict_get_ref?(16, -2);
 | 
			
		||||
      ;; update: category length now u256 instead of i16, owner index is now 0 instead of -2
 | 
			
		||||
      var (oinfo, ok) = data.udict_get_ref?(256, 0);
 | 
			
		||||
      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);
 | 
			
		||||
      (_, _, int minok) = data.udict_get_min?(256); ;; update: category length now u256 instead of i16
 | 
			
		||||
      (_, _, int maxok) = data.udict_get_max?(256); ;; update: category length now u256 instead of i16
 | 
			
		||||
      throw_unless(31, minok & maxok);
 | 
			
		||||
    }
 | 
			
		||||
  } else {
 | 
			
		||||
| 
						 | 
				
			
			@ -410,7 +423,8 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
  (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
 | 
			
		||||
    ;; return (0, null(), 0, null());  ;; zero-length input
 | 
			
		||||
    throw(30); ;; update: throw exception for empty input
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  int domain_last_byte = domain.slice_last(8).preload_uint(8);
 | 
			
		||||
| 
						 | 
				
			
			@ -420,7 +434,14 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
    bits += 8;
 | 
			
		||||
  }
 | 
			
		||||
  if (bits == 8) {
 | 
			
		||||
    return (0, null(), 0, null()); ;; zero-length input, but with zero byte
 | 
			
		||||
    return (0, null(), 8, null()); ;; zero-length input, but with zero byte
 | 
			
		||||
    ;; update: return 8 as resolved, but with no data
 | 
			
		||||
  }
 | 
			
		||||
  int domain_first_byte = domain.preload_uint(8);
 | 
			
		||||
  if (domain_first_byte == 0) {
 | 
			
		||||
    ;; update: remove prefix \0
 | 
			
		||||
    domain~load_uint(8);
 | 
			
		||||
    bits -= 8;
 | 
			
		||||
  }
 | 
			
		||||
  var ds = get_data().begin_parse();
 | 
			
		||||
  (_, cell root) = (ds~load_ref(), ds~load_dict());
 | 
			
		||||
| 
						 | 
				
			
			@ -453,10 +474,11 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
(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());
 | 
			
		||||
    return (exact?, null()); ;; update: reuse exact? to return 8 for \0
 | 
			
		||||
  }
 | 
			
		||||
  ifnot (exact?) { ;; incomplete subdomain found, must return next resolver (-1)
 | 
			
		||||
    category = -1;
 | 
			
		||||
    category = "dns_next_resolver"H; ;; 0x19f02441ee588fdb26ee24b2568dd035c3c9206e11ab979be62e55558a1d17ff
 | 
			
		||||
    ;; update: next resolver is now sha256("dns_next_resolver") instead of -1
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  int pfx_bits = pfx.slice_bits();
 | 
			
		||||
| 
						 | 
				
			
			@ -467,7 +489,7 @@ int check_owner(cell cat_table, cell owner_info, int src_wc, int src_addr, int s
 | 
			
		|||
  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);
 | 
			
		||||
    cell cat_found = cat_table.udict_get_ref_(256, category); ;; update: category length now u256 instead of i16
 | 
			
		||||
    return (pfx_bits, cat_found);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,12 +7,15 @@
 | 
			
		|||
  | Author: Oleksandr Murzin (tg: @skydev / em: alexhacker64@gmail.com)    |
 | 
			
		||||
  |         October 2019                                                   |
 | 
			
		||||
  \------------------------------------------------------------------------/
 | 
			
		||||
  Updated to actual DNS standard version by starlightduck in 2022
 | 
			
		||||
-}
 | 
			
		||||
 | 
			
		||||
;;===========================================================================;;
 | 
			
		||||
;; Custom ASM instructions                                                   ;;
 | 
			
		||||
;;===========================================================================;;
 | 
			
		||||
 | 
			
		||||
cell udict_get_ref_(cell dict, int key_len, int index) asm(index dict key_len) "DICTUGETOPTREF";
 | 
			
		||||
 | 
			
		||||
(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, ());
 | 
			
		||||
| 
						 | 
				
			
			@ -67,9 +70,9 @@
 | 
			
		|||
  Operations (continuation of message):
 | 
			
		||||
  00 Contract initialization message (only if seqno = 0) (x=-)
 | 
			
		||||
  11 VSet: set specified value to specified subdomain->category (x=2)
 | 
			
		||||
    [Int<16b>:category] [Name<?>:subdomain] [Cell<1r>:value]
 | 
			
		||||
    [UInt<256b>:category] [Name<?>:subdomain] [Cell<1r>:value]
 | 
			
		||||
  12 VDel: delete specified subdomain->category (x=2)
 | 
			
		||||
    [Int<16b>:category] [Name<?>:subdomain]
 | 
			
		||||
    [UInt<256b>:category] [Name<?>:subdomain]
 | 
			
		||||
  21 DSet: replace entire category dictionary of domain with provided (x=0)
 | 
			
		||||
    [Name<?>:subdomain] [Cell<1r>:new_cat_table]
 | 
			
		||||
  22 DDel: delete entire category dictionary of specified domain (x=0)
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +115,7 @@
 | 
			
		|||
  int cat = 0;
 | 
			
		||||
  if (op < 20) {
 | 
			
		||||
    ;; for operations with codes 10..19 category is required
 | 
			
		||||
    cat = ops~load_int(16);
 | 
			
		||||
    cat = ops~load_uint(256); ;; update: category length now u256 instead of i16
 | 
			
		||||
  }
 | 
			
		||||
  slice name = null();   ;; any slice value
 | 
			
		||||
  cell cat_table = null();
 | 
			
		||||
| 
						 | 
				
			
			@ -159,13 +162,13 @@
 | 
			
		|||
  ;; 11 VSet: set specified value to specified subdomain->category
 | 
			
		||||
  if (op == 11) {
 | 
			
		||||
    cell new_value = ops~load_maybe_ref();
 | 
			
		||||
    cat_table~idict_set_get_ref(16, cat, new_value);
 | 
			
		||||
    cat_table~udict_set_get_ref(256, cat, new_value); ;; update: category length now u256 instead of i16
 | 
			
		||||
    root~pfxdict_set_ref(1023, name, cat_table);
 | 
			
		||||
    return (root, ops);
 | 
			
		||||
  }
 | 
			
		||||
  ;; 12 VDel: delete specified subdomain->category value
 | 
			
		||||
  if (op == 12) {
 | 
			
		||||
    if (cat_table~idict_delete?(16, cat)) {
 | 
			
		||||
    if (cat_table~udict_delete?(256, cat)) { ;; update: category length now u256 instead of i16
 | 
			
		||||
       root~pfxdict_set_ref(1023, name, cat_table);      
 | 
			
		||||
    }
 | 
			
		||||
    return (root, ops);
 | 
			
		||||
| 
						 | 
				
			
			@ -261,7 +264,7 @@ cell process_ops(cell root, slice ops) inline_ref {
 | 
			
		|||
  Data structure:
 | 
			
		||||
  Root cell: [UInt<32b>:seqno] [UInt<256b>:owner_public_key] 
 | 
			
		||||
         [OptRef<1b+1r?>:Hashmap<PfxDict:Slice->CatTable>:domains]
 | 
			
		||||
  <CatTable> := HashmapE 16 ^DNSRecord
 | 
			
		||||
  <CatTable> := HashmapE 256 (~~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
 | 
			
		||||
| 
						 | 
				
			
			@ -291,7 +294,8 @@ int get_public_key() method_id {
 | 
			
		|||
(int, cell) dnsresolve(slice subdomain, int category) method_id {
 | 
			
		||||
  int bits = subdomain.slice_bits();
 | 
			
		||||
  ifnot (bits) {
 | 
			
		||||
    return (0, null());  ;; zero-length input
 | 
			
		||||
    ;; return (0, null());  ;; zero-length input
 | 
			
		||||
    throw(30); ;; update: throw exception for empty input
 | 
			
		||||
  }
 | 
			
		||||
  throw_if(30, bits & 7); ;; malformed input (~ 8n-bit)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -302,7 +306,14 @@ int get_public_key() method_id {
 | 
			
		|||
    bits += 8;
 | 
			
		||||
  }
 | 
			
		||||
  if (bits == 8) {
 | 
			
		||||
    return (0, null()); ;; zero-length input, but with zero byte
 | 
			
		||||
    return (8, null()); ;; zero-length input, but with zero byte
 | 
			
		||||
    ;; update: return 8 as resolved, but with no data
 | 
			
		||||
  }
 | 
			
		||||
  int name_first_byte = subdomain.preload_uint(8);
 | 
			
		||||
  if (name_first_byte == 0) {
 | 
			
		||||
    ;; update: remove prefix \0
 | 
			
		||||
    subdomain~load_uint(8);
 | 
			
		||||
    bits -= 8;
 | 
			
		||||
  }
 | 
			
		||||
  (_, _, _, cell root, _) = load_data();
 | 
			
		||||
  
 | 
			
		||||
| 
						 | 
				
			
			@ -332,7 +343,9 @@ int get_public_key() method_id {
 | 
			
		|||
  zeros = - zeros;
 | 
			
		||||
 | 
			
		||||
  ifnot (tail.slice_empty?()) { ;; if we have tail then len(pfx) < len(subdomain)
 | 
			
		||||
    category = -1; ;; incomplete subdomain found, must return next resolver (-1)
 | 
			
		||||
    ;; incomplete subdomain found, must return next resolver
 | 
			
		||||
    category = "dns_next_resolver"H; ;; 0x19f02441ee588fdb26ee24b2568dd035c3c9206e11ab979be62e55558a1d17ff
 | 
			
		||||
    ;; update: next resolver is now sha256("dns_next_resolver") instead of -1
 | 
			
		||||
  }
 | 
			
		||||
  int pfx_bits = pfx.slice_bits() - 7;
 | 
			
		||||
  cell cat_table = val;
 | 
			
		||||
| 
						 | 
				
			
			@ -342,7 +355,7 @@ int get_public_key() method_id {
 | 
			
		|||
  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);
 | 
			
		||||
    cell cat_found = cat_table.udict_get_ref_(256, category); ;; update: category length now u256 instead of i16
 | 
			
		||||
    return (pfx_bits, cat_found);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ variable Actions
 | 
			
		|||
{ @end? abort"subdomain name expected" @next dup $len 127 > abort"subdomain name too long"
 | 
			
		||||
} : parse-domain
 | 
			
		||||
{ @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 256 fits not abort"category does not fit into 256 bit integer"
 | 
			
		||||
  dup 0= abort"category must be non-zero"
 | 
			
		||||
} : parse-cat-num
 | 
			
		||||
{ @end? abort"`cat` expected" @next "cat" $= not abort"`cat` expected" parse-cat-num
 | 
			
		||||
| 
						 | 
				
			
			@ -107,11 +107,11 @@ file-base +"-dns" +contractid +".addr" load-address
 | 
			
		|||
{ dup first
 | 
			
		||||
  dup `add eq? {
 | 
			
		||||
    drop 4 untuple <b swap value, b> -rot
 | 
			
		||||
    <b 11 6 u, swap 16 i, swap subdomain,
 | 
			
		||||
    <b 11 6 u, swap 256 u, swap subdomain,
 | 
			
		||||
    swap dict, nip } {
 | 
			
		||||
  dup `delete eq? {
 | 
			
		||||
    drop untriple rot drop
 | 
			
		||||
    <b 12 6 u, swap 16 i, swap subdomain, } {
 | 
			
		||||
    <b 12 6 u, swap 256 u, swap subdomain, } {
 | 
			
		||||
  dup `drop eq? {
 | 
			
		||||
    drop second <b 22 6 u, swap subdomain, } {
 | 
			
		||||
  dup `upgrade eq? {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue