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

@ -1845,7 +1845,7 @@ bool Transaction::compute_state() {
// code:(Maybe ^Cell) data:(Maybe ^Cell) library:(HashmapE 256 SimpleLib)
auto frozen_state = cb2.finalize();
frozen_hash = frozen_state->get_hash().bits();
if (verbosity >= 3 * 0) { // !!!DEBUG!!!
if (verbosity >= 3 * 1) { // !!!DEBUG!!!
std::cerr << "freezing state of smart contract: ";
block::gen::t_StateInit.print_ref(std::cerr, frozen_state);
CHECK(block::gen::t_StateInit.validate_ref(frozen_state));

View file

@ -466,6 +466,7 @@ x{CF28} @Defop STILE4
x{CF29} @Defop STULE4
x{CF2A} @Defop STILE8
x{CF2B} @Defop STULE8
x{CF30} @Defop BDEPTH
x{CF31} @Defop BBITS
x{CF32} @Defop BREFS
x{CF33} @Defop BBITREFS
@ -493,6 +494,7 @@ x{CF42} @Defop STSAME
} : STSLICECONST
x{CF81} @Defop STZERO
x{CF83} @Defop STONE
// cell deserialization (CellSlice primitives)
x{D0} @Defop CTOS
x{D1} @Defop ENDS
@ -593,6 +595,8 @@ x{D75F} @Defop PLDULE8Q
x{D760} @Defop LDZEROES
x{D761} @Defop LDONES
x{D762} @Defop LDSAME
x{D764} @Defop SDEPTH
x{D765} @Defop CDEPTH
//
// continuation / flow control primitives
x{D8} dup @Defop EXECUTE @Defop CALLX

View file

@ -195,3 +195,6 @@ recursive append-long-bytes {
} : parse-adnl-addr
{ adnl>$ type } : .adnl
{ bl word parse-adnl-addr 1 'nop } ::_ adnl:
// ( x a b -- a<=x<=b )
{ 2 pick >= -rot >= and } : in-range?

View file

@ -1271,6 +1271,12 @@ struct StackTransform {
bool is_pop(int* i) const;
bool is_rot() const;
bool is_rotrev() const;
bool is_push_rot(int i) const;
bool is_push_rot(int* i) const;
bool is_push_rotrev(int i) const;
bool is_push_rotrev(int* i) const;
bool is_push_xchg(int i, int j, int k) const;
bool is_push_xchg(int* i, int* j, int* k) const;
bool is_xchg2(int i, int j) const;
bool is_xchg2(int* i, int* j) const;
bool is_xcpu(int i, int j) const;
@ -1404,6 +1410,9 @@ struct Optimizer {
bool is_push(int* i);
bool is_pop(int* i);
bool is_nop();
bool is_push_rot(int* i);
bool is_push_rotrev(int* i);
bool is_push_xchg(int* i, int* j, int* k);
bool is_xchg2(int* i, int* j);
bool is_xcpu(int* i, int* j);
bool is_puxc(int* i, int* j);

View file

@ -401,6 +401,19 @@ bool Optimizer::is_pop(int* i) {
return is_pred([i](const auto& t) { return t.is_pop(i) && *i < 256; });
}
bool Optimizer::is_push_rot(int* i) {
return is_pred([i](const auto& t) { return t.is_push_rot(i) && *i < 16; }, 3);
}
bool Optimizer::is_push_rotrev(int* i) {
return is_pred([i](const auto& t) { return t.is_push_rotrev(i) && *i < 16; }, 3);
}
bool Optimizer::is_push_xchg(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_push_xchg(i, j, k) && *i < 16 && *j < 16 && *k < 16; }) &&
!(p_ == 2 && op_[0]->is_push() && op_[1]->is_xchg());
}
bool Optimizer::is_xchg2(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_xchg2(i, j) && *i < 16 && *j < 16; });
}
@ -434,7 +447,8 @@ bool Optimizer::is_xcpu2(int* i, int* j, int* k) {
}
bool Optimizer::is_puxc2(int* i, int* j, int* k) {
return is_pred([i, j, k](const auto& t) { return t.is_puxc2(i, j, k) && *i < 16 && *j < 15 && *k < 15; });
return is_pred(
[i, j, k](const auto& t) { return t.is_puxc2(i, j, k) && *i < 16 && *j < 15 && *k < 15 && *j + *k != -1; });
}
bool Optimizer::is_puxcpu(int* i, int* j, int* k) {
@ -545,6 +559,9 @@ bool Optimizer::find_at_least(int pb) {
(is_xcpu(&i, &j) && rewrite(AsmOp::XcPu(i, j))) || (is_puxc(&i, &j) && rewrite(AsmOp::PuXc(i, j))) ||
(is_push2(&i, &j) && rewrite(AsmOp::Push2(i, j))) || (is_blkswap(&i, &j) && rewrite(AsmOp::BlkSwap(i, j))) ||
(is_blkpush(&i, &j) && rewrite(AsmOp::BlkPush(i, j))) || (is_blkdrop(&i) && rewrite(AsmOp::BlkDrop(i))) ||
(is_push_rot(&i) && rewrite(AsmOp::Push(i), AsmOp::Custom("ROT"))) ||
(is_push_rotrev(&i) && rewrite(AsmOp::Push(i), AsmOp::Custom("-ROT"))) ||
(is_push_xchg(&i, &j, &k) && rewrite(AsmOp::Push(i), AsmOp::Xchg(j, k))) ||
(is_reverse(&i, &j) && rewrite(AsmOp::BlkReverse(i, j))) ||
(is_nip_seq(&i, &j) && rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
(is_pop_blkdrop(&i, &k) && rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||

View file

@ -454,6 +454,53 @@ bool StackTransform::is_rotrev() const {
return equal(rot_rev, true);
}
// PUSH i ; ROT == 1 i 0 2 3
bool StackTransform::is_push_rot(int i) const {
return is_valid() && d == -1 && i >= 0 && is_trivial_after(3) && get(0) == 1 && get(1) == i && get(2) == 0;
}
bool StackTransform::is_push_rot(int *i) const {
return is_valid() && (*i = get(1)) >= 0 && is_push_rot(*i);
}
// PUSH i ; -ROT == 0 1 i 2 3
bool StackTransform::is_push_rotrev(int i) const {
return is_valid() && d == -1 && i >= 0 && is_trivial_after(3) && get(0) == 0 && get(1) == 1 && get(2) == i;
}
bool StackTransform::is_push_rotrev(int *i) const {
return is_valid() && (*i = get(2)) >= 0 && is_push_rotrev(*i);
}
// PUSH s(i) ; XCHG s(j),s(k) --> i 0 1 .. i ..
// PUSH s(i) ; XCHG s(0),s(k) --> k-1 0 1 .. k-2 i k ..
bool StackTransform::is_push_xchg(int i, int j, int k) const {
StackTransform t;
return is_valid() && d == -1 && n <= 3 && t.apply_push(i) && t.apply_xchg(j, k) && t <= *this;
}
bool StackTransform::is_push_xchg(int *i, int *j, int *k) const {
if (!(is_valid() && d == -1 && n <= 3 && n > 0)) {
return false;
}
int s = get(0);
if (s < 0) {
return false;
}
*i = s;
*j = 0;
if (n == 1) {
*k = 0;
} else if (n == 2) {
*k = s + 1;
*i = get(s + 1);
} else {
*j = A[1].first + 1;
*k = A[2].first + 1;
}
return is_push_xchg(*i, *j, *k);
}
// XCHG s1,s(i) ; XCHG s0,s(j)
bool StackTransform::is_xchg2(int i, int j) const {
StackTransform t;

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);
}

View file

@ -176,8 +176,8 @@ elector_addr config.elector_smc!
config.special!
// gas_price gas_limit special_gas_limit gas_credit block_gas_limit freeze_due_limit delete_due_limit flat_gas_limit flat_gas_price --
1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 100 100000 config.gas_prices!
10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 100 1000000 config.mc_gas_prices!
1000 sg* 1 *M dup 10000 10 *M GR$0.1 GR$1.0 1000 1000000 config.gas_prices!
10000 sg* 1 *M 10 *M 10000 10 *M GR$0.1 GR$1.0 1000 10000000 config.mc_gas_prices!
// lump_price bit_price cell_price ihr_factor first_frac next_frac
1000000 1000 sg* 100000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.fwd_prices!
10000000 10000 sg* 1000000 sg* 3/2 sg*/ 1/3 sg*/ 1/3 sg*/ config.mc_fwd_prices!

View file

@ -0,0 +1,66 @@
#!/usr/bin/fift -s
"TonUtil.fif" include
"Asm.fif" include
"GetOpt.fif" include
{ show-options-help 1 halt } : usage
Basechain =: wc // create smart contract in basechain
"new-dns-query.boc" =: savefile
0 =: contract-id
variable dns-dict dictnew dns-dict !
begin-options
"<filename-base> <address> <reg-period> <reg-price> <ng-per-bit> <ng-per-cell> [-w<workchain>] [-r<random-id>] [-o<savefile-boc>]" +cr +tab
+"Creates a new automatic dns smart contract with 32-bit identifier <random-id> controlled from wallet with address <address> "
+"and saves it into <savefile-boc> ('" savefile $+ +"' by default)"
disable-digit-options generic-help-setopt
"w" "--workchain" { parse-workchain-id =: wc } short-long-option-arg
"Selects workchain to create smart contract (" wc (.) $+ +" by default)" option-help
"r" "--random-id" { parse-int =: contract-id } short-long-option-arg
"Sets 'random' smart contract identifier (" contract-id (.) $+ +" by default)" option-help
"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
$# 6 <> ' usage if
6 :$1..n
$1 =: file-base
$2 false parse-load-address drop 2=: ctl-addr
$3 parse-int dup 0 10000000 in-range? ' usage ifnot =: reg-period
$4 $>GR =: reg-price
$5 parse-int dup 0< ' usage if =: ng-pb
$6 parse-int dup 0< ' usage if =: ng-pc
contract-id 32 fits ' usage ifnot
{ contract-id ?dup { (.) $+ } if } : +contractid
."Creating new automatic DNS smart contract in workchain " wc .
."with random id " contract-id . cr
."Controlling wallet (smart contract) is " ctl-addr 6 .Addr cr
."Subdomain registration period is " reg-period . ."seconds" cr
."Subdomain registration price is " reg-price .GR
."+ " ng-pc . ."per cell + " ng-pb . ."per bit" cr
// Create new automatic DNS; source code included from `auto/dns-auto-code.fif`
"auto/dns-auto-code.fif" include
// code
<b <b ctl-addr -rot 8 i, swap 256 u, contract-id 32 i, b> ref, // ctl
dns-dict @ dict, dictnew dict, // dom_dict gc
reg-period 30 u, reg-price Gram, ng-pc Gram, ng-pb Gram, // stdper ppc ppb
0 64 u, // nhk lhk
b> // data
null // no libraries
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hashu wc swap 2dup 2constant wallet_addr
."new automatic DNS smartcontract address = " 2dup .addr cr
2dup file-base +"-dns" +contractid +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr
."Bounceable address (for later access): " 6 .Addr cr
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, b>
dup ."External message for initialization is " <s csr. cr
2 boc+>B dup Bx. cr
savefile tuck B>file
."(Saved dns smart-contract creating query to file " type .")" cr

View file

@ -82,6 +82,7 @@ slice skip_dict(slice s) asm "SKIPDICT";
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
int cell_depth(cell c) asm "CDEPTH";
int slice_refs(slice s) asm "SREFS";
int slice_bits(slice s) asm "SBITS";
@ -89,9 +90,11 @@ int slice_bits(slice s) asm "SBITS";
int slice_empty?(slice s) asm "SEMPTY";
int slice_data_empty?(slice s) asm "SDEMPTY";
int slice_refs_empty?(slice s) asm "SREMPTY";
int slice_depth(slice s) asm "SDEPTH";
int builder_refs(builder b) asm "BREFS";
int builder_bits(builder b) asm "BBITS";
int builder_depth(builder b) asm "BDEPTH";
builder begin_cell() asm "NEWC";
cell end_cell(builder b) asm "ENDC";

View file

@ -81,7 +81,7 @@ td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::Private
}
td::Ref<vm::Cell> HighloadWallet::get_init_code() noexcept {
return SmartContractCode::highload_wallet();
return SmartContractCode::get_code(SmartContractCode::HighloadWalletV1);
}
vm::CellHash HighloadWallet::get_init_code_hash() noexcept {

View file

@ -104,7 +104,7 @@ td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::Priva
}
td::Ref<vm::Cell> HighloadWalletV2::get_init_code(td::int32 revision) noexcept {
return SmartContractCode::highload_wallet_v2(revision);
return SmartContractCode::get_code(SmartContractCode::HighloadWalletV2, revision);
}
vm::CellHash HighloadWalletV2::get_init_code_hash() noexcept {

View file

@ -178,15 +178,17 @@ td::Result<std::vector<DnsInterface::Entry>> DnsInterface::resolve(td::Slice nam
*/
// creation
td::Ref<ManualDns> ManualDns::create(td::Ref<vm::Cell> data, int revision) {
return td::Ref<ManualDns>(true, State{ton::SmartContractCode::dns_manual(revision), std::move(data)});
return td::Ref<ManualDns>(
true, State{ton::SmartContractCode::get_code(ton::SmartContractCode::ManualDns, revision), std::move(data)});
}
td::Ref<ManualDns> ManualDns::create(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, int revision) {
return create(create_init_data_fast(public_key, wallet_id), revision);
}
td::optional<td::int32> ManualDns::guess_revision(const vm::Cell::Hash& code_hash) {
for (auto i : {-1, 1}) {
if (ton::SmartContractCode::dns_manual(i)->get_hash() == code_hash) {
for (auto i : ton::SmartContractCode::get_revisions(ton::SmartContractCode::ManualDns)) {
if (ton::SmartContractCode::get_code(ton::SmartContractCode::ManualDns, i)->get_hash() == code_hash) {
return i;
}
}

View file

@ -48,7 +48,8 @@ td::Ref<vm::Cell> MultisigWallet::QueryBuilder::create(td::int32 id, td::Ed25519
}
td::Ref<MultisigWallet> MultisigWallet::create(td::Ref<vm::Cell> data) {
return td::Ref<MultisigWallet>(true, State{ton::SmartContractCode::multisig(), std::move(data)});
return td::Ref<MultisigWallet>(
true, State{ton::SmartContractCode::get_code(ton::SmartContractCode::Multisig), std::move(data)});
}
int MultisigWallet::processed(td::uint64 query_id) const {

View file

@ -96,19 +96,6 @@ const auto& get_map() {
"FwCEMQLTAAHAAZPUAdCY0wUBqgLXGAHiINdJwg/"
"ypiB41yLXCwfyaHBTEddJqTYCmNMHAcAAEqEB5DDIywYBzxbJ0FADACBZ9KhvpSCUAvQEMJIybeICACg0A4AQ9FqZECOECUBE8AEBkjAx4gBmM"
"SLAFZwy9AQQI4QJUELwAQHgIsAWmDIChAn0czAB4DAyIMAfkzD0BODAIJJtAeDyLG0B");
//auto check_revision = [&](td::Slice name, td::int32 default_revision) {
//auto it = map.find(name);
//CHECK(it != map.end());
//auto other_it = map.find(PSLICE() << name << "-r" << default_revision);
//CHECK(other_it != map.end());
//CHECK(it->second->get_hash() == other_it->second->get_hash());
//};
//check_revision("highload-wallet", HIGHLOAD_WALLET_REVISION);
//check_revision("highload-wallet-v2", HIGHLOAD_WALLET2_REVISION);
//check_revision("simple-wallet", WALLET_REVISION);
//check_revision("wallet", WALLET2_REVISION);
//check_revision("wallet3", WALLET3_REVISION);
return map;
}();
return map;
@ -123,60 +110,93 @@ td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) {
}
return it->second;
}
td::Ref<vm::Cell> SmartContractCode::multisig() {
auto res = load("multisig").move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::wallet3(int revision) {
if (revision == 0) {
revision = WALLET3_REVISION;
td::Span<int> SmartContractCode::get_revisions(Type type) {
switch (type) {
case Type::WalletV1: {
static int res[] = {1, 2};
return res;
}
case Type::WalletV2: {
static int res[] = {1, 2};
return res;
}
case Type::WalletV3: {
static int res[] = {1, 2};
return res;
}
case Type::WalletV1Ext: {
static int res[] = {-1};
return res;
}
case Type::HighloadWalletV1: {
static int res[] = {-1, 1, 2};
return res;
}
case Type::HighloadWalletV2: {
static int res[] = {-1, 1, 2};
return res;
}
case Type::Multisig: {
static int res[] = {-1};
return res;
}
case Type::ManualDns: {
static int res[] = {-1, 1};
return res;
}
}
auto res = load(PSLICE() << "wallet3-r" << revision).move_as_ok();
return res;
UNREACHABLE();
return {};
}
td::Ref<vm::Cell> SmartContractCode::wallet(int revision) {
if (revision == 0) {
revision = WALLET2_REVISION;
}
auto res = load(PSLICE() << "wallet-r" << revision).move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::simple_wallet(int revision) {
if (revision == 0) {
revision = WALLET_REVISION;
}
auto res = load(PSLICE() << "simple-wallet-r" << revision).move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::simple_wallet_ext() {
static auto res = load("simple-wallet-ext").move_as_ok();
return res;
}
td::Ref<vm::Cell> SmartContractCode::highload_wallet(int revision) {
td::Result<int> SmartContractCode::validate_revision(Type type, int revision) {
auto revisions = get_revisions(type);
if (revision == -1) {
return load("highload-wallet").move_as_ok();
if (revisions[0] == -1) {
return -1;
}
return revisions[revisions.size() - 1];
}
if (revision == 0) {
revision = HIGHLOAD_WALLET_REVISION;
return revisions[revisions.size() - 1];
}
return load(PSLICE() << "highload-wallet-r" << revision).move_as_ok();
for (auto x : revisions) {
if (x == revision) {
return revision;
}
}
return td::Status::Error("No such revision");
}
td::Ref<vm::Cell> SmartContractCode::highload_wallet_v2(int revision) {
td::Ref<vm::Cell> SmartContractCode::get_code(Type type, int ext_revision) {
auto revision = validate_revision(type, ext_revision).move_as_ok();
auto basename = [](Type type) -> td::Slice {
switch (type) {
case Type::WalletV1:
return "simple-wallet";
case Type::WalletV2:
return "wallet";
case Type::WalletV3:
return "wallet3";
case Type::WalletV1Ext:
return "simple-wallet-ext";
case Type::HighloadWalletV1:
return "highload-wallet";
case Type::HighloadWalletV2:
return "highload-wallet-v2";
case Type::Multisig:
return "multisig";
case Type::ManualDns:
return "dns-manual";
}
UNREACHABLE();
return "";
}(type);
if (revision == -1) {
return load("highload-wallet-v2").move_as_ok();
return load(basename).move_as_ok();
}
if (revision == 0) {
revision = HIGHLOAD_WALLET2_REVISION;
}
return load(PSLICE() << "highload-wallet-v2-r" << revision).move_as_ok();
}
td::Ref<vm::Cell> SmartContractCode::dns_manual(int revision) {
if (revision == -1) {
return load("dns-manual").move_as_ok();
}
if (revision == 0) {
revision = DNS_REVISION;
}
return load(PSLICE() << "dns-manual-r" << revision).move_as_ok();
return load(PSLICE() << basename << "-r" << revision).move_as_ok();
}
} // namespace ton

View file

@ -18,17 +18,16 @@
*/
#include "vm/cells.h"
#include "td/utils/Span.h"
namespace ton {
class SmartContractCode {
public:
static td::Result<td::Ref<vm::Cell>> load(td::Slice name);
static td::Ref<vm::Cell> multisig();
static td::Ref<vm::Cell> wallet3(int revision = 0);
static td::Ref<vm::Cell> wallet(int revision = 0);
static td::Ref<vm::Cell> simple_wallet(int revision = 0);
static td::Ref<vm::Cell> simple_wallet_ext();
static td::Ref<vm::Cell> highload_wallet(int revision = 0);
static td::Ref<vm::Cell> highload_wallet_v2(int revision = 0);
static td::Ref<vm::Cell> dns_manual(int revision = 0);
enum Type { WalletV1 = 1, WalletV1Ext, WalletV2, WalletV3, HighloadWalletV1, HighloadWalletV2, ManualDns, Multisig };
static td::Span<int> get_revisions(Type type);
static td::Result<int> validate_revision(Type type, int revision);
static td::Ref<vm::Cell> get_code(Type type, int revision = 0);
};
} // namespace ton

View file

@ -64,7 +64,7 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message_static(const td::Ed25519::Priv
}
td::Ref<vm::Cell> TestWallet::get_init_code(td::int32 revision) noexcept {
return ton::SmartContractCode::simple_wallet(revision);
return ton::SmartContractCode::get_code(ton::SmartContractCode::WalletV1, revision);
}
vm::CellHash TestWallet::get_init_code_hash() noexcept {

View file

@ -70,7 +70,7 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
}
td::Ref<vm::Cell> Wallet::get_init_code(td::int32 revision) noexcept {
return SmartContractCode::wallet(revision);
return SmartContractCode::get_code(ton::SmartContractCode::WalletV2, revision);
}
vm::CellHash Wallet::get_init_code_hash() noexcept {

View file

@ -80,7 +80,7 @@ td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& p
}
td::Ref<vm::Cell> WalletV3::get_init_code(td::int32 revision) noexcept {
return SmartContractCode::wallet3(revision);
return SmartContractCode::get_code(ton::SmartContractCode::WalletV3, revision);
}
vm::CellHash WalletV3::get_init_code_hash() noexcept {

View file

@ -497,13 +497,16 @@ class SimpleWallet : public ton::SmartContract {
}
static td::Ref<SimpleWallet> create_empty() {
return td::Ref<SimpleWallet>(true, State{ton::SmartContractCode::simple_wallet_ext(), {}});
return td::Ref<SimpleWallet>(true,
State{ton::SmartContractCode::get_code(ton::SmartContractCode::WalletV1Ext), {}});
}
static td::Ref<SimpleWallet> create(td::Ref<vm::Cell> data) {
return td::Ref<SimpleWallet>(true, State{ton::SmartContractCode::simple_wallet_ext(), std::move(data)});
return td::Ref<SimpleWallet>(
true, State{ton::SmartContractCode::get_code(ton::SmartContractCode::WalletV1Ext), std::move(data)});
}
static td::Ref<SimpleWallet> create_fast(td::Ref<vm::Cell> data) {
return td::Ref<SimpleWallet>(true, State{ton::SmartContractCode::simple_wallet(), std::move(data)});
return td::Ref<SimpleWallet>(
true, State{ton::SmartContractCode::get_code(ton::SmartContractCode::WalletV1), std::move(data)});
}
td::int32 seqno() const {

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include <tl/tlblib.hpp>
@ -138,22 +138,23 @@ bool TLB::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
return pp.fail("invalid value");
}
pp.raw_nl();
cs_copy.print_rec(pp.os, pp.indent);
return pp.mkindent() && pp.close();
return cs_copy.print_rec(pp.os, &pp.limit, pp.indent) && pp.mkindent() && pp.close();
}
bool TLB::print_special(PrettyPrinter& pp, vm::CellSlice& cs) const {
pp.open("raw@");
pp << *this << ' ';
pp.raw_nl();
cs.print_rec(pp.os, pp.indent);
return pp.mkindent() && pp.close();
return cs.print_rec(pp.os, &pp.limit, pp.indent) && pp.mkindent() && pp.close();
}
bool TLB::print_ref(PrettyPrinter& pp, Ref<vm::Cell> cell_ref) const {
if (cell_ref.is_null()) {
return pp.fail("null cell reference");
}
if (!pp.register_recursive_call()) {
return pp.fail("too many recursive calls while printing a TL-B value");
}
bool is_special;
auto cs = load_cell_slice_special(std::move(cell_ref), is_special);
if (is_special) {
@ -163,18 +164,21 @@ bool TLB::print_ref(PrettyPrinter& pp, Ref<vm::Cell> cell_ref) const {
}
}
bool TLB::print_skip(std::ostream& os, vm::CellSlice& cs, int indent) const {
bool TLB::print_skip(std::ostream& os, vm::CellSlice& cs, int indent, int rec_limit) const {
PrettyPrinter pp{os, indent};
pp.set_limit(rec_limit);
return pp.fail_unless(print_skip(pp, cs));
}
bool TLB::print(std::ostream& os, const vm::CellSlice& cs, int indent) const {
bool TLB::print(std::ostream& os, const vm::CellSlice& cs, int indent, int rec_limit) const {
PrettyPrinter pp{os, indent};
pp.set_limit(rec_limit);
return pp.fail_unless(print(pp, cs));
}
bool TLB::print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent) const {
bool TLB::print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent, int rec_limit) const {
PrettyPrinter pp{os, indent};
pp.set_limit(rec_limit);
return pp.fail_unless(print_ref(pp, std::move(cell_ref)));
}

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <iostream>
@ -208,12 +208,12 @@ class TLB {
bool print(PrettyPrinter& pp, Ref<vm::CellSlice> cs_ref) const {
return print(pp, *cs_ref);
}
bool print_skip(std::ostream& os, vm::CellSlice& cs, int indent = 0) const;
bool print(std::ostream& os, const vm::CellSlice& cs, int indent = 0) const;
bool print(std::ostream& os, Ref<vm::CellSlice> cs_ref, int indent = 0) const {
return print(os, *cs_ref, indent);
bool print_skip(std::ostream& os, vm::CellSlice& cs, int indent = 0, int rec_limit = 0) const;
bool print(std::ostream& os, const vm::CellSlice& cs, int indent = 0, int rec_limit = 0) const;
bool print(std::ostream& os, Ref<vm::CellSlice> cs_ref, int indent = 0, int rec_limit = 0) const {
return print(os, *cs_ref, indent, rec_limit);
}
bool print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0) const;
bool print_ref(std::ostream& os, Ref<vm::Cell> cell_ref, int indent = 0, int rec_limit = 0) const;
std::string as_string_skip(vm::CellSlice& cs, int indent = 0) const;
std::string as_string(const vm::CellSlice& cs, int indent = 0) const;
std::string as_string(Ref<vm::CellSlice> cs_ref, int indent = 0) const {
@ -456,15 +456,20 @@ bool store_from(vm::CellBuilder& cb, const T& tlb_type, Ref<vm::CellSlice> field
namespace tlb {
struct PrettyPrinter {
enum { default_print_limit = 4096 };
std::ostream& os;
int indent;
int level;
bool failed;
bool nl_used;
int mode;
int limit{default_print_limit};
PrettyPrinter(std::ostream& _os, int _indent = 0, int _mode = 1)
: os(_os), indent(_indent), level(0), failed(false), nl_used(false), mode(_mode) {
}
PrettyPrinter(int _limit, std::ostream& _os, int _indent = 0, int _mode = 1)
: os(_os), indent(_indent), level(0), failed(false), nl_used(false), mode(_mode), limit(_limit) {
}
~PrettyPrinter();
bool ok() const {
return !failed && !level;
@ -489,6 +494,14 @@ struct PrettyPrinter {
bool field_int(long long value, std::string name);
bool field_uint(unsigned long long value);
bool field_uint(unsigned long long value, std::string name);
bool register_recursive_call() {
return limit--;
}
void set_limit(int new_limit) {
if (new_limit > 0) {
limit = new_limit;
}
}
bool out(std::string str) {
os << str;
return true;

View file

@ -814,6 +814,9 @@ void register_cell_serialize_ops(OpcodeTable& cp0) {
compute_len_store_const_ref))
.insert(OpcodeInstr::mksimple(0xcf23, 16, "ENDXC", exec_builder_to_special_cell))
.insert(OpcodeInstr::mkfixed(0xcf28 >> 2, 14, 2, dump_store_le_int, exec_store_le_int))
.insert(OpcodeInstr::mksimple(
0xcf30, 16, "BDEPTH",
std::bind(exec_int_builder_func, _1, "BDEPTH", [](Ref<CellBuilder> b) { return b->get_depth(); })))
.insert(OpcodeInstr::mksimple(
0xcf31, 16, "BBITS",
std::bind(exec_int_builder_func, _1, "BBITS", [](Ref<CellBuilder> b) { return b->size(); })))
@ -1321,6 +1324,22 @@ int exec_load_same(VmState* st, const char* name, int x) {
return 0;
}
int exec_cell_depth(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute CDEPTH";
auto cell = stack.pop_maybe_cell();
stack.push_smallint(cell.not_null() ? cell->get_depth() : 0);
return 0;
}
int exec_slice_depth(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute SDEPTH";
auto cs = stack.pop_cellslice();
stack.push_smallint(cs->get_depth());
return 0;
}
void register_cell_deserialize_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
cp0.insert(OpcodeInstr::mksimple(0xd0, 8, "CTOS", exec_cell_to_slice))
@ -1407,7 +1426,9 @@ void register_cell_deserialize_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mkfixed(0xd75, 12, 4, dump_load_le_int, exec_load_le_int))
.insert(OpcodeInstr::mksimple(0xd760, 16, "LDZEROES", std::bind(exec_load_same, _1, "LDZEROES", 0)))
.insert(OpcodeInstr::mksimple(0xd761, 16, "LDONES", std::bind(exec_load_same, _1, "LDONES", 1)))
.insert(OpcodeInstr::mksimple(0xd762, 16, "LDSAME", std::bind(exec_load_same, _1, "LDSAME", -1)));
.insert(OpcodeInstr::mksimple(0xd762, 16, "LDSAME", std::bind(exec_load_same, _1, "LDSAME", -1)))
.insert(OpcodeInstr::mksimple(0xd764, 16, "SDEPTH", exec_slice_depth))
.insert(OpcodeInstr::mksimple(0xd765, 16, "CDEPTH", exec_cell_depth));
}
void register_cell_ops(OpcodeTable& cp0) {

View file

@ -52,7 +52,7 @@ Ref<DataCell> CellBuilder::finalize_copy(bool special) const {
}
auto res = DataCell::create(data, size(), td::span(refs.data(), size_refs()), special);
if (res.is_error()) {
LOG(ERROR) << res.error();
LOG(DEBUG) << res.error();
throw CellWriteError{};
}
auto cell = res.move_as_ok();
@ -60,7 +60,7 @@ Ref<DataCell> CellBuilder::finalize_copy(bool special) const {
if (vm_state_interface) {
vm_state_interface->register_new_cell(cell);
if (cell.is_null()) {
LOG(ERROR) << "cannot register new data cell";
LOG(DEBUG) << "cannot register new data cell";
throw CellWriteError{};
}
}
@ -71,7 +71,7 @@ Ref<DataCell> CellBuilder::finalize_novm(bool special) {
auto res = DataCell::create(data, size(), td::mutable_span(refs.data(), size_refs()), special);
bits = refs_cnt = 0;
if (res.is_error()) {
LOG(ERROR) << res.error();
LOG(DEBUG) << res.error();
throw CellWriteError{};
}
CHECK(res.ok().not_null());
@ -87,7 +87,7 @@ Ref<DataCell> CellBuilder::finalize(bool special) {
auto cell = finalize_novm(special);
vm_state_interface->register_new_cell(cell);
if (cell.is_null()) {
LOG(ERROR) << "cannot register new data cell";
LOG(DEBUG) << "cannot register new data cell";
throw CellWriteError{};
}
return cell;
@ -102,6 +102,7 @@ Ref<Cell> CellBuilder::create_pruned_branch(Ref<Cell> cell, td::uint32 new_level
}
return do_create_pruned_branch(std::move(cell), new_level, virt_level);
}
Ref<DataCell> CellBuilder::do_create_pruned_branch(Ref<Cell> cell, td::uint32 new_level, td::uint32 virt_level) {
auto level_mask = cell->get_level_mask().apply(virt_level);
auto level = level_mask.get_level();
@ -386,6 +387,14 @@ CellBuilder& CellBuilder::store_ref(Ref<Cell> ref) {
return ensure_pass(store_ref_bool(std::move(ref)));
}
td::uint16 CellBuilder::get_depth() const {
int d = 0;
for (unsigned i = 0; i < refs_cnt; i++) {
d = std::max(d, 1 + refs[i]->get_depth());
}
return static_cast<td::uint16>(d);
}
bool CellBuilder::append_data_cell_bool(const DataCell& cell) {
unsigned len = cell.size();
if (can_extend_by(len, cell.size_refs())) {

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "vm/cells/DataCell.h"
@ -78,6 +78,7 @@ class CellBuilder : public td::CntObject {
const unsigned char* get_data() const {
return data;
}
td::uint16 get_depth() const;
td::ConstBitPtr data_bits() const {
return data;
}

View file

@ -776,6 +776,14 @@ bool CellSlice::fetch_maybe_ref(Ref<vm::Cell>& res) {
}
}
td::uint16 CellSlice::get_depth() const {
int d = 0;
for (unsigned i = 0; i < size_refs(); ++i) {
d = std::max(d, prefetch_ref(i)->get_depth() + 1);
}
return static_cast<td::uint16>(d);
}
bool CellSlice::begins_with(unsigned bits, unsigned long long value) const {
return have(bits) && !((prefetch_ulong(bits) ^ value) & ((1ULL << bits) - 1));
}
@ -980,13 +988,18 @@ void CellSlice::dump_hex(std::ostream& os, int mode, bool endl) const {
}
}
void CellSlice::print_rec(std::ostream& os, int indent) const {
bool CellSlice::print_rec(std::ostream& os, int* limit, int indent) const {
for (int i = 0; i < indent; i++) {
os << ' ';
}
if (!limit || *limit <= 0) {
os << "<cell output limit reached>" << std::endl;
return false;
}
--*limit;
if (cell.is_null()) {
os << "NULL" << std::endl;
return;
return true;
}
if (is_special()) {
os << "SPECIAL ";
@ -994,8 +1007,20 @@ void CellSlice::print_rec(std::ostream& os, int indent) const {
os << "x{" << as_bitslice().to_hex() << '}' << std::endl;
for (unsigned i = 0; i < size_refs(); i++) {
CellSlice cs{NoVm(), prefetch_ref(i)};
cs.print_rec(os, indent + 1);
if (!cs.print_rec(os, limit, indent + 1)) {
return false;
}
}
return true;
}
bool CellSlice::print_rec(std::ostream& os, int indent) const {
int limit = default_recursive_print_limit;
return print_rec(os, &limit, indent);
}
bool CellSlice::print_rec(int limit, std::ostream& os, int indent) const {
return print_rec(os, &limit, indent);
}
td::StringBuilder& operator<<(td::StringBuilder& sb, const CellSlice& cs) {

View file

@ -44,6 +44,7 @@ class CellSlice : public td::CntObject {
public:
static constexpr long long fetch_long_eof = (static_cast<unsigned long long>(-1LL) << 63);
static constexpr unsigned long long fetch_ulong_eof = (unsigned long long)-1LL;
enum { default_recursive_print_limit = 100 };
struct CellReadError {};
CellSlice(NoVm, Ref<Cell> cell_ref);
@ -129,6 +130,7 @@ class CellSlice : public td::CntObject {
const unsigned char* data() const {
return cell->get_data();
}
td::uint16 get_depth() const;
td::ConstBitPtr data_bits() const {
return td::ConstBitPtr{data(), (int)cur_pos()};
}
@ -252,7 +254,9 @@ class CellSlice : public td::CntObject {
bool contents_equal(const CellSlice& cs2) const;
void dump(std::ostream& os, int level = 0, bool endl = true) const;
void dump_hex(std::ostream& os, int mode = 0, bool endl = false) const;
void print_rec(std::ostream& os, int indent = 0) const;
bool print_rec(std::ostream& os, int indent = 0) const;
bool print_rec(std::ostream& os, int* limit, int indent = 0) const;
bool print_rec(int limit, std::ostream& os, int indent = 0) const;
void error() const {
throw CellReadError{};
}

View file

@ -77,7 +77,7 @@ int exec_set_gas_limit(VmState* st) {
int exec_commit(VmState* st) {
VM_LOG(st) << "execute COMMIT";
st->commit();
st->force_commit();
return 0;
}

View file

@ -434,12 +434,33 @@ int VmState::run() {
}
} while (!res);
// LOG(INFO) << "[EN] data cells: " << DataCell::get_total_data_cells();
if ((res | 1) == -1) {
commit();
if ((res | 1) == -1 && !try_commit()) {
VM_LOG(this) << "automatic commit failed (new data or action cells too deep)";
get_stack().clear();
get_stack().push_smallint(0);
return ~(int)Excno::cell_ov;
}
return res;
}
bool VmState::try_commit() {
if (cr.d[0].not_null() && cr.d[1].not_null() && cr.d[0]->get_depth() <= max_data_depth &&
cr.d[1]->get_depth() <= max_data_depth) {
cstate.c4 = cr.d[0];
cstate.c5 = cr.d[1];
cstate.committed = true;
return true;
} else {
return false;
}
}
void VmState::force_commit() {
if (!try_commit()) {
throw VmError{Excno::cell_ov, "cannot commit too deep cells as new data/actions"};
}
}
ControlData* force_cdata(Ref<Continuation>& cont) {
if (!cont->get_cdata()) {
cont = Ref<ArgContExt>{true, cont};

View file

@ -103,7 +103,8 @@ class VmState final : public VmStateInterface {
cell_reload_gas_price = 25,
cell_create_gas_price = 500,
exception_gas_price = 50,
tuple_entry_gas_price = 1
tuple_entry_gas_price = 1,
max_data_depth = 512
};
VmState();
VmState(Ref<CellSlice> _code);
@ -291,11 +292,8 @@ class VmState final : public VmStateInterface {
return cont->is_unique() ? cont.unique_write().jump_w(this) : cont->jump(this);
}
static Ref<CellSlice> convert_code_cell(Ref<Cell> code_cell);
void commit() {
cstate.c4 = cr.d[0];
cstate.c5 = cr.d[1];
cstate.committed = true;
}
bool try_commit();
void force_commit();
void set_chksig_always_succeed(bool flag) {
chksig_always_succeed = flag;