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:
parent
28735ddc9e
commit
efd47af432
42 changed files with 750 additions and 307 deletions
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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?
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))) ||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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!
|
||||
|
|
66
crypto/smartcont/new-auto-dns.fif
Normal file
66
crypto/smartcont/new-auto-dns.fif
Normal 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
|
|
@ -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";
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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())) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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{};
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue