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