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/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-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)
 | 
					  GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext)
 | 
				
			||||||
endif()
 | 
					endif()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -463,6 +463,7 @@ Transaction::Transaction(const Account& _account, int ttype, ton::LogicalTime re
 | 
				
			||||||
    , my_addr(_account.my_addr)
 | 
					    , my_addr(_account.my_addr)
 | 
				
			||||||
    , my_addr_exact(_account.my_addr_exact)
 | 
					    , my_addr_exact(_account.my_addr_exact)
 | 
				
			||||||
    , balance(_account.balance)
 | 
					    , balance(_account.balance)
 | 
				
			||||||
 | 
					    , original_balance(_account.balance)
 | 
				
			||||||
    , due_payment(_account.due_payment)
 | 
					    , due_payment(_account.due_payment)
 | 
				
			||||||
    , last_paid(_account.last_paid)
 | 
					    , last_paid(_account.last_paid)
 | 
				
			||||||
    , new_code(_account.code)
 | 
					    , new_code(_account.code)
 | 
				
			||||||
| 
						 | 
					@ -912,6 +913,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
 | 
				
			||||||
  // ...
 | 
					  // ...
 | 
				
			||||||
  compute_phase = std::make_unique<ComputePhase>();
 | 
					  compute_phase = std::make_unique<ComputePhase>();
 | 
				
			||||||
  ComputePhase& cp = *(compute_phase.get());
 | 
					  ComputePhase& cp = *(compute_phase.get());
 | 
				
			||||||
 | 
					  original_balance -= total_fees;
 | 
				
			||||||
  if (td::sgn(balance.grams) <= 0) {
 | 
					  if (td::sgn(balance.grams) <= 0) {
 | 
				
			||||||
    // no gas
 | 
					    // no gas
 | 
				
			||||||
    cp.skip_reason = ComputePhase::sk_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) {
 | 
					int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap, const ActionPhaseConfig& cfg) {
 | 
				
			||||||
  block::gen::OutAction::Record_action_reserve_currency rec;
 | 
					  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;
 | 
					    return -1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  int mode = rec.mode;
 | 
					  int mode = rec.mode;
 | 
				
			||||||
| 
						 | 
					@ -1661,7 +1663,21 @@ int Transaction::try_action_reserve_currency(vm::CellSlice& cs, ActionPhase& ap,
 | 
				
			||||||
    return -1;
 | 
					    return -1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  LOG(DEBUG) << "action_reserve_currency: mode=" << mode << ", reserve=" << reserve.to_str()
 | 
					  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 (reserve.grams > ap.remaining_balance.grams) {
 | 
				
			||||||
    if (mode & 2) {
 | 
					    if (mode & 2) {
 | 
				
			||||||
      reserve.grams = ap.remaining_balance.grams;
 | 
					      reserve.grams = ap.remaining_balance.grams;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -194,10 +194,6 @@ struct ActionPhase {
 | 
				
			||||||
  Ref<vm::Cell> new_code;
 | 
					  Ref<vm::Cell> new_code;
 | 
				
			||||||
  td::BitArray<256> action_list_hash;
 | 
					  td::BitArray<256> action_list_hash;
 | 
				
			||||||
  block::CurrencyCollection remaining_balance, reserved_balance;
 | 
					  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>> action_list;  // processed in reverse order
 | 
				
			||||||
  std::vector<Ref<vm::Cell>> out_msgs;
 | 
					  std::vector<Ref<vm::Cell>> out_msgs;
 | 
				
			||||||
  ton::LogicalTime end_lt;
 | 
					  ton::LogicalTime end_lt;
 | 
				
			||||||
| 
						 | 
					@ -319,7 +315,7 @@ struct Transaction {
 | 
				
			||||||
  const Account& account;                     // only `commit` method modifies the account
 | 
					  const Account& account;                     // only `commit` method modifies the account
 | 
				
			||||||
  Ref<vm::CellSlice> my_addr, my_addr_exact;  // almost the same as in account.*
 | 
					  Ref<vm::CellSlice> my_addr, my_addr_exact;  // almost the same as in account.*
 | 
				
			||||||
  ton::LogicalTime start_lt, end_lt;
 | 
					  ton::LogicalTime start_lt, end_lt;
 | 
				
			||||||
  block::CurrencyCollection balance;
 | 
					  block::CurrencyCollection balance, original_balance;
 | 
				
			||||||
  block::CurrencyCollection msg_balance_remaining;
 | 
					  block::CurrencyCollection msg_balance_remaining;
 | 
				
			||||||
  td::RefInt256 due_payment;
 | 
					  td::RefInt256 due_payment;
 | 
				
			||||||
  td::RefInt256 in_fwd_fee, msg_fwd_fees;
 | 
					  td::RefInt256 in_fwd_fee, msg_fwd_fees;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -165,6 +165,8 @@ x{68} @Defop DEPTH
 | 
				
			||||||
x{69} @Defop CHKDEPTH
 | 
					x{69} @Defop CHKDEPTH
 | 
				
			||||||
x{6A} @Defop ONLYTOPX
 | 
					x{6A} @Defop ONLYTOPX
 | 
				
			||||||
x{6B} @Defop ONLYX
 | 
					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
 | 
					// null primitives
 | 
				
			||||||
x{6D} dup @Defop NULL @Defop PUSHNULL
 | 
					x{6D} dup @Defop NULL @Defop PUSHNULL
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "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);
 | 
					      show_var_list(os, right, vars);
 | 
				
			||||||
      os << std::endl;
 | 
					      os << std::endl;
 | 
				
			||||||
      break;
 | 
					      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:
 | 
					    case _IntConst:
 | 
				
			||||||
      os << pfx << dis << "CONST ";
 | 
					      os << pfx << dis << "CONST ";
 | 
				
			||||||
      show_var_list(os, left, vars);
 | 
					      show_var_list(os, left, vars);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "func.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,7 @@ namespace funC {
 | 
				
			||||||
void CodeBlob::simplify_var_types() {
 | 
					void CodeBlob::simplify_var_types() {
 | 
				
			||||||
  for (TmpVar& var : vars) {
 | 
					  for (TmpVar& var : vars) {
 | 
				
			||||||
    TypeExpr::remove_indirect(var.v_type);
 | 
					    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 _IntConst:
 | 
				
			||||||
    case _GlobVar:
 | 
					    case _GlobVar:
 | 
				
			||||||
    case _Call:
 | 
					    case _Call:
 | 
				
			||||||
    case _CallInd: {
 | 
					    case _CallInd:
 | 
				
			||||||
 | 
					    case _Tuple:
 | 
				
			||||||
 | 
					    case _UnTuple: {
 | 
				
			||||||
      // left = EXEC right;
 | 
					      // left = EXEC right;
 | 
				
			||||||
      if (!next_var_info.count_used(left) && is_pure()) {
 | 
					      if (!next_var_info.count_used(left) && is_pure()) {
 | 
				
			||||||
        // all variables in `left` are not needed
 | 
					        // all variables in `left` are not needed
 | 
				
			||||||
| 
						 | 
					@ -541,6 +544,8 @@ bool prune_unreachable(std::unique_ptr<Op>& ops) {
 | 
				
			||||||
    case Op::_SetGlob:
 | 
					    case Op::_SetGlob:
 | 
				
			||||||
    case Op::_Call:
 | 
					    case Op::_Call:
 | 
				
			||||||
    case Op::_CallInd:
 | 
					    case Op::_CallInd:
 | 
				
			||||||
 | 
					    case Op::_Tuple:
 | 
				
			||||||
 | 
					    case Op::_UnTuple:
 | 
				
			||||||
    case Op::_Import:
 | 
					    case Op::_Import:
 | 
				
			||||||
      reach = true;
 | 
					      reach = true;
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
| 
						 | 
					@ -724,6 +729,8 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    case _Tuple:
 | 
				
			||||||
 | 
					    case _UnTuple:
 | 
				
			||||||
    case _GlobVar:
 | 
					    case _GlobVar:
 | 
				
			||||||
    case _CallInd: {
 | 
					    case _CallInd: {
 | 
				
			||||||
      for (var_idx_t i : left) {
 | 
					      for (var_idx_t i : left) {
 | 
				
			||||||
| 
						 | 
					@ -842,6 +849,8 @@ bool Op::mark_noreturn() {
 | 
				
			||||||
    case _Import:
 | 
					    case _Import:
 | 
				
			||||||
    case _IntConst:
 | 
					    case _IntConst:
 | 
				
			||||||
    case _Let:
 | 
					    case _Let:
 | 
				
			||||||
 | 
					    case _Tuple:
 | 
				
			||||||
 | 
					    case _UnTuple:
 | 
				
			||||||
    case _SetGlob:
 | 
					    case _SetGlob:
 | 
				
			||||||
    case _GlobVar:
 | 
					    case _GlobVar:
 | 
				
			||||||
    case _CallInd:
 | 
					    case _CallInd:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "parser/srcread.h"
 | 
				
			||||||
#include "func.h"
 | 
					#include "func.h"
 | 
				
			||||||
| 
						 | 
					@ -121,12 +121,49 @@ AsmOp AsmOp::BlkDrop(int a) {
 | 
				
			||||||
  return AsmOp::Custom(os.str(), a, 0);
 | 
					  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) {
 | 
					AsmOp AsmOp::BlkReverse(int a, int b) {
 | 
				
			||||||
  std::ostringstream os;
 | 
					  std::ostringstream os;
 | 
				
			||||||
  os << a << " " << b << " REVERSE";
 | 
					  os << a << " " << b << " REVERSE";
 | 
				
			||||||
  return AsmOp::Custom(os.str(), a + b, a + b);
 | 
					  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) {
 | 
					AsmOp AsmOp::IntConst(td::RefInt256 x) {
 | 
				
			||||||
  if (x->signed_fits_bits(8)) {
 | 
					  if (x->signed_fits_bits(8)) {
 | 
				
			||||||
    return AsmOp::Const(dec_string(std::move(x)) + " PUSHINT");
 | 
					    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
 | 
					    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/>.
 | 
					    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 "func.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -318,7 +318,7 @@ bool Op::generate_code_step(Stack& stack) {
 | 
				
			||||||
        stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
 | 
					        stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
 | 
				
			||||||
        if (left.size() != 1) {
 | 
					        if (left.size() != 1) {
 | 
				
			||||||
          assert(left.size() <= 15);
 | 
					          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) {
 | 
					        for (auto i : left) {
 | 
				
			||||||
          stack.push_new_var(i);
 | 
					          stack.push_new_var(i);
 | 
				
			||||||
| 
						 | 
					@ -396,6 +396,32 @@ bool Op::generate_code_step(Stack& stack) {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return true;
 | 
					      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 _Call:
 | 
				
			||||||
    case _CallInd: {
 | 
					    case _CallInd: {
 | 
				
			||||||
      if (disabled()) {
 | 
					      if (disabled()) {
 | 
				
			||||||
| 
						 | 
					@ -483,7 +509,7 @@ bool Op::generate_code_step(Stack& stack) {
 | 
				
			||||||
        assert(stack.s[k + i].first == right[i]);
 | 
					        assert(stack.s[k + i].first == right[i]);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      if (right.size() > 1) {
 | 
					      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()) {
 | 
					      if (!right.empty()) {
 | 
				
			||||||
        std::string name = sym::symbols.get_name(fun_ref->sym_idx);
 | 
					        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
 | 
					    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/>.
 | 
					    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
 | 
					#pragma once
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
| 
						 | 
					@ -36,7 +36,7 @@ namespace funC {
 | 
				
			||||||
extern int verbosity;
 | 
					extern int verbosity;
 | 
				
			||||||
extern bool op_rewrite_comments;
 | 
					extern bool op_rewrite_comments;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
constexpr int optimize_depth = 12;
 | 
					constexpr int optimize_depth = 20;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum Keyword {
 | 
					enum Keyword {
 | 
				
			||||||
  _Eof = -1,
 | 
					  _Eof = -1,
 | 
				
			||||||
| 
						 | 
					@ -101,6 +101,7 @@ enum Keyword {
 | 
				
			||||||
  _Extern,
 | 
					  _Extern,
 | 
				
			||||||
  _Inline,
 | 
					  _Inline,
 | 
				
			||||||
  _InlineRef,
 | 
					  _InlineRef,
 | 
				
			||||||
 | 
					  _AutoApply,
 | 
				
			||||||
  _MethodId,
 | 
					  _MethodId,
 | 
				
			||||||
  _Operator,
 | 
					  _Operator,
 | 
				
			||||||
  _Infix,
 | 
					  _Infix,
 | 
				
			||||||
| 
						 | 
					@ -134,7 +135,7 @@ class IdSc {
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct TypeExpr {
 | 
					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 {
 | 
					  enum {
 | 
				
			||||||
    _Int = Keyword::_Int,
 | 
					    _Int = Keyword::_Int,
 | 
				
			||||||
    _Cell = Keyword::_Cell,
 | 
					    _Cell = Keyword::_Cell,
 | 
				
			||||||
| 
						 | 
					@ -160,6 +161,9 @@ struct TypeExpr {
 | 
				
			||||||
      : constr(_constr), value((int)list.size()), args(std::move(list)) {
 | 
					      : constr(_constr), value((int)list.size()), args(std::move(list)) {
 | 
				
			||||||
    compute_width();
 | 
					    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)
 | 
					  TypeExpr(te_type _constr, TypeExpr* elem0, std::vector<TypeExpr*> list)
 | 
				
			||||||
      : constr(_constr), value((int)list.size() + 1), args{elem0} {
 | 
					      : constr(_constr), value((int)list.size() + 1), args{elem0} {
 | 
				
			||||||
    args.insert(args.end(), list.begin(), list.end());
 | 
					    args.insert(args.end(), list.begin(), list.end());
 | 
				
			||||||
| 
						 | 
					@ -185,6 +189,9 @@ struct TypeExpr {
 | 
				
			||||||
  bool is_map() const {
 | 
					  bool is_map() const {
 | 
				
			||||||
    return constr == te_Map;
 | 
					    return constr == te_Map;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  bool is_tuple() const {
 | 
				
			||||||
 | 
					    return constr == te_Tuple;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  bool has_fixed_width() const {
 | 
					  bool has_fixed_width() const {
 | 
				
			||||||
    return minw == maxw;
 | 
					    return minw == maxw;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -226,6 +233,15 @@ struct TypeExpr {
 | 
				
			||||||
  static TypeExpr* new_tensor(TypeExpr* te1, TypeExpr* te2, TypeExpr* te3) {
 | 
					  static TypeExpr* new_tensor(TypeExpr* te1, TypeExpr* te2, TypeExpr* te3) {
 | 
				
			||||||
    return new_tensor({te1, te2, 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() {
 | 
					  static TypeExpr* new_var() {
 | 
				
			||||||
    return new TypeExpr{te_Var, --type_vars, 1};
 | 
					    return new TypeExpr{te_Var, --type_vars, 1};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -505,6 +521,8 @@ struct Op {
 | 
				
			||||||
    _SetGlob,
 | 
					    _SetGlob,
 | 
				
			||||||
    _Import,
 | 
					    _Import,
 | 
				
			||||||
    _Return,
 | 
					    _Return,
 | 
				
			||||||
 | 
					    _Tuple,
 | 
				
			||||||
 | 
					    _UnTuple,
 | 
				
			||||||
    _If,
 | 
					    _If,
 | 
				
			||||||
    _While,
 | 
					    _While,
 | 
				
			||||||
    _Until,
 | 
					    _Until,
 | 
				
			||||||
| 
						 | 
					@ -789,7 +807,8 @@ struct Expr {
 | 
				
			||||||
    _Apply,
 | 
					    _Apply,
 | 
				
			||||||
    _VarApply,
 | 
					    _VarApply,
 | 
				
			||||||
    _TypeApply,
 | 
					    _TypeApply,
 | 
				
			||||||
    _Tuple,
 | 
					    _MkTuple,
 | 
				
			||||||
 | 
					    _Tensor,
 | 
				
			||||||
    _Const,
 | 
					    _Const,
 | 
				
			||||||
    _Var,
 | 
					    _Var,
 | 
				
			||||||
    _Glob,
 | 
					    _Glob,
 | 
				
			||||||
| 
						 | 
					@ -843,6 +862,12 @@ struct Expr {
 | 
				
			||||||
  bool is_type() const {
 | 
					  bool is_type() const {
 | 
				
			||||||
    return flags & _IsType;
 | 
					    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_rvalue(const Lexem& lem) const;
 | 
				
			||||||
  void chk_lvalue(const Lexem& lem) const;
 | 
					  void chk_lvalue(const Lexem& lem) const;
 | 
				
			||||||
  void chk_type(const Lexem& lem) const;
 | 
					  void chk_type(const Lexem& lem) const;
 | 
				
			||||||
| 
						 | 
					@ -855,7 +880,8 @@ struct Expr {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  int define_new_vars(CodeBlob& code);
 | 
					  int define_new_vars(CodeBlob& code);
 | 
				
			||||||
  int predefine_vars();
 | 
					  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;
 | 
					  var_idx_t new_tmp(CodeBlob& code) const;
 | 
				
			||||||
  std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
 | 
					  std::vector<var_idx_t> new_tmp_vect(CodeBlob& code) const {
 | 
				
			||||||
    return {new_tmp(code)};
 | 
					    return {new_tmp(code)};
 | 
				
			||||||
| 
						 | 
					@ -1004,6 +1030,7 @@ struct AsmOp {
 | 
				
			||||||
  static AsmOp BlkSwap(int a, int b);
 | 
					  static AsmOp BlkSwap(int a, int b);
 | 
				
			||||||
  static AsmOp BlkPush(int a, int b);
 | 
					  static AsmOp BlkPush(int a, int b);
 | 
				
			||||||
  static AsmOp BlkDrop(int a);
 | 
					  static AsmOp BlkDrop(int a);
 | 
				
			||||||
 | 
					  static AsmOp BlkDrop2(int a, int b);
 | 
				
			||||||
  static AsmOp BlkReverse(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_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);
 | 
					  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);
 | 
					    return AsmOp(a_custom, args, retv, custom_op);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  static AsmOp Parse(std::string custom_op, int args, int retv = 1);
 | 
					  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) {
 | 
					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_blkpush(int* i, int* j) const;
 | 
					  bool is_blkpush(int* i, int* j) const;
 | 
				
			||||||
  bool is_blkdrop(int* i) 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_reverse(int* i, int* j) const;
 | 
					  bool is_reverse(int* i, int* j) const;
 | 
				
			||||||
  bool is_nip_seq(int i, int j = 0) 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_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_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;
 | 
					  void show(std::ostream& os, int mode = 0) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1307,11 +1341,12 @@ struct Optimizer {
 | 
				
			||||||
  AsmOpCons* op_cons_[n];
 | 
					  AsmOpCons* op_cons_[n];
 | 
				
			||||||
  int offs_[n];
 | 
					  int offs_[n];
 | 
				
			||||||
  StackTransform tr_[n];
 | 
					  StackTransform tr_[n];
 | 
				
			||||||
 | 
					  int mode_{0};
 | 
				
			||||||
  Optimizer() {
 | 
					  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));
 | 
					    set_code(std::move(code));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  void set_code(AsmOpConsList code_);
 | 
					  void set_code(AsmOpConsList code_);
 | 
				
			||||||
| 
						 | 
					@ -1327,12 +1362,15 @@ struct Optimizer {
 | 
				
			||||||
  void show_head() const;
 | 
					  void show_head() const;
 | 
				
			||||||
  void show_left() const;
 | 
					  void show_left() const;
 | 
				
			||||||
  void show_right() const;
 | 
					  void show_right() const;
 | 
				
			||||||
 | 
					  bool find_const_op(int* op_idx, int cst);
 | 
				
			||||||
  bool is_const_push_swap() const;
 | 
					  bool is_const_push_swap() const;
 | 
				
			||||||
  bool rewrite_const_push_swap();
 | 
					  bool rewrite_const_push_swap();
 | 
				
			||||||
  bool is_const_push_xchgs();
 | 
					  bool is_const_push_xchgs();
 | 
				
			||||||
  bool rewrite_const_push_xchgs();
 | 
					  bool rewrite_const_push_xchgs();
 | 
				
			||||||
  bool is_const_rot() const;
 | 
					  bool is_const_rot(int* c) const;
 | 
				
			||||||
  bool rewrite_const_rot();
 | 
					  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_op);
 | 
				
			||||||
  bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2);
 | 
					  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);
 | 
					  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_blkswap(int* i, int* j);
 | 
				
			||||||
  bool is_blkpush(int* i, int* j);
 | 
					  bool is_blkpush(int* i, int* j);
 | 
				
			||||||
  bool is_blkdrop(int* i);
 | 
					  bool is_blkdrop(int* i);
 | 
				
			||||||
 | 
					  bool is_blkdrop2(int* i, int* j);
 | 
				
			||||||
  bool is_reverse(int* i, int* j);
 | 
					  bool is_reverse(int* i, int* j);
 | 
				
			||||||
  bool is_nip_seq(int* i, int* j);
 | 
					  bool is_nip_seq(int* i, int* j);
 | 
				
			||||||
  bool is_pop_blkdrop(int* i, int* k);
 | 
					  bool is_pop_blkdrop(int* i, int* k);
 | 
				
			||||||
| 
						 | 
					@ -1381,8 +1420,8 @@ struct Optimizer {
 | 
				
			||||||
  AsmOpConsList extract_code();
 | 
					  AsmOpConsList extract_code();
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AsmOpConsList optimize_code_head(AsmOpConsList op_list);
 | 
					AsmOpConsList optimize_code_head(AsmOpConsList op_list, int mode = 0);
 | 
				
			||||||
AsmOpConsList optimize_code(AsmOpConsList op_list);
 | 
					AsmOpConsList optimize_code(AsmOpConsList op_list, int mode);
 | 
				
			||||||
void optimize_code(AsmOpList& ops);
 | 
					void optimize_code(AsmOpList& ops);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Stack {
 | 
					struct Stack {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,10 +14,12 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "func.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace std::literals::string_literals;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace funC {
 | 
					namespace funC {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
| 
						 | 
					@ -164,7 +166,8 @@ bool Expr::deduce_type(const Lexem& lem) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int Expr::define_new_vars(CodeBlob& code) {
 | 
					int Expr::define_new_vars(CodeBlob& code) {
 | 
				
			||||||
  switch (cls) {
 | 
					  switch (cls) {
 | 
				
			||||||
    case _Tuple:
 | 
					    case _Tensor:
 | 
				
			||||||
 | 
					    case _MkTuple:
 | 
				
			||||||
    case _TypeApply: {
 | 
					    case _TypeApply: {
 | 
				
			||||||
      int res = 0;
 | 
					      int res = 0;
 | 
				
			||||||
      for (const auto& x : args) {
 | 
					      for (const auto& x : args) {
 | 
				
			||||||
| 
						 | 
					@ -189,7 +192,8 @@ int Expr::define_new_vars(CodeBlob& code) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int Expr::predefine_vars() {
 | 
					int Expr::predefine_vars() {
 | 
				
			||||||
  switch (cls) {
 | 
					  switch (cls) {
 | 
				
			||||||
    case _Tuple:
 | 
					    case _Tensor:
 | 
				
			||||||
 | 
					    case _MkTuple:
 | 
				
			||||||
    case _TypeApply: {
 | 
					    case _TypeApply: {
 | 
				
			||||||
      int res = 0;
 | 
					      int res = 0;
 | 
				
			||||||
      for (const auto& x : args) {
 | 
					      for (const auto& x : args) {
 | 
				
			||||||
| 
						 | 
					@ -201,6 +205,7 @@ int Expr::predefine_vars() {
 | 
				
			||||||
      if (!sym) {
 | 
					      if (!sym) {
 | 
				
			||||||
        assert(val < 0 && here.defined());
 | 
					        assert(val < 0 && here.defined());
 | 
				
			||||||
        sym = sym::define_symbol(~val, false, here);
 | 
					        sym = sym::define_symbol(~val, false, here);
 | 
				
			||||||
 | 
					        // std::cerr << "predefining variable " << sym::symbols.get_name(~val) << std::endl;
 | 
				
			||||||
        if (!sym) {
 | 
					        if (!sym) {
 | 
				
			||||||
          throw src::ParseError{here, std::string{"redefined variable `"} + sym::symbols.get_name(~val) + "`"};
 | 
					          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);
 | 
					  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) {
 | 
					  switch (cls) {
 | 
				
			||||||
    case _Tuple: {
 | 
					    case _Tensor: {
 | 
				
			||||||
      std::vector<var_idx_t> res;
 | 
					      std::vector<var_idx_t> res;
 | 
				
			||||||
      for (const auto& x : args) {
 | 
					      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());
 | 
					        res.insert(res.end(), add.cbegin(), add.cend());
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return res;
 | 
					      return res;
 | 
				
			||||||
| 
						 | 
					@ -253,7 +297,7 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
 | 
				
			||||||
      return rvect;
 | 
					      return rvect;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case _TypeApply:
 | 
					    case _TypeApply:
 | 
				
			||||||
      return args[0]->pre_compile(code);
 | 
					      return args[0]->pre_compile(code, lval);
 | 
				
			||||||
    case _Var:
 | 
					    case _Var:
 | 
				
			||||||
    case _Hole:
 | 
					    case _Hole:
 | 
				
			||||||
      return {val};
 | 
					      return {val};
 | 
				
			||||||
| 
						 | 
					@ -289,25 +333,22 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
 | 
				
			||||||
      return rvect;
 | 
					      return rvect;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case _Letop: {
 | 
					    case _Letop: {
 | 
				
			||||||
      auto right = args[1]->pre_compile(code);
 | 
					      return pre_compile_let(code, args.at(0), args.at(1), here);
 | 
				
			||||||
      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;
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case _LetFirst: {
 | 
					    case _LetFirst: {
 | 
				
			||||||
      auto rvect = new_tmp_vect(code);
 | 
					      auto rvect = new_tmp_vect(code);
 | 
				
			||||||
      auto right = args[1]->pre_compile(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]);
 | 
					      left.push_back(rvect[0]);
 | 
				
			||||||
      code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
 | 
					      code.emplace_back(here, Op::_Let, std::move(left), std::move(right));
 | 
				
			||||||
      return rvect;
 | 
					      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: {
 | 
					    case _CondExpr: {
 | 
				
			||||||
      auto cond = args[0]->pre_compile(code);
 | 
					      auto cond = args[0]->pre_compile(code);
 | 
				
			||||||
      assert(cond.size() == 1);
 | 
					      assert(cond.size() == 1);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "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(']')
 | 
				
			||||||
      .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("impure", Kw::_Impure)
 | 
				
			||||||
      .add_keyword("inline", Kw::_Inline)
 | 
					      .add_keyword("inline", Kw::_Inline)
 | 
				
			||||||
      .add_keyword("inline_ref", Kw::_InlineRef)
 | 
					      .add_keyword("inline_ref", Kw::_InlineRef)
 | 
				
			||||||
 | 
					      .add_keyword("auto_apply", Kw::_AutoApply)
 | 
				
			||||||
      .add_keyword("method_id", Kw::_MethodId)
 | 
					      .add_keyword("method_id", Kw::_MethodId)
 | 
				
			||||||
      .add_keyword("operator", Kw::_Operator)
 | 
					      .add_keyword("operator", Kw::_Operator)
 | 
				
			||||||
      .add_keyword("infix", Kw::_Infix)
 | 
					      .add_keyword("infix", Kw::_Infix)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "func.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -134,6 +134,16 @@ bool Optimizer::say(std::string str) const {
 | 
				
			||||||
  return true;
 | 
					  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 {
 | 
					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();
 | 
					  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;
 | 
					  return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool Optimizer::is_const_rot() const {
 | 
					bool Optimizer::is_const_rot(int* c) const {
 | 
				
			||||||
  return pb_ >= 3 && pb_ <= l2_ && op_[0]->is_gconst() && tr_[pb_ - 1].is_const_rot();
 | 
					  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_;
 | 
					  p_ = pb_;
 | 
				
			||||||
  q_ = 2;
 | 
					  q_ = 2;
 | 
				
			||||||
 | 
					  int idx = -1;
 | 
				
			||||||
 | 
					  if (!(p_ >= 2 && find_const_op(&idx, c) && idx < p_)) {
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  show_left();
 | 
					  show_left();
 | 
				
			||||||
  oq_[0] = std::move(op_[0]);
 | 
					  oq_[0] = std::move(op_[idx]);
 | 
				
			||||||
  oq_[1] = std::move(op_[1]);
 | 
					  oq_[1] = std::move(op_[idx ? 0 : 1]);
 | 
				
			||||||
  *oq_[1] = AsmOp::Custom("ROT", 3, 3);
 | 
					  *oq_[1] = AsmOp::Custom("ROT", 3, 3);
 | 
				
			||||||
  show_right();
 | 
					  show_right();
 | 
				
			||||||
  return true;
 | 
					  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() {
 | 
					bool Optimizer::is_const_push_xchgs() {
 | 
				
			||||||
  if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) {
 | 
					  if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) {
 | 
				
			||||||
    return false;
 | 
					    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; });
 | 
					  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) {
 | 
					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; });
 | 
					  return is_pred([i, j](const auto& t) { return t.is_reverse(i, j) && *i >= 2 && *i <= 17 && *j < 16; });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -488,38 +525,42 @@ bool Optimizer::find_at_least(int pb) {
 | 
				
			||||||
  p_ = q_ = 0;
 | 
					  p_ = q_ = 0;
 | 
				
			||||||
  pb_ = pb;
 | 
					  pb_ = pb;
 | 
				
			||||||
  // show_stack_transforms();
 | 
					  // 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()) ||
 | 
					  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_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))) ||
 | 
				
			||||||
         (is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
 | 
					         (!(mode_ & 1) &&
 | 
				
			||||||
         (is_2dup() && simple_rewrite(AsmOp::Custom("2DUP", 2, 4))) ||
 | 
					          ((is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) ||
 | 
				
			||||||
         (is_2swap() && simple_rewrite(AsmOp::Custom("2SWAP", 2, 4))) ||
 | 
					           (is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
 | 
				
			||||||
         (is_2over() && simple_rewrite(AsmOp::Custom("2OVER", 2, 4))) ||
 | 
					           (is_2dup() && simple_rewrite(AsmOp::Custom("2DUP", 2, 4))) ||
 | 
				
			||||||
         (is_tuck() && simple_rewrite(AsmOp::Custom("TUCK", 2, 3))) ||
 | 
					           (is_2swap() && simple_rewrite(AsmOp::Custom("2SWAP", 2, 4))) ||
 | 
				
			||||||
         (is_2drop() && simple_rewrite(AsmOp::Custom("2DROP", 2, 0))) ||
 | 
					           (is_2over() && simple_rewrite(AsmOp::Custom("2OVER", 2, 4))) ||
 | 
				
			||||||
         (is_xchg2(&i, &j) && simple_rewrite(AsmOp::Xchg2(i, j))) ||
 | 
					           (is_tuck() && simple_rewrite(AsmOp::Custom("TUCK", 2, 3))) ||
 | 
				
			||||||
         (is_xcpu(&i, &j) && simple_rewrite(AsmOp::XcPu(i, j))) ||
 | 
					           (is_2drop() && simple_rewrite(AsmOp::Custom("2DROP", 2, 0))) ||
 | 
				
			||||||
         (is_puxc(&i, &j) && simple_rewrite(AsmOp::PuXc(i, j))) ||
 | 
					           (is_xchg2(&i, &j) && simple_rewrite(AsmOp::Xchg2(i, j))) ||
 | 
				
			||||||
         (is_push2(&i, &j) && simple_rewrite(AsmOp::Push2(i, j))) ||
 | 
					           (is_xcpu(&i, &j) && simple_rewrite(AsmOp::XcPu(i, j))) ||
 | 
				
			||||||
         (is_blkswap(&i, &j) && simple_rewrite(AsmOp::BlkSwap(i, j))) ||
 | 
					           (is_puxc(&i, &j) && simple_rewrite(AsmOp::PuXc(i, j))) ||
 | 
				
			||||||
         (is_blkpush(&i, &j) && simple_rewrite(AsmOp::BlkPush(i, j))) ||
 | 
					           (is_push2(&i, &j) && simple_rewrite(AsmOp::Push2(i, j))) ||
 | 
				
			||||||
         (is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
 | 
					           (is_blkswap(&i, &j) && simple_rewrite(AsmOp::BlkSwap(i, j))) ||
 | 
				
			||||||
         (is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
 | 
					           (is_blkpush(&i, &j) && simple_rewrite(AsmOp::BlkPush(i, j))) ||
 | 
				
			||||||
         (is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
 | 
					           (is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
 | 
				
			||||||
         (is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
 | 
					           (is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
 | 
				
			||||||
         (is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
 | 
					           (is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
 | 
				
			||||||
                                              ? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
 | 
					           (is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
 | 
				
			||||||
                                              : simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||
 | 
					           (is_blkdrop2(&i, &j) && simple_rewrite(AsmOp::BlkDrop2(i, j))) ||
 | 
				
			||||||
         (is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
 | 
					           (is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
 | 
				
			||||||
         (is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
 | 
					                                                ? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
 | 
				
			||||||
         (is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||
 | 
					                                                : simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||
 | 
				
			||||||
         (is_xcpu2(&i, &j, &k) && simple_rewrite(AsmOp::XcPu2(i, j, k))) ||
 | 
					           (is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
 | 
				
			||||||
         (is_puxc2(&i, &j, &k) && simple_rewrite(AsmOp::PuXc2(i, j, k))) ||
 | 
					           (is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
 | 
				
			||||||
         (is_puxcpu(&i, &j, &k) && simple_rewrite(AsmOp::PuXcPu(i, j, k))) ||
 | 
					           (is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||
 | 
				
			||||||
         (is_pu2xc(&i, &j, &k) && simple_rewrite(AsmOp::Pu2Xc(i, j, k))) ||
 | 
					           (is_xcpu2(&i, &j, &k) && simple_rewrite(AsmOp::XcPu2(i, j, k))) ||
 | 
				
			||||||
         (is_push3(&i, &j, &k) && simple_rewrite(AsmOp::Push3(i, j, k)));
 | 
					           (is_puxc2(&i, &j, &k) && simple_rewrite(AsmOp::PuXc2(i, j, k))) ||
 | 
				
			||||||
 | 
					           (is_puxcpu(&i, &j, &k) && simple_rewrite(AsmOp::PuXcPu(i, j, k))) ||
 | 
				
			||||||
 | 
					           (is_pu2xc(&i, &j, &k) && simple_rewrite(AsmOp::Pu2Xc(i, j, k))) ||
 | 
				
			||||||
 | 
					           (is_push3(&i, &j, &k) && simple_rewrite(AsmOp::Push3(i, j, k)))));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool Optimizer::find() {
 | 
					bool Optimizer::find() {
 | 
				
			||||||
| 
						 | 
					@ -544,17 +585,17 @@ bool Optimizer::optimize() {
 | 
				
			||||||
  return f;
 | 
					  return f;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AsmOpConsList optimize_code_head(AsmOpConsList op_list) {
 | 
					AsmOpConsList optimize_code_head(AsmOpConsList op_list, int mode) {
 | 
				
			||||||
  Optimizer opt(std::move(op_list), op_rewrite_comments);
 | 
					  Optimizer opt(std::move(op_list), op_rewrite_comments, mode);
 | 
				
			||||||
  opt.optimize();
 | 
					  opt.optimize();
 | 
				
			||||||
  return opt.extract_code();
 | 
					  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;
 | 
					  std::vector<std::unique_ptr<AsmOp>> v;
 | 
				
			||||||
  while (op_list) {
 | 
					  while (op_list) {
 | 
				
			||||||
    if (!op_list->car->is_comment()) {
 | 
					    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) {
 | 
					    if (op_list) {
 | 
				
			||||||
      v.push_back(std::move(op_list->car));
 | 
					      v.push_back(std::move(op_list->car));
 | 
				
			||||||
| 
						 | 
					@ -568,11 +609,14 @@ AsmOpConsList optimize_code(AsmOpConsList op_list) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void optimize_code(AsmOpList& ops) {
 | 
					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) {
 | 
					  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 = 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();
 | 
					  ops.list_.clear();
 | 
				
			||||||
  while (op_list) {
 | 
					  while (op_list) {
 | 
				
			||||||
    ops.list_.push_back(std::move(*(op_list->car)));
 | 
					    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
 | 
					    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/>.
 | 
					    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 "func.h"
 | 
				
			||||||
#include "td/utils/crypto.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.cur().error_at("`", "` is not a type identifier");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  lex.expect('(');
 | 
					  int c;
 | 
				
			||||||
  if (lex.tp() == ')') {
 | 
					  if (lex.tp() == '[') {
 | 
				
			||||||
    lex.next();
 | 
					    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);
 | 
					  auto t1 = parse_type(lex);
 | 
				
			||||||
  if (lex.tp() != ',') {
 | 
					  if (lex.tp() != ',') {
 | 
				
			||||||
    lex.expect(')');
 | 
					    lex.expect(c);
 | 
				
			||||||
    return t1;
 | 
					    return t1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  std::vector<TypeExpr*> tlist{1, t1};
 | 
					  std::vector<TypeExpr*> tlist{1, t1};
 | 
				
			||||||
| 
						 | 
					@ -115,8 +122,8 @@ TypeExpr* parse_type1(Lexer& lex) {
 | 
				
			||||||
    lex.next();
 | 
					    lex.next();
 | 
				
			||||||
    tlist.push_back(parse_type(lex));
 | 
					    tlist.push_back(parse_type(lex));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  lex.expect(')');
 | 
					  lex.expect(c);
 | 
				
			||||||
  return TypeExpr::new_tensor(std::move(tlist));
 | 
					  return c == ')' ? TypeExpr::new_tensor(std::move(tlist)) : TypeExpr::new_tuple(std::move(tlist));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TypeExpr* parse_type(Lexer& lex) {
 | 
					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* make_func_apply(Expr* fun, Expr* x) {
 | 
				
			||||||
  Expr* res;
 | 
					  Expr* res;
 | 
				
			||||||
  if (fun->cls == Expr::_Glob) {
 | 
					  if (fun->cls == Expr::_Glob) {
 | 
				
			||||||
    if (x->cls == Expr::_Tuple) {
 | 
					    if (x->cls == Expr::_Tensor) {
 | 
				
			||||||
      res = new Expr{Expr::_Apply, fun->sym, x->args};
 | 
					      res = new Expr{Expr::_Apply, fun->sym, x->args};
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      res = new Expr{Expr::_Apply, fun->sym, {x}};
 | 
					      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);
 | 
					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) {
 | 
					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};
 | 
					    SrcLocation loc{lex.cur().loc};
 | 
				
			||||||
    lex.next();
 | 
					    lex.next();
 | 
				
			||||||
    if (lex.tp() == ')') {
 | 
					    if (lex.tp() == clbr) {
 | 
				
			||||||
      lex.next();
 | 
					      lex.next();
 | 
				
			||||||
      Expr* res = new Expr{Expr::_Tuple, {}};
 | 
					      Expr* res = new Expr{Expr::_Tensor, {}};
 | 
				
			||||||
      res->flags = Expr::_IsRvalue;
 | 
					      res->flags = Expr::_IsRvalue;
 | 
				
			||||||
      res->here = loc;
 | 
					      res->here = loc;
 | 
				
			||||||
      res->e_type = TypeExpr::new_unit();
 | 
					      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;
 | 
					      return res;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    Expr* res = parse_expr(lex, code, nv);
 | 
					    Expr* res = parse_expr(lex, code, nv);
 | 
				
			||||||
    if (lex.tp() != ',') {
 | 
					    if (lex.tp() != ',') {
 | 
				
			||||||
      lex.expect(')');
 | 
					      lex.expect(clbr);
 | 
				
			||||||
      return res;
 | 
					      return res;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    std::vector<TypeExpr*> type_list;
 | 
					    std::vector<TypeExpr*> type_list;
 | 
				
			||||||
    type_list.push_back(res->e_type);
 | 
					    type_list.push_back(res->e_type);
 | 
				
			||||||
    int f = res->flags;
 | 
					    int f = res->flags;
 | 
				
			||||||
    res = new Expr{Expr::_Tuple, {res}};
 | 
					    res = new Expr{Expr::_Tensor, {res}};
 | 
				
			||||||
    while (lex.tp() == ',') {
 | 
					    while (lex.tp() == ',') {
 | 
				
			||||||
      lex.next();
 | 
					      lex.next();
 | 
				
			||||||
      auto x = parse_expr(lex, code, nv);
 | 
					      auto x = parse_expr(lex, code, nv);
 | 
				
			||||||
| 
						 | 
					@ -351,8 +366,14 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    res->here = loc;
 | 
					    res->here = loc;
 | 
				
			||||||
    res->flags = f;
 | 
					    res->flags = f;
 | 
				
			||||||
    res->e_type = TypeExpr::new_tensor(std::move(type_list));
 | 
					    res->e_type = TypeExpr::new_tensor(std::move(type_list), !tf);
 | 
				
			||||||
    lex.expect(')');
 | 
					    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;
 | 
					    return res;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  int t = lex.tp();
 | 
					  int t = lex.tp();
 | 
				
			||||||
| 
						 | 
					@ -382,7 +403,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
 | 
				
			||||||
    lex.next();
 | 
					    lex.next();
 | 
				
			||||||
    return res;
 | 
					    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};
 | 
					    Expr* res = new Expr{Expr::_Type, lex.cur().loc};
 | 
				
			||||||
    res->flags = Expr::_IsType;
 | 
					    res->flags = Expr::_IsType;
 | 
				
			||||||
    res->e_type = TypeExpr::new_atomic(t);
 | 
					    res->e_type = TypeExpr::new_atomic(t);
 | 
				
			||||||
| 
						 | 
					@ -458,7 +479,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
 | 
				
			||||||
// parse E { E }
 | 
					// parse E { E }
 | 
				
			||||||
Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) {
 | 
					Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) {
 | 
				
			||||||
  Expr* res = parse_expr100(lex, code, 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()) {
 | 
					    if (res->is_type()) {
 | 
				
			||||||
      Expr* x = parse_expr100(lex, code, true);
 | 
					      Expr* x = parse_expr100(lex, code, true);
 | 
				
			||||||
      x->chk_lvalue(lex.cur());  // chk_lrvalue() ?
 | 
					      x->chk_lvalue(lex.cur());  // chk_lrvalue() ?
 | 
				
			||||||
| 
						 | 
					@ -523,7 +544,7 @@ Expr* parse_expr80(Lexer& lex, CodeBlob& code, bool nv) {
 | 
				
			||||||
    lex.next();
 | 
					    lex.next();
 | 
				
			||||||
    auto x = parse_expr100(lex, code, false);
 | 
					    auto x = parse_expr100(lex, code, false);
 | 
				
			||||||
    x->chk_rvalue(lex.cur());
 | 
					    x->chk_rvalue(lex.cur());
 | 
				
			||||||
    if (x->cls == Expr::_Tuple) {
 | 
					    if (x->cls == Expr::_Tensor) {
 | 
				
			||||||
      res = new Expr{Expr::_Apply, name, {obj}};
 | 
					      res = new Expr{Expr::_Apply, name, {obj}};
 | 
				
			||||||
      res->args.insert(res->args.end(), x->args.begin(), x->args.end());
 | 
					      res->args.insert(res->args.end(), x->args.begin(), x->args.end());
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
| 
						 | 
					@ -1269,7 +1290,7 @@ void parse_func_def(Lexer& lex) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool parse_source(std::istream* is, const src::FileDescr* fdescr) {
 | 
					bool parse_source(std::istream* is, const src::FileDescr* fdescr) {
 | 
				
			||||||
  src::SourceReader reader{is, fdescr};
 | 
					  src::SourceReader reader{is, fdescr};
 | 
				
			||||||
  Lexer lex{reader, true};
 | 
					  Lexer lex{reader, true, ";,()[] ~."};
 | 
				
			||||||
  while (lex.tp() != _Eof) {
 | 
					  while (lex.tp() != _Eof) {
 | 
				
			||||||
    if (lex.tp() == _Global) {
 | 
					    if (lex.tp() == _Global) {
 | 
				
			||||||
      parse_global_var_decls(lex);
 | 
					      parse_global_var_decls(lex);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "func.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -742,6 +742,28 @@ bool StackTransform::is_blkdrop(int *i) const {
 | 
				
			||||||
  return false;
 | 
					  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)
 | 
					// equivalent to i times PUSH s(j)
 | 
				
			||||||
bool StackTransform::is_blkpush(int *i, int *j) const {
 | 
					bool StackTransform::is_blkpush(int *i, int *j) const {
 | 
				
			||||||
  if (!is_valid() || d >= 0) {
 | 
					  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
 | 
					// PUSHCONST c ; ROT == 1 -1000 0 2 3
 | 
				
			||||||
bool StackTransform::is_const_rot() const {
 | 
					bool StackTransform::is_const_rot(int c) const {
 | 
				
			||||||
  return is_valid() && d == -1 && is_trivial_after(3) && get(0) == 1 && get(1) <= c_start && get(2) == 0;
 | 
					  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 {
 | 
					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
 | 
					    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/>.
 | 
					    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 "func.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,6 +47,12 @@ void TypeExpr::compute_width() {
 | 
				
			||||||
        maxw = w_inf;
 | 
					        maxw = w_inf;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      break;
 | 
					      break;
 | 
				
			||||||
 | 
					    case te_Tuple:
 | 
				
			||||||
 | 
					      minw = maxw = 1;
 | 
				
			||||||
 | 
					      for (TypeExpr* arg : args) {
 | 
				
			||||||
 | 
					        arg->compute_width();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      break;
 | 
				
			||||||
    case te_Indirect:
 | 
					    case te_Indirect:
 | 
				
			||||||
      minw = args[0]->minw;
 | 
					      minw = args[0]->minw;
 | 
				
			||||||
      maxw = args[0]->maxw;
 | 
					      maxw = args[0]->maxw;
 | 
				
			||||||
| 
						 | 
					@ -84,6 +90,14 @@ bool TypeExpr::recompute_width() {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      return true;
 | 
					      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:
 | 
					    default:
 | 
				
			||||||
      return false;
 | 
					      return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -233,7 +247,9 @@ std::ostream& TypeExpr::print(std::ostream& os, int lex_level) {
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    case te_Tensor: {
 | 
					    case te_Tensor: {
 | 
				
			||||||
      os << "(";
 | 
					      if (lex_level > -127) {
 | 
				
			||||||
 | 
					        os << "(";
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
      auto c = args.size();
 | 
					      auto c = args.size();
 | 
				
			||||||
      if (c) {
 | 
					      if (c) {
 | 
				
			||||||
        for (const auto& x : args) {
 | 
					        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: {
 | 
					    case te_Map: {
 | 
				
			||||||
      assert(args.size() == 2);
 | 
					      assert(args.size() == 2);
 | 
				
			||||||
| 
						 | 
					@ -312,7 +346,7 @@ void check_update_widths(TypeExpr* te1, TypeExpr* te2) {
 | 
				
			||||||
  check_width_compat(te1, te2);
 | 
					  check_width_compat(te1, te2);
 | 
				
			||||||
  te1->minw = te2->minw = std::max(te1->minw, te2->minw);
 | 
					  te1->minw = te2->minw = std::max(te1->minw, te2->minw);
 | 
				
			||||||
  te1->maxw = te2->maxw = std::min(te1->maxw, te2->maxw);
 | 
					  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) {
 | 
					void unify(TypeExpr*& te1, TypeExpr*& te2) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										474
									
								
								crypto/smartcont/dns-auto-code.fc
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										474
									
								
								crypto/smartcont/dns-auto-code.fc
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,474 @@
 | 
				
			||||||
 | 
					{-
 | 
				
			||||||
 | 
					  Adapted from original version written by:
 | 
				
			||||||
 | 
					  /------------------------------------------------------------------------\
 | 
				
			||||||
 | 
					  | Created for: Telegram (Open Network) Blockchain Contest                |
 | 
				
			||||||
 | 
					  |      Task 2: DNS Resolver (Automatically registering)                  |
 | 
				
			||||||
 | 
					  >------------------------------------------------------------------------<
 | 
				
			||||||
 | 
					  | Author: Oleksandr Murzin (tg: @skydev / em: alexhacker64@gmail.com)    |
 | 
				
			||||||
 | 
					  |         October 2019                                                   |
 | 
				
			||||||
 | 
					  \------------------------------------------------------------------------/
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					;; Utility functions                                                         ;;
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-
 | 
				
			||||||
 | 
					  Data structure:
 | 
				
			||||||
 | 
					  Root cell: [OptRef<1b+1r?>:Hashmap<PfxDict:Slice->UInt<32b>,CatTable>:domains]
 | 
				
			||||||
 | 
					         [OptRef<1b+1r?>:Hashmap<UInt<64b>(Time|Hash32)->Slice(DomName)>:gc]
 | 
				
			||||||
 | 
					         [UInt<32b>:stdperiod] [Gram:PPReg] [Gram:PPCell] [Gram:PPBit]
 | 
				
			||||||
 | 
					         [UInt<32b>:lasthousekeeping] 
 | 
				
			||||||
 | 
					  <CatTable> := HashmapE 16 ^DNSRecord
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  STORED DOMAIN NAME SLICE FORMAT: (#ZeroChars<7b>) (Domain name value)
 | 
				
			||||||
 | 
					  #Zeros allows to simultaneously store, for example, com\0 and com\0google\0
 | 
				
			||||||
 | 
					  That will be stored as \1com\0 and \2com\0google\0 (pfx tree has restricitons)
 | 
				
			||||||
 | 
					  This will allow to resolve more specific requests to subdomains, and resort
 | 
				
			||||||
 | 
					      to parent domain next resolver lookup if subdomain is not found
 | 
				
			||||||
 | 
					  com\0goo\0 lookup will, for example look up \2com\0goo\0 and then
 | 
				
			||||||
 | 
					      \1com\0goo\0 which will return \1com\0 (as per pfx tree) with -1 cat
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(cell, cell, [int, int, int, int], int, int) load_data() inline_ref {
 | 
				
			||||||
 | 
					  slice cs = get_data().begin_parse();
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    cs~load_dict(),       ;; pfx tree: domains data and exp
 | 
				
			||||||
 | 
					    cs~load_dict(),       ;; gc auxillary with expiry and 32 bit hash slice
 | 
				
			||||||
 | 
					    [ cs~load_uint(32),   ;; length of this period of time in seconds
 | 
				
			||||||
 | 
					      cs~load_grams(),    ;; standard payment for registering a new subdomain
 | 
				
			||||||
 | 
					      cs~load_grams(),    ;; price paid for each cell (PPC)
 | 
				
			||||||
 | 
					      cs~load_grams() ],  ;;                 and bit  (PPB)
 | 
				
			||||||
 | 
					    cs~load_uint(32),     ;; next housekeeping to be done at
 | 
				
			||||||
 | 
					    cs~load_uint(32)      ;; last housekeeping done at
 | 
				
			||||||
 | 
					  );
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(int, int, int, int) load_prices() inline_ref {
 | 
				
			||||||
 | 
					  slice cs = get_data().begin_parse();
 | 
				
			||||||
 | 
					  (cs~load_dict(), cs~load_dict());
 | 
				
			||||||
 | 
					  return (cs~load_uint(32), cs~load_grams(), cs~load_grams(), cs~load_grams());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					() store_data(cell dd, cell gc, prices, int nhk, int lhk) impure {
 | 
				
			||||||
 | 
					  var [sp, ppr, ppc, ppb] = prices;
 | 
				
			||||||
 | 
					  set_data(begin_cell()
 | 
				
			||||||
 | 
					      .store_dict(dd) ;; domains data and exp
 | 
				
			||||||
 | 
					      .store_dict(gc) ;; keyed expiration time and 32 bit hash slice
 | 
				
			||||||
 | 
					      .store_int(sp, 32) ;; standard period
 | 
				
			||||||
 | 
					      .store_grams(ppr) ;; price per registration
 | 
				
			||||||
 | 
					      .store_grams(ppc) ;; price per cell
 | 
				
			||||||
 | 
					      .store_grams(ppb) ;; price per bit
 | 
				
			||||||
 | 
					      .store_uint(nhk, 32) ;; next housekeeping
 | 
				
			||||||
 | 
					      .store_uint(lhk, 32) ;; last housekeeping
 | 
				
			||||||
 | 
					      .end_cell());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					global var query_info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					() send_message(slice addr, int tag,   int query_id, 
 | 
				
			||||||
 | 
					                int   body, int grams, int mode) impure {
 | 
				
			||||||
 | 
					  ;; int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool 
 | 
				
			||||||
 | 
					  ;;                src:MsgAddress -> 011000 0x18
 | 
				
			||||||
 | 
					  var msg = begin_cell()
 | 
				
			||||||
 | 
					    .store_uint (0x18, 6)
 | 
				
			||||||
 | 
					    .store_slice(addr)
 | 
				
			||||||
 | 
					    .store_grams(grams)
 | 
				
			||||||
 | 
					    .store_uint (0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
 | 
				
			||||||
 | 
					    .store_uint (tag, 32)
 | 
				
			||||||
 | 
					    .store_uint (query_id, 64);
 | 
				
			||||||
 | 
					  if (body >= 0) {
 | 
				
			||||||
 | 
					    msg~store_uint(body, 32);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  send_raw_message(msg.end_cell(), mode);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					() send_error(int error_code) impure {
 | 
				
			||||||
 | 
					  var (addr, query_id, op) = query_info;
 | 
				
			||||||
 | 
					  return send_message(addr, error_code, query_id, op, 0, 64);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					() send_ok(int price) impure {
 | 
				
			||||||
 | 
					  raw_reserve(price, 4);
 | 
				
			||||||
 | 
					  var (addr, query_id, op) = query_info;
 | 
				
			||||||
 | 
					  return send_message(addr, 0xef6b6179, query_id, op, 0, 128);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					() housekeeping(cell dd, cell gc, prices, int nhk, int lhk) impure {
 | 
				
			||||||
 | 
					  int n = now();
 | 
				
			||||||
 | 
					  if (n < max(nhk, lhk + 60)) { ;; housekeeping cooldown: 1 minute
 | 
				
			||||||
 | 
					    ;; if housekeeping was done recently, or if next housekeeping is in the future, just save
 | 
				
			||||||
 | 
					    return store_data(dd, gc, prices, nhk, lhk);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ;; need to do some housekeeping - maybe remove entry with
 | 
				
			||||||
 | 
					  ;;     least expiration but only if it is already expired
 | 
				
			||||||
 | 
					  ;; no iterating and deleting all to not put too much gas gc
 | 
				
			||||||
 | 
					  ;;     burden on any random specific user request
 | 
				
			||||||
 | 
					  ;; over time it will do the garbage collection required
 | 
				
			||||||
 | 
					  (int mkey, cell name, int found?) = gc.udict_get_min_ref?(64);
 | 
				
			||||||
 | 
					  if (found?) { ;; no short circuit optimization, two nested ifs
 | 
				
			||||||
 | 
					    nhk = (mkey >> 32);
 | 
				
			||||||
 | 
					    if (nhk < n) {
 | 
				
			||||||
 | 
					      slice sname = name.begin_parse();
 | 
				
			||||||
 | 
					      (_, slice val, _, found?) = dd.pfxdict_get?(1023, sname);
 | 
				
			||||||
 | 
					      if (found?) {
 | 
				
			||||||
 | 
					        int exp = val.preload_uint(32);
 | 
				
			||||||
 | 
					        if (exp <= n) {
 | 
				
			||||||
 | 
					          dd~pfxdict_delete?(1023, sname);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      gc~udict_delete?(64, mkey);
 | 
				
			||||||
 | 
					      (mkey, _, found?) = gc.udict_get_min_ref?(64);
 | 
				
			||||||
 | 
					      nhk = (found? ? mkey >> 32 : 0xffffffff);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  store_data(dd, gc, prices, nhk, n);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int _calcprice(cell data, ppc, ppb) inline_ref { ;; only for internal calcs
 | 
				
			||||||
 | 
					  var (_, bits, refs) = compute_data_size(data, 100);  ;; 100 cells max
 | 
				
			||||||
 | 
					  return ppc * refs + ppb * bits;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int check_owner(cell cat_table, int src_wc, int src_addr) inline_ref {
 | 
				
			||||||
 | 
					  if (cat_table.null?()) { ;; domain not found: return notf | 2^31
 | 
				
			||||||
 | 
					    return 0xee6f7466;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  cell cown = cat_table.idict_get_ref(16, -2);
 | 
				
			||||||
 | 
					  if (cown.null?()) { ;; no owner on this domain: no-2
 | 
				
			||||||
 | 
					    return 0xee6f2d32;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  var ERR_BAD2 = 0xe2616432;
 | 
				
			||||||
 | 
					  slice sown = cown.begin_parse();
 | 
				
			||||||
 | 
					  if (sown.slice_bits() < 16 + 3 + 8 + 256) { ;; bad owner record: bad2
 | 
				
			||||||
 | 
					    return ERR_BAD2;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (sown~load_uint(16 + 3) != 0x9fd3 * 8 + 4) {
 | 
				
			||||||
 | 
					    return ERR_BAD2;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  (int owner_wc, int owner_addr) = (sown~load_int(8), sown.preload_uint(256));
 | 
				
			||||||
 | 
					  if ((owner_wc != src_wc) | (owner_addr != src_addr)) { ;; not owner: nown
 | 
				
			||||||
 | 
					    return 0xee6f776e;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return 0;  ;; ok
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					;; Internal message handler (Code 0)                                         ;;
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{-
 | 
				
			||||||
 | 
					  Internal message cell structure:
 | 
				
			||||||
 | 
					                   8             4           2            1
 | 
				
			||||||
 | 
					    int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
 | 
				
			||||||
 | 
					    src:MsgAddressInt dest:MsgAddressInt
 | 
				
			||||||
 | 
					    value:CurrencyCollection ihr_fee:Grams fwd_fee:Grams
 | 
				
			||||||
 | 
					    created_lt:uint64 created_at:uint32  
 | 
				
			||||||
 | 
					  Internal message data structure:
 | 
				
			||||||
 | 
					    [UInt<32b>:op] [UInt<64b>:query_id] [Ref<1r>:name] 
 | 
				
			||||||
 | 
					    (if not prolong: [Ref<1r>:value->CatTable])
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					-}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; Must send at least GR$1 more for possible gas fees!
 | 
				
			||||||
 | 
					() recv_internal(int ct_bal, int msg_value, cell in_msg_cell, slice in_msg) impure {
 | 
				
			||||||
 | 
					  ;; this time very interested in internal messages
 | 
				
			||||||
 | 
					  if (in_msg.slice_bits() < 32) { 
 | 
				
			||||||
 | 
					    return ();  ;; simple transfer or short
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  slice cs = in_msg_cell.begin_parse();
 | 
				
			||||||
 | 
					  int flags = cs~load_uint(4);
 | 
				
			||||||
 | 
					  if (flags & 1) { 
 | 
				
			||||||
 | 
					    return ();  ;; bounced messages
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  slice s_addr = cs~load_msg_addr();
 | 
				
			||||||
 | 
					  (int src_wc, int src_addr) = s_addr.parse_std_addr();
 | 
				
			||||||
 | 
					  int op = in_msg~load_uint(32);
 | 
				
			||||||
 | 
					  ifnot (op) { 
 | 
				
			||||||
 | 
					    return ();   ;; simple transfer with comment
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  int query_id = 0;
 | 
				
			||||||
 | 
					  if (in_msg.slice_bits() >= 64) { 
 | 
				
			||||||
 | 
					    query_id = in_msg~load_uint(64);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  query_info = (s_addr, query_id, op);
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  if (op & (1 << 31)) {
 | 
				
			||||||
 | 
					    return ();   ;; an answer to our query
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  int qt = (op == 0x72656764) * 1 + (op == 0x70726f6c) * 2 + (op == 0x75706464) * 4 + (op == 0x676f6763) * 8;
 | 
				
			||||||
 | 
					  ifnot (qt) { ;; unknown query, return error
 | 
				
			||||||
 | 
					    return send_error(0xffffffff);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  qt = - qt;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  (cell domdata, cell gc, [int, int, int, int] prices, int nhk, int lhk) = load_data();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  if (qt == 8) { ;; 0x676f6763 -> GO, GC! go!!!
 | 
				
			||||||
 | 
					    ;; Manual garbage collection iteration
 | 
				
			||||||
 | 
					    housekeeping(domdata, gc, prices, nhk, 1); ;; forced
 | 
				
			||||||
 | 
					    return send_error(0xef6b6179);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  slice name = null();
 | 
				
			||||||
 | 
					  cell name_cell = in_msg~load_maybe_ref();
 | 
				
			||||||
 | 
					  if (name_cell.null?()) {
 | 
				
			||||||
 | 
					    int bytes = in_msg~load_uint(6);
 | 
				
			||||||
 | 
					    name = in_msg~load_bits(bytes * 8);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    name = name_cell.begin_parse();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  (_, int name_last_byte) = name.slice_last(8).load_uint(8);
 | 
				
			||||||
 | 
					  if (name_last_byte != 0) { ;; name must end with \0! no\0 error
 | 
				
			||||||
 | 
					    return send_error(0xee6f5c30);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					  int zeros = 0;
 | 
				
			||||||
 | 
					  slice cname = name;
 | 
				
			||||||
 | 
					  repeat (cname.slice_bits() ^>> 3) {
 | 
				
			||||||
 | 
					    int c = cname~load_uint(8);
 | 
				
			||||||
 | 
					    zeros -= (c == 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  ;; if (zeros != 1) { ;; too much zero chars (overflow): ov\0
 | 
				
			||||||
 | 
					  ;;  return send_error(0xef765c30); }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  name = begin_cell().store_uint(zeros, 7).store_slice(name).end_cell().begin_parse();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  (slice pfx, slice val, slice tail, int found?) = domdata.pfxdict_get?(1023, name);
 | 
				
			||||||
 | 
					  int n = now();
 | 
				
			||||||
 | 
					  cell cat_table = null();
 | 
				
			||||||
 | 
					  int exp = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (found?) {
 | 
				
			||||||
 | 
					    exp = val~load_uint(32);
 | 
				
			||||||
 | 
					    if (n > exp) { ;; expired domains behave as not registered
 | 
				
			||||||
 | 
					      found? = false;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      cat_table = val.preload_ref();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  ;; ##########################################################################
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  int err = 0;
 | 
				
			||||||
 | 
					  if (qt != 1) {  ;; not a "register", check that domain exists and is controlled by correct smc
 | 
				
			||||||
 | 
					    err = check_owner(cat_table, src_wc, src_addr);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (err) {
 | 
				
			||||||
 | 
					    return send_error(err);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ;; ##########################################################################
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  ;; load desired data (reuse old for a "prolong" operation)
 | 
				
			||||||
 | 
					  cell data = null();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  if (qt != 2) {  ;; not a "prolong", load data dictionary
 | 
				
			||||||
 | 
					    data = in_msg~load_ref();
 | 
				
			||||||
 | 
					    ;; basic integrity check of (client-provided) dictionary
 | 
				
			||||||
 | 
					    ifnot (data.dict_empty?()) { ;; 1000 gas!
 | 
				
			||||||
 | 
					      (int dmin, _, int minok) = idict_get_min?(data, 16);
 | 
				
			||||||
 | 
					      (int dmax, _, int maxok) = idict_get_max?(data, 16);
 | 
				
			||||||
 | 
					      throw_unless(31, minok & maxok & (dmin <= dmax));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    data = cat_table;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ;; compute action price
 | 
				
			||||||
 | 
					  var [stdper, ppr, ppc, ppb] = prices;
 | 
				
			||||||
 | 
					  int price = _calcprice(data, ppc, ppb) + (ppr & (qt != 4));
 | 
				
			||||||
 | 
					  if (msg_value - (1 << 30) < price) { ;; gr<p: grams - GR$1 < price
 | 
				
			||||||
 | 
					    return send_error(0xe7723c70);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  ;; load desired expiration unixtime
 | 
				
			||||||
 | 
					  int req_expires_at = in_msg~load_uint(32);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ;; ##########################################################################
 | 
				
			||||||
 | 
					  if (qt == 2) { ;; 0x70726f6c -> prol | prolong domain
 | 
				
			||||||
 | 
					    slice value = begin_cell().store_uint(exp + stdper, 32).store_ref(data).end_cell().begin_parse();
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					    ifnot (domdata~pfxdict_set?(1023, name, value)) { ;; Set ERR | 2^31
 | 
				
			||||||
 | 
					      return send_error(0xf3657272);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					    int sh_low = name.slice_hash() & ((1 << 32) - 1);
 | 
				
			||||||
 | 
					    int gckeyO = (exp << 32) + sh_low;
 | 
				
			||||||
 | 
					    int gckeyN = gckeyO + (stdper << 32);
 | 
				
			||||||
 | 
					    gc~udict_delete?(64, gckeyO); ;; delete old gc entry, add new
 | 
				
			||||||
 | 
					    gc~udict_set_ref(64, gckeyN, begin_cell().store_slice(name).end_cell());
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    housekeeping(domdata, gc, prices, nhk, lhk);
 | 
				
			||||||
 | 
					    return send_ok(price);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  ;; ##########################################################################
 | 
				
			||||||
 | 
					  if (qt == 1) { ;; 0x72656764 -> regd | register domain
 | 
				
			||||||
 | 
					    if (found?) { ;; domain already exists: return alre | 2^31
 | 
				
			||||||
 | 
					      return send_error(0xe16c7265);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int expires_at = n + stdper;
 | 
				
			||||||
 | 
					    slice value = begin_cell().store_uint(expires_at, 32).store_ref(data).end_cell().begin_parse();
 | 
				
			||||||
 | 
					    ifnot (domdata~pfxdict_set?(1023, name, value)) { ;; Set ERR | 2^31
 | 
				
			||||||
 | 
					      return send_error(0xf3657272);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    int gckey = (expires_at << 32) | (name.slice_hash() & ((1 << 32) - 1));
 | 
				
			||||||
 | 
					    gc~udict_set_ref(64, gckey, begin_cell().store_slice(name).end_cell());
 | 
				
			||||||
 | 
					    ;; using ref requires additional cell, but using value (DICTUSET) may
 | 
				
			||||||
 | 
					    ;;     cause problems with very long names or complex dictionaries
 | 
				
			||||||
 | 
					    housekeeping(domdata, gc, prices, min(nhk, expires_at), lhk);
 | 
				
			||||||
 | 
					    return send_ok(price);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ;; ##########################################################################
 | 
				
			||||||
 | 
					  if (qt == 4) { ;; 0x75706464 -> updd | update domain (data)
 | 
				
			||||||
 | 
					    slice value = begin_cell().store_uint(exp, 32).store_ref(data).end_cell().begin_parse();
 | 
				
			||||||
 | 
					       
 | 
				
			||||||
 | 
					    ifnot (domdata~pfxdict_set?(1023, name, value)) { ;; Set ERR | 2^31
 | 
				
			||||||
 | 
					      return send_error(0xf3657272);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ;; no need to update gc here
 | 
				
			||||||
 | 
					    housekeeping(domdata, gc, prices, nhk, lhk);
 | 
				
			||||||
 | 
					    return send_ok(price);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ;; ##########################################################################
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return (); ;; should NEVER reach this part of code!
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					;; External message handler (Code -1)                                        ;;
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					() recv_external(slice in_msg) impure {
 | 
				
			||||||
 | 
					  ;; not interested at all! but need to init!
 | 
				
			||||||
 | 
					  (cell dd, cell gc, var prices, int nhk, int lhk) = load_data();
 | 
				
			||||||
 | 
					  ifnot (lhk) {
 | 
				
			||||||
 | 
					    accept_message();
 | 
				
			||||||
 | 
					    store_data(dd, gc, prices, 0, now());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					;; Getter methods                                                            ;;
 | 
				
			||||||
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(int, cell, int, slice) dnsdictlookup(slice subdomain, int nowtime) inline_ref {
 | 
				
			||||||
 | 
					  int bits = subdomain.slice_bits();
 | 
				
			||||||
 | 
					  ifnot (bits) {
 | 
				
			||||||
 | 
					    return (0, null(), 0, null());  ;; zero-length input
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  throw_if(30, bits & 7); ;; malformed input (~ 8n-bit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int name_last_byte = subdomain.slice_last(8).preload_uint(8);
 | 
				
			||||||
 | 
					  if (name_last_byte) {
 | 
				
			||||||
 | 
					    subdomain = begin_cell().store_slice(subdomain) ;; append zero byte
 | 
				
			||||||
 | 
					                            .store_uint(0, 8).end_cell().begin_parse();
 | 
				
			||||||
 | 
					    bits += 8;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (bits == 8) {
 | 
				
			||||||
 | 
					    return (0, null(), 0, null()); ;; zero-length input, but with zero byte
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  (_, cell root) = get_data().begin_parse().load_dict();
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  slice cname = subdomain;
 | 
				
			||||||
 | 
					  int zeros = 0;
 | 
				
			||||||
 | 
					  repeat (bits >> 3) {
 | 
				
			||||||
 | 
					    int c = cname~load_uint(8);
 | 
				
			||||||
 | 
					    zeros -= (c == 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ;; can't move these declarations lower, will cause errors!
 | 
				
			||||||
 | 
					  slice pfx = cname;
 | 
				
			||||||
 | 
					  slice val = null();
 | 
				
			||||||
 | 
					  slice tail = cname;
 | 
				
			||||||
 | 
					  int exp = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  do {
 | 
				
			||||||
 | 
					    slice pfxname = begin_cell().store_uint(zeros, 7)
 | 
				
			||||||
 | 
					      .store_slice(subdomain).end_cell().begin_parse();
 | 
				
			||||||
 | 
					    (pfx, val, tail, int succ) = root.pfxdict_get?(1023, pfxname);
 | 
				
			||||||
 | 
					    if (succ) {
 | 
				
			||||||
 | 
					      int exp = val~load_uint(32);
 | 
				
			||||||
 | 
					      if (nowtime > exp) {  ;; entry expired, skip
 | 
				
			||||||
 | 
					        succ = false;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    zeros = succ ^ (zeros - 1);   ;; break on success
 | 
				
			||||||
 | 
					  } until (zeros <= 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ifnot (zeros) {
 | 
				
			||||||
 | 
					    return (0, null(), 0, null()); ;; failed to find entry in prefix dictionary
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  zeros = - zeros;
 | 
				
			||||||
 | 
					  return (exp, val.preload_ref(), tail.slice_empty?(), pfx);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;;8m  dns-record-value
 | 
				
			||||||
 | 
					(int, cell) dnsresolve(slice subdomain, int category) method_id {
 | 
				
			||||||
 | 
					  (int exp, cell cat_table, int exact?, slice pfx) = dnsdictlookup(subdomain, now());
 | 
				
			||||||
 | 
					  ifnot (exp) {
 | 
				
			||||||
 | 
					    return (0, null());
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  ifnot (exact?) { ;; incomplete subdomain found, must return next resolver (-1)
 | 
				
			||||||
 | 
					    category = -1;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  int pfx_bits = pfx.slice_bits() - 7;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  ;; pfx.slice_bits() will contain 8m, where m is number of bytes in subdomain 
 | 
				
			||||||
 | 
					  ;;   COUNTING the zero byte (if structurally correct: no multiple-ZB keys)
 | 
				
			||||||
 | 
					  ;;   which corresponds to 8m, m=one plus the number of bytes in the subdomain found)
 | 
				
			||||||
 | 
					  if (category == 0) {
 | 
				
			||||||
 | 
					    return (pfx_bits, cat_table); ;; return cell with entire dictionary for 0
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    cell cat_found = cat_table.idict_get_ref(16, category);
 | 
				
			||||||
 | 
					    return (pfx_bits, cat_found);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					;; getexpiration needs to know the current time to skip any possible expired
 | 
				
			||||||
 | 
					;;     subdomains in the chain. it will return 0 if not found or expired.
 | 
				
			||||||
 | 
					int getexpirationx(slice subdomain, int nowtime) inline method_id {
 | 
				
			||||||
 | 
					  (int exp, _, _, _) = dnsdictlookup(subdomain, nowtime);
 | 
				
			||||||
 | 
					  return exp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int getexpiration(slice subdomain) method_id {
 | 
				
			||||||
 | 
					  return getexpirationx(subdomain, now()); 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int getstdperiod() method_id {
 | 
				
			||||||
 | 
					  (int stdper, _, _, _) = load_prices();
 | 
				
			||||||
 | 
					  return stdper;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int getppr() method_id {
 | 
				
			||||||
 | 
					  (_, int ppr, _, _) = load_prices();
 | 
				
			||||||
 | 
					  return ppr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int getppc() method_id {
 | 
				
			||||||
 | 
					  (_, _, int ppc, _) = load_prices();
 | 
				
			||||||
 | 
					  return ppc;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int getppb() method_id {
 | 
				
			||||||
 | 
					  ( _, _, _, int ppb) = load_prices();
 | 
				
			||||||
 | 
					  return ppb;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int calcprice(cell val) method_id { ;; only for external gets (not efficient)
 | 
				
			||||||
 | 
					  (_, _, int ppc, int ppb) = load_prices();
 | 
				
			||||||
 | 
					  return _calcprice(val, ppc, ppb);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int calcregprice(cell val) method_id { ;; only for external gets (not efficient)
 | 
				
			||||||
 | 
					  (_, int ppr, int ppc, int ppb) = load_prices();
 | 
				
			||||||
 | 
					  return ppr + _calcprice(val, ppc, ppb); 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -13,25 +13,7 @@
 | 
				
			||||||
;; Custom ASM instructions                                                   ;;
 | 
					;; Custom ASM instructions                                                   ;;
 | 
				
			||||||
;;===========================================================================;;
 | 
					;;===========================================================================;;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
;; Args: s D n | Success: s' x s'' -1 | Failure: s 0 -> s N N 0
 | 
					(cell, ()) pfxdict_set_ref(cell dict, int key_len, slice key, cell value) {
 | 
				
			||||||
(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()));
 | 
					  throw_unless(33, dict~pfxdict_set?(key_len, key, begin_cell().store_maybe_ref(value).end_cell().begin_parse()));
 | 
				
			||||||
  return (dict, ());
 | 
					  return (dict, ());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,7 +123,7 @@ _ ~credit_to(credits, addr, amount) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  ;; parse current election data
 | 
					  ;; parse current election data
 | 
				
			||||||
  var (elect_at, elect_close, min_stake, total_stake, members, failed, finished) = elect.unpack_elect();
 | 
					  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
 | 
					  msg_value -= 1000000000;   ;; deduct GR$1 for sending confirmation
 | 
				
			||||||
  if ((msg_value << 12) < total_stake) {
 | 
					  if ((msg_value << 12) < total_stake) {
 | 
				
			||||||
    ;; stake smaller than 1/4096 of the total accumulated stakes, return
 | 
					    ;; stake smaller than 1/4096 of the total accumulated stakes, return
 | 
				
			||||||
| 
						 | 
					@ -443,7 +443,7 @@ _ compute_total_stake(l, n, m_stake) {
 | 
				
			||||||
    if (f) {
 | 
					    if (f) {
 | 
				
			||||||
      var (stake, _, pubkey) = (min(key~load_uint(128), max_stake), key~load_uint(32), key.preload_uint(256));
 | 
					      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));
 | 
					      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);
 | 
					  } until (~ f);
 | 
				
			||||||
  ;; l is the list of all stakes in decreasing order
 | 
					  ;; 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
 | 
					  ;; we have to select first m validators from list l
 | 
				
			||||||
  l1 = touch(l);
 | 
					  l1 = touch(l);
 | 
				
			||||||
  l1~dump();  ;; DEBUG
 | 
					  ;; l1~dump();  ;; DEBUG
 | 
				
			||||||
  repeat (m - 1) {
 | 
					  repeat (m - 1) {
 | 
				
			||||||
    l1 = cdr(l1);
 | 
					    l1 = cdr(l1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -480,7 +480,7 @@ _ compute_total_stake(l, n, m_stake) {
 | 
				
			||||||
  var vset = new_dict();
 | 
					  var vset = new_dict();
 | 
				
			||||||
  var frozen = new_dict();
 | 
					  var frozen = new_dict();
 | 
				
			||||||
  do {
 | 
					  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
 | 
					    ;; lookup source address first
 | 
				
			||||||
    var (val, f) = members.udict_get?(256, pubkey);
 | 
					    var (val, f) = members.udict_get?(256, pubkey);
 | 
				
			||||||
    throw_unless(61, f);
 | 
					    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();
 | 
					  (_, var min_stake) = config_param(17).begin_parse().load_grams();
 | 
				
			||||||
  ;; announce new elections
 | 
					  ;; announce new elections
 | 
				
			||||||
  var elect_at = t + elect_begin_before;
 | 
					  var elect_at = t + elect_begin_before;
 | 
				
			||||||
  elect_at~dump();
 | 
					  ;; elect_at~dump();
 | 
				
			||||||
  var elect_close = elect_at - elect_end_before;
 | 
					  var elect_close = elect_at - elect_end_before;
 | 
				
			||||||
  elect = pack_elect(elect_at, elect_close, min_stake, 0, new_dict(), false, false);
 | 
					  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());
 | 
					  set_data(begin_cell().store_dict(elect).store_dict(credits).store_slice(ds).end_cell());
 | 
				
			||||||
| 
						 | 
					@ -786,7 +786,7 @@ _ participant_list() method_id {
 | 
				
			||||||
  do {
 | 
					  do {
 | 
				
			||||||
    (id, var fs, var f) = members.udict_get_prev?(256, id);
 | 
					    (id, var fs, var f) = members.udict_get_prev?(256, id);
 | 
				
			||||||
    if (f) {
 | 
					    if (f) {
 | 
				
			||||||
      l = cons(pair(id, fs~load_grams()), l);
 | 
					      l = cons([id, fs~load_grams()], l);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  } until (~ f);
 | 
					  } until (~ f);
 | 
				
			||||||
  return l;
 | 
					  return l;
 | 
				
			||||||
| 
						 | 
					@ -806,7 +806,7 @@ _ participant_list_extended() method_id {
 | 
				
			||||||
    if (f) {
 | 
					    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));
 | 
					      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();
 | 
					      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);
 | 
					  } until (~ f);
 | 
				
			||||||
  return (elect_at, elect_close, min_stake, total_stake, l, failed, finished);
 | 
					  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
 | 
					} : subdomain>s
 | 
				
			||||||
// ( b V -- b' )
 | 
					// ( b V -- b' )
 | 
				
			||||||
{ dup first
 | 
					{ dup first
 | 
				
			||||||
  dup `smc eq? { drop untriple rot nip rot x{9fd3} 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 rot nip rot x{ba93} 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 `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 $, } { 
 | 
					  dup `text eq? { drop second swap x{1eda01} s, over $len 8 u, swap $, } { 
 | 
				
			||||||
    abort"unknown value type"
 | 
					    abort"unknown value type"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,5 +69,5 @@ int get_public_key() method_id {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int balance() 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();
 | 
					  var rdict = ds~load_dict();
 | 
				
			||||||
  ds.end_parse();
 | 
					  ds.end_parse();
 | 
				
			||||||
  var ts = days_passed();
 | 
					  var ts = days_passed();
 | 
				
			||||||
  var balance = get_balance().first();
 | 
					  var balance = get_balance().pair_first();
 | 
				
			||||||
  var (_, value, found) = rdict.idict_get_preveq?(16, ts);
 | 
					  var (_, value, found) = rdict.idict_get_preveq?(16, ts);
 | 
				
			||||||
  if (found) {
 | 
					  if (found) {
 | 
				
			||||||
    balance = max(balance - value~load_grams(), 0);
 | 
					    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 -> (tuple, X) list_next(tuple list) asm( -> 1 0) "UNCONS";
 | 
				
			||||||
forall X -> X car(tuple list) asm "CAR";
 | 
					forall X -> X car(tuple list) asm "CAR";
 | 
				
			||||||
tuple cdr(tuple list) asm "CDR";
 | 
					tuple cdr(tuple list) asm "CDR";
 | 
				
			||||||
forall X, Y -> tuple pair(X x, Y y) asm "PAIR";
 | 
					forall X, Y -> [X, Y] pair(X x, Y y) asm "PAIR";
 | 
				
			||||||
forall X, Y -> (X, Y) unpair(tuple t) asm "UNPAIR";
 | 
					forall X, Y -> (X, Y) unpair([X, Y] t) asm "UNPAIR";
 | 
				
			||||||
forall X, Y, Z -> tuple triple(X x, Y y, Z z) asm "TRIPLE";
 | 
					forall X, Y, Z -> [X, Y, Z] 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 -> (X, Y, Z) untriple([X, Y, Z] 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] 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, 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 first(tuple t) asm "FIRST";
 | 
				
			||||||
forall X -> X second(tuple t) asm "SECOND";
 | 
					forall X -> X second(tuple t) asm "SECOND";
 | 
				
			||||||
forall X -> X third(tuple t) asm "THIRD";
 | 
					forall X -> X third(tuple t) asm "THIRD";
 | 
				
			||||||
forall X -> X fourth(tuple t) asm "3 INDEX";
 | 
					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";
 | 
					forall X -> X null() asm "PUSHNULL";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int now() asm "NOW";
 | 
					int now() asm "NOW";
 | 
				
			||||||
slice my_address() asm "MYADDR";
 | 
					slice my_address() asm "MYADDR";
 | 
				
			||||||
tuple get_balance() asm "BALANCE";
 | 
					[int, cell] get_balance() asm "BALANCE";
 | 
				
			||||||
int cur_lt() asm "LTIME";
 | 
					int cur_lt() asm "LTIME";
 | 
				
			||||||
int block_lt() asm "BLOCKLT";
 | 
					int block_lt() asm "BLOCKLT";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,6 +52,7 @@ cont bless(slice s) impure asm "BLESS";
 | 
				
			||||||
() accept_message() impure asm "ACCEPT";
 | 
					() accept_message() impure asm "ACCEPT";
 | 
				
			||||||
() set_gas_limit(int limit) impure asm "SETGASLIMIT";
 | 
					() set_gas_limit(int limit) impure asm "SETGASLIMIT";
 | 
				
			||||||
() commit() impure asm "COMMIT";
 | 
					() commit() impure asm "COMMIT";
 | 
				
			||||||
 | 
					() buy_gas(int gram) impure asm "BUYGAS";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int min(int x, int y) asm "MIN";
 | 
					int min(int x, int y) asm "MIN";
 | 
				
			||||||
int max(int x, int y) asm "MAX";
 | 
					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 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, ()) ~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";
 | 
					(slice, cell) load_dict(slice s) asm( -> 1 0) "LDDICT";
 | 
				
			||||||
cell preload_dict(slice s) asm "PLDDICT";
 | 
					cell preload_dict(slice s) asm "PLDDICT";
 | 
				
			||||||
slice skip_dict(slice s) asm "SKIPDICT";
 | 
					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_refs(slice s) asm "SREFS";
 | 
				
			||||||
int slice_bits(slice s) asm "SBITS";
 | 
					int slice_bits(slice s) asm "SBITS";
 | 
				
			||||||
(int, int) slice_bits_refs(slice s) asm "SBITREFS";
 | 
					(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_data_empty?(slice s) asm "SDEMPTY";
 | 
				
			||||||
int slice_refs_empty?(slice s) asm "SREMPTY";
 | 
					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";
 | 
					builder begin_cell() asm "NEWC";
 | 
				
			||||||
cell end_cell(builder b) asm "ENDC";
 | 
					cell end_cell(builder b) asm "ENDC";
 | 
				
			||||||
builder store_ref(builder b, cell c) asm(c b) "STREF";
 | 
					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";
 | 
					cell new_dict() asm "NEWDICT";
 | 
				
			||||||
int dict_empty?(cell c) asm "DICTEMPTY";
 | 
					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";
 | 
					cell config_param(int x) asm "CONFIGOPTPARAM";
 | 
				
			||||||
int cell_null?(cell c) asm "ISNULL";
 | 
					int cell_null?(cell c) asm "ISNULL";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
() raw_reserve(int amount, int mode) impure asm "RAWRESERVE";
 | 
					() 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";
 | 
					() send_raw_message(cell msg, int mode) impure asm "SENDRAWMSG";
 | 
				
			||||||
() set_code(cell new_code) impure asm "SETCODE";
 | 
					() 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]
 | 
							[UInt<256b>:new_public_key]
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
// creation
 | 
					// creation
 | 
				
			||||||
td::Ref<ManualDns> ManualDns::create(td::Ref<vm::Cell> data) {
 | 
					td::Ref<ManualDns> ManualDns::create(td::Ref<vm::Cell> data, int revision) {
 | 
				
			||||||
  return td::Ref<ManualDns>(true, State{ton::SmartContractCode::dns_manual(), std::move(data)});
 | 
					  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) {
 | 
					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));
 | 
					  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 {
 | 
					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) {
 | 
					  static td::Ref<ManualDns> create(State state) {
 | 
				
			||||||
    return td::Ref<ManualDns>(true, std::move(state));
 | 
					    return td::Ref<ManualDns>(true, std::move(state));
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  static td::Ref<ManualDns> create(td::Ref<vm::Cell> data = {});
 | 
					  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);
 | 
					  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 std::string serialize_data(const EntryData& data);
 | 
				
			||||||
  static td::Result<td::optional<ManualDns::EntryData>> parse_data(td::Slice cmd);
 | 
					  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<ManualDns::ActionExt> parse_line(td::Slice cmd);
 | 
				
			||||||
  static td::Result<std::vector<ManualDns::ActionExt>> parse(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 {
 | 
					  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);
 | 
					    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 WALLET3_REVISION = 2;
 | 
				
			||||||
constexpr static int HIGHLOAD_WALLET_REVISION = 2;
 | 
					constexpr static int HIGHLOAD_WALLET_REVISION = 2;
 | 
				
			||||||
constexpr static int HIGHLOAD_WALLET2_REVISION = 2;
 | 
					constexpr static int HIGHLOAD_WALLET2_REVISION = 2;
 | 
				
			||||||
 | 
					constexpr static int DNS_REVISION = 1;
 | 
				
			||||||
const auto& get_map() {
 | 
					const auto& get_map() {
 | 
				
			||||||
  static auto map = [] {
 | 
					  static auto map = [] {
 | 
				
			||||||
    std::map<std::string, td::Ref<vm::Cell>, std::less<>> 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",
 | 
					    with_tvm_code("wallet3-r2",
 | 
				
			||||||
                  "te6ccgEBAQEAcQAA3v8AIN0gggFMl7ohggEznLqxn3Gw7UTQ0x/THzHXC//jBOCk8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/"
 | 
					                  "te6ccgEBAQEAcQAA3v8AIN0gggFMl7ohggEznLqxn3Gw7UTQ0x/THzHXC//jBOCk8mCDCNcYINMf0x/TH/gjE7vyY+1E0NMf0x/"
 | 
				
			||||||
                  "T/9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA==");
 | 
					                  "T/9FRMrryoVFEuvKiBPkBVBBV+RDyo/gAkyDXSpbTB9QC+wDo0QGkyMsfyx/L/8ntVA==");
 | 
				
			||||||
    auto check_revision = [&](td::Slice name, td::int32 default_revision) {
 | 
					    with_tvm_code(
 | 
				
			||||||
      auto it = map.find(name);
 | 
					        "dns-manual-r1",
 | 
				
			||||||
      CHECK(it != map.end());
 | 
					        "te6ccgECGAEAAtAAART/APSkE/S88sgLAQIBIAIDAgFIBAUC7PLbPAWDCNcYIPkBAdMf0z/"
 | 
				
			||||||
      auto other_it = map.find(PSLICE() << name << "-r" << default_revision);
 | 
					        "4I6ofUyC58mNTKoBA9A5voTHyYFKUuvKiVBNG+RDyo/gAItcLBcAzmDQBdtch0/"
 | 
				
			||||||
      CHECK(other_it != map.end());
 | 
					        "8wjoVa2zxAA+"
 | 
				
			||||||
      CHECK(it->second->get_hash() == other_it->second->get_hash());
 | 
					        "IDgyWhyEAHgED0Q44aIIBA9JZvpTJREJQwUwe53iCTMzUBkjIw4rPmNVUD8AQREgICxQYHAgEgDA0CAc8ICQAIqoJfAwIBSAoLACHWQK5Y+"
 | 
				
			||||||
    };
 | 
					        "J5Z/l//oAegBk9qpAAFF8DgABcyPQAydBBM/Rw8qGAAF72c52omhpr5jrhf/"
 | 
				
			||||||
    check_revision("highload-wallet", HIGHLOAD_WALLET_REVISION);
 | 
					        "AIBIA4PABG7Nz7UTQ1wsfgD+"
 | 
				
			||||||
    check_revision("highload-wallet-v2", HIGHLOAD_WALLET2_REVISION);
 | 
					        "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("simple-wallet", WALLET_REVISION);
 | 
				
			||||||
    //check_revision("wallet", WALLET2_REVISION);
 | 
					    //check_revision("wallet", WALLET2_REVISION);
 | 
				
			||||||
| 
						 | 
					@ -137,21 +153,30 @@ td::Ref<vm::Cell> SmartContractCode::simple_wallet_ext() {
 | 
				
			||||||
  return res;
 | 
					  return res;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
td::Ref<vm::Cell> SmartContractCode::highload_wallet(int revision) {
 | 
					td::Ref<vm::Cell> SmartContractCode::highload_wallet(int revision) {
 | 
				
			||||||
 | 
					  if (revision == -1) {
 | 
				
			||||||
 | 
					    return load("highload-wallet").move_as_ok();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (revision == 0) {
 | 
					  if (revision == 0) {
 | 
				
			||||||
    revision = HIGHLOAD_WALLET_REVISION;
 | 
					    revision = HIGHLOAD_WALLET_REVISION;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  auto res = load(PSLICE() << "highload-wallet-r" << revision).move_as_ok();
 | 
					  return load(PSLICE() << "highload-wallet-r" << revision).move_as_ok();
 | 
				
			||||||
  return res;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
td::Ref<vm::Cell> SmartContractCode::highload_wallet_v2(int revision) {
 | 
					td::Ref<vm::Cell> SmartContractCode::highload_wallet_v2(int revision) {
 | 
				
			||||||
 | 
					  if (revision == -1) {
 | 
				
			||||||
 | 
					    return load("highload-wallet-v2").move_as_ok();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (revision == 0) {
 | 
					  if (revision == 0) {
 | 
				
			||||||
    revision = HIGHLOAD_WALLET2_REVISION;
 | 
					    revision = HIGHLOAD_WALLET2_REVISION;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  auto res = load(PSLICE() << "highload-wallet-v2-r" << revision).move_as_ok();
 | 
					  return load(PSLICE() << "highload-wallet-v2-r" << revision).move_as_ok();
 | 
				
			||||||
  return res;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
td::Ref<vm::Cell> SmartContractCode::dns_manual() {
 | 
					td::Ref<vm::Cell> SmartContractCode::dns_manual(int revision) {
 | 
				
			||||||
  static auto res = load("dns-manual").move_as_ok();
 | 
					  if (revision == -1) {
 | 
				
			||||||
  return res;
 | 
					    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
 | 
					}  // namespace ton
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,6 +29,6 @@ class SmartContractCode {
 | 
				
			||||||
  static td::Ref<vm::Cell> simple_wallet_ext();
 | 
					  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(int revision = 0);
 | 
				
			||||||
  static td::Ref<vm::Cell> highload_wallet_v2(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
 | 
					}  // namespace ton
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -409,12 +409,12 @@ TEST(Tonlib, HighloadWalletV2) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
 | 
					  td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}};
 | 
				
			||||||
  auto pub_key = priv_key.get_public_key().move_as_ok();
 | 
					  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 init_message = ton::HighloadWalletV2::get_init_message(priv_key, 239, 65535);
 | 
				
			||||||
  auto address = ton::GenericAccount::get_address(0, init_state);
 | 
					  auto address = ton::GenericAccount::get_address(0, init_state);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  ton::HighloadWalletV2 wallet(
 | 
					  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());
 | 
					  ASSERT_EQ(239u, wallet.get_wallet_id().ok());
 | 
				
			||||||
  CHECK(pub_key.as_octet_string() == wallet.get_public_key().ok().as_octet_string());
 | 
					  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
 | 
					    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/>.
 | 
					    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
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -348,6 +348,10 @@ class Stack : public td::CntObject {
 | 
				
			||||||
  void pop_many(int count) {
 | 
					  void pop_many(int count) {
 | 
				
			||||||
    stack.resize(stack.size() - 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) {
 | 
					  void drop_bottom(int count) {
 | 
				
			||||||
    std::move(stack.cbegin() + count, stack.cend(), stack.begin());
 | 
					    std::move(stack.cbegin() + count, stack.cend(), stack.begin());
 | 
				
			||||||
    pop_many(count);
 | 
					    pop_many(count);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -379,6 +379,15 @@ int exec_blkdrop(VmState* st, unsigned args) {
 | 
				
			||||||
  return 0;
 | 
					  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 exec_blkpush(VmState* st, unsigned args) {
 | 
				
			||||||
  int x = ((args >> 4) & 15), y = (args & 15);
 | 
					  int x = ((args >> 4) & 15), y = (args & 15);
 | 
				
			||||||
  Stack& stack = st->get_stack();
 | 
					  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(0x68, 8, "DEPTH", exec_depth))
 | 
				
			||||||
      .insert(OpcodeInstr::mksimple(0x69, 8, "CHKDEPTH", exec_chkdepth))
 | 
					      .insert(OpcodeInstr::mksimple(0x69, 8, "CHKDEPTH", exec_chkdepth))
 | 
				
			||||||
      .insert(OpcodeInstr::mksimple(0x6a, 8, "ONLYTOPX", exec_onlytop_x))
 | 
					      .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
 | 
					}  // namespace vm
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -811,26 +811,24 @@ bool store_grams(CellBuilder& cb, td::RefInt256 value) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int exec_reserve_raw(VmState* st, int mode) {
 | 
					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& stack = st->get_stack();
 | 
				
			||||||
  stack.check_underflow(2);
 | 
					  stack.check_underflow(2 + (mode & 1));
 | 
				
			||||||
  int f = stack.pop_smallint_range(3);
 | 
					  int f = stack.pop_smallint_range(15);
 | 
				
			||||||
  td::RefInt256 x;
 | 
					  Ref<Cell> y;
 | 
				
			||||||
  Ref<CellSlice> csr;
 | 
					 | 
				
			||||||
  if (mode & 1) {
 | 
					  if (mode & 1) {
 | 
				
			||||||
    csr = stack.pop_cellslice();
 | 
					    y = stack.pop_maybe_cell();
 | 
				
			||||||
  } else {
 | 
					  }
 | 
				
			||||||
    x = stack.pop_int_finite();
 | 
					  auto x = stack.pop_int_finite();
 | 
				
			||||||
    if (td::sgn(x) < 0) {
 | 
					  if (td::sgn(x) < 0) {
 | 
				
			||||||
      throw VmError{Excno::range_chk, "amount of nanograms must be non-negative"};
 | 
					    throw VmError{Excno::range_chk, "amount of nanograms must be non-negative"};
 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  CellBuilder cb;
 | 
					  CellBuilder cb;
 | 
				
			||||||
  if (!(cb.store_ref_bool(get_actions(st))     // out_list$_ {n:#} prev:^(OutList n)
 | 
					  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(0x36e6b809, 32)  // action_reserve_currency#36e6b809
 | 
				
			||||||
        && cb.store_long_bool(f, 8)            // mode:(## 8)
 | 
					        && cb.store_long_bool(f, 8)            // mode:(## 8)
 | 
				
			||||||
        && (mode & 1 ? cb.append_cellslice_bool(std::move(csr))
 | 
					        && store_grams(cb, std::move(x))       //
 | 
				
			||||||
                     : (store_grams(cb, std::move(x)) && cb.store_bool_bool(false))))) {
 | 
					        && cb.store_maybe_ref(std::move(y)))) {
 | 
				
			||||||
    throw VmError{Excno::cell_ov, "cannot serialize raw reserved currency amount into an output action cell"};
 | 
					    throw VmError{Excno::cell_ov, "cannot serialize raw reserved currency amount into an output action cell"};
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return install_output_action(st, cb.finalize());
 | 
					  return install_output_action(st, cb.finalize());
 | 
				
			||||||
| 
						 | 
					@ -886,8 +884,8 @@ int exec_change_lib(VmState* st) {
 | 
				
			||||||
void register_ton_message_ops(OpcodeTable& cp0) {
 | 
					void register_ton_message_ops(OpcodeTable& cp0) {
 | 
				
			||||||
  using namespace std::placeholders;
 | 
					  using namespace std::placeholders;
 | 
				
			||||||
  cp0.insert(OpcodeInstr::mksimple(0xfb00, 16, "SENDRAWMSG", exec_send_raw_message))
 | 
					  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(0xfb02, 16, "RAWRESERVE", std::bind(exec_reserve_raw, _1, 0)))
 | 
				
			||||||
      .insert(OpcodeInstr::mksimple(0xfb03, 16, "RESERVERAWX", std::bind(exec_reserve_raw, _1, 1)))
 | 
					      .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(0xfb04, 16, "SETCODE", exec_set_code))
 | 
				
			||||||
      .insert(OpcodeInstr::mksimple(0xfb06, 16, "SETLIBCODE", exec_set_lib_code))
 | 
					      .insert(OpcodeInstr::mksimple(0xfb06, 16, "SETLIBCODE", exec_set_lib_code))
 | 
				
			||||||
      .insert(OpcodeInstr::mksimple(0xfb07, 16, "CHANGELIB", exec_change_lib));
 | 
					      .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 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 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 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}
 | 
					\end{itemize}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
\mysubsection{Tuple, List, and Null primitives}
 | 
					\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}
 | 
					\nxsubpoint\emb{Outbound message and output action primitives}
 | 
				
			||||||
\begin{itemize}
 | 
					\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 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 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} ($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 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 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 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.
 | 
					\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 .
 | 
					pushd .
 | 
				
			||||||
mkdir -p build_native
 | 
					mkdir -p build_native
 | 
				
			||||||
cd build_native
 | 
					cd build_native
 | 
				
			||||||
cmake .. || exit 1
 | 
					cmake -DTON_ONLY_TONLIB=ON .. || exit 1
 | 
				
			||||||
cmake --build . --target prepare_cross_compiling || exit 1
 | 
					cmake --build . --target prepare_cross_compiling || exit 2
 | 
				
			||||||
#cmake --build . --target tl_generate_java || exit 1
 | 
					#cmake --build . --target tl_generate_java || exit 1
 | 
				
			||||||
popd
 | 
					popd
 | 
				
			||||||
php AddIntDef.php src/drinkless/org/ton/TonApi.java || exit 1
 | 
					php AddIntDef.php src/drinkless/org/ton/TonApi.java || exit 1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,5 +1,5 @@
 | 
				
			||||||
#include "rlimit.h"
 | 
					#include "rlimit.h"
 | 
				
			||||||
#if TD_LINUX
 | 
					#if TD_LINUX || TD_ANDROID
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
#include <sys/types.h>
 | 
					#include <sys/types.h>
 | 
				
			||||||
#include <sys/time.h>
 | 
					#include <sys/time.h>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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 "terminal.hpp"
 | 
				
			||||||
#include "td/utils/port/StdStreams.h"
 | 
					#include "td/utils/port/StdStreams.h"
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,7 @@ void TerminalLogInterface::append(CSlice slice, int log_level) {
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      color = TC_GREEN;
 | 
					      color = TC_GREEN;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    td::TsCerr() << color << slice << TC_EMPTY;
 | 
					    std::cerr << color << slice.c_str() << TC_EMPTY;
 | 
				
			||||||
    instance_->reactivate_readline();
 | 
					    instance_->reactivate_readline();
 | 
				
			||||||
    if (log_level == VERBOSITY_NAME(FATAL)) {
 | 
					    if (log_level == VERBOSITY_NAME(FATAL)) {
 | 
				
			||||||
      process_fatal_error(slice);
 | 
					      process_fatal_error(slice);
 | 
				
			||||||
| 
						 | 
					@ -82,6 +82,16 @@ void TerminalIOImpl::output_line(std::string line) {
 | 
				
			||||||
  reactivate_readline();
 | 
					  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() {
 | 
					void TerminalIOImpl::start_up() {
 | 
				
			||||||
  instance_ = this;
 | 
					  instance_ = this;
 | 
				
			||||||
  self_ = actor_id(this);
 | 
					  self_ = actor_id(this);
 | 
				
			||||||
| 
						 | 
					@ -246,7 +256,7 @@ void TerminalIO::output(std::string line) {
 | 
				
			||||||
    std::cout << line;
 | 
					    std::cout << line;
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    instance_->deactivate_readline();
 | 
					    instance_->deactivate_readline();
 | 
				
			||||||
    td::TsCerr() << line;
 | 
					    std::cout << line;
 | 
				
			||||||
    instance_->reactivate_readline();
 | 
					    instance_->reactivate_readline();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -254,10 +264,40 @@ void TerminalIO::output(std::string line) {
 | 
				
			||||||
void TerminalIO::output(td::Slice line) {
 | 
					void TerminalIO::output(td::Slice line) {
 | 
				
			||||||
  auto instance_ = TerminalIOImpl::instance();
 | 
					  auto instance_ = TerminalIOImpl::instance();
 | 
				
			||||||
  if (!instance_) {
 | 
					  if (!instance_) {
 | 
				
			||||||
    td::TsCerr() << line;
 | 
					    std::cout.write(line.begin(), line.size());
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    instance_->deactivate_readline();
 | 
					    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();
 | 
					    instance_->reactivate_readline();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -265,7 +305,11 @@ void TerminalIO::output(td::Slice line) {
 | 
				
			||||||
TerminalIOOutputter::~TerminalIOOutputter() {
 | 
					TerminalIOOutputter::~TerminalIOOutputter() {
 | 
				
			||||||
  if (buffer_) {
 | 
					  if (buffer_) {
 | 
				
			||||||
    CHECK(sb_);
 | 
					    CHECK(sb_);
 | 
				
			||||||
    TerminalIO::output(sb_->as_cslice());
 | 
					    if (is_err_) {
 | 
				
			||||||
 | 
					      TerminalIO::output_stderr(sb_->as_cslice());
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      TerminalIO::output(sb_->as_cslice());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
    delete[] buffer_;
 | 
					    delete[] buffer_;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -29,8 +29,10 @@ namespace td {
 | 
				
			||||||
class TerminalIOOutputter {
 | 
					class TerminalIOOutputter {
 | 
				
			||||||
 public:
 | 
					 public:
 | 
				
			||||||
  static const size_t BUFFER_SIZE = 128 * 1024;
 | 
					  static const size_t BUFFER_SIZE = 128 * 1024;
 | 
				
			||||||
  TerminalIOOutputter()
 | 
					  TerminalIOOutputter(bool is_err)
 | 
				
			||||||
      : buffer_(new char[BUFFER_SIZE]), sb_(std::make_unique<StringBuilder>(td::MutableSlice{buffer_, BUFFER_SIZE})) {
 | 
					      : buffer_(new char[BUFFER_SIZE])
 | 
				
			||||||
 | 
					      , is_err_(is_err)
 | 
				
			||||||
 | 
					      , sb_(std::make_unique<StringBuilder>(td::MutableSlice{buffer_, BUFFER_SIZE})) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  TerminalIOOutputter(TerminalIOOutputter &&X) = default;
 | 
					  TerminalIOOutputter(TerminalIOOutputter &&X) = default;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,6 +60,7 @@ class TerminalIOOutputter {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 private:
 | 
					 private:
 | 
				
			||||||
  char *buffer_;
 | 
					  char *buffer_;
 | 
				
			||||||
 | 
					  bool is_err_;
 | 
				
			||||||
  std::unique_ptr<StringBuilder> sb_;
 | 
					  std::unique_ptr<StringBuilder> sb_;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,11 +78,18 @@ class TerminalIO : public actor::Actor {
 | 
				
			||||||
  virtual void update_callback(std::unique_ptr<Callback> callback) = 0;
 | 
					  virtual void update_callback(std::unique_ptr<Callback> callback) = 0;
 | 
				
			||||||
  static void output(std::string line);
 | 
					  static void output(std::string line);
 | 
				
			||||||
  static void output(td::Slice slice);
 | 
					  static void output(td::Slice slice);
 | 
				
			||||||
 | 
					  static void output_stderr(std::string line);
 | 
				
			||||||
 | 
					  static void output_stderr(td::Slice slice);
 | 
				
			||||||
  static TerminalIOOutputter out() {
 | 
					  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(std::string line) = 0;
 | 
				
			||||||
 | 
					  virtual void output_line_stderr(std::string line) = 0;
 | 
				
			||||||
  virtual void set_log_interface() = 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,
 | 
					  static td::actor::ActorOwn<TerminalIO> create(std::string prompt, bool use_readline,
 | 
				
			||||||
                                                std::unique_ptr<Callback> callback);
 | 
					                                                std::unique_ptr<Callback> callback);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -45,6 +45,10 @@ class TerminalIOImpl : public TerminalIO, td::ObserverBase {
 | 
				
			||||||
  void deactivate_readline();
 | 
					  void deactivate_readline();
 | 
				
			||||||
  void reactivate_readline();
 | 
					  void reactivate_readline();
 | 
				
			||||||
  void output_line(std::string line) override;
 | 
					  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 set_log_interface() override;
 | 
				
			||||||
  //void read_line();
 | 
					  //void read_line();
 | 
				
			||||||
  void loop() override;
 | 
					  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;
 | 
					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.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.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;
 | 
					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 {
 | 
					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" ||
 | 
					  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 {
 | 
					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") {
 | 
					  if (name == "Bytes" || name == "SecureBytes") {
 | 
				
			||||||
    return "byte[]";
 | 
					    return "byte[]";
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (name == "Object") {
 | 
				
			||||||
 | 
					    return gen_base_type_class_name(0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (name == "Function") {
 | 
				
			||||||
 | 
					    return gen_base_function_class_name();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (name == "Vector") {
 | 
					  if (name == "Vector") {
 | 
				
			||||||
    assert(t->arity == 1);
 | 
					    assert(t->arity == 1);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,8 +24,10 @@
 | 
				
			||||||
namespace td {
 | 
					namespace td {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool TD_TL_writer_jni_cpp::is_built_in_simple_type(const std::string &name) const {
 | 
					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" ||
 | 
					  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 {
 | 
					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]);
 | 
					    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);
 | 
					    res = gen_vector_fetch(field_name, child, vars, parser_type);
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
 | 
					    assert(gen_main_class_name(tree_type->type) != "function");
 | 
				
			||||||
    if (field_name == "") {
 | 
					    if (field_name == "") {
 | 
				
			||||||
      return gen_main_class_name(tree_type->type) + "::fetch(env, p)";
 | 
					      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 {
 | 
					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" ||
 | 
					  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 {
 | 
					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") {
 | 
					  if (name == "Object") {
 | 
				
			||||||
    //assert(false);
 | 
					    //assert(false);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  if (name == "function") {
 | 
				
			||||||
 | 
					    return "Function";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if (name == "object") {
 | 
				
			||||||
 | 
					    return "Object";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  if (name == "#") {
 | 
					  if (name == "#") {
 | 
				
			||||||
    return "std::int32_t";
 | 
					    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 secret = "secret";
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    std::string data = "some private data";
 | 
					    std::string data = "some private data";
 | 
				
			||||||
    std::string wrong_secret = "wrong secret";
 | 
					    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();
 | 
					    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);
 | 
					    CHECK(data == decrypted_data);
 | 
				
			||||||
    SimpleEncryption::decrypt_data(encrypted_data, wrong_secret).ensure_error();
 | 
					    Encryption::decrypt_data(encrypted_data, wrong_secret).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data("", secret).ensure_error();
 | 
					    Encryption::decrypt_data("", secret).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(32, 'a'), secret).ensure_error();
 | 
					    Encryption::decrypt_data(std::string(32, 'a'), secret).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(33, 'a'), secret).ensure_error();
 | 
					    Encryption::decrypt_data(std::string(33, 'a'), secret).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(64, 'a'), secret).ensure_error();
 | 
					    Encryption::decrypt_data(std::string(64, 'a'), secret).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(128, 'a'), secret).ensure_error();
 | 
					    Encryption::decrypt_data(std::string(128, 'a'), secret).ensure_error();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (size_t i = 0; i < 255; i++) {
 | 
					  for (size_t i = 0; i < 255; i++) {
 | 
				
			||||||
    auto data = td::rand_string('a', 'z', static_cast<int>(i));
 | 
					    auto data = td::rand_string('a', 'z', static_cast<int>(i));
 | 
				
			||||||
    auto encrypted_data = SimpleEncryption::encrypt_data(data, secret);
 | 
					    auto encrypted_data = Encryption::encrypt_data(data, secret);
 | 
				
			||||||
    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);
 | 
					    CHECK(data == decrypted_data);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					TEST(Tonlib, SimpleEncryption) {
 | 
				
			||||||
 | 
					  test_encryption<SimpleEncryption>();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TEST(Tonlib, SimpleEncryptionV2) {
 | 
				
			||||||
 | 
					  test_encryption<SimpleEncryptionV2>();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TEST(Tonlib, SimpleEncryptionAsym) {
 | 
					TEST(Tonlib, SimpleEncryptionAsym) {
 | 
				
			||||||
  auto private_key = td::Ed25519::generate_private_key().move_as_ok();
 | 
					  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();
 | 
					  auto wrong_private_key = td::Ed25519::generate_private_key().move_as_ok();
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
    std::string data = "some private data";
 | 
					    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();
 | 
					    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);
 | 
					    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);
 | 
					    CHECK(data == decrypted_data2);
 | 
				
			||||||
    SimpleEncryption::decrypt_data(encrypted_data, wrong_private_key).ensure_error();
 | 
					    SimpleEncryptionV2::decrypt_data(encrypted_data, wrong_private_key).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data("", private_key).ensure_error();
 | 
					    SimpleEncryptionV2::decrypt_data("", private_key).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(32, 'a'), private_key).ensure_error();
 | 
					    SimpleEncryptionV2::decrypt_data(std::string(32, 'a'), private_key).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(33, 'a'), private_key).ensure_error();
 | 
					    SimpleEncryptionV2::decrypt_data(std::string(33, 'a'), private_key).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(64, 'a'), private_key).ensure_error();
 | 
					    SimpleEncryptionV2::decrypt_data(std::string(64, 'a'), private_key).ensure_error();
 | 
				
			||||||
    SimpleEncryption::decrypt_data(std::string(128, 'a'), private_key).ensure_error();
 | 
					    SimpleEncryptionV2::decrypt_data(std::string(128, 'a'), private_key).ensure_error();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (size_t i = 0; i < 255; i++) {
 | 
					  for (size_t i = 0; i < 255; i++) {
 | 
				
			||||||
    auto data = td::rand_string('a', 'z', static_cast<int>(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 encrypted_data = SimpleEncryptionV2::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 decrypted_data = SimpleEncryptionV2::decrypt_data(encrypted_data, private_key).move_as_ok();
 | 
				
			||||||
    CHECK(data == decrypted_data);
 | 
					    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);
 | 
					    CHECK(data == decrypted_data2);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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"
 | 
					#include "KeyStorage.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -14,7 +14,7 @@
 | 
				
			||||||
    You should have received a copy of the GNU Lesser General Public License
 | 
					    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/>.
 | 
					    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
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -386,6 +386,15 @@ class AccountState {
 | 
				
			||||||
          {ton::HighloadWalletV2::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)});
 | 
					          {ton::HighloadWalletV2::get_init_code(wallet_revision_), ton::WalletV3::get_init_data(key, wallet_id_)});
 | 
				
			||||||
      return wallet_type_;
 | 
					      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 ==
 | 
					    if (ton::GenericAccount::get_address(address_.workchain, ton::TestWallet::get_init_state(key)).addr ==
 | 
				
			||||||
        address_.addr) {
 | 
					        address_.addr) {
 | 
				
			||||||
      set_new_state({ton::TestWallet::get_init_code(), ton::TestWallet::get_init_data(key)});
 | 
					      set_new_state({ton::TestWallet::get_init_code(), ton::TestWallet::get_init_data(key)});
 | 
				
			||||||
| 
						 | 
					@ -399,12 +408,6 @@ class AccountState {
 | 
				
			||||||
                   .addr == address_.addr) {
 | 
					                   .addr == address_.addr) {
 | 
				
			||||||
      set_new_state({ton::HighloadWallet::get_init_code(), ton::HighloadWallet::get_init_data(key, wallet_id_)});
 | 
					      set_new_state({ton::HighloadWallet::get_init_code(), ton::HighloadWallet::get_init_data(key, wallet_id_)});
 | 
				
			||||||
      wallet_type_ = WalletType::HighloadWalletV1;
 | 
					      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_;
 | 
					    return wallet_type_;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
| 
						 | 
					@ -466,6 +469,12 @@ class AccountState {
 | 
				
			||||||
      wallet_revision_ = o_revision.value();
 | 
					      wallet_revision_ = o_revision.value();
 | 
				
			||||||
      return wallet_type_;
 | 
					      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()) {
 | 
					    if (code_hash == ton::TestGiver::get_init_code_hash()) {
 | 
				
			||||||
      wallet_type_ = WalletType::Giver;
 | 
					      wallet_type_ = WalletType::Giver;
 | 
				
			||||||
| 
						 | 
					@ -475,8 +484,6 @@ class AccountState {
 | 
				
			||||||
      wallet_type_ = WalletType::Wallet;
 | 
					      wallet_type_ = WalletType::Wallet;
 | 
				
			||||||
    } else if (code_hash == ton::HighloadWallet::get_init_code_hash()) {
 | 
					    } else if (code_hash == ton::HighloadWallet::get_init_code_hash()) {
 | 
				
			||||||
      wallet_type_ = WalletType::HighloadWalletV1;
 | 
					      wallet_type_ = WalletType::HighloadWalletV1;
 | 
				
			||||||
    } else if (code_hash == ton::SmartContractCode::dns_manual()->get_hash()) {
 | 
					 | 
				
			||||||
      wallet_type_ = WalletType::ManualDns;
 | 
					 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice());
 | 
					      LOG(WARNING) << "Unknown code hash: " << td::base64_encode(code_hash.as_slice());
 | 
				
			||||||
      wallet_type_ = WalletType::Unknown;
 | 
					      wallet_type_ = WalletType::Unknown;
 | 
				
			||||||
| 
						 | 
					@ -1308,7 +1315,7 @@ td::Result<block::StdAddress> get_account_address(const tonlib_api::dns_initialA
 | 
				
			||||||
                                                  td::int32 revision) {
 | 
					                                                  td::int32 revision) {
 | 
				
			||||||
  TRY_RESULT(key_bytes, get_public_key(dns_state.public_key_));
 | 
					  TRY_RESULT(key_bytes, get_public_key(dns_state.public_key_));
 | 
				
			||||||
  auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.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) {
 | 
					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();
 | 
					        auto body_hash = vm::CellBuilder().append_cellslice(*body).finalize()->get_hash().as_slice().str();
 | 
				
			||||||
        std::string body_message;
 | 
					        std::string body_message;
 | 
				
			||||||
 | 
					        bool is_encrypted = false;
 | 
				
			||||||
        if (body->size() >= 32 && body->prefetch_long(32) <= 1) {
 | 
					        if (body->size() >= 32 && body->prefetch_long(32) <= 1) {
 | 
				
			||||||
          auto type = body.write().fetch_long(32);
 | 
					          auto type = body.write().fetch_long(32);
 | 
				
			||||||
          auto r_body_message = vm::CellString::load(body.write());
 | 
					          auto r_body_message = vm::CellString::load(body.write());
 | 
				
			||||||
| 
						 | 
					@ -1631,7 +1639,8 @@ struct ToRawTransactions {
 | 
				
			||||||
              if (!private_key_) {
 | 
					              if (!private_key_) {
 | 
				
			||||||
                return TonlibError::EmptyField("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();
 | 
					              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,
 | 
					        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),
 | 
					                                                                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: {
 | 
					      case block::gen::CommonMsgInfo::ext_in_msg_info: {
 | 
				
			||||||
        block::gen::CommonMsgInfo::Record_ext_in_msg_info 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();
 | 
					        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),
 | 
					        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: {
 | 
					      case block::gen::CommonMsgInfo::ext_out_msg_info: {
 | 
				
			||||||
        block::gen::CommonMsgInfo::Record_ext_out_msg_info 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");
 | 
					          return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        TRY_RESULT(src, to_std_address(msg_info.src));
 | 
					        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");
 | 
					          return TonlibError::EmptyField("private_key");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        if (!destination->is_wallet()) {
 | 
					        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();
 | 
					        auto wallet = destination->get_wallet();
 | 
				
			||||||
        TRY_RESULT_PREFIX(public_key, wallet->get_public_key(),
 | 
					        TRY_RESULT_PREFIX(public_key, wallet->get_public_key(),
 | 
				
			||||||
                          TonlibError::AccountActionUnsupported("Get public key (in destination)"));
 | 
					                          TonlibError::AccountActionUnsupported("Get public key (in destination)"));
 | 
				
			||||||
        TRY_RESULT_PREFIX(encrypted_message,
 | 
					        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());
 | 
					                          TonlibError::Internal());
 | 
				
			||||||
        gift.message = encrypted_message.as_slice().str();
 | 
					        gift.message = encrypted_message.as_slice().str();
 | 
				
			||||||
        gift.is_encrypted = true;
 | 
					        gift.is_encrypted = true;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -95,6 +95,9 @@ struct TonlibError {
 | 
				
			||||||
  static td::Status DangerousTransaction(td::Slice reason) {
 | 
					  static td::Status DangerousTransaction(td::Slice reason) {
 | 
				
			||||||
    return td::Status::Error(400, PSLICE() << "DANGEROUS_TRANSACTION: " << 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() {
 | 
					  static td::Status AccountNotInited() {
 | 
				
			||||||
    return td::Status::Error(400, "ACCOUNT_NOT_INITED");
 | 
					    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());
 | 
					  return calc_aes_cbc_state_hash(hash.as_slice());
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
td::SecureString SimpleEncryption::gen_random_prefix(td::int64 data_size) {
 | 
					td::SecureString SimpleEncryption::gen_random_prefix(td::int64 data_size, td::int64 min_padding) {
 | 
				
			||||||
  td::SecureString buff(td::narrow_cast<size_t>(((32 + 15 + data_size) & -16) - data_size), 0);
 | 
					  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());
 | 
					  td::Random::secure_bytes(buff.as_mutable_slice());
 | 
				
			||||||
  buff.as_mutable_slice()[0] = td::narrow_cast<td::uint8>(buff.size());
 | 
					  buff.as_mutable_slice()[0] = td::narrow_cast<td::uint8>(buff.size());
 | 
				
			||||||
  CHECK((buff.size() + data_size) % 16 == 0);
 | 
					  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) {
 | 
					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());
 | 
					  td::SecureString combined(prefix.size() + data.size());
 | 
				
			||||||
  combined.as_mutable_slice().copy_from(prefix);
 | 
					  combined.as_mutable_slice().copy_from(prefix);
 | 
				
			||||||
  combined.as_mutable_slice().substr(prefix.size()).copy_from(data);
 | 
					  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));
 | 
					  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());
 | 
					  TRY_RESULT(tmp_private_key, td::Ed25519::generate_private_key());
 | 
				
			||||||
  return encrypt_data(data, public_key, tmp_private_key);
 | 
					  return encrypt_data(data, public_key, tmp_private_key);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -122,8 +123,8 @@ td::SecureString secure_xor(td::Slice a, td::Slice b) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
}  // namespace
 | 
					}  // 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) {
 | 
					                                                              const td::Ed25519::PrivateKey &private_key) {
 | 
				
			||||||
  TRY_RESULT(shared_secret, td::Ed25519::compute_shared_secret(public_key, private_key));
 | 
					  TRY_RESULT(shared_secret, td::Ed25519::compute_shared_secret(public_key, private_key));
 | 
				
			||||||
  auto encrypted = encrypt_data(data, shared_secret.as_slice());
 | 
					  auto encrypted = encrypt_data(data, shared_secret.as_slice());
 | 
				
			||||||
  TRY_RESULT(tmp_public_key, private_key.get_public_key());
 | 
					  TRY_RESULT(tmp_public_key, private_key.get_public_key());
 | 
				
			||||||
| 
						 | 
					@ -135,8 +136,8 @@ td::Result<td::SecureString> SimpleEncryption::encrypt_data(td::Slice data, cons
 | 
				
			||||||
  return std::move(prefixed_encrypted);
 | 
					  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) {
 | 
					                                                              const td::Ed25519::PrivateKey &private_key) {
 | 
				
			||||||
  if (data.size() < td::Ed25519::PublicKey::LENGTH) {
 | 
					  if (data.size() < td::Ed25519::PublicKey::LENGTH) {
 | 
				
			||||||
    return td::Status::Error("Failed to decrypte: data is too small");
 | 
					    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()));
 | 
					  TRY_RESULT(decrypted, decrypt_data(data.substr(td::Ed25519::PublicKey::LENGTH), shared_secret.as_slice()));
 | 
				
			||||||
  return std::move(decrypted);
 | 
					  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
 | 
					}  // namespace tonlib
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,16 +31,26 @@ class SimpleEncryption {
 | 
				
			||||||
  static td::SecureString combine_secrets(td::Slice a, td::Slice b);
 | 
					  static td::SecureString combine_secrets(td::Slice a, td::Slice b);
 | 
				
			||||||
  static td::SecureString kdf(td::Slice secret, td::Slice password, int iterations);
 | 
					  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> 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> 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,
 | 
					  static td::Result<td::SecureString> encrypt_data(td::Slice data, const td::Ed25519::PublicKey &public_key,
 | 
				
			||||||
                                                   const td::Ed25519::PrivateKey &private_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:
 | 
					 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);
 | 
					  static td::SecureString encrypt_data_with_prefix(td::Slice data, td::Slice secret);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
}  // namespace tonlib
 | 
					}  // 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(input_key), ton::move_tl_object_as<tonlib_api::accountAddress>(std::move(address.address)),
 | 
				
			||||||
                   std::move(state->last_transaction_id_)),
 | 
					                   std::move(state->last_transaction_id_)),
 | 
				
			||||||
               promise.wrap([](auto res) {
 | 
					               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();
 | 
					                 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) {
 | 
					  void transfer(td::ConstParser& parser, td::Slice cmd, td::Promise<td::Unit> cmd_promise) {
 | 
				
			||||||
    bool from_file = false;
 | 
					    bool from_file = false;
 | 
				
			||||||
    bool force = false;
 | 
					    bool force = false;
 | 
				
			||||||
 | 
					    bool use_encryption = false;
 | 
				
			||||||
    if (cmd != "init") {
 | 
					    if (cmd != "init") {
 | 
				
			||||||
      td::ConstParser cmd_parser(cmd);
 | 
					      td::ConstParser cmd_parser(cmd);
 | 
				
			||||||
      cmd_parser.advance(td::Slice("transfer").size());
 | 
					      cmd_parser.advance(td::Slice("transfer").size());
 | 
				
			||||||
| 
						 | 
					@ -1374,6 +1418,8 @@ class TonlibCli : public td::actor::Actor {
 | 
				
			||||||
          from_file = true;
 | 
					          from_file = true;
 | 
				
			||||||
        } else if (c == 'f') {
 | 
					        } else if (c == 'f') {
 | 
				
			||||||
          force = true;
 | 
					          force = true;
 | 
				
			||||||
 | 
					        } else if (c == 'e') {
 | 
				
			||||||
 | 
					          use_encryption = true;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          cmd_promise.set_error(td::Status::Error(PSLICE() << "Unknown suffix '" << c << "'"));
 | 
					          cmd_promise.set_error(td::Status::Error(PSLICE() << "Unknown suffix '" << c << "'"));
 | 
				
			||||||
          return;
 | 
					          return;
 | 
				
			||||||
| 
						 | 
					@ -1394,13 +1440,21 @@ class TonlibCli : public td::actor::Actor {
 | 
				
			||||||
    auto add_message = [&](td::ConstParser& parser) {
 | 
					    auto add_message = [&](td::ConstParser& parser) {
 | 
				
			||||||
      auto to = parser.read_word();
 | 
					      auto to = parser.read_word();
 | 
				
			||||||
      auto grams = parser.read_word();
 | 
					      auto grams = parser.read_word();
 | 
				
			||||||
      auto message = parser.read_word();
 | 
					      parser.skip_whitespaces();
 | 
				
			||||||
 | 
					      auto message = parser.read_all();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      Message res;
 | 
					      Message res;
 | 
				
			||||||
      TRY_RESULT(address, to_account_address(to, false));
 | 
					      TRY_RESULT(address, to_account_address(to, false));
 | 
				
			||||||
      TRY_RESULT(amount, parse_grams(grams));
 | 
					      TRY_RESULT(amount, parse_grams(grams));
 | 
				
			||||||
      messages.push_back(tonlib_api::make_object<tonlib_api::msg_message>(
 | 
					      tonlib_api::object_ptr<tonlib_api::msg_Data> data;
 | 
				
			||||||
          std::move(address.address), amount.nano, tonlib_api::make_object<tonlib_api::msg_dataText>(message.str())));
 | 
					
 | 
				
			||||||
 | 
					      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();
 | 
					      return td::Status::OK();
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue