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