1
0
Fork 0
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:
ton 2020-02-15 20:03:17 +04:00
parent 493ae2410c
commit a73d202ba2
50 changed files with 1340 additions and 271 deletions

View file

@ -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()

View file

@ -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;

View file

@ -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;

View file

@ -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

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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);

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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:

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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");

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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);

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include <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 {

View file

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

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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)

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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)));

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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);

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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 {

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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) {

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

View file

@ -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, ());
}

View file

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

View file

@ -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"

View file

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

View file

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

View file

@ -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";

View file

@ -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 {

View file

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

View file

@ -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

View file

@ -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

View file

@ -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());

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -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);

View file

@ -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

View file

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

View file

@ -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.

View file

@ -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

View file

@ -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>

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "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_;
}
}

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -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);

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
@ -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;

View file

@ -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.

View file

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

View file

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

View file

@ -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 {

View file

@ -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";
}

View file

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

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#include "KeyStorage.h"

View file

@ -14,7 +14,7 @@
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2019 Telegram Systems LLP
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once

View file

@ -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;

View file

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

View file

@ -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

View file

@ -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

View file

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