mirror of
https://github.com/ton-blockchain/ton
synced 2025-02-12 11:12:16 +00:00
updated func and tonlib
This commit is contained in:
parent
493ae2410c
commit
a73d202ba2
50 changed files with 1340 additions and 271 deletions
|
@ -375,6 +375,7 @@ if (NOT CMAKE_CROSSCOMPILING)
|
|||
GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2)
|
||||
|
||||
GenFif(DEST smartcont/auto/dns-manual-code SOURCE smartcont/dns-manual-code.fc NAME dns-manual)
|
||||
GenFif(DEST smartcont/auto/dns-auto-code SOURCE smartcont/dns-auto-code.fc NAME dns-auto)
|
||||
|
||||
GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext)
|
||||
endif()
|
||||
|
|
|
@ -463,6 +463,7 @@ Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime re
|
|||
, my_addr(_account.my_addr)
|
||||
, my_addr_exact(_account.my_addr_exact)
|
||||
, balance(_account.balance)
|
||||
, original_balance(_account.balance)
|
||||
, due_payment(_account.due_payment)
|
||||
, last_paid(_account.last_paid)
|
||||
, new_code(_account.code)
|
||||
|
@ -912,6 +913,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
// ...
|
||||
compute_phase = std::make_unique<ComputePhase>();
|
||||
ComputePhase& cp = *(compute_phase.get());
|
||||
original_balance -= total_fees;
|
||||
if (td::sgn(balance.grams) <= 0) {
|
||||
// no gas
|
||||
cp.skip_reason = ComputePhase::sk_no_gas;
|
||||
|
@ -1650,7 +1652,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
|
||||
int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) {
|
||||
block::gen::OutAction::Record_action_reserve_currency rec;
|
||||
if (!tlb::unpack_exact(cs, rec) || (rec.mode & ~3)) {
|
||||
if (!tlb::unpack_exact(cs, rec) || (rec.mode & ~15)) {
|
||||
return -1;
|
||||
}
|
||||
int mode = rec.mode;
|
||||
|
@ -1661,7 +1663,21 @@ int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap,
|
|||
return -1;
|
||||
}
|
||||
LOG(DEBUG) << "action_reserve_currency: mode=" << mode << ", reserve=" << reserve.to_str()
|
||||
<< ", balance=" << ap.remaining_balance.to_str();
|
||||
<< ", balance=" << ap.remaining_balance.to_str() << ", original balance=" << original_balance.to_str();
|
||||
if (mode & 4) {
|
||||
if (mode & 8) {
|
||||
reserve = original_balance - reserve;
|
||||
} else {
|
||||
reserve += original_balance;
|
||||
}
|
||||
} else if (mode & 8) {
|
||||
LOG(DEBUG) << "invalid reserve mode " << mode;
|
||||
return -1;
|
||||
}
|
||||
if (!reserve.is_valid() || td::sgn(reserve.grams) < 0) {
|
||||
LOG(DEBUG) << "cannot reserve a negative amount: " << reserve.to_str();
|
||||
return -1;
|
||||
}
|
||||
if (reserve.grams > ap.remaining_balance.grams) {
|
||||
if (mode & 2) {
|
||||
reserve.grams = ap.remaining_balance.grams;
|
||||
|
|
|
@ -194,10 +194,6 @@ struct ActionPhase {
|
|||
Ref<vm::Cell> new_code;
|
||||
td::BitArray<256> action_list_hash;
|
||||
block::CurrencyCollection remaining_balance, reserved_balance;
|
||||
// td::RefInt256 remaining_balance;
|
||||
// Ref<vm::Cell> remaining_extra;
|
||||
// td::RefInt256 reserved_balance;
|
||||
// Ref<vm::Cell> reserved_extra;
|
||||
std::vector<Ref<vm::Cell>> action_list; // processed in reverse order
|
||||
std::vector<Ref<vm::Cell>> out_msgs;
|
||||
ton::LogicalTime end_lt;
|
||||
|
@ -319,7 +315,7 @@ struct Transaction {
|
|||
const Account& account; // only `commit` method modifies the account
|
||||
Ref<vm::CellSlice> my_addr, my_addr_exact; // almost the same as in account.*
|
||||
ton::LogicalTime start_lt, end_lt;
|
||||
block::CurrencyCollection balance;
|
||||
block::CurrencyCollection balance, original_balance;
|
||||
block::CurrencyCollection msg_balance_remaining;
|
||||
td::RefInt256 due_payment;
|
||||
td::RefInt256 in_fwd_fee, msg_fwd_fees;
|
||||
|
|
|
@ -165,6 +165,8 @@ x{68} @Defop DEPTH
|
|||
x{69} @Defop CHKDEPTH
|
||||
x{6A} @Defop ONLYTOPX
|
||||
x{6B} @Defop ONLYX
|
||||
{ over 0= abort"first argument must be non-zero"
|
||||
<b x{6C} s, rot 4 u, swap 4 u, @addopb } : BLKDROP2
|
||||
|
||||
// null primitives
|
||||
x{6D} dup @Defop NULL @Defop PUSHNULL
|
||||
|
|
|
@ -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 "func.h"
|
||||
|
||||
|
@ -323,6 +323,20 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx
|
|||
show_var_list(os, right, vars);
|
||||
os << std::endl;
|
||||
break;
|
||||
case _Tuple:
|
||||
os << pfx << dis << "MKTUPLE ";
|
||||
show_var_list(os, left, vars);
|
||||
os << " := ";
|
||||
show_var_list(os, right, vars);
|
||||
os << std::endl;
|
||||
break;
|
||||
case _UnTuple:
|
||||
os << pfx << dis << "UNTUPLE ";
|
||||
show_var_list(os, left, vars);
|
||||
os << " := ";
|
||||
show_var_list(os, right, vars);
|
||||
os << std::endl;
|
||||
break;
|
||||
case _IntConst:
|
||||
os << pfx << dis << "CONST ";
|
||||
show_var_list(os, left, vars);
|
||||
|
|
|
@ -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 "func.h"
|
||||
|
||||
|
@ -29,6 +29,7 @@ namespace funC {
|
|||
void CodeBlob::simplify_var_types() {
|
||||
for (TmpVar& var : vars) {
|
||||
TypeExpr::remove_indirect(var.v_type);
|
||||
var.v_type->recompute_width();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,7 +355,9 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
|
|||
case _IntConst:
|
||||
case _GlobVar:
|
||||
case _Call:
|
||||
case _CallInd: {
|
||||
case _CallInd:
|
||||
case _Tuple:
|
||||
case _UnTuple: {
|
||||
// left = EXEC right;
|
||||
if (!next_var_info.count_used(left) && is_pure()) {
|
||||
// all variables in `left` are not needed
|
||||
|
@ -541,6 +544,8 @@ bool prune_unreachable(std::unique_ptr<Op>& ops) {
|
|||
case Op::_SetGlob:
|
||||
case Op::_Call:
|
||||
case Op::_CallInd:
|
||||
case Op::_Tuple:
|
||||
case Op::_UnTuple:
|
||||
case Op::_Import:
|
||||
reach = true;
|
||||
break;
|
||||
|
@ -724,6 +729,8 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
case _Tuple:
|
||||
case _UnTuple:
|
||||
case _GlobVar:
|
||||
case _CallInd: {
|
||||
for (var_idx_t i : left) {
|
||||
|
@ -842,6 +849,8 @@ bool Op::mark_noreturn() {
|
|||
case _Import:
|
||||
case _IntConst:
|
||||
case _Let:
|
||||
case _Tuple:
|
||||
case _UnTuple:
|
||||
case _SetGlob:
|
||||
case _GlobVar:
|
||||
case _CallInd:
|
||||
|
|
|
@ -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 "parser/srcread.h"
|
||||
#include "func.h"
|
||||
|
@ -121,12 +121,49 @@ AsmOp AsmOp::BlkDrop(int a) {
|
|||
return AsmOp::Custom(os.str(), a, 0);
|
||||
}
|
||||
|
||||
AsmOp AsmOp::BlkDrop2(int a, int b) {
|
||||
if (!b) {
|
||||
return BlkDrop(a);
|
||||
}
|
||||
std::ostringstream os;
|
||||
os << a << " " << b << " BLKDROP2";
|
||||
return AsmOp::Custom(os.str(), a + b, b);
|
||||
}
|
||||
|
||||
AsmOp AsmOp::BlkReverse(int a, int b) {
|
||||
std::ostringstream os;
|
||||
os << a << " " << b << " REVERSE";
|
||||
return AsmOp::Custom(os.str(), a + b, a + b);
|
||||
}
|
||||
|
||||
AsmOp AsmOp::Tuple(int a) {
|
||||
switch (a) {
|
||||
case 1:
|
||||
return AsmOp::Custom("SINGLE", 1, 1);
|
||||
case 2:
|
||||
return AsmOp::Custom("PAIR", 2, 1);
|
||||
case 3:
|
||||
return AsmOp::Custom("TRIPLE", 3, 1);
|
||||
}
|
||||
std::ostringstream os;
|
||||
os << a << " TUPLE";
|
||||
return AsmOp::Custom(os.str(), a, 1);
|
||||
}
|
||||
|
||||
AsmOp AsmOp::UnTuple(int a) {
|
||||
switch (a) {
|
||||
case 1:
|
||||
return AsmOp::Custom("UNSINGLE", 1, 1);
|
||||
case 2:
|
||||
return AsmOp::Custom("UNPAIR", 1, 2);
|
||||
case 3:
|
||||
return AsmOp::Custom("UNTRIPLE", 1, 3);
|
||||
}
|
||||
std::ostringstream os;
|
||||
os << a << " UNTUPLE";
|
||||
return AsmOp::Custom(os.str(), 1, a);
|
||||
}
|
||||
|
||||
AsmOp AsmOp::IntConst(td::RefInt256 x) {
|
||||
if (x->signed_fits_bits(8)) {
|
||||
return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT");
|
||||
|
|
|
@ -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 "func.h"
|
||||
|
||||
|
@ -318,7 +318,7 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
|
||||
if (left.size() != 1) {
|
||||
assert(left.size() <= 15);
|
||||
stack.o << exec_arg_op("UNTUPLE", (int)left.size(), 1, (int)left.size());
|
||||
stack.o << AsmOp::UnTuple((int)left.size());
|
||||
}
|
||||
for (auto i : left) {
|
||||
stack.push_new_var(i);
|
||||
|
@ -396,6 +396,32 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case _Tuple:
|
||||
case _UnTuple: {
|
||||
if (disabled()) {
|
||||
return true;
|
||||
}
|
||||
std::vector<bool> last;
|
||||
for (var_idx_t x : right) {
|
||||
last.push_back(var_info[x] && var_info[x]->is_last());
|
||||
}
|
||||
stack.rearrange_top(right, std::move(last));
|
||||
stack.opt_show();
|
||||
int k = (int)stack.depth() - (int)right.size();
|
||||
assert(k >= 0);
|
||||
if (cl == _Tuple) {
|
||||
stack.o << AsmOp::Tuple((int)right.size());
|
||||
assert(left.size() == 1);
|
||||
} else {
|
||||
stack.o << AsmOp::UnTuple((int)left.size());
|
||||
assert(right.size() == 1);
|
||||
}
|
||||
stack.s.resize(k);
|
||||
for (int i = 0; i < (int)left.size(); i++) {
|
||||
stack.push_new_var(left.at(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
case _Call:
|
||||
case _CallInd: {
|
||||
if (disabled()) {
|
||||
|
@ -483,7 +509,7 @@ bool Op::generate_code_step(Stack& stack) {
|
|||
assert(stack.s[k + i].first == right[i]);
|
||||
}
|
||||
if (right.size() > 1) {
|
||||
stack.o << exec_arg_op("TUPLE", (int)right.size(), (int)right.size(), 1);
|
||||
stack.o << AsmOp::Tuple((int)right.size());
|
||||
}
|
||||
if (!right.empty()) {
|
||||
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
|
||||
|
|
|
@ -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 <vector>
|
||||
|
@ -36,7 +36,7 @@ namespace funC {
|
|||
extern int verbosity;
|
||||
extern bool op_rewrite_comments;
|
||||
|
||||
constexpr int optimize_depth = 12;
|
||||
constexpr int optimize_depth = 20;
|
||||
|
||||
enum Keyword {
|
||||
_Eof = -1,
|
||||
|
@ -101,6 +101,7 @@ enum Keyword {
|
|||
_Extern,
|
||||
_Inline,
|
||||
_InlineRef,
|
||||
_AutoApply,
|
||||
_MethodId,
|
||||
_Operator,
|
||||
_Infix,
|
||||
|
@ -134,7 +135,7 @@ class IdSc {
|
|||
*/
|
||||
|
||||
struct TypeExpr {
|
||||
enum te_type { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Map, te_Type, te_ForAll } constr;
|
||||
enum te_type { te_Unknown, te_Var, te_Indirect, te_Atomic, te_Tensor, te_Tuple, te_Map, te_Type, te_ForAll } constr;
|
||||
enum {
|
||||
_Int = Keyword::_Int,
|
||||
_Cell = Keyword::_Cell,
|
||||
|
@ -160,6 +161,9 @@ struct TypeExpr {
|
|||
: constr(_constr), value((int)list.size()), args(std::move(list)) {
|
||||
compute_width();
|
||||
}
|
||||
TypeExpr(te_type _constr, TypeExpr* elem0) : constr(_constr), value(1), args{elem0} {
|
||||
compute_width();
|
||||
}
|
||||
TypeExpr(te_type _constr, TypeExpr* elem0, std::vector<TypeExpr*> list)
|
||||
: constr(_constr), value((int)list.size() + 1), args{elem0} {
|
||||
args.insert(args.end(), list.begin(), list.end());
|
||||
|
@ -185,6 +189,9 @@ struct TypeExpr {
|
|||
bool is_map() const {
|
||||
return constr == te_Map;
|
||||
}
|
||||
bool is_tuple() const {
|
||||
return constr == te_Tuple;
|
||||
}
|
||||
bool has_fixed_width() const {
|
||||
return minw == maxw;
|
||||
}
|
||||
|
@ -226,6 +233,15 @@ struct TypeExpr {
|
|||
static TypeExpr* new_tensor(TypeExpr* te1, TypeExpr* te2, TypeExpr* te3) {
|
||||
return new_tensor({te1, te2, te3});
|
||||
}
|
||||
static TypeExpr* new_tuple(TypeExpr* arg0) {
|
||||
return new TypeExpr{te_Tuple, arg0};
|
||||
}
|
||||
static TypeExpr* new_tuple(std::vector<TypeExpr*> list, bool red = false) {
|
||||
return new_tuple(new_tensor(std::move(list), red));
|
||||
}
|
||||
static TypeExpr* new_tuple(std::initializer_list<TypeExpr*> list) {
|
||||
return new_tuple(new_tensor(std::move(list)));
|
||||
}
|
||||
static TypeExpr* new_var() {
|
||||
return new TypeExpr{te_Var, --type_vars, 1};
|
||||
}
|
||||
|
@ -505,6 +521,8 @@ struct Op {
|
|||
_SetGlob,
|
||||
_Import,
|
||||
_Return,
|
||||
_Tuple,
|
||||
_UnTuple,
|
||||
_If,
|
||||
_While,
|
||||
_Until,
|
||||
|
@ -789,7 +807,8 @@ struct Expr {
|
|||
_Apply,
|
||||
_VarApply,
|
||||
_TypeApply,
|
||||
_Tuple,
|
||||
_MkTuple,
|
||||
_Tensor,
|
||||
_Const,
|
||||
_Var,
|
||||
_Glob,
|
||||
|
@ -843,6 +862,12 @@ struct Expr {
|
|||
bool is_type() const {
|
||||
return flags & _IsType;
|
||||
}
|
||||
bool is_type_apply() const {
|
||||
return cls == _TypeApply;
|
||||
}
|
||||
bool is_mktuple() const {
|
||||
return cls == _MkTuple;
|
||||
}
|
||||
void chk_rvalue(const Lexem& lem) const;
|
||||
void chk_lvalue(const Lexem& lem) const;
|
||||
void chk_type(const Lexem& lem) const;
|
||||
|
@ -855,7 +880,8 @@ struct Expr {
|
|||
}
|
||||
int define_new_vars(CodeBlob& code);
|
||||
int predefine_vars();
|
||||
std::vector<var_idx_t> pre_compile(CodeBlob& code) const;
|
||||
std::vector<var_idx_t> pre_compile(CodeBlob& code, bool lval = false) const;
|
||||
static std::vector<var_idx_t> pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here);
|
||||
var_idx_t new_tmp(CodeBlob& code) const;
|
||||
std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
|
||||
return {new_tmp(code)};
|
||||
|
@ -1004,6 +1030,7 @@ struct AsmOp {
|
|||
static AsmOp BlkSwap(int a, int b);
|
||||
static AsmOp BlkPush(int a, int b);
|
||||
static AsmOp BlkDrop(int a);
|
||||
static AsmOp BlkDrop2(int a, int b);
|
||||
static AsmOp BlkReverse(int a, int b);
|
||||
static AsmOp make_stk2(int a, int b, const char* str, int delta);
|
||||
static AsmOp make_stk3(int a, int b, int c, const char* str, int delta);
|
||||
|
@ -1024,6 +1051,8 @@ struct AsmOp {
|
|||
return AsmOp(a_custom, args, retv, custom_op);
|
||||
}
|
||||
static AsmOp Parse(std::string custom_op, int args, int retv = 1);
|
||||
static AsmOp Tuple(int a);
|
||||
static AsmOp UnTuple(int a);
|
||||
};
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& os, const AsmOp& op) {
|
||||
|
@ -1261,6 +1290,8 @@ struct StackTransform {
|
|||
bool is_blkpush(int i, int j) const;
|
||||
bool is_blkpush(int* i, int* j) const;
|
||||
bool is_blkdrop(int* i) const;
|
||||
bool is_blkdrop2(int i, int j) const;
|
||||
bool is_blkdrop2(int* i, int* j) const;
|
||||
bool is_reverse(int i, int j) const;
|
||||
bool is_reverse(int* i, int* j) const;
|
||||
bool is_nip_seq(int i, int j = 0) const;
|
||||
|
@ -1270,7 +1301,10 @@ struct StackTransform {
|
|||
bool is_pop_blkdrop(int* i, int* k) const;
|
||||
bool is_2pop_blkdrop(int i, int j, int k) const;
|
||||
bool is_2pop_blkdrop(int* i, int* j, int* k) const;
|
||||
bool is_const_rot() const;
|
||||
bool is_const_rot(int c) const;
|
||||
bool is_const_rot(int* c) const;
|
||||
bool is_const_pop(int c, int i) const;
|
||||
bool is_const_pop(int* c, int* i) const;
|
||||
|
||||
void show(std::ostream& os, int mode = 0) const;
|
||||
|
||||
|
@ -1307,11 +1341,12 @@ struct Optimizer {
|
|||
AsmOpCons* op_cons_[n];
|
||||
int offs_[n];
|
||||
StackTransform tr_[n];
|
||||
int mode_{0};
|
||||
Optimizer() {
|
||||
}
|
||||
Optimizer(bool debug) : debug_(debug) {
|
||||
Optimizer(bool debug, int mode = 0) : debug_(debug), mode_(mode) {
|
||||
}
|
||||
Optimizer(AsmOpConsList code, bool debug = false) : Optimizer(debug) {
|
||||
Optimizer(AsmOpConsList code, bool debug = false, int mode = 0) : Optimizer(debug, mode) {
|
||||
set_code(std::move(code));
|
||||
}
|
||||
void set_code(AsmOpConsList code_);
|
||||
|
@ -1327,12 +1362,15 @@ struct Optimizer {
|
|||
void show_head() const;
|
||||
void show_left() const;
|
||||
void show_right() const;
|
||||
bool find_const_op(int* op_idx, int cst);
|
||||
bool is_const_push_swap() const;
|
||||
bool rewrite_const_push_swap();
|
||||
bool is_const_push_xchgs();
|
||||
bool rewrite_const_push_xchgs();
|
||||
bool is_const_rot() const;
|
||||
bool rewrite_const_rot();
|
||||
bool is_const_rot(int* c) const;
|
||||
bool rewrite_const_rot(int c);
|
||||
bool is_const_pop(int* c, int* i) const;
|
||||
bool rewrite_const_pop(int c, int i);
|
||||
bool simple_rewrite(int p, AsmOp&& new_op);
|
||||
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2);
|
||||
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3);
|
||||
|
@ -1374,6 +1412,7 @@ struct Optimizer {
|
|||
bool is_blkswap(int* i, int* j);
|
||||
bool is_blkpush(int* i, int* j);
|
||||
bool is_blkdrop(int* i);
|
||||
bool is_blkdrop2(int* i, int* j);
|
||||
bool is_reverse(int* i, int* j);
|
||||
bool is_nip_seq(int* i, int* j);
|
||||
bool is_pop_blkdrop(int* i, int* k);
|
||||
|
@ -1381,8 +1420,8 @@ struct Optimizer {
|
|||
AsmOpConsList extract_code();
|
||||
};
|
||||
|
||||
AsmOpConsList optimize_code_head(AsmOpConsList op_list);
|
||||
AsmOpConsList optimize_code(AsmOpConsList op_list);
|
||||
AsmOpConsList optimize_code_head(AsmOpConsList op_list, int mode = 0);
|
||||
AsmOpConsList optimize_code(AsmOpConsList op_list, int mode);
|
||||
void optimize_code(AsmOpList& ops);
|
||||
|
||||
struct Stack {
|
||||
|
|
|
@ -14,10 +14,12 @@
|
|||
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 "func.h"
|
||||
|
||||
using namespace std::literals::string_literals;
|
||||
|
||||
namespace funC {
|
||||
|
||||
/*
|
||||
|
@ -164,7 +166,8 @@ bool Expr::deduce_type(const Lexem& lem) {
|
|||
|
||||
int Expr::define_new_vars(CodeBlob& code) {
|
||||
switch (cls) {
|
||||
case _Tuple:
|
||||
case _Tensor:
|
||||
case _MkTuple:
|
||||
case _TypeApply: {
|
||||
int res = 0;
|
||||
for (const auto& x : args) {
|
||||
|
@ -189,7 +192,8 @@ int Expr::define_new_vars(CodeBlob& code) {
|
|||
|
||||
int Expr::predefine_vars() {
|
||||
switch (cls) {
|
||||
case _Tuple:
|
||||
case _Tensor:
|
||||
case _MkTuple:
|
||||
case _TypeApply: {
|
||||
int res = 0;
|
||||
for (const auto& x : args) {
|
||||
|
@ -201,6 +205,7 @@ int Expr::predefine_vars() {
|
|||
if (!sym) {
|
||||
assert(val < 0 && here.defined());
|
||||
sym = sym::define_symbol(~val, false, here);
|
||||
// std::cerr << "predefining variable " << sym::symbols.get_name(~val) << std::endl;
|
||||
if (!sym) {
|
||||
throw src::ParseError{here, std::string{"redefined variable `"} + sym::symbols.get_name(~val) + "`"};
|
||||
}
|
||||
|
@ -216,12 +221,51 @@ var_idx_t Expr::new_tmp(CodeBlob& code) const {
|
|||
return code.create_tmp_var(e_type, &here);
|
||||
}
|
||||
|
||||
std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
|
||||
std::vector<var_idx_t> Expr::pre_compile_let(CodeBlob& code, Expr* lhs, Expr* rhs, const SrcLocation& here) {
|
||||
while (lhs->is_type_apply()) {
|
||||
lhs = lhs->args.at(0);
|
||||
}
|
||||
while (rhs->is_type_apply()) {
|
||||
rhs = rhs->args.at(0);
|
||||
}
|
||||
if (lhs->is_mktuple()) {
|
||||
if (rhs->is_mktuple()) {
|
||||
return pre_compile_let(code, lhs->args.at(0), rhs->args.at(0), here);
|
||||
}
|
||||
auto right = rhs->pre_compile(code);
|
||||
TypeExpr::remove_indirect(rhs->e_type);
|
||||
auto unpacked_type = rhs->e_type->args.at(0);
|
||||
std::vector<var_idx_t> tmp{code.create_tmp_var(unpacked_type, &rhs->here)};
|
||||
code.emplace_back(lhs->here, Op::_UnTuple, tmp, std::move(right));
|
||||
auto tvar = new Expr{_Var};
|
||||
tvar->set_val(tmp[0]);
|
||||
tvar->set_location(rhs->here);
|
||||
tvar->e_type = unpacked_type;
|
||||
pre_compile_let(code, lhs->args.at(0), tvar, here);
|
||||
return tmp;
|
||||
}
|
||||
auto right = rhs->pre_compile(code);
|
||||
if (lhs->cls == Expr::_GlobVar) {
|
||||
assert(lhs->sym);
|
||||
auto& op = code.emplace_back(here, Op::_SetGlob, std::vector<var_idx_t>{}, right, lhs->sym);
|
||||
op.flags |= Op::_Impure;
|
||||
} else {
|
||||
auto left = lhs->pre_compile(code, true);
|
||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||
}
|
||||
return right;
|
||||
}
|
||||
|
||||
std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code, bool lval) const {
|
||||
if (lval && !(cls == _Tensor || cls == _Var || cls == _Hole || cls == _TypeApply)) {
|
||||
std::cerr << "lvalue expression constructor is " << cls << std::endl;
|
||||
throw src::Fatal{"cannot compile lvalue expression with unknown constructor"};
|
||||
}
|
||||
switch (cls) {
|
||||
case _Tuple: {
|
||||
case _Tensor: {
|
||||
std::vector<var_idx_t> res;
|
||||
for (const auto& x : args) {
|
||||
auto add = x->pre_compile(code);
|
||||
auto add = x->pre_compile(code, lval);
|
||||
res.insert(res.end(), add.cbegin(), add.cend());
|
||||
}
|
||||
return res;
|
||||
|
@ -253,7 +297,7 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
|
|||
return rvect;
|
||||
}
|
||||
case _TypeApply:
|
||||
return args[0]->pre_compile(code);
|
||||
return args[0]->pre_compile(code, lval);
|
||||
case _Var:
|
||||
case _Hole:
|
||||
return {val};
|
||||
|
@ -289,25 +333,22 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
|
|||
return rvect;
|
||||
}
|
||||
case _Letop: {
|
||||
auto right = args[1]->pre_compile(code);
|
||||
if (args[0]->cls == Expr::_GlobVar) {
|
||||
assert(args[0]->sym);
|
||||
auto& op = code.emplace_back(here, Op::_SetGlob, std::vector<var_idx_t>{}, right, args[0]->sym);
|
||||
op.flags |= Op::_Impure;
|
||||
} else {
|
||||
auto left = args[0]->pre_compile(code);
|
||||
code.emplace_back(here, Op::_Let, std::move(left), right);
|
||||
}
|
||||
return right;
|
||||
return pre_compile_let(code, args.at(0), args.at(1), here);
|
||||
}
|
||||
case _LetFirst: {
|
||||
auto rvect = new_tmp_vect(code);
|
||||
auto right = args[1]->pre_compile(code);
|
||||
auto left = args[0]->pre_compile(code);
|
||||
auto left = args[0]->pre_compile(code, true);
|
||||
left.push_back(rvect[0]);
|
||||
code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
|
||||
return rvect;
|
||||
}
|
||||
case _MkTuple: {
|
||||
auto left = new_tmp_vect(code);
|
||||
auto right = args[0]->pre_compile(code);
|
||||
code.emplace_back(here, Op::_Tuple, left, std::move(right));
|
||||
return left;
|
||||
}
|
||||
case _CondExpr: {
|
||||
auto cond = args[0]->pre_compile(code);
|
||||
assert(cond.size() == 1);
|
||||
|
|
|
@ -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 "func.h"
|
||||
|
||||
|
@ -46,6 +46,8 @@ void define_keywords() {
|
|||
.add_kw_char(';')
|
||||
.add_kw_char('(')
|
||||
.add_kw_char(')')
|
||||
.add_kw_char('[')
|
||||
.add_kw_char(']')
|
||||
.add_kw_char('{')
|
||||
.add_kw_char('}')
|
||||
.add_kw_char('=')
|
||||
|
@ -118,6 +120,7 @@ void define_keywords() {
|
|||
.add_keyword("impure", Kw::_Impure)
|
||||
.add_keyword("inline", Kw::_Inline)
|
||||
.add_keyword("inline_ref", Kw::_InlineRef)
|
||||
.add_keyword("auto_apply", Kw::_AutoApply)
|
||||
.add_keyword("method_id", Kw::_MethodId)
|
||||
.add_keyword("operator", Kw::_Operator)
|
||||
.add_keyword("infix", Kw::_Infix)
|
||||
|
|
|
@ -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 "func.h"
|
||||
|
||||
|
@ -134,6 +134,16 @@ bool Optimizer::say(std::string str) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::find_const_op(int* op_idx, int cst) {
|
||||
for (int i = 0; i < l2_; i++) {
|
||||
if (op_[i]->is_gconst() && tr_[i].get(0) == cst) {
|
||||
*op_idx = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_push_swap() const {
|
||||
return l_ >= 3 && op_[0]->is_gconst() && op_[1]->is_push() && op_[1]->a >= 1 && op_[2]->is_swap();
|
||||
}
|
||||
|
@ -150,21 +160,44 @@ bool Optimizer::rewrite_const_push_swap() {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_rot() const {
|
||||
return pb_ >= 3 && pb_ <= l2_ && op_[0]->is_gconst() && tr_[pb_ - 1].is_const_rot();
|
||||
bool Optimizer::is_const_rot(int* c) const {
|
||||
return pb_ >= 3 && pb_ <= l2_ && tr_[pb_ - 1].is_const_rot(c);
|
||||
}
|
||||
|
||||
bool Optimizer::rewrite_const_rot() {
|
||||
bool Optimizer::rewrite_const_rot(int c) {
|
||||
p_ = pb_;
|
||||
q_ = 2;
|
||||
int idx = -1;
|
||||
if (!(p_ >= 2 && find_const_op(&idx, c) && idx < p_)) {
|
||||
return false;
|
||||
}
|
||||
show_left();
|
||||
oq_[0] = std::move(op_[0]);
|
||||
oq_[1] = std::move(op_[1]);
|
||||
oq_[0] = std::move(op_[idx]);
|
||||
oq_[1] = std::move(op_[idx ? 0 : 1]);
|
||||
*oq_[1] = AsmOp::Custom("ROT", 3, 3);
|
||||
show_right();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_pop(int* c, int* i) const {
|
||||
return pb_ >= 3 && pb_ <= l2_ && tr_[pb_ - 1].is_const_pop(c, i);
|
||||
}
|
||||
|
||||
bool Optimizer::rewrite_const_pop(int c, int i) {
|
||||
p_ = pb_;
|
||||
q_ = 2;
|
||||
int idx = -1;
|
||||
if (!(p_ >= 2 && find_const_op(&idx, c) && idx < p_)) {
|
||||
return false;
|
||||
}
|
||||
show_left();
|
||||
oq_[0] = std::move(op_[idx]);
|
||||
oq_[1] = std::move(op_[idx ? 0 : 1]);
|
||||
*oq_[1] = AsmOp::Pop(i);
|
||||
show_right();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Optimizer::is_const_push_xchgs() {
|
||||
if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) {
|
||||
return false;
|
||||
|
@ -424,6 +457,10 @@ bool Optimizer::is_blkdrop(int* i) {
|
|||
return is_pred([i](const auto& t) { return t.is_blkdrop(i) && *i > 0 && *i < 16; });
|
||||
}
|
||||
|
||||
bool Optimizer::is_blkdrop2(int* i, int* j) {
|
||||
return is_pred([i, j](const auto& t) { return t.is_blkdrop2(i, j) && *i > 0 && *i < 16 && *j > 0 && *j < 16; });
|
||||
}
|
||||
|
||||
bool Optimizer::is_reverse(int* i, int* j) {
|
||||
return is_pred([i, j](const auto& t) { return t.is_reverse(i, j) && *i >= 2 && *i <= 17 && *j < 16; });
|
||||
}
|
||||
|
@ -488,38 +525,42 @@ bool Optimizer::find_at_least(int pb) {
|
|||
p_ = q_ = 0;
|
||||
pb_ = pb;
|
||||
// show_stack_transforms();
|
||||
int i = -100, j = -100, k = -100;
|
||||
int i = -100, j = -100, k = -100, c = 0;
|
||||
return (is_const_push_swap() && 3 >= pb && rewrite_const_push_swap()) || (is_nop() && simple_rewrite_nop()) ||
|
||||
(is_const_rot() && rewrite_const_rot()) || (is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
|
||||
(!(mode_ & 1) && is_const_rot(&c) && rewrite_const_rot(c)) ||
|
||||
(is_const_push_xchgs() && rewrite_const_push_xchgs()) || (is_const_pop(&c, &i) && rewrite_const_pop(c, i)) ||
|
||||
(is_xchg(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && simple_rewrite(AsmOp::Push(i))) ||
|
||||
(is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) || (is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) ||
|
||||
(is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
|
||||
(is_2dup() && simple_rewrite(AsmOp::Custom("2DUP", 2, 4))) ||
|
||||
(is_2swap() && simple_rewrite(AsmOp::Custom("2SWAP", 2, 4))) ||
|
||||
(is_2over() && simple_rewrite(AsmOp::Custom("2OVER", 2, 4))) ||
|
||||
(is_tuck() && simple_rewrite(AsmOp::Custom("TUCK", 2, 3))) ||
|
||||
(is_2drop() && simple_rewrite(AsmOp::Custom("2DROP", 2, 0))) ||
|
||||
(is_xchg2(&i, &j) && simple_rewrite(AsmOp::Xchg2(i, j))) ||
|
||||
(is_xcpu(&i, &j) && simple_rewrite(AsmOp::XcPu(i, j))) ||
|
||||
(is_puxc(&i, &j) && simple_rewrite(AsmOp::PuXc(i, j))) ||
|
||||
(is_push2(&i, &j) && simple_rewrite(AsmOp::Push2(i, j))) ||
|
||||
(is_blkswap(&i, &j) && simple_rewrite(AsmOp::BlkSwap(i, j))) ||
|
||||
(is_blkpush(&i, &j) && simple_rewrite(AsmOp::BlkPush(i, j))) ||
|
||||
(is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
|
||||
(is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
|
||||
(is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
|
||||
(is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
|
||||
(is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
|
||||
? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
|
||||
: simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||
|
||||
(is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
|
||||
(is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
|
||||
(is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||
|
||||
(is_xcpu2(&i, &j, &k) && simple_rewrite(AsmOp::XcPu2(i, j, k))) ||
|
||||
(is_puxc2(&i, &j, &k) && simple_rewrite(AsmOp::PuXc2(i, j, k))) ||
|
||||
(is_puxcpu(&i, &j, &k) && simple_rewrite(AsmOp::PuXcPu(i, j, k))) ||
|
||||
(is_pu2xc(&i, &j, &k) && simple_rewrite(AsmOp::Pu2Xc(i, j, k))) ||
|
||||
(is_push3(&i, &j, &k) && simple_rewrite(AsmOp::Push3(i, j, k)));
|
||||
(is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) ||
|
||||
(!(mode_ & 1) &&
|
||||
((is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) ||
|
||||
(is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
|
||||
(is_2dup() && simple_rewrite(AsmOp::Custom("2DUP", 2, 4))) ||
|
||||
(is_2swap() && simple_rewrite(AsmOp::Custom("2SWAP", 2, 4))) ||
|
||||
(is_2over() && simple_rewrite(AsmOp::Custom("2OVER", 2, 4))) ||
|
||||
(is_tuck() && simple_rewrite(AsmOp::Custom("TUCK", 2, 3))) ||
|
||||
(is_2drop() && simple_rewrite(AsmOp::Custom("2DROP", 2, 0))) ||
|
||||
(is_xchg2(&i, &j) && simple_rewrite(AsmOp::Xchg2(i, j))) ||
|
||||
(is_xcpu(&i, &j) && simple_rewrite(AsmOp::XcPu(i, j))) ||
|
||||
(is_puxc(&i, &j) && simple_rewrite(AsmOp::PuXc(i, j))) ||
|
||||
(is_push2(&i, &j) && simple_rewrite(AsmOp::Push2(i, j))) ||
|
||||
(is_blkswap(&i, &j) && simple_rewrite(AsmOp::BlkSwap(i, j))) ||
|
||||
(is_blkpush(&i, &j) && simple_rewrite(AsmOp::BlkPush(i, j))) ||
|
||||
(is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
|
||||
(is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
|
||||
(is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
|
||||
(is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
|
||||
(is_blkdrop2(&i, &j) && simple_rewrite(AsmOp::BlkDrop2(i, j))) ||
|
||||
(is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
|
||||
? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
|
||||
: simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||
|
||||
(is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
|
||||
(is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
|
||||
(is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||
|
||||
(is_xcpu2(&i, &j, &k) && simple_rewrite(AsmOp::XcPu2(i, j, k))) ||
|
||||
(is_puxc2(&i, &j, &k) && simple_rewrite(AsmOp::PuXc2(i, j, k))) ||
|
||||
(is_puxcpu(&i, &j, &k) && simple_rewrite(AsmOp::PuXcPu(i, j, k))) ||
|
||||
(is_pu2xc(&i, &j, &k) && simple_rewrite(AsmOp::Pu2Xc(i, j, k))) ||
|
||||
(is_push3(&i, &j, &k) && simple_rewrite(AsmOp::Push3(i, j, k)))));
|
||||
}
|
||||
|
||||
bool Optimizer::find() {
|
||||
|
@ -544,17 +585,17 @@ bool Optimizer::optimize() {
|
|||
return f;
|
||||
}
|
||||
|
||||
AsmOpConsList optimize_code_head(AsmOpConsList op_list) {
|
||||
Optimizer opt(std::move(op_list), op_rewrite_comments);
|
||||
AsmOpConsList optimize_code_head(AsmOpConsList op_list, int mode) {
|
||||
Optimizer opt(std::move(op_list), op_rewrite_comments, mode);
|
||||
opt.optimize();
|
||||
return opt.extract_code();
|
||||
}
|
||||
|
||||
AsmOpConsList optimize_code(AsmOpConsList op_list) {
|
||||
AsmOpConsList optimize_code(AsmOpConsList op_list, int mode) {
|
||||
std::vector<std::unique_ptr<AsmOp>> v;
|
||||
while (op_list) {
|
||||
if (!op_list->car->is_comment()) {
|
||||
op_list = optimize_code_head(std::move(op_list));
|
||||
op_list = optimize_code_head(std::move(op_list), mode);
|
||||
}
|
||||
if (op_list) {
|
||||
v.push_back(std::move(op_list->car));
|
||||
|
@ -568,11 +609,14 @@ AsmOpConsList optimize_code(AsmOpConsList op_list) {
|
|||
}
|
||||
|
||||
void optimize_code(AsmOpList& ops) {
|
||||
std::unique_ptr<AsmOpCons> op_list;
|
||||
AsmOpConsList op_list;
|
||||
for (auto it = ops.list_.rbegin(); it < ops.list_.rend(); ++it) {
|
||||
op_list = AsmOpCons::cons(std::make_unique<AsmOp>(std::move(*it)), std::move(op_list));
|
||||
}
|
||||
op_list = optimize_code(std::move(op_list));
|
||||
op_list = optimize_code(std::move(op_list), 1);
|
||||
op_list = optimize_code(std::move(op_list), 1);
|
||||
op_list = optimize_code(std::move(op_list), 0);
|
||||
op_list = optimize_code(std::move(op_list), 0);
|
||||
ops.list_.clear();
|
||||
while (op_list) {
|
||||
ops.list_.push_back(std::move(*(op_list->car)));
|
||||
|
|
|
@ -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 "func.h"
|
||||
#include "td/utils/crypto.h"
|
||||
|
@ -100,14 +100,21 @@ TypeExpr* parse_type1(Lexer& lex) {
|
|||
lex.cur().error_at("`", "` is not a type identifier");
|
||||
}
|
||||
}
|
||||
lex.expect('(');
|
||||
if (lex.tp() == ')') {
|
||||
int c;
|
||||
if (lex.tp() == '[') {
|
||||
lex.next();
|
||||
return TypeExpr::new_unit();
|
||||
c = ']';
|
||||
} else {
|
||||
lex.expect('(');
|
||||
c = ')';
|
||||
}
|
||||
if (lex.tp() == c) {
|
||||
lex.next();
|
||||
return c == ')' ? TypeExpr::new_unit() : TypeExpr::new_tuple({});
|
||||
}
|
||||
auto t1 = parse_type(lex);
|
||||
if (lex.tp() != ',') {
|
||||
lex.expect(')');
|
||||
lex.expect(c);
|
||||
return t1;
|
||||
}
|
||||
std::vector<TypeExpr*> tlist{1, t1};
|
||||
|
@ -115,8 +122,8 @@ TypeExpr* parse_type1(Lexer& lex) {
|
|||
lex.next();
|
||||
tlist.push_back(parse_type(lex));
|
||||
}
|
||||
lex.expect(')');
|
||||
return TypeExpr::new_tensor(std::move(tlist));
|
||||
lex.expect(c);
|
||||
return c == ')' ? TypeExpr::new_tensor(std::move(tlist)) : TypeExpr::new_tuple(std::move(tlist));
|
||||
}
|
||||
|
||||
TypeExpr* parse_type(Lexer& lex) {
|
||||
|
@ -302,7 +309,7 @@ bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) {
|
|||
Expr* make_func_apply(Expr* fun, Expr* x) {
|
||||
Expr* res;
|
||||
if (fun->cls == Expr::_Glob) {
|
||||
if (x->cls == Expr::_Tuple) {
|
||||
if (x->cls == Expr::_Tensor) {
|
||||
res = new Expr{Expr::_Apply, fun->sym, x->args};
|
||||
} else {
|
||||
res = new Expr{Expr::_Apply, fun->sym, {x}};
|
||||
|
@ -317,28 +324,36 @@ Expr* make_func_apply(Expr* fun, Expr* x) {
|
|||
|
||||
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
|
||||
|
||||
// parse ( E { , E } ) | () | id | num | _
|
||||
// parse ( E { , E } ) | () | [ E { , E } ] | [] | id | num | _
|
||||
Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
||||
if (lex.tp() == '(') {
|
||||
if (lex.tp() == '(' || lex.tp() == '[') {
|
||||
bool tf = (lex.tp() == '[');
|
||||
int clbr = (tf ? ']' : ')');
|
||||
SrcLocation loc{lex.cur().loc};
|
||||
lex.next();
|
||||
if (lex.tp() == ')') {
|
||||
if (lex.tp() == clbr) {
|
||||
lex.next();
|
||||
Expr* res = new Expr{Expr::_Tuple, {}};
|
||||
Expr* res = new Expr{Expr::_Tensor, {}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
res->here = loc;
|
||||
res->e_type = TypeExpr::new_unit();
|
||||
if (tf) {
|
||||
res = new Expr{Expr::_MkTuple, {res}};
|
||||
res->flags = Expr::_IsRvalue;
|
||||
res->here = loc;
|
||||
res->e_type = TypeExpr::new_tuple(res->args.at(0)->e_type);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
Expr* res = parse_expr(lex, code, nv);
|
||||
if (lex.tp() != ',') {
|
||||
lex.expect(')');
|
||||
lex.expect(clbr);
|
||||
return res;
|
||||
}
|
||||
std::vector<TypeExpr*> type_list;
|
||||
type_list.push_back(res->e_type);
|
||||
int f = res->flags;
|
||||
res = new Expr{Expr::_Tuple, {res}};
|
||||
res = new Expr{Expr::_Tensor, {res}};
|
||||
while (lex.tp() == ',') {
|
||||
lex.next();
|
||||
auto x = parse_expr(lex, code, nv);
|
||||
|
@ -351,8 +366,14 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
}
|
||||
res->here = loc;
|
||||
res->flags = f;
|
||||
res->e_type = TypeExpr::new_tensor(std::move(type_list));
|
||||
lex.expect(')');
|
||||
res->e_type = TypeExpr::new_tensor(std::move(type_list), !tf);
|
||||
if (tf) {
|
||||
res = new Expr{Expr::_MkTuple, {res}};
|
||||
res->flags = f;
|
||||
res->here = loc;
|
||||
res->e_type = TypeExpr::new_tuple(res->args.at(0)->e_type);
|
||||
}
|
||||
lex.expect(clbr);
|
||||
return res;
|
||||
}
|
||||
int t = lex.tp();
|
||||
|
@ -382,7 +403,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
lex.next();
|
||||
return res;
|
||||
}
|
||||
if (t == _Int || t == _Cell || t == _Slice || t == _Builder || t == _Cont || t == _Type) {
|
||||
if (t == _Int || t == _Cell || t == _Slice || t == _Builder || t == _Cont || t == _Type || t == _Tuple) {
|
||||
Expr* res = new Expr{Expr::_Type, lex.cur().loc};
|
||||
res->flags = Expr::_IsType;
|
||||
res->e_type = TypeExpr::new_atomic(t);
|
||||
|
@ -458,7 +479,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
// parse E { E }
|
||||
Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) {
|
||||
Expr* res = parse_expr100(lex, code, nv);
|
||||
while (lex.tp() == '(' || (lex.tp() == _Ident && !is_special_ident(lex.cur().val))) {
|
||||
while (lex.tp() == '(' || lex.tp() == '[' || (lex.tp() == _Ident && !is_special_ident(lex.cur().val))) {
|
||||
if (res->is_type()) {
|
||||
Expr* x = parse_expr100(lex, code, true);
|
||||
x->chk_lvalue(lex.cur()); // chk_lrvalue() ?
|
||||
|
@ -523,7 +544,7 @@ Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) {
|
|||
lex.next();
|
||||
auto x = parse_expr100(lex, code, false);
|
||||
x->chk_rvalue(lex.cur());
|
||||
if (x->cls == Expr::_Tuple) {
|
||||
if (x->cls == Expr::_Tensor) {
|
||||
res = new Expr{Expr::_Apply, name, {obj}};
|
||||
res->args.insert(res->args.end(), x->args.begin(), x->args.end());
|
||||
} else {
|
||||
|
@ -1269,7 +1290,7 @@ void parse_func_def(Lexer& lex) {
|
|||
|
||||
bool parse_source(std::istream* is, const src::FileDescr* fdescr) {
|
||||
src::SourceReader reader{is, fdescr};
|
||||
Lexer lex{reader, true};
|
||||
Lexer lex{reader, true, ";,()[] ~."};
|
||||
while (lex.tp() != _Eof) {
|
||||
if (lex.tp() == _Global) {
|
||||
parse_global_var_decls(lex);
|
||||
|
|
|
@ -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 "func.h"
|
||||
|
||||
|
@ -742,6 +742,28 @@ bool StackTransform::is_blkdrop(int *i) const {
|
|||
return false;
|
||||
}
|
||||
|
||||
// 0 1 .. j-1 j+i j+i+1 ...
|
||||
bool StackTransform::is_blkdrop2(int i, int j) const {
|
||||
if (!is_valid() || d != i || i <= 0 || j < 0 || dp < i + j || n != j || !is_trivial_after(j)) {
|
||||
return false;
|
||||
}
|
||||
for (int s = 0; s < j; s++) {
|
||||
if (get(s) != s) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StackTransform::is_blkdrop2(int *i, int *j) const {
|
||||
if (is_valid() && is_blkdrop2(d, n)) {
|
||||
*i = d;
|
||||
*j = n;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// equivalent to i times PUSH s(j)
|
||||
bool StackTransform::is_blkpush(int *i, int *j) const {
|
||||
if (!is_valid() || d >= 0) {
|
||||
|
@ -846,8 +868,27 @@ bool StackTransform::is_2pop_blkdrop(int *i, int *j, int *k) const {
|
|||
}
|
||||
|
||||
// PUSHCONST c ; ROT == 1 -1000 0 2 3
|
||||
bool StackTransform::is_const_rot() const {
|
||||
return is_valid() && d == -1 && is_trivial_after(3) && get(0) == 1 && get(1) <= c_start && get(2) == 0;
|
||||
bool StackTransform::is_const_rot(int c) const {
|
||||
return is_valid() && d == -1 && is_trivial_after(3) && get(0) == 1 && c <= c_start && get(1) == c && get(2) == 0;
|
||||
}
|
||||
|
||||
bool StackTransform::is_const_rot(int *c) const {
|
||||
return is_valid() && (*c = get(1)) <= c_start && is_const_rot(*c);
|
||||
}
|
||||
|
||||
// PUSHCONST c ; POP s(i) == 0 1 .. i-1 -1000 i+1 ...
|
||||
bool StackTransform::is_const_pop(int c, int i) const {
|
||||
return is_valid() && !d && n == 1 && i > 0 && c <= c_start && get(i - 1) == c;
|
||||
}
|
||||
|
||||
bool StackTransform::is_const_pop(int *c, int *i) const {
|
||||
if (is_valid() && !d && n == 1 && A[0].second <= c_start) {
|
||||
*i = A[0].first + 1;
|
||||
*c = A[0].second;
|
||||
return is_const_pop(*c, *i);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void StackTransform::show(std::ostream &os, int mode) 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 "func.h"
|
||||
|
||||
|
@ -47,6 +47,12 @@ void TypeExpr::compute_width() {
|
|||
maxw = w_inf;
|
||||
}
|
||||
break;
|
||||
case te_Tuple:
|
||||
minw = maxw = 1;
|
||||
for (TypeExpr* arg : args) {
|
||||
arg->compute_width();
|
||||
}
|
||||
break;
|
||||
case te_Indirect:
|
||||
minw = args[0]->minw;
|
||||
maxw = args[0]->maxw;
|
||||
|
@ -84,6 +90,14 @@ bool TypeExpr::recompute_width() {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
case te_Tuple: {
|
||||
for (TypeExpr* arg : args) {
|
||||
if (arg->minw > 1 || arg->maxw < 1 || arg->minw > arg->maxw) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
@ -233,7 +247,9 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) {
|
|||
}
|
||||
}
|
||||
case te_Tensor: {
|
||||
os << "(";
|
||||
if (lex_level > -127) {
|
||||
os << "(";
|
||||
}
|
||||
auto c = args.size();
|
||||
if (c) {
|
||||
for (const auto& x : args) {
|
||||
|
@ -243,7 +259,25 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return os << ")";
|
||||
if (lex_level > -127) {
|
||||
os << ")";
|
||||
}
|
||||
return os;
|
||||
}
|
||||
case te_Tuple: {
|
||||
os << "[";
|
||||
auto c = args.size();
|
||||
if (c == 1 && args[0]->constr == te_Tensor) {
|
||||
args[0]->print(os, -127);
|
||||
} else if (c) {
|
||||
for (const auto& x : args) {
|
||||
x->print(os);
|
||||
if (--c) {
|
||||
os << ", ";
|
||||
}
|
||||
}
|
||||
}
|
||||
return os << "]";
|
||||
}
|
||||
case te_Map: {
|
||||
assert(args.size() == 2);
|
||||
|
@ -312,7 +346,7 @@ void check_update_widths(TypeExpr* te1, TypeExpr* te2) {
|
|||
check_width_compat(te1, te2);
|
||||
te1->minw = te2->minw = std::max(te1->minw, te2->minw);
|
||||
te1->maxw = te2->maxw = std::min(te1->maxw, te2->maxw);
|
||||
assert(te1->minw <= te2->minw);
|
||||
assert(te1->minw <= te1->maxw);
|
||||
}
|
||||
|
||||
void unify(TypeExpr*& te1, TypeExpr*& te2) {
|
||||
|
|
474
crypto/smartcont/dns-auto-code.fc
Normal file
474
crypto/smartcont/dns-auto-code.fc
Normal file
|
@ -0,0 +1,474 @@
|
|||
{-
|
||||
Adapted from original version written by:
|
||||
/------------------------------------------------------------------------\
|
||||
| Created for: Telegram (Open Network) Blockchain Contest |
|
||||
| Task 2: DNS Resolver (Automatically registering) |
|
||||
>------------------------------------------------------------------------<
|
||||
| Author: Oleksandr Murzin (tg: @skydev / em: alexhacker64@gmail.com) |
|
||||
| October 2019 |
|
||||
\------------------------------------------------------------------------/
|
||||
-}
|
||||
|
||||
;;===========================================================================;;
|
||||
;; Utility functions ;;
|
||||
;;===========================================================================;;
|
||||
|
||||
{-
|
||||
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]
|
||||
[UInt<32b>:stdperiod] [Gram:PPReg] [Gram:PPCell] [Gram:PPBit]
|
||||
[UInt<32b>:lasthousekeeping]
|
||||
<CatTable> := HashmapE 16 ^DNSRecord
|
||||
|
||||
STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value)
|
||||
#Zeros allows to simultaneously store, for example, com\0 and com\0google\0
|
||||
That will be stored as \1com\0 and \2com\0google\0 (pfx tree has restricitons)
|
||||
This will allow to resolve more specific requests to subdomains, and resort
|
||||
to parent domain next resolver lookup if subdomain is not found
|
||||
com\0goo\0 lookup will, for example look up \2com\0goo\0 and then
|
||||
\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 {
|
||||
slice cs = get_data().begin_parse();
|
||||
return (
|
||||
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_grams(), ;; standard payment for registering a new subdomain
|
||||
cs~load_grams(), ;; price paid for each cell (PPC)
|
||||
cs~load_grams() ], ;; and bit (PPB)
|
||||
cs~load_uint(32), ;; next housekeeping to be done at
|
||||
cs~load_uint(32) ;; last housekeeping done at
|
||||
);
|
||||
}
|
||||
|
||||
(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());
|
||||
}
|
||||
|
||||
() store_data(cell dd, cell gc, prices, int nhk, int lhk) impure {
|
||||
var [sp, ppr, ppc, ppb] = prices;
|
||||
set_data(begin_cell()
|
||||
.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_grams(ppr) ;; price per registration
|
||||
.store_grams(ppc) ;; price per cell
|
||||
.store_grams(ppb) ;; price per bit
|
||||
.store_uint(nhk, 32) ;; next housekeeping
|
||||
.store_uint(lhk, 32) ;; last housekeeping
|
||||
.end_cell());
|
||||
}
|
||||
|
||||
global var query_info;
|
||||
|
||||
() send_message(slice addr, int tag, int query_id,
|
||||
int body, int grams, int mode) impure {
|
||||
;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
;; src:MsgAddress -> 011000 0x18
|
||||
var msg = begin_cell()
|
||||
.store_uint (0x18, 6)
|
||||
.store_slice(addr)
|
||||
.store_grams(grams)
|
||||
.store_uint (0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
|
||||
.store_uint (tag, 32)
|
||||
.store_uint (query_id, 64);
|
||||
if (body >= 0) {
|
||||
msg~store_uint(body, 32);
|
||||
}
|
||||
send_raw_message(msg.end_cell(), mode);
|
||||
}
|
||||
|
||||
() send_error(int error_code) impure {
|
||||
var (addr, query_id, op) = query_info;
|
||||
return send_message(addr, error_code, query_id, op, 0, 64);
|
||||
}
|
||||
|
||||
() send_ok(int price) impure {
|
||||
raw_reserve(price, 4);
|
||||
var (addr, query_id, op) = query_info;
|
||||
return send_message(addr, 0xef6b6179, query_id, op, 0, 128);
|
||||
}
|
||||
|
||||
() housekeeping(cell dd, cell gc, prices, int nhk, int lhk) 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);
|
||||
}
|
||||
;; 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);
|
||||
if (nhk < n) {
|
||||
slice sname = name.begin_parse();
|
||||
(_, slice val, _, found?) = dd.pfxdict_get?(1023, sname);
|
||||
if (found?) {
|
||||
int exp = val.preload_uint(32);
|
||||
if (exp <= n) {
|
||||
dd~pfxdict_delete?(1023, sname);
|
||||
}
|
||||
}
|
||||
gc~udict_delete?(64, mkey);
|
||||
(mkey, _, found?) = gc.udict_get_min_ref?(64);
|
||||
nhk = (found? ? mkey >> 32 : 0xffffffff);
|
||||
}
|
||||
}
|
||||
store_data(dd, gc, prices, nhk, n);
|
||||
}
|
||||
|
||||
int _calcprice(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;
|
||||
}
|
||||
|
||||
int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
|
||||
if (cat_table.null?()) { ;; domain not found: return notf | 2^31
|
||||
return 0xee6f7466;
|
||||
}
|
||||
cell cown = cat_table.idict_get_ref(16, -2);
|
||||
if (cown.null?()) { ;; no owner on this domain: no-2
|
||||
return 0xee6f2d32;
|
||||
}
|
||||
var ERR_BAD2 = 0xe2616432;
|
||||
slice sown = cown.begin_parse();
|
||||
if (sown.slice_bits() < 16 + 3 + 8 + 256) { ;; bad owner record: bad2
|
||||
return ERR_BAD2;
|
||||
}
|
||||
if (sown~load_uint(16 + 3) != 0x9fd3 * 8 + 4) {
|
||||
return ERR_BAD2;
|
||||
}
|
||||
(int owner_wc, int owner_addr) = (sown~load_int(8), sown.preload_uint(256));
|
||||
if ((owner_wc != src_wc) | (owner_addr != src_addr)) { ;; not owner: nown
|
||||
return 0xee6f776e;
|
||||
}
|
||||
return 0; ;; ok
|
||||
}
|
||||
|
||||
;;===========================================================================;;
|
||||
;; Internal message handler (Code 0) ;;
|
||||
;;===========================================================================;;
|
||||
|
||||
{-
|
||||
Internal message cell structure:
|
||||
8 4 2 1
|
||||
int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
src:MsgAddressInt dest:MsgAddressInt
|
||||
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]
|
||||
(if not prolong: [Ref<1r>:value->CatTable])
|
||||
|
||||
-}
|
||||
|
||||
;; 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 {
|
||||
;; this time very interested in internal messages
|
||||
if (in_msg.slice_bits() < 32) {
|
||||
return (); ;; simple transfer or short
|
||||
}
|
||||
slice cs = in_msg_cell.begin_parse();
|
||||
int flags = cs~load_uint(4);
|
||||
if (flags & 1) {
|
||||
return (); ;; bounced messages
|
||||
}
|
||||
slice s_addr = cs~load_msg_addr();
|
||||
(int src_wc, int src_addr) = s_addr.parse_std_addr();
|
||||
int op = in_msg~load_uint(32);
|
||||
ifnot (op) {
|
||||
return (); ;; simple transfer with comment
|
||||
}
|
||||
int query_id = 0;
|
||||
if (in_msg.slice_bits() >= 64) {
|
||||
query_id = in_msg~load_uint(64);
|
||||
}
|
||||
|
||||
query_info = (s_addr, query_id, op);
|
||||
|
||||
if (op & (1 << 31)) {
|
||||
return (); ;; an answer to our query
|
||||
}
|
||||
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();
|
||||
|
||||
if (qt == 8) { ;; 0x676f6763 -> GO, GC! go!!!
|
||||
;; Manual garbage collection iteration
|
||||
housekeeping(domdata, gc, prices, nhk, 1); ;; forced
|
||||
return send_error(0xef6b6179);
|
||||
}
|
||||
|
||||
slice name = null();
|
||||
cell name_cell = in_msg~load_maybe_ref();
|
||||
if (name_cell.null?()) {
|
||||
int bytes = in_msg~load_uint(6);
|
||||
name = in_msg~load_bits(bytes * 8);
|
||||
} else {
|
||||
name = name_cell.begin_parse();
|
||||
}
|
||||
|
||||
(_, int name_last_byte) = name.slice_last(8).load_uint(8);
|
||||
if (name_last_byte != 0) { ;; name must end with \0! no\0 error
|
||||
return send_error(0xee6f5c30);
|
||||
}
|
||||
|
||||
int zeros = 0;
|
||||
slice cname = name;
|
||||
repeat (cname.slice_bits() ^>> 3) {
|
||||
int c = cname~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();
|
||||
|
||||
(slice pfx, slice val, slice tail, int found?) = domdata.pfxdict_get?(1023, name);
|
||||
int n = now();
|
||||
cell cat_table = null();
|
||||
int exp = 0;
|
||||
|
||||
if (found?) {
|
||||
exp = val~load_uint(32);
|
||||
if (n > exp) { ;; expired domains behave as not registered
|
||||
found? = false;
|
||||
} else {
|
||||
cat_table = val.preload_ref();
|
||||
}
|
||||
}
|
||||
|
||||
;; ##########################################################################
|
||||
|
||||
int err = 0;
|
||||
if (qt != 1) { ;; not a "register", check that domain exists and is controlled by correct smc
|
||||
err = check_owner(cat_table, src_wc, src_addr);
|
||||
}
|
||||
if (err) {
|
||||
return send_error(err);
|
||||
}
|
||||
|
||||
;; ##########################################################################
|
||||
|
||||
;; load desired data (reuse old for a "prolong" operation)
|
||||
cell data = null();
|
||||
|
||||
if (qt != 2) { ;; not a "prolong", load data dictionary
|
||||
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));
|
||||
}
|
||||
} else {
|
||||
data = cat_table;
|
||||
}
|
||||
|
||||
;; compute action price
|
||||
var [stdper, ppr, ppc, ppb] = prices;
|
||||
int price = _calcprice(data, ppc, ppb) + (ppr & (qt != 4));
|
||||
if (msg_value - (1 << 30) < price) { ;; gr<p: grams - GR$1 < price
|
||||
return send_error(0xe7723c70);
|
||||
}
|
||||
|
||||
;; load desired expiration unixtime
|
||||
int req_expires_at = in_msg~load_uint(32);
|
||||
|
||||
;; ##########################################################################
|
||||
if (qt == 2) { ;; 0x70726f6c -> prol | prolong domain
|
||||
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
|
||||
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());
|
||||
|
||||
housekeeping(domdata, gc, prices, nhk, lhk);
|
||||
return send_ok(price);
|
||||
}
|
||||
|
||||
;; ##########################################################################
|
||||
if (qt == 1) { ;; 0x72656764 -> regd | register domain
|
||||
if (found?) { ;; domain already exists: return alre | 2^31
|
||||
return send_error(0xe16c7265);
|
||||
}
|
||||
int expires_at = n + stdper;
|
||||
slice value = begin_cell().store_uint(expires_at, 32).store_ref(data).end_cell().begin_parse();
|
||||
ifnot (domdata~pfxdict_set?(1023, name, 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());
|
||||
;; 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);
|
||||
return send_ok(price);
|
||||
}
|
||||
|
||||
;; ##########################################################################
|
||||
if (qt == 4) { ;; 0x75706464 -> updd | update domain (data)
|
||||
slice value = begin_cell().store_uint(exp, 32).store_ref(data).end_cell().begin_parse();
|
||||
|
||||
ifnot (domdata~pfxdict_set?(1023, name, value)) { ;; Set ERR | 2^31
|
||||
return send_error(0xf3657272);
|
||||
}
|
||||
;; no need to update gc here
|
||||
housekeeping(domdata, gc, prices, nhk, lhk);
|
||||
return send_ok(price);
|
||||
}
|
||||
;; ##########################################################################
|
||||
|
||||
return (); ;; should NEVER reach this part of code!
|
||||
}
|
||||
|
||||
;;===========================================================================;;
|
||||
;; External message handler (Code -1) ;;
|
||||
;;===========================================================================;;
|
||||
|
||||
() 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();
|
||||
ifnot (lhk) {
|
||||
accept_message();
|
||||
store_data(dd, gc, prices, 0, now());
|
||||
}
|
||||
}
|
||||
|
||||
;;===========================================================================;;
|
||||
;; Getter methods ;;
|
||||
;;===========================================================================;;
|
||||
|
||||
(int, cell, int, slice) dnsdictlookup(slice subdomain, int nowtime) inline_ref {
|
||||
int bits = subdomain.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
|
||||
.store_uint(0, 8).end_cell().begin_parse();
|
||||
bits += 8;
|
||||
}
|
||||
if (bits == 8) {
|
||||
return (0, null(), 0, null()); ;; zero-length input, but with zero byte
|
||||
}
|
||||
(_, cell root) = get_data().begin_parse().load_dict();
|
||||
|
||||
slice cname = subdomain;
|
||||
int zeros = 0;
|
||||
repeat (bits >> 3) {
|
||||
int c = cname~load_uint(8);
|
||||
zeros -= (c == 0);
|
||||
}
|
||||
|
||||
;; can't move these declarations lower, will cause errors!
|
||||
slice pfx = cname;
|
||||
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();
|
||||
(pfx, val, tail, int succ) = root.pfxdict_get?(1023, pfxname);
|
||||
if (succ) {
|
||||
int exp = val~load_uint(32);
|
||||
if (nowtime > exp) { ;; entry expired, skip
|
||||
succ = false;
|
||||
}
|
||||
}
|
||||
zeros = succ ^ (zeros - 1); ;; break on success
|
||||
} until (zeros <= 0);
|
||||
|
||||
ifnot (zeros) {
|
||||
return (0, null(), 0, null()); ;; failed to find entry in prefix dictionary
|
||||
}
|
||||
|
||||
zeros = - zeros;
|
||||
return (exp, val.preload_ref(), tail.slice_empty?(), pfx);
|
||||
}
|
||||
|
||||
;;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());
|
||||
ifnot (exp) {
|
||||
return (0, null());
|
||||
}
|
||||
ifnot (exact?) { ;; incomplete subdomain found, must return next resolver (-1)
|
||||
category = -1;
|
||||
}
|
||||
|
||||
int pfx_bits = pfx.slice_bits() - 7;
|
||||
|
||||
;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain
|
||||
;; COUNTING the zero byte (if structurally correct: no multiple-ZB keys)
|
||||
;; which corresponds to 8m, m=one plus the number of bytes in the subdomain found)
|
||||
if (category == 0) {
|
||||
return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
|
||||
} else {
|
||||
cell cat_found = cat_table.idict_get_ref(16, category);
|
||||
return (pfx_bits, cat_found);
|
||||
}
|
||||
}
|
||||
|
||||
;; 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);
|
||||
return exp;
|
||||
}
|
||||
|
||||
int getexpiration(slice subdomain) method_id {
|
||||
return getexpirationx(subdomain, now());
|
||||
}
|
||||
|
||||
int getstdperiod() method_id {
|
||||
(int stdper, _, _, _) = load_prices();
|
||||
return stdper;
|
||||
}
|
||||
|
||||
int getppr() method_id {
|
||||
(_, int ppr, _, _) = load_prices();
|
||||
return ppr;
|
||||
}
|
||||
|
||||
int getppc() method_id {
|
||||
(_, _, int ppc, _) = load_prices();
|
||||
return ppc;
|
||||
}
|
||||
|
||||
int getppb() method_id {
|
||||
( _, _, _, int ppb) = load_prices();
|
||||
return ppb;
|
||||
}
|
||||
|
||||
int calcprice(cell val) method_id { ;; only for external gets (not efficient)
|
||||
(_, _, int ppc, int ppb) = load_prices();
|
||||
return _calcprice(val, ppc, ppb);
|
||||
}
|
||||
|
||||
int calcregprice(cell val) method_id { ;; only for external gets (not efficient)
|
||||
(_, int ppr, int ppc, int ppb) = load_prices();
|
||||
return ppr + _calcprice(val, ppc, ppb);
|
||||
}
|
|
@ -13,25 +13,7 @@
|
|||
;; Custom ASM instructions ;;
|
||||
;;===========================================================================;;
|
||||
|
||||
;; Args: s D n | Success: s' x s'' -1 | Failure: s 0 -> s N N 0
|
||||
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key)
|
||||
asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
|
||||
|
||||
;; Args: x k D n | Success: D' -1 | Failure: D 0
|
||||
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value)
|
||||
asm(value key dict key_len) "PFXDICTSET";
|
||||
|
||||
;; Args: k D n | Success: D' -1 | Failure: D 0
|
||||
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key)
|
||||
asm(key dict key_len) "PFXDICTDEL";
|
||||
|
||||
slice slice_last(slice s, int len) asm "SDCUTLAST";
|
||||
|
||||
;; Actually, equivalent to dictionaries, provided for clarity
|
||||
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
|
||||
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
|
||||
|
||||
(cell, ()) pfxdict_set_ref(cell dict, int key_len, slice key, cell value) {
|
||||
(cell, ()) pfxdict_set_ref(cell dict, int key_len, slice key, cell value) {
|
||||
throw_unless(33, dict~pfxdict_set?(key_len, key, begin_cell().store_maybe_ref(value).end_cell().begin_parse()));
|
||||
return (dict, ());
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ _ ~credit_to(credits, addr, amount) {
|
|||
}
|
||||
;; parse current election data
|
||||
var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
|
||||
elect_at~dump();
|
||||
;; elect_at~dump();
|
||||
msg_value -= 1000000000; ;; deduct GR$1 for sending confirmation
|
||||
if ((msg_value << 12) < total_stake) {
|
||||
;; stake smaller than 1/4096 of the total accumulated stakes, return
|
||||
|
@ -443,7 +443,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
if (f) {
|
||||
var (stake, _, pubkey) = (min(key~load_uint(128), max_stake), key~load_uint(32), key.preload_uint(256));
|
||||
var (max_f, _, adnl_addr) = (cs~load_uint(32), cs~load_uint(256), cs.preload_uint(256));
|
||||
l = cons(tuple4(stake, max_f, pubkey, adnl_addr), l);
|
||||
l = cons([stake, max_f, pubkey, adnl_addr], l);
|
||||
}
|
||||
} until (~ f);
|
||||
;; l is the list of all stakes in decreasing order
|
||||
|
@ -468,7 +468,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
}
|
||||
;; we have to select first m validators from list l
|
||||
l1 = touch(l);
|
||||
l1~dump(); ;; DEBUG
|
||||
;; l1~dump(); ;; DEBUG
|
||||
repeat (m - 1) {
|
||||
l1 = cdr(l1);
|
||||
}
|
||||
|
@ -480,7 +480,7 @@ _ compute_total_stake(l, n, m_stake) {
|
|||
var vset = new_dict();
|
||||
var frozen = new_dict();
|
||||
do {
|
||||
var (stake, max_f, pubkey, adnl_addr) = l~list_next().untuple4();
|
||||
var [stake, max_f, pubkey, adnl_addr] = l~list_next();
|
||||
;; lookup source address first
|
||||
var (val, f) = members.udict_get?(256, pubkey);
|
||||
throw_unless(61, f);
|
||||
|
@ -733,7 +733,7 @@ int announce_new_elections(ds, elect, credits) {
|
|||
(_, var min_stake) = config_param(17).begin_parse().load_grams();
|
||||
;; announce new elections
|
||||
var elect_at = t + elect_begin_before;
|
||||
elect_at~dump();
|
||||
;; elect_at~dump();
|
||||
var elect_close = elect_at - elect_end_before;
|
||||
elect = pack_elect(elect_at, elect_close, min_stake, 0, new_dict(), false, false);
|
||||
set_data(begin_cell().store_dict(elect).store_dict(credits).store_slice(ds).end_cell());
|
||||
|
@ -786,7 +786,7 @@ _ participant_list() method_id {
|
|||
do {
|
||||
(id, var fs, var f) = members.udict_get_prev?(256, id);
|
||||
if (f) {
|
||||
l = cons(pair(id, fs~load_grams()), l);
|
||||
l = cons([id, fs~load_grams()], l);
|
||||
}
|
||||
} until (~ f);
|
||||
return l;
|
||||
|
@ -806,7 +806,7 @@ _ participant_list_extended() method_id {
|
|||
if (f) {
|
||||
var (stake, time, max_factor, addr, adnl_addr) = (cs~load_grams(), cs~load_uint(32), cs~load_uint(32), cs~load_uint(256), cs~load_uint(256));
|
||||
cs.end_parse();
|
||||
l = cons(pair(id, tuple4(stake, max_factor, addr, adnl_addr)), l);
|
||||
l = cons([id, [stake, max_factor, addr, adnl_addr]], l);
|
||||
}
|
||||
} until (~ f);
|
||||
return (elect_at, elect_close, min_stake, total_stake, l, failed, finished);
|
||||
|
|
|
@ -93,8 +93,8 @@ file-base +"-dns" +contractid +".addr" load-address
|
|||
} : subdomain>s
|
||||
// ( b V -- b' )
|
||||
{ dup first
|
||||
dup `smc eq? { drop untriple rot nip rot x{9fd3} s, -rot Addr, 0 8 u, } {
|
||||
dup `next eq? { drop untriple rot nip rot x{ba93} s, -rot Addr, 0 8 u, } {
|
||||
dup `smc eq? { drop untriple 2swap drop x{9fd3} s, -rot Addr, 0 8 u, } {
|
||||
dup `next eq? { drop untriple 2swap drop x{ba93} s, -rot Addr, 0 8 u, } {
|
||||
dup `adnl eq? { drop second swap x{ad01} s, swap 256 u, } {
|
||||
dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } {
|
||||
abort"unknown value type"
|
||||
|
|
|
@ -69,5 +69,5 @@ int get_public_key() method_id {
|
|||
}
|
||||
|
||||
int balance() method_id {
|
||||
return restricted?() ? 0 : get_balance().first();
|
||||
return restricted?() ? 0 : get_balance().pair_first();
|
||||
}
|
||||
|
|
|
@ -66,7 +66,7 @@ int balance() method_id {
|
|||
var rdict = ds~load_dict();
|
||||
ds.end_parse();
|
||||
var ts = days_passed();
|
||||
var balance = get_balance().first();
|
||||
var balance = get_balance().pair_first();
|
||||
var (_, value, found) = rdict.idict_get_preveq?(16, ts);
|
||||
if (found) {
|
||||
balance = max(balance - value~load_grams(), 0);
|
||||
|
|
|
@ -6,21 +6,26 @@ forall X -> (X, tuple) uncons(tuple list) asm "UNCONS";
|
|||
forall X -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
|
||||
forall X -> X car(tuple list) asm "CAR";
|
||||
tuple cdr(tuple list) asm "CDR";
|
||||
forall X, Y -> tuple pair(X x, Y y) asm "PAIR";
|
||||
forall X, Y -> (X, Y) unpair(tuple t) asm "UNPAIR";
|
||||
forall X, Y, Z -> tuple triple(X x, Y y, Z z) asm "TRIPLE";
|
||||
forall X, Y, Z -> (X, Y, Z) untriple(tuple t) asm "UNTRIPLE";
|
||||
forall X, Y, Z, W -> tuple tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
|
||||
forall X, Y, Z, W -> (X, Y, Z, W) untuple4(tuple t) asm "4 UNTUPLE";
|
||||
forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
|
||||
forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
|
||||
forall X, Y, Z -> [X, Y, Z] triple(X x, Y y, Z z) asm "TRIPLE";
|
||||
forall X, Y, Z -> (X, Y, Z) untriple([X, Y, Z] t) asm "UNTRIPLE";
|
||||
forall X, Y, Z, W -> [X, Y, Z, W] tuple4(X x, Y y, Z z, W w) asm "4 TUPLE";
|
||||
forall X, Y, Z, W -> (X, Y, Z, W) untuple4([X, Y, Z, W] t) asm "4 UNTUPLE";
|
||||
forall X -> X first(tuple t) asm "FIRST";
|
||||
forall X -> X second(tuple t) asm "SECOND";
|
||||
forall X -> X third(tuple t) asm "THIRD";
|
||||
forall X -> X fourth(tuple t) asm "3 INDEX";
|
||||
forall X, Y -> X pair_first([X, Y] p) asm "FIRST";
|
||||
forall X, Y -> Y pair_second([X, Y] p) asm "SECOND";
|
||||
forall X, Y, Z -> X triple_first([X, Y, Z] p) asm "FIRST";
|
||||
forall X, Y, Z -> Y triple_second([X, Y, Z] p) asm "SECOND";
|
||||
forall X, Y, Z -> Z triple_third([X, Y, Z] p) asm "THIRD";
|
||||
forall X -> X null() asm "PUSHNULL";
|
||||
|
||||
int now() asm "NOW";
|
||||
slice my_address() asm "MYADDR";
|
||||
tuple get_balance() asm "BALANCE";
|
||||
[int, cell] get_balance() asm "BALANCE";
|
||||
int cur_lt() asm "LTIME";
|
||||
int block_lt() asm "BLOCKLT";
|
||||
|
||||
|
@ -47,6 +52,7 @@ cont bless(slice s) impure asm "BLESS";
|
|||
() accept_message() impure asm "ACCEPT";
|
||||
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
|
||||
() commit() impure asm "COMMIT";
|
||||
() buy_gas(int gram) impure asm "BUYGAS";
|
||||
|
||||
int min(int x, int y) asm "MIN";
|
||||
int max(int x, int y) asm "MAX";
|
||||
|
@ -67,10 +73,16 @@ slice skip_bits(slice s, int len) asm "SDSKIPFIRST";
|
|||
slice first_bits(slice s, int len) asm "SDCUTFIRST";
|
||||
slice skip_last_bits(slice s, int len) asm "SDSKIPLAST";
|
||||
(slice, ()) ~skip_last_bits(slice s, int len) asm "SDSKIPLAST";
|
||||
slice slice_last(slice s, int len) asm "SDCUTLAST";
|
||||
(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
|
||||
cell preload_dict(slice s) asm "PLDDICT";
|
||||
slice skip_dict(slice s) asm "SKIPDICT";
|
||||
|
||||
(slice, cell) load_maybe_ref(slice s) asm( -> 1 0) "LDOPTREF";
|
||||
cell preload_maybe_ref(slice s) asm "PLDOPTREF";
|
||||
builder store_maybe_ref(builder b, cell c) asm(c b) "STOPTREF";
|
||||
|
||||
|
||||
int slice_refs(slice s) asm "SREFS";
|
||||
int slice_bits(slice s) asm "SBITS";
|
||||
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
|
||||
|
@ -78,6 +90,9 @@ int slice_empty?(slice s) asm "SEMPTY";
|
|||
int slice_data_empty?(slice s) asm "SDEMPTY";
|
||||
int slice_refs_empty?(slice s) asm "SREMPTY";
|
||||
|
||||
int builder_refs(builder b) asm "BREFS";
|
||||
int builder_bits(builder b) asm "BBITS";
|
||||
|
||||
builder begin_cell() asm "NEWC";
|
||||
cell end_cell(builder b) asm "ENDC";
|
||||
builder store_ref(builder b, cell c) asm(c b) "STREF";
|
||||
|
@ -159,11 +174,15 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va
|
|||
cell new_dict() asm "NEWDICT";
|
||||
int dict_empty?(cell c) asm "DICTEMPTY";
|
||||
|
||||
(slice, slice, slice, int) pfxdict_get?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTGETQ" "NULLSWAPIFNOT2";
|
||||
(cell, int) pfxdict_set?(cell dict, int key_len, slice key, slice value) asm(value key dict key_len) "PFXDICTSET";
|
||||
(cell, int) pfxdict_delete?(cell dict, int key_len, slice key) asm(key dict key_len) "PFXDICTDEL";
|
||||
|
||||
cell config_param(int x) asm "CONFIGOPTPARAM";
|
||||
int cell_null?(cell c) asm "ISNULL";
|
||||
|
||||
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
|
||||
() raw_reserve_extra(slice currencies, int mode) impure asm "RAWRESERVEX";
|
||||
() raw_reserve_extra(int amount, cell extra_amount, int mode) impure asm "RAWRESERVEX";
|
||||
() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
|
||||
() set_code(cell new_code) impure asm "SETCODE";
|
||||
|
||||
|
|
|
@ -177,11 +177,30 @@ td::Result<std::vector<DnsInterface::Entry>> DnsInterface::resolve(td::Slice nam
|
|||
[UInt<256b>:new_public_key]
|
||||
*/
|
||||
// creation
|
||||
td::Ref<ManualDns> ManualDns::create(td::Ref<vm::Cell> data) {
|
||||
return td::Ref<ManualDns>(true, State{ton::SmartContractCode::dns_manual(), std::move(data)});
|
||||
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)});
|
||||
}
|
||||
td::Ref<ManualDns> ManualDns::create(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
|
||||
return create(create_init_data_fast(public_key, wallet_id));
|
||||
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) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
td::optional<td::int32> ManualDns::guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id) {
|
||||
for (auto i : {-1, 1}) {
|
||||
auto dns = ton::ManualDns::create(public_key, wallet_id, i);
|
||||
if (dns->get_address() == address) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
td::Result<td::uint32> ManualDns::get_wallet_id() const {
|
||||
|
|
|
@ -180,14 +180,18 @@ class ManualDns : public ton::SmartContract, public DnsInterface {
|
|||
static td::Ref<ManualDns> create(State state) {
|
||||
return td::Ref<ManualDns>(true, std::move(state));
|
||||
}
|
||||
static td::Ref<ManualDns> create(td::Ref<vm::Cell> data = {});
|
||||
static td::Ref<ManualDns> create(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
|
||||
static td::Ref<ManualDns> create(td::Ref<vm::Cell> data = {}, int revision = 0);
|
||||
static td::Ref<ManualDns> create(const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id, int revision = 0);
|
||||
|
||||
static std::string serialize_data(const EntryData& data);
|
||||
static td::Result<td::optional<ManualDns::EntryData>> parse_data(td::Slice cmd);
|
||||
static td::Result<ManualDns::ActionExt> parse_line(td::Slice cmd);
|
||||
static td::Result<std::vector<ManualDns::ActionExt>> parse(td::Slice cmd);
|
||||
|
||||
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
|
||||
static td::optional<td::int32> guess_revision(const block::StdAddress& address,
|
||||
const td::Ed25519::PublicKey& public_key, td::uint32 wallet_id);
|
||||
|
||||
td::Ref<vm::Cell> create_init_data(const td::Ed25519::PublicKey& public_key, td::uint32 valid_until) const {
|
||||
return create_init_data_fast(public_key, valid_until);
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@ constexpr static int WALLET2_REVISION = 2;
|
|||
constexpr static int WALLET3_REVISION = 2;
|
||||
constexpr static int HIGHLOAD_WALLET_REVISION = 2;
|
||||
constexpr static int HIGHLOAD_WALLET2_REVISION = 2;
|
||||
constexpr static int DNS_REVISION = 1;
|
||||
const auto& get_map() {
|
||||
static auto map = [] {
|
||||
std::map<std::string, td::Ref<vm::Cell>, std::less<>> map;
|
||||
|
@ -80,15 +81,30 @@ const auto& get_map() {
|
|||
with_tvm_code("wallet3-r2",
|
||||
"te6ccgEBAQEAcQAA3v8AIN0gggFMl7ohggEznLqxn3Gw7UTQ0x/THzHXC//jBOCk8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/"
|
||||
"T/9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA==");
|
||||
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);
|
||||
with_tvm_code(
|
||||
"dns-manual-r1",
|
||||
"te6ccgECGAEAAtAAART/APSkE/S88sgLAQIBIAIDAgFIBAUC7PLbPAWDCNcYIPkBAdMf0z/"
|
||||
"4I6ofUyC58mNTKoBA9A5voTHyYFKUuvKiVBNG+RDyo/gAItcLBcAzmDQBdtch0/"
|
||||
"8wjoVa2zxAA+"
|
||||
"IDgyWhyEAHgED0Q44aIIBA9JZvpTJREJQwUwe53iCTMzUBkjIw4rPmNVUD8AQREgICxQYHAgEgDA0CAc8ICQAIqoJfAwIBSAoLACHWQK5Y+"
|
||||
"J5Z/l//oAegBk9qpAAFF8DgABcyPQAydBBM/Rw8qGAAF72c52omhpr5jrhf/"
|
||||
"AIBIA4PABG7Nz7UTQ1wsfgD+"
|
||||
"7owwh10kglF8DcG3hIHew8l4ieNci1wsHnnDIUATPFhPLB8nQAqYI3iDACJRfA3Bt4Ns8FF8EI3ADqwKY0wcBwAAToQLkIG2OnF8DIcjLBiTPF"
|
||||
"snQhAlUQgHbPAWlFbIgwQEVQzDmMzUilF8FcG3hMgHHAJMxfwHfAtdJpvmBEVEAAYIcAAkjEB4AKAEPRqABztRNDTH9M/0//"
|
||||
"0BPQE0QE2cFmOlNs8IMcBnCDXSpPUMNCTMn8C4t4i5jAxEwT20wUhwQqOLCGRMeEhwAGXMdMH1AL7AOABwAmOFNQh+wTtQwLQ7R7tU1RiA/"
|
||||
"EGgvIA4PIt4HAiwRSUMNIPAd5tbSTBHoreJMEUjpElhAkj2zwzApUyxwDyo5Fb4t4kwAuOEzQC9ARQJIAQ9G4wECOECVnwAQHgJMAMiuAwFBUW"
|
||||
"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);
|
||||
|
@ -137,21 +153,30 @@ td::Ref<vm::Cell> SmartContractCode::simple_wallet_ext() {
|
|||
return res;
|
||||
}
|
||||
td::Ref<vm::Cell> SmartContractCode::highload_wallet(int revision) {
|
||||
if (revision == -1) {
|
||||
return load("highload-wallet").move_as_ok();
|
||||
}
|
||||
if (revision == 0) {
|
||||
revision = HIGHLOAD_WALLET_REVISION;
|
||||
}
|
||||
auto res = load(PSLICE() << "highload-wallet-r" << revision).move_as_ok();
|
||||
return res;
|
||||
return load(PSLICE() << "highload-wallet-r" << revision).move_as_ok();
|
||||
}
|
||||
td::Ref<vm::Cell> SmartContractCode::highload_wallet_v2(int revision) {
|
||||
if (revision == -1) {
|
||||
return load("highload-wallet-v2").move_as_ok();
|
||||
}
|
||||
if (revision == 0) {
|
||||
revision = HIGHLOAD_WALLET2_REVISION;
|
||||
}
|
||||
auto res = load(PSLICE() << "highload-wallet-v2-r" << revision).move_as_ok();
|
||||
return res;
|
||||
return load(PSLICE() << "highload-wallet-v2-r" << revision).move_as_ok();
|
||||
}
|
||||
td::Ref<vm::Cell> SmartContractCode::dns_manual() {
|
||||
static auto res = load("dns-manual").move_as_ok();
|
||||
return res;
|
||||
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();
|
||||
}
|
||||
} // namespace ton
|
||||
|
|
|
@ -29,6 +29,6 @@ class SmartContractCode {
|
|||
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();
|
||||
static td::Ref<vm::Cell> dns_manual(int revision = 0);
|
||||
};
|
||||
} // namespace ton
|
||||
|
|
|
@ -409,12 +409,12 @@ TEST(Tonlib, HighloadWalletV2) {
|
|||
|
||||
td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
|
||||
auto pub_key = priv_key.get_public_key().move_as_ok();
|
||||
auto init_state = ton::HighloadWalletV2::get_init_state(pub_key, 239, 0);
|
||||
auto init_state = ton::HighloadWalletV2::get_init_state(pub_key, 239, -1);
|
||||
auto init_message = ton::HighloadWalletV2::get_init_message(priv_key, 239, 65535);
|
||||
auto address = ton::GenericAccount::get_address(0, init_state);
|
||||
|
||||
ton::HighloadWalletV2 wallet(
|
||||
{ton::HighloadWalletV2::get_init_code(0), ton::HighloadWalletV2::get_init_data(pub_key, 239)});
|
||||
{ton::HighloadWalletV2::get_init_code(-1), ton::HighloadWalletV2::get_init_data(pub_key, 239)});
|
||||
ASSERT_EQ(239u, wallet.get_wallet_id().ok());
|
||||
CHECK(pub_key.as_octet_string() == wallet.get_public_key().ok().as_octet_string());
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -348,6 +348,10 @@ class Stack : public td::CntObject {
|
|||
void pop_many(int count) {
|
||||
stack.resize(stack.size() - count);
|
||||
}
|
||||
void pop_many(int count, int offs) {
|
||||
std::move(stack.cend() - offs, stack.cend(), stack.end() - (count + offs));
|
||||
pop_many(count);
|
||||
}
|
||||
void drop_bottom(int count) {
|
||||
std::move(stack.cbegin() + count, stack.cend(), stack.begin());
|
||||
pop_many(count);
|
||||
|
|
|
@ -379,6 +379,15 @@ int exec_blkdrop(VmState* st, unsigned args) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int exec_blkdrop2(VmState* st, unsigned args) {
|
||||
int x = ((args >> 4) & 15), y = (args & 15);
|
||||
Stack& stack = st->get_stack();
|
||||
VM_LOG(st) << "execute BLKDROP2 " << x << ',' << y;
|
||||
stack.check_underflow(x + y);
|
||||
stack.pop_many(x, y);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_blkpush(VmState* st, unsigned args) {
|
||||
int x = ((args >> 4) & 15), y = (args & 15);
|
||||
Stack& stack = st->get_stack();
|
||||
|
@ -570,7 +579,8 @@ void register_stack_ops(OpcodeTable& cp0) {
|
|||
.insert(OpcodeInstr::mksimple(0x68, 8, "DEPTH", exec_depth))
|
||||
.insert(OpcodeInstr::mksimple(0x69, 8, "CHKDEPTH", exec_chkdepth))
|
||||
.insert(OpcodeInstr::mksimple(0x6a, 8, "ONLYTOPX", exec_onlytop_x))
|
||||
.insert(OpcodeInstr::mksimple(0x6b, 8, "ONLYX", exec_only_x));
|
||||
.insert(OpcodeInstr::mksimple(0x6b, 8, "ONLYX", exec_only_x))
|
||||
.insert(OpcodeInstr::mkfixedrange(0x6c10, 0x6d00, 16, 8, instr::dump_2c("BLKDROP2 ", ","), exec_blkdrop2));
|
||||
}
|
||||
|
||||
} // namespace vm
|
||||
|
|
|
@ -811,26 +811,24 @@ bool store_grams(CellBuilder& cb, td::RefInt256 value) {
|
|||
}
|
||||
|
||||
int exec_reserve_raw(VmState* st, int mode) {
|
||||
VM_LOG(st) << "execute RESERVERAW" << (mode & 1 ? "X" : "");
|
||||
VM_LOG(st) << "execute RAWRESERVE" << (mode & 1 ? "X" : "");
|
||||
Stack& stack = st->get_stack();
|
||||
stack.check_underflow(2);
|
||||
int f = stack.pop_smallint_range(3);
|
||||
td::RefInt256 x;
|
||||
Ref<CellSlice> csr;
|
||||
stack.check_underflow(2 + (mode & 1));
|
||||
int f = stack.pop_smallint_range(15);
|
||||
Ref<Cell> y;
|
||||
if (mode & 1) {
|
||||
csr = stack.pop_cellslice();
|
||||
} else {
|
||||
x = stack.pop_int_finite();
|
||||
if (td::sgn(x) < 0) {
|
||||
throw VmError{Excno::range_chk, "amount of nanograms must be non-negative"};
|
||||
}
|
||||
y = stack.pop_maybe_cell();
|
||||
}
|
||||
auto x = stack.pop_int_finite();
|
||||
if (td::sgn(x) < 0) {
|
||||
throw VmError{Excno::range_chk, "amount of nanograms must be non-negative"};
|
||||
}
|
||||
CellBuilder cb;
|
||||
if (!(cb.store_ref_bool(get_actions(st)) // out_list$_ {n:#} prev:^(OutList n)
|
||||
&& cb.store_long_bool(0x36e6b809, 32) // action_reserve_currency#36e6b809
|
||||
&& cb.store_long_bool(f, 8) // mode:(## 8)
|
||||
&& (mode & 1 ? cb.append_cellslice_bool(std::move(csr))
|
||||
: (store_grams(cb, std::move(x)) && cb.store_bool_bool(false))))) {
|
||||
&& store_grams(cb, std::move(x)) //
|
||||
&& cb.store_maybe_ref(std::move(y)))) {
|
||||
throw VmError{Excno::cell_ov, "cannot serialize raw reserved currency amount into an output action cell"};
|
||||
}
|
||||
return install_output_action(st, cb.finalize());
|
||||
|
@ -886,8 +884,8 @@ int exec_change_lib(VmState* st) {
|
|||
void register_ton_message_ops(OpcodeTable& cp0) {
|
||||
using namespace std::placeholders;
|
||||
cp0.insert(OpcodeInstr::mksimple(0xfb00, 16, "SENDRAWMSG", exec_send_raw_message))
|
||||
.insert(OpcodeInstr::mksimple(0xfb02, 16, "RESERVERAW", std::bind(exec_reserve_raw, _1, 0)))
|
||||
.insert(OpcodeInstr::mksimple(0xfb03, 16, "RESERVERAWX", std::bind(exec_reserve_raw, _1, 1)))
|
||||
.insert(OpcodeInstr::mksimple(0xfb02, 16, "RAWRESERVE", std::bind(exec_reserve_raw, _1, 0)))
|
||||
.insert(OpcodeInstr::mksimple(0xfb03, 16, "RAWRESERVEX", std::bind(exec_reserve_raw, _1, 1)))
|
||||
.insert(OpcodeInstr::mksimple(0xfb04, 16, "SETCODE", exec_set_code))
|
||||
.insert(OpcodeInstr::mksimple(0xfb06, 16, "SETLIBCODE", exec_set_lib_code))
|
||||
.insert(OpcodeInstr::mksimple(0xfb07, 16, "CHANGELIB", exec_change_lib));
|
||||
|
|
|
@ -1377,7 +1377,8 @@ Parameters $i$, $j$, and $k$ of the following primitives all are 4-bit integers
|
|||
\item {\tt 69} --- {\tt CHKDEPTH}, pops integer $i$ from the stack, then checks whether there are at least $i$ elements, generating a stack underflow exception otherwise.
|
||||
\item {\tt 6A} --- {\tt ONLYTOPX}, pops integer $i$ from the stack, then removes all but the top $i$ elements.
|
||||
\item {\tt 6B} --- {\tt ONLYX}, pops integer $i$ from the stack, then leaves only the bottom $i$ elements. Approximately equivalent to {\tt DEPTH}; {\tt SWAP}; {\tt SUB}; {\tt DROPX}.
|
||||
\item {\tt 6C} --- reserved for stack operations.
|
||||
\item {\tt 6C00}--{\tt 6C0F} --- reserved for stack operations.
|
||||
\item {\tt 6C$ij$} --- {\tt BLKDROP2 $i$,$j$}, drops $i$ stack elements under the top $j$ elements, where $1\leq i\leq15$ and $0\leq j\leq 15$. Equivalent to {\tt REVERSE $i+j$,0}; {\tt BLKDROP $i$}; {\tt REVERSE $j$,0}.
|
||||
\end{itemize}
|
||||
|
||||
\mysubsection{Tuple, List, and Null primitives}
|
||||
|
@ -2297,8 +2298,8 @@ The following primitives, which use the above conventions, are defined:
|
|||
\nxsubpoint\emb{Outbound message and output action primitives}
|
||||
\begin{itemize}
|
||||
\item {\tt FB00} --- {\tt SENDRAWMSG} ($c$ $x$ -- ), sends a raw message contained in {\em Cell $c$}, which should contain a correctly serialized object {\tt Message $X$}, with the only exception that the source address is allowed to have dummy value {\tt addr\_none} (to be automatically replaced with the current smart-contract address), and {\tt ihr\_fee}, {\tt fwd\_fee}, {\tt created\_lt} and {\tt created\_at} fields can have arbitrary values (to be rewritten with correct values during the action phase of the current transaction). Integer parameter $x$ contains the flags. Currently $x=0$ is used for ordinary messages; $x=128$ is used for messages that are to carry all the remaining balance of the current smart contract (instead of the value originally indicated in the message); $x=64$ is used for messages that carry all the remaining value of the inbound message in addition to the value initially indicated in the new message (if bit 0 is not set, the gas fees are deducted from this amount); $x'=x+1$ means that the sender wants to pay transfer fees separately; $x'=x+2$ means that any errors arising while processing this message during the action phase should be ignored.
|
||||
\item {\tt FB02} --- {\tt RAWRESERVE} ($x$ $y$ -- ), creates an output action which would reserve exactly $x$ nanograms (if $y=0$), at most $x$ nanograms (if $y=2$), or all but $x$ nanograms (if $y=1$ or $y=3$), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying $x$ nanograms (or $b-x$ nanograms, where $b$ is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit $+2$ in $y$ means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Currently $x$ must be a non-negative integer, and $y$ must be in the range $0\ldots 3$.
|
||||
\item {\tt FB03} --- {\tt RAWRESERVEX} ($s$ $y$ -- ), similar to {\tt RAWRESERVE}, but accepts a {\em Slice $s$} with a {\em CurrencyCollection\/} as an argument. In this way currencies other than Grams can be reserved.
|
||||
\item {\tt FB02} --- {\tt RAWRESERVE} ($x$ $y$ -- ), creates an output action which would reserve exactly $x$ nanograms (if $y=0$), at most $x$ nanograms (if $y=2$), or all but $x$ nanograms (if $y=1$ or $y=3$), from the remaining balance of the account. It is roughly equivalent to creating an outbound message carrying $x$ nanograms (or $b-x$ nanograms, where $b$ is the remaining balance) to oneself, so that the subsequent output actions would not be able to spend more money than the remainder. Bit $+2$ in $y$ means that the external action does not fail if the specified amount cannot be reserved; instead, all remaining balance is reserved. Bit $+8$ in $y$ means $x\leftarrow -x$ before performing any further actions. Bit $+4$ in $y$ means that $x$ is increased by the original balance of the current account (before the compute phase), including all extra currencies, before performing any other checks and actions. Currently $x$ must be a non-negative integer, and $y$ must be in the range $0\ldots 15$.
|
||||
\item {\tt FB03} --- {\tt RAWRESERVEX} ($x$ $D$ $y$ -- ), similar to {\tt RAWRESERVE}, but also accepts a dictionary~$D$ (represented by a {\em Cell\/} or {\em Null\/}) with extra currencies. In this way currencies other than Grams can be reserved.
|
||||
\item {\tt FB04} --- {\tt SETCODE} ($c$ -- ), creates an output action that would change this smart contract code to that given by {\em Cell\/}~$c$. Notice that this change will take effect only after the successful termination of the current run of the smart contract.
|
||||
\item {\tt FB06} --- {\tt SETLIBCODE} ($c$ $x$ -- ), creates an output action that would modify the collection of this smart contract libraries by adding or removing library with code given in {\em Cell\/}~$c$. If $x=0$, the library is actually removed if it was previously present in the collection (if not, this action does nothing). If $x=1$, the library is added as a private library, and if $x=2$, the library is added as a public library (and becomes available to all smart contracts if the current smart contract resides in the masterchain); if the library was present in the collection before, its public/private status is changed according to $x$. Values of $x$ other than $0\ldots 2$ are invalid.
|
||||
\item {\tt FB07} --- {\tt CHANGELIB} ($h$ $x$ -- ), creates an output action similarly to {\tt SETLIBCODE}, but instead of the library code accepts its hash as an unsigned 256-bit integer $h$. If $x\neq0$ and the library with hash $h$ is absent from the library collection of this smart contract, this output action will fail.
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
pushd .
|
||||
mkdir -p build_native
|
||||
cd build_native
|
||||
cmake .. || exit 1
|
||||
cmake --build . --target prepare_cross_compiling || exit 1
|
||||
cmake -DTON_ONLY_TONLIB=ON .. || exit 1
|
||||
cmake --build . --target prepare_cross_compiling || exit 2
|
||||
#cmake --build . --target tl_generate_java || exit 1
|
||||
popd
|
||||
php AddIntDef.php src/drinkless/org/ton/TonApi.java || exit 1
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#include "rlimit.h"
|
||||
#if TD_LINUX
|
||||
#if TD_LINUX || TD_ANDROID
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
|
|
|
@ -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 "terminal.hpp"
|
||||
#include "td/utils/port/StdStreams.h"
|
||||
|
@ -42,7 +42,7 @@ void TerminalLogInterface::append(CSlice slice, int log_level) {
|
|||
} else {
|
||||
color = TC_GREEN;
|
||||
}
|
||||
td::TsCerr() << color << slice << TC_EMPTY;
|
||||
std::cerr << color << slice.c_str() << TC_EMPTY;
|
||||
instance_->reactivate_readline();
|
||||
if (log_level == VERBOSITY_NAME(FATAL)) {
|
||||
process_fatal_error(slice);
|
||||
|
@ -82,6 +82,16 @@ void TerminalIOImpl::output_line(std::string line) {
|
|||
reactivate_readline();
|
||||
}
|
||||
|
||||
void TerminalIOImpl::output_line_stderr(std::string line) {
|
||||
deactivate_readline();
|
||||
if (use_readline_) {
|
||||
Stdout().write(line).ensure();
|
||||
} else {
|
||||
Stderr().write(line).ensure();
|
||||
}
|
||||
reactivate_readline();
|
||||
}
|
||||
|
||||
void TerminalIOImpl::start_up() {
|
||||
instance_ = this;
|
||||
self_ = actor_id(this);
|
||||
|
@ -246,7 +256,7 @@ void TerminalIO::output(std::string line) {
|
|||
std::cout << line;
|
||||
} else {
|
||||
instance_->deactivate_readline();
|
||||
td::TsCerr() << line;
|
||||
std::cout << line;
|
||||
instance_->reactivate_readline();
|
||||
}
|
||||
}
|
||||
|
@ -254,10 +264,40 @@ void TerminalIO::output(std::string line) {
|
|||
void TerminalIO::output(td::Slice line) {
|
||||
auto instance_ = TerminalIOImpl::instance();
|
||||
if (!instance_) {
|
||||
td::TsCerr() << line;
|
||||
std::cout.write(line.begin(), line.size());
|
||||
} else {
|
||||
instance_->deactivate_readline();
|
||||
td::TsCerr() << line;
|
||||
std::cout.write(line.begin(), line.size());
|
||||
instance_->reactivate_readline();
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalIO::output_stderr(std::string line) {
|
||||
auto instance_ = TerminalIOImpl::instance();
|
||||
if (!instance_) {
|
||||
std::cout << line;
|
||||
} else {
|
||||
instance_->deactivate_readline();
|
||||
if (instance_->readline_used()) {
|
||||
std::cout << line;
|
||||
} else {
|
||||
std::cerr << line;
|
||||
}
|
||||
instance_->reactivate_readline();
|
||||
}
|
||||
}
|
||||
|
||||
void TerminalIO::output_stderr(td::Slice line) {
|
||||
auto instance_ = TerminalIOImpl::instance();
|
||||
if (!instance_) {
|
||||
std::cerr.write(line.begin(), line.size());
|
||||
} else {
|
||||
instance_->deactivate_readline();
|
||||
if (instance_->readline_used()) {
|
||||
std::cout.write(line.begin(), line.size());
|
||||
} else {
|
||||
std::cerr.write(line.begin(), line.size());
|
||||
}
|
||||
instance_->reactivate_readline();
|
||||
}
|
||||
}
|
||||
|
@ -265,7 +305,11 @@ void TerminalIO::output(td::Slice line) {
|
|||
TerminalIOOutputter::~TerminalIOOutputter() {
|
||||
if (buffer_) {
|
||||
CHECK(sb_);
|
||||
TerminalIO::output(sb_->as_cslice());
|
||||
if (is_err_) {
|
||||
TerminalIO::output_stderr(sb_->as_cslice());
|
||||
} else {
|
||||
TerminalIO::output(sb_->as_cslice());
|
||||
}
|
||||
delete[] buffer_;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -29,8 +29,10 @@ namespace td {
|
|||
class TerminalIOOutputter {
|
||||
public:
|
||||
static const size_t BUFFER_SIZE = 128 * 1024;
|
||||
TerminalIOOutputter()
|
||||
: buffer_(new char[BUFFER_SIZE]), sb_(std::make_unique<StringBuilder>(td::MutableSlice{buffer_, BUFFER_SIZE})) {
|
||||
TerminalIOOutputter(bool is_err)
|
||||
: buffer_(new char[BUFFER_SIZE])
|
||||
, is_err_(is_err)
|
||||
, sb_(std::make_unique<StringBuilder>(td::MutableSlice{buffer_, BUFFER_SIZE})) {
|
||||
}
|
||||
TerminalIOOutputter(TerminalIOOutputter &&X) = default;
|
||||
|
||||
|
@ -58,6 +60,7 @@ class TerminalIOOutputter {
|
|||
|
||||
private:
|
||||
char *buffer_;
|
||||
bool is_err_;
|
||||
std::unique_ptr<StringBuilder> sb_;
|
||||
};
|
||||
|
||||
|
@ -75,11 +78,18 @@ class TerminalIO : public actor::Actor {
|
|||
virtual void update_callback(std::unique_ptr<Callback> callback) = 0;
|
||||
static void output(std::string line);
|
||||
static void output(td::Slice slice);
|
||||
static void output_stderr(std::string line);
|
||||
static void output_stderr(td::Slice slice);
|
||||
static TerminalIOOutputter out() {
|
||||
return TerminalIOOutputter{};
|
||||
return TerminalIOOutputter{false};
|
||||
}
|
||||
static TerminalIOOutputter err() {
|
||||
return TerminalIOOutputter{true};
|
||||
}
|
||||
virtual void output_line(std::string line) = 0;
|
||||
virtual void output_line_stderr(std::string line) = 0;
|
||||
virtual void set_log_interface() = 0;
|
||||
virtual bool readline_used() const = 0;
|
||||
|
||||
static td::actor::ActorOwn<TerminalIO> create(std::string prompt, bool use_readline,
|
||||
std::unique_ptr<Callback> callback);
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -45,6 +45,10 @@ class TerminalIOImpl : public TerminalIO, td::ObserverBase {
|
|||
void deactivate_readline();
|
||||
void reactivate_readline();
|
||||
void output_line(std::string line) override;
|
||||
void output_line_stderr(std::string line) override;
|
||||
bool readline_used() const override {
|
||||
return use_readline_;
|
||||
}
|
||||
void set_log_interface() override;
|
||||
//void read_line();
|
||||
void loop() override;
|
||||
|
|
|
@ -51,7 +51,7 @@ ton.blockId workchain:int32 shard:int64 seqno:int32 = internal.BlockId;
|
|||
ton.blockIdExt workchain:int32 shard:int64 seqno:int32 root_hash:bytes file_hash:bytes = ton.BlockIdExt;
|
||||
|
||||
raw.fullAccountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId block_id:ton.blockIdExt frozen_hash:bytes sync_utime:int53 = raw.FullAccountState;
|
||||
raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes message:bytes = raw.Message;
|
||||
raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes message:bytes is_message_encrypted:Bool = raw.Message;
|
||||
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
|
||||
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
|
||||
|
||||
|
|
Binary file not shown.
|
@ -34,7 +34,8 @@ int TD_TL_writer_java::get_max_arity() const {
|
|||
|
||||
bool TD_TL_writer_java::is_built_in_simple_type(const std::string &name) const {
|
||||
return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" ||
|
||||
name == "String" || name == "Bytes" || name == "SecureString" || name == "SecureBytes";
|
||||
name == "String" || name == "Bytes" || name == "SecureString" || name == "SecureBytes" || name == "Object" ||
|
||||
name == "Function";
|
||||
}
|
||||
|
||||
bool TD_TL_writer_java::is_built_in_complex_type(const std::string &name) const {
|
||||
|
@ -168,6 +169,12 @@ std::string TD_TL_writer_java::gen_type_name(const tl::tl_tree_type *tree_type)
|
|||
if (name == "Bytes" || name == "SecureBytes") {
|
||||
return "byte[]";
|
||||
}
|
||||
if (name == "Object") {
|
||||
return gen_base_type_class_name(0);
|
||||
}
|
||||
if (name == "Function") {
|
||||
return gen_base_function_class_name();
|
||||
}
|
||||
|
||||
if (name == "Vector") {
|
||||
assert(t->arity == 1);
|
||||
|
|
|
@ -24,8 +24,10 @@
|
|||
namespace td {
|
||||
|
||||
bool TD_TL_writer_jni_cpp::is_built_in_simple_type(const std::string &name) const {
|
||||
assert(name != "function");
|
||||
return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" ||
|
||||
name == "String" || name == "Bytes" || name == "SecureString" || name == "SecureBytes";
|
||||
name == "String" || name == "Bytes" || name == "SecureString" || name == "SecureBytes" || name == "Function" ||
|
||||
name == "Object";
|
||||
}
|
||||
|
||||
bool TD_TL_writer_jni_cpp::is_built_in_complex_type(const std::string &name) const {
|
||||
|
@ -197,6 +199,7 @@ std::string TD_TL_writer_jni_cpp::gen_type_fetch(const std::string &field_name,
|
|||
const tl::tl_tree_type *child = static_cast<const tl::tl_tree_type *>(tree_type->children[0]);
|
||||
res = gen_vector_fetch(field_name, child, vars, parser_type);
|
||||
} else {
|
||||
assert(gen_main_class_name(tree_type->type) != "function");
|
||||
if (field_name == "") {
|
||||
return gen_main_class_name(tree_type->type) + "::fetch(env, p)";
|
||||
}
|
||||
|
|
|
@ -24,7 +24,8 @@ namespace td {
|
|||
|
||||
bool TD_TL_writer_jni_h::is_built_in_simple_type(const std::string &name) const {
|
||||
return name == "Bool" || name == "Int32" || name == "Int53" || name == "Int64" || name == "Double" ||
|
||||
name == "String" || name == "Bytes" || name == "SecureString" || name == "SecureBytes";
|
||||
name == "String" || name == "Bytes" || name == "SecureString" || name == "SecureBytes" || name == "Function" ||
|
||||
name == "Object";
|
||||
}
|
||||
|
||||
bool TD_TL_writer_jni_h::is_built_in_complex_type(const std::string &name) const {
|
||||
|
|
|
@ -124,6 +124,12 @@ std::string TD_TL_writer::gen_class_name(std::string name) const {
|
|||
if (name == "Object") {
|
||||
//assert(false);
|
||||
}
|
||||
if (name == "function") {
|
||||
return "Function";
|
||||
}
|
||||
if (name == "object") {
|
||||
return "Object";
|
||||
}
|
||||
if (name == "#") {
|
||||
return "std::int32_t";
|
||||
}
|
||||
|
|
|
@ -154,30 +154,38 @@ TEST(Tonlib, InitClose) {
|
|||
}
|
||||
}
|
||||
|
||||
TEST(Tonlib, SimpleEncryption) {
|
||||
template <class Encryption>
|
||||
void test_encryption() {
|
||||
std::string secret = "secret";
|
||||
{
|
||||
std::string data = "some private data";
|
||||
std::string wrong_secret = "wrong secret";
|
||||
auto encrypted_data = SimpleEncryption::encrypt_data(data, secret);
|
||||
auto encrypted_data = Encryption::encrypt_data(data, secret);
|
||||
LOG(ERROR) << encrypted_data.size();
|
||||
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, secret).move_as_ok();
|
||||
auto decrypted_data = Encryption::decrypt_data(encrypted_data, secret).move_as_ok();
|
||||
CHECK(data == decrypted_data);
|
||||
SimpleEncryption::decrypt_data(encrypted_data, wrong_secret).ensure_error();
|
||||
SimpleEncryption::decrypt_data("", secret).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(32, 'a'), secret).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(33, 'a'), secret).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(64, 'a'), secret).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(128, 'a'), secret).ensure_error();
|
||||
Encryption::decrypt_data(encrypted_data, wrong_secret).ensure_error();
|
||||
Encryption::decrypt_data("", secret).ensure_error();
|
||||
Encryption::decrypt_data(std::string(32, 'a'), secret).ensure_error();
|
||||
Encryption::decrypt_data(std::string(33, 'a'), secret).ensure_error();
|
||||
Encryption::decrypt_data(std::string(64, 'a'), secret).ensure_error();
|
||||
Encryption::decrypt_data(std::string(128, 'a'), secret).ensure_error();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 255; i++) {
|
||||
auto data = td::rand_string('a', 'z', static_cast<int>(i));
|
||||
auto encrypted_data = SimpleEncryption::encrypt_data(data, secret);
|
||||
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, secret).move_as_ok();
|
||||
auto encrypted_data = Encryption::encrypt_data(data, secret);
|
||||
auto decrypted_data = Encryption::decrypt_data(encrypted_data, secret).move_as_ok();
|
||||
CHECK(data == decrypted_data);
|
||||
}
|
||||
}
|
||||
TEST(Tonlib, SimpleEncryption) {
|
||||
test_encryption<SimpleEncryption>();
|
||||
}
|
||||
|
||||
TEST(Tonlib, SimpleEncryptionV2) {
|
||||
test_encryption<SimpleEncryptionV2>();
|
||||
}
|
||||
|
||||
TEST(Tonlib, SimpleEncryptionAsym) {
|
||||
auto private_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
|
@ -187,26 +195,26 @@ TEST(Tonlib, SimpleEncryptionAsym) {
|
|||
auto wrong_private_key = td::Ed25519::generate_private_key().move_as_ok();
|
||||
{
|
||||
std::string data = "some private data";
|
||||
auto encrypted_data = SimpleEncryption::encrypt_data(data, public_key, other_private_key).move_as_ok();
|
||||
auto encrypted_data = SimpleEncryptionV2::encrypt_data(data, public_key, other_private_key).move_as_ok();
|
||||
LOG(ERROR) << encrypted_data.size();
|
||||
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, private_key).move_as_ok();
|
||||
auto decrypted_data = SimpleEncryptionV2::decrypt_data(encrypted_data, private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data);
|
||||
auto decrypted_data2 = SimpleEncryption::decrypt_data(encrypted_data, other_private_key).move_as_ok();
|
||||
auto decrypted_data2 = SimpleEncryptionV2::decrypt_data(encrypted_data, other_private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data2);
|
||||
SimpleEncryption::decrypt_data(encrypted_data, wrong_private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data("", private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(32, 'a'), private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(33, 'a'), private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(64, 'a'), private_key).ensure_error();
|
||||
SimpleEncryption::decrypt_data(std::string(128, 'a'), private_key).ensure_error();
|
||||
SimpleEncryptionV2::decrypt_data(encrypted_data, wrong_private_key).ensure_error();
|
||||
SimpleEncryptionV2::decrypt_data("", private_key).ensure_error();
|
||||
SimpleEncryptionV2::decrypt_data(std::string(32, 'a'), private_key).ensure_error();
|
||||
SimpleEncryptionV2::decrypt_data(std::string(33, 'a'), private_key).ensure_error();
|
||||
SimpleEncryptionV2::decrypt_data(std::string(64, 'a'), private_key).ensure_error();
|
||||
SimpleEncryptionV2::decrypt_data(std::string(128, 'a'), private_key).ensure_error();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 255; i++) {
|
||||
auto data = td::rand_string('a', 'z', static_cast<int>(i));
|
||||
auto encrypted_data = SimpleEncryption::encrypt_data(data, public_key, other_private_key).move_as_ok();
|
||||
auto decrypted_data = SimpleEncryption::decrypt_data(encrypted_data, private_key).move_as_ok();
|
||||
auto encrypted_data = SimpleEncryptionV2::encrypt_data(data, public_key, other_private_key).move_as_ok();
|
||||
auto decrypted_data = SimpleEncryptionV2::decrypt_data(encrypted_data, private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data);
|
||||
auto decrypted_data2 = SimpleEncryption::decrypt_data(encrypted_data, other_private_key).move_as_ok();
|
||||
auto decrypted_data2 = SimpleEncryptionV2::decrypt_data(encrypted_data, other_private_key).move_as_ok();
|
||||
CHECK(data == decrypted_data2);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 "KeyStorage.h"
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -386,6 +386,15 @@ class AccountState {
|
|||
{ton::HighloadWalletV2::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)});
|
||||
return wallet_type_;
|
||||
}
|
||||
o_revision = ton::ManualDns::guess_revision(address_, key, wallet_id_);
|
||||
if (o_revision) {
|
||||
wallet_type_ = WalletType::ManualDns;
|
||||
wallet_revision_ = o_revision.value();
|
||||
LOG(ERROR) << "!!!" << wallet_revision_;
|
||||
auto dns = ton::ManualDns::create(key, wallet_id_, wallet_revision_);
|
||||
set_new_state(dns->get_state());
|
||||
return wallet_type_;
|
||||
}
|
||||
if (ton::GenericAccount::get_address(address_.workchain, ton::TestWallet::get_init_state(key)).addr ==
|
||||
address_.addr) {
|
||||
set_new_state({ton::TestWallet::get_init_code(), ton::TestWallet::get_init_data(key)});
|
||||
|
@ -399,12 +408,6 @@ class AccountState {
|
|||
.addr == address_.addr) {
|
||||
set_new_state({ton::HighloadWallet::get_init_code(), ton::HighloadWallet::get_init_data(key, wallet_id_)});
|
||||
wallet_type_ = WalletType::HighloadWalletV1;
|
||||
} else {
|
||||
auto dns = ton::ManualDns::create(key, wallet_id_);
|
||||
if (dns->get_address().addr == address_.addr) {
|
||||
set_new_state(dns->get_state());
|
||||
wallet_type_ = WalletType::ManualDns;
|
||||
}
|
||||
}
|
||||
return wallet_type_;
|
||||
}
|
||||
|
@ -466,6 +469,12 @@ class AccountState {
|
|||
wallet_revision_ = o_revision.value();
|
||||
return wallet_type_;
|
||||
}
|
||||
o_revision = ton::ManualDns::guess_revision(code_hash);
|
||||
if (o_revision) {
|
||||
wallet_type_ = WalletType::ManualDns;
|
||||
wallet_revision_ = o_revision.value();
|
||||
return wallet_type_;
|
||||
}
|
||||
|
||||
if (code_hash == ton::TestGiver::get_init_code_hash()) {
|
||||
wallet_type_ = WalletType::Giver;
|
||||
|
@ -475,8 +484,6 @@ class AccountState {
|
|||
wallet_type_ = WalletType::Wallet;
|
||||
} else if (code_hash == ton::HighloadWallet::get_init_code_hash()) {
|
||||
wallet_type_ = WalletType::HighloadWalletV1;
|
||||
} else if (code_hash == ton::SmartContractCode::dns_manual()->get_hash()) {
|
||||
wallet_type_ = WalletType::ManualDns;
|
||||
} else {
|
||||
LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice());
|
||||
wallet_type_ = WalletType::Unknown;
|
||||
|
@ -1308,7 +1315,7 @@ td::Result<block::StdAddress> get_account_address(const tonlib_api::dns_initialA
|
|||
td::int32 revision) {
|
||||
TRY_RESULT(key_bytes, get_public_key(dns_state.public_key_));
|
||||
auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key));
|
||||
return ton::ManualDns::create(key, static_cast<td::uint32>(dns_state.wallet_id_))->get_address();
|
||||
return ton::ManualDns::create(key, static_cast<td::uint32>(dns_state.wallet_id_), revision)->get_address();
|
||||
}
|
||||
|
||||
td::Result<block::StdAddress> get_account_address(td::Slice account_address) {
|
||||
|
@ -1621,6 +1628,7 @@ struct ToRawTransactions {
|
|||
}
|
||||
auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str();
|
||||
std::string body_message;
|
||||
bool is_encrypted = false;
|
||||
if (body->size() >= 32 && body->prefetch_long(32) <= 1) {
|
||||
auto type = body.write().fetch_long(32);
|
||||
auto r_body_message = vm::CellString::load(body.write());
|
||||
|
@ -1631,7 +1639,8 @@ struct ToRawTransactions {
|
|||
if (!private_key_) {
|
||||
return TonlibError::EmptyField("private_key");
|
||||
}
|
||||
TRY_RESULT(decrypted, SimpleEncryption::decrypt_data(message, private_key_.value()));
|
||||
TRY_RESULT(decrypted, SimpleEncryptionV2::decrypt_data(message, private_key_.value()));
|
||||
is_encrypted = true;
|
||||
return decrypted.as_slice().str();
|
||||
}();
|
||||
}
|
||||
|
@ -1644,7 +1653,7 @@ struct ToRawTransactions {
|
|||
|
||||
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), std::move(dest), balance, fwd_fee,
|
||||
ihr_fee, created_lt, std::move(body_hash),
|
||||
std::move(body_message));
|
||||
std::move(body_message), is_encrypted);
|
||||
}
|
||||
case block::gen::CommonMsgInfo::ext_in_msg_info: {
|
||||
block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info;
|
||||
|
@ -1661,7 +1670,7 @@ struct ToRawTransactions {
|
|||
}
|
||||
auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str();
|
||||
return tonlib_api::make_object<tonlib_api::raw_message>("", std::move(dest), 0, 0, 0, 0, std::move(body_hash),
|
||||
"");
|
||||
"", false);
|
||||
}
|
||||
case block::gen::CommonMsgInfo::ext_out_msg_info: {
|
||||
block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info;
|
||||
|
@ -1669,7 +1678,7 @@ struct ToRawTransactions {
|
|||
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info");
|
||||
}
|
||||
TRY_RESULT(src, to_std_address(msg_info.src));
|
||||
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0, 0, 0, 0, "", "");
|
||||
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0, 0, 0, 0, "", "", false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2207,13 +2216,13 @@ class GenericCreateSendGrams : public TonlibQueryActor {
|
|||
return TonlibError::EmptyField("private_key");
|
||||
}
|
||||
if (!destination->is_wallet()) {
|
||||
return TonlibError::AccountActionUnsupported("Get public key (in destination)");
|
||||
return TonlibError::MessageEncryption("Get public key (in destination)");
|
||||
}
|
||||
auto wallet = destination->get_wallet();
|
||||
TRY_RESULT_PREFIX(public_key, wallet->get_public_key(),
|
||||
TonlibError::AccountActionUnsupported("Get public key (in destination)"));
|
||||
TRY_RESULT_PREFIX(encrypted_message,
|
||||
SimpleEncryption::encrypt_data(action.message, public_key, private_key_.value()),
|
||||
SimpleEncryptionV2::encrypt_data(action.message, public_key, private_key_.value()),
|
||||
TonlibError::Internal());
|
||||
gift.message = encrypted_message.as_slice().str();
|
||||
gift.is_encrypted = true;
|
||||
|
|
|
@ -95,6 +95,9 @@ struct TonlibError {
|
|||
static td::Status DangerousTransaction(td::Slice reason) {
|
||||
return td::Status::Error(400, PSLICE() << "DANGEROUS_TRANSACTION: " << reason);
|
||||
}
|
||||
static td::Status MessageEncryption(td::Slice reason) {
|
||||
return td::Status::Error(400, PSLICE() << "MESSAGE_ENCRYPTION: " << reason);
|
||||
}
|
||||
static td::Status AccountNotInited() {
|
||||
return td::Status::Error(400, "ACCOUNT_NOT_INITED");
|
||||
}
|
||||
|
|
|
@ -37,8 +37,8 @@ td::AesCbcState SimpleEncryption::calc_aes_cbc_state_sha512(td::Slice seed) {
|
|||
return calc_aes_cbc_state_hash(hash.as_slice());
|
||||
}
|
||||
|
||||
td::SecureString SimpleEncryption::gen_random_prefix(td::int64 data_size) {
|
||||
td::SecureString buff(td::narrow_cast<size_t>(((32 + 15 + data_size) & -16) - data_size), 0);
|
||||
td::SecureString SimpleEncryption::gen_random_prefix(td::int64 data_size, td::int64 min_padding) {
|
||||
td::SecureString buff(td::narrow_cast<size_t>(((min_padding + 15 + data_size) & -16) - data_size), 0);
|
||||
td::Random::secure_bytes(buff.as_mutable_slice());
|
||||
buff.as_mutable_slice()[0] = td::narrow_cast<td::uint8>(buff.size());
|
||||
CHECK((buff.size() + data_size) % 16 == 0);
|
||||
|
@ -72,7 +72,7 @@ td::SecureString SimpleEncryption::encrypt_data_with_prefix(td::Slice data, td::
|
|||
}
|
||||
|
||||
td::SecureString SimpleEncryption::encrypt_data(td::Slice data, td::Slice secret) {
|
||||
auto prefix = gen_random_prefix(data.size());
|
||||
auto prefix = gen_random_prefix(data.size(), 32);
|
||||
td::SecureString combined(prefix.size() + data.size());
|
||||
combined.as_mutable_slice().copy_from(prefix);
|
||||
combined.as_mutable_slice().substr(prefix.size()).copy_from(data);
|
||||
|
@ -106,7 +106,8 @@ td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice encrypted_
|
|||
return td::SecureString(decrypted_data.as_slice().substr(prefix_size));
|
||||
}
|
||||
|
||||
td::Result<td::SecureString> SimpleEncryption::encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key) {
|
||||
td::Result<td::SecureString> SimpleEncryptionV2::encrypt_data(td::Slice data,
|
||||
const td::Ed25519::PublicKey &public_key) {
|
||||
TRY_RESULT(tmp_private_key, td::Ed25519::generate_private_key());
|
||||
return encrypt_data(data, public_key, tmp_private_key);
|
||||
}
|
||||
|
@ -122,8 +123,8 @@ td::SecureString secure_xor(td::Slice a, td::Slice b) {
|
|||
}
|
||||
} // namespace
|
||||
|
||||
td::Result<td::SecureString> SimpleEncryption::encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
|
||||
const td::Ed25519::PrivateKey &private_key) {
|
||||
td::Result<td::SecureString> SimpleEncryptionV2::encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
|
||||
const td::Ed25519::PrivateKey &private_key) {
|
||||
TRY_RESULT(shared_secret, td::Ed25519::compute_shared_secret(public_key, private_key));
|
||||
auto encrypted = encrypt_data(data, shared_secret.as_slice());
|
||||
TRY_RESULT(tmp_public_key, private_key.get_public_key());
|
||||
|
@ -135,8 +136,8 @@ td::Result<td::SecureString> SimpleEncryption::encrypt_data(td::Slice data, cons
|
|||
return std::move(prefixed_encrypted);
|
||||
}
|
||||
|
||||
td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice data,
|
||||
const td::Ed25519::PrivateKey &private_key) {
|
||||
td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data(td::Slice data,
|
||||
const td::Ed25519::PrivateKey &private_key) {
|
||||
if (data.size() < td::Ed25519::PublicKey::LENGTH) {
|
||||
return td::Status::Error("Failed to decrypte: data is too small");
|
||||
}
|
||||
|
@ -147,4 +148,53 @@ td::Result<td::SecureString> SimpleEncryption::decrypt_data(td::Slice data,
|
|||
TRY_RESULT(decrypted, decrypt_data(data.substr(td::Ed25519::PublicKey::LENGTH), shared_secret.as_slice()));
|
||||
return std::move(decrypted);
|
||||
}
|
||||
td::SecureString SimpleEncryptionV2::encrypt_data(td::Slice data, td::Slice secret) {
|
||||
auto prefix = SimpleEncryption::gen_random_prefix(data.size(), 16);
|
||||
td::SecureString combined(prefix.size() + data.size());
|
||||
combined.as_mutable_slice().copy_from(prefix);
|
||||
combined.as_mutable_slice().substr(prefix.size()).copy_from(data);
|
||||
return encrypt_data_with_prefix(combined.as_slice(), secret);
|
||||
}
|
||||
td::Result<td::SecureString> SimpleEncryptionV2::decrypt_data(td::Slice encrypted_data, td::Slice secret) {
|
||||
if (encrypted_data.size() < 17) {
|
||||
return td::Status::Error("Failed to decrypt: data is too small");
|
||||
}
|
||||
if (encrypted_data.size() % 16 != 0) {
|
||||
return td::Status::Error("Failed to decrypt: data size is not divisible by 16");
|
||||
}
|
||||
auto msg_key = encrypted_data.substr(0, 16);
|
||||
encrypted_data = encrypted_data.substr(16);
|
||||
|
||||
auto cbc_state = SimpleEncryption::calc_aes_cbc_state_hash(SimpleEncryption::combine_secrets(secret, msg_key));
|
||||
td::SecureString decrypted_data(encrypted_data.size(), 0);
|
||||
cbc_state.decrypt(encrypted_data, decrypted_data.as_mutable_slice());
|
||||
|
||||
auto data_hash = SimpleEncryption::combine_secrets(decrypted_data, secret);
|
||||
auto got_msg_key = data_hash.as_slice().substr(0, 16);
|
||||
// check hash
|
||||
if (msg_key != got_msg_key) {
|
||||
return td::Status::Error("Failed to decrypt: hash mismatch");
|
||||
}
|
||||
|
||||
td::uint8 prefix_size = static_cast<td::uint8>(decrypted_data[0]);
|
||||
if (prefix_size > decrypted_data.size() || prefix_size < 16) {
|
||||
return td::Status::Error("Failed to decrypt: invalid prefix size");
|
||||
}
|
||||
|
||||
return td::SecureString(decrypted_data.as_slice().substr(prefix_size));
|
||||
}
|
||||
td::SecureString SimpleEncryptionV2::encrypt_data_with_prefix(td::Slice data, td::Slice secret) {
|
||||
CHECK(data.size() % 16 == 0);
|
||||
auto data_hash = SimpleEncryption::combine_secrets(data, secret);
|
||||
auto msg_key = data_hash.as_slice().substr(0, 16);
|
||||
|
||||
td::SecureString res_buf(data.size() + 16, 0);
|
||||
auto res = res_buf.as_mutable_slice();
|
||||
res.copy_from(msg_key);
|
||||
|
||||
auto cbc_state = SimpleEncryption::calc_aes_cbc_state_hash(SimpleEncryption::combine_secrets(secret, msg_key));
|
||||
cbc_state.encrypt(data, res.substr(16));
|
||||
|
||||
return res_buf;
|
||||
}
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -31,16 +31,26 @@ class SimpleEncryption {
|
|||
static td::SecureString combine_secrets(td::Slice a, td::Slice b);
|
||||
static td::SecureString kdf(td::Slice secret, td::Slice password, int iterations);
|
||||
|
||||
private:
|
||||
static td::AesCbcState calc_aes_cbc_state_hash(td::Slice hash);
|
||||
static td::AesCbcState calc_aes_cbc_state_sha512(td::Slice seed);
|
||||
static td::SecureString gen_random_prefix(td::int64 data_size, td::int64 min_padding);
|
||||
|
||||
static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret);
|
||||
|
||||
friend class SimpleEncryptionV2;
|
||||
};
|
||||
|
||||
class SimpleEncryptionV2 {
|
||||
public:
|
||||
static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key);
|
||||
static td::Result<td::SecureString> decrypt_data(td::Slice data, const td::Ed25519::PrivateKey &private_key);
|
||||
static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
|
||||
const td::Ed25519::PrivateKey &private_key);
|
||||
static td::SecureString encrypt_data(td::Slice data, td::Slice secret);
|
||||
static td::Result<td::SecureString> decrypt_data(td::Slice encrypted_data, td::Slice secret);
|
||||
|
||||
private:
|
||||
static td::AesCbcState calc_aes_cbc_state_hash(td::Slice hash);
|
||||
static td::AesCbcState calc_aes_cbc_state_sha512(td::Slice seed);
|
||||
static td::SecureString gen_random_prefix(td::int64 data_size);
|
||||
|
||||
static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret);
|
||||
};
|
||||
} // namespace tonlib
|
||||
|
|
|
@ -1356,7 +1356,50 @@ class TonlibCli : public td::actor::Actor {
|
|||
std::move(input_key), ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address)),
|
||||
std::move(state->last_transaction_id_)),
|
||||
promise.wrap([](auto res) {
|
||||
td::TerminalIO::out() << to_string(res) << "\n";
|
||||
td::StringBuilder sb;
|
||||
for (tonlib_api::object_ptr<tonlib_api::raw_transaction>& t : res->transactions_) {
|
||||
td::int64 balance = 0;
|
||||
balance += t->in_msg_->value_;
|
||||
for (auto& ot : t->out_msgs_) {
|
||||
balance -= ot->value_;
|
||||
}
|
||||
if (balance >= 0) {
|
||||
sb << Grams{td::uint64(balance)};
|
||||
} else {
|
||||
sb << "-" << Grams{td::uint64(-balance)};
|
||||
}
|
||||
sb << " Fee: " << Grams{td::uint64(t->fee_)};
|
||||
if (t->in_msg_->source_.empty()) {
|
||||
sb << " External ";
|
||||
} else {
|
||||
sb << " From " << t->in_msg_->source_;
|
||||
}
|
||||
if (!t->in_msg_->message_.empty()) {
|
||||
sb << " ";
|
||||
if (t->in_msg_->is_message_encrypted_) {
|
||||
sb << "e";
|
||||
}
|
||||
sb << "msg{" << t->in_msg_->message_ << "}";
|
||||
}
|
||||
for (auto& ot : t->out_msgs_) {
|
||||
sb << "\n\t";
|
||||
if (ot->destination_.empty()) {
|
||||
sb << " External ";
|
||||
} else {
|
||||
sb << " To " << ot->destination_;
|
||||
}
|
||||
sb << " " << Grams{td::uint64(ot->value_)};
|
||||
if (!ot->message_.empty()) {
|
||||
sb << " ";
|
||||
if (ot->is_message_encrypted_) {
|
||||
sb << "e";
|
||||
}
|
||||
sb << "msg{" << ot->message_ << "}";
|
||||
}
|
||||
}
|
||||
sb << "\n";
|
||||
}
|
||||
td::TerminalIO::out() << sb.as_cslice() << "\n";
|
||||
return td::Unit();
|
||||
}));
|
||||
}
|
||||
|
@ -1364,6 +1407,7 @@ class TonlibCli : public td::actor::Actor {
|
|||
void transfer(td::ConstParser& parser, td::Slice cmd, td::Promise<td::Unit> cmd_promise) {
|
||||
bool from_file = false;
|
||||
bool force = false;
|
||||
bool use_encryption = false;
|
||||
if (cmd != "init") {
|
||||
td::ConstParser cmd_parser(cmd);
|
||||
cmd_parser.advance(td::Slice("transfer").size());
|
||||
|
@ -1374,6 +1418,8 @@ class TonlibCli : public td::actor::Actor {
|
|||
from_file = true;
|
||||
} else if (c == 'f') {
|
||||
force = true;
|
||||
} else if (c == 'e') {
|
||||
use_encryption = true;
|
||||
} else {
|
||||
cmd_promise.set_error(td::Status::Error(PSLICE() << "Unknown suffix '" << c << "'"));
|
||||
return;
|
||||
|
@ -1394,13 +1440,21 @@ class TonlibCli : public td::actor::Actor {
|
|||
auto add_message = [&](td::ConstParser& parser) {
|
||||
auto to = parser.read_word();
|
||||
auto grams = parser.read_word();
|
||||
auto message = parser.read_word();
|
||||
parser.skip_whitespaces();
|
||||
auto message = parser.read_all();
|
||||
|
||||
Message res;
|
||||
TRY_RESULT(address, to_account_address(to, false));
|
||||
TRY_RESULT(amount, parse_grams(grams));
|
||||
messages.push_back(tonlib_api::make_object<tonlib_api::msg_message>(
|
||||
std::move(address.address), amount.nano, tonlib_api::make_object<tonlib_api::msg_dataText>(message.str())));
|
||||
tonlib_api::object_ptr<tonlib_api::msg_Data> data;
|
||||
|
||||
if (use_encryption) {
|
||||
data = tonlib_api::make_object<tonlib_api::msg_dataEncryptedText>(message.str());
|
||||
} else {
|
||||
data = tonlib_api::make_object<tonlib_api::msg_dataText>(message.str());
|
||||
}
|
||||
messages.push_back(
|
||||
tonlib_api::make_object<tonlib_api::msg_message>(std::move(address.address), amount.nano, std::move(data)));
|
||||
return td::Status::OK();
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue