1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-12 11:12:16 +00:00

updated func/fift

- updated func/fift
- updated liteclient/liteserver
- bugfixes
This commit is contained in:
ton 2019-12-29 12:14:12 +03:00
parent d41ce55305
commit acf16718e6
45 changed files with 1360 additions and 185 deletions

View file

@ -241,6 +241,28 @@ bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, ton::WorkchainId& wor
return false;
}
bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain,
const ton::StdSmcAddress& addr) const {
if (workchain >= -128 && workchain < 128) {
return cb.store_long_bool(4, 3) // addr_std$10 anycast:(Maybe Anycast)
&& cb.store_long_bool(workchain, 8) // workchain_id:int8
&& cb.store_bits_bool(addr); // address:bits256 = MsgAddressInt;
} else {
return cb.store_long_bool(0xd00, 12) // addr_var$11 anycast:(Maybe Anycast) addr_len:(## 9)
&& cb.store_long_bool(workchain, 32) // workchain_id:int32
&& cb.store_bits_bool(addr); // address:(bits addr_len) = MsgAddressInt;
}
}
Ref<vm::CellSlice> MsgAddressInt::pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const {
vm::CellBuilder cb;
if (store_std_address(cb, workchain, addr)) {
return vm::load_cell_slice_ref(cb.finalize());
} else {
return {};
}
}
const MsgAddressInt t_MsgAddressInt;
bool MsgAddress::validate_skip(vm::CellSlice& cs, bool weak) const {

View file

@ -285,6 +285,8 @@ struct MsgAddressInt final : TLB_Complex {
bool rewrite = true) const;
bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr,
bool rewrite = true) const;
bool store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
Ref<vm::CellSlice> pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
};
extern const MsgAddressInt t_MsgAddressInt;

View file

@ -727,3 +727,33 @@ after:^VmCont = VmCont;
vmc_while_body$110011 cond:^VmCont body:^VmCont
after:^VmCont = VmCont;
vmc_pushint$1111 value:int32 next:^VmCont = VmCont;
//
// DNS RECORDS
//
_ (HashmapE 16 ^DNSRecord) = DNS_RecordSet;
chunk_ref$_ {n:#} ref:^(TextChunks (n + 1)) = TextChunkRef (n + 1);
chunk_ref_empty$_ = TextChunkRef 0;
text_chunk$_ {n:#} len:(## 8) data:(bits (len * 8)) next:(TextChunkRef n) = TextChunks (n + 1);
text_chunk_empty$_ = TextChunks 0;
text$_ chunks:(## 8) rest:(TextChunks chunks) = Text;
dns_text#1eda _:Text = DNSRecord;
dns_next_resolver#ba93 resolver:MsgAddressInt = DNSRecord; // usually in record #-1
dns_adnl_address#ad01 adnl_addr:bits256 flags:(## 8) { flags <= 1 }
proto_list:flags . 0?ProtoList = DNSRecord; // often in record #2
proto_list_nil$0 = ProtoList;
proto_list_next$1 head:Protocol tail:ProtoList = ProtoList;
proto_http#4854 = Protocol;
dns_smc_address#9fd3 smc_addr:MsgAddressInt flags:(## 8) { flags <= 1 }
cap_list:flags . 0?SmcCapList = DNSRecord; // often in record #1
cap_list_nil$0 = SmcCapList;
cap_list_next$1 head:SmcCapability tail:SmcCapList = SmcCapList;
cap_method_seqno#5371 = SmcCapability;
cap_method_pubkey#71f4 = SmcCapability;
cap_is_wallet#2177 = SmcCapability;
cap_name#ff name:Text = SmcCapability;

View file

@ -633,6 +633,88 @@ void init_words_custom(fift::Dictionary& d) {
d.def_stack_word("isWorkchainDescr? ", interpret_is_workchain_descr);
}
tlb::TypenameLookup tlb_dict;
// ( S -- T -1 or 0 ) Looks up TLB type by name
void interpret_tlb_type_lookup(vm::Stack& stack) {
auto ptr = tlb_dict.lookup(stack.pop_string());
if (ptr) {
stack.push_make_object<tlb::TlbTypeHolder>(ptr);
}
stack.push_bool(ptr);
}
td::Ref<tlb::TlbTypeHolder> pop_tlb_type(vm::Stack& stack) {
auto res = stack.pop_object<tlb::TlbTypeHolder>();
if (res.is_null()) {
throw vm::VmError{vm::Excno::type_chk, "not a TLB type"};
}
return res;
}
// ( T -- S ) Gets TLB type name
void interpret_tlb_type_name(vm::Stack& stack) {
stack.push_string((*pop_tlb_type(stack))->get_type_name());
}
// ( T -- ) Prints TLB type name
void interpret_print_tlb_type(vm::Stack& stack) {
std::cout << (*pop_tlb_type(stack))->get_type_name();
}
// ( s T -- ) Dumps (part of) slice s as a value of TLB type T
void interpret_tlb_dump_as(vm::Stack& stack) {
auto tp = pop_tlb_type(stack);
(*tp)->print(std::cout, stack.pop_cellslice());
}
// ( s T -- s' S -1 or 0 )
// Detects prefix of slice s that is a value of TLB type T, returns the remainder as s', and prints the value into String S.
void interpret_tlb_dump_to_str(vm::Stack& stack) {
auto tp = pop_tlb_type(stack);
auto cs = stack.pop_cellslice();
std::ostringstream os;
bool ok = (*tp)->print_skip(os, cs.write());
if (ok) {
stack.push(std::move(cs));
stack.push_string(os.str());
}
stack.push_bool(ok);
}
// ( s T -- s' -1 or 0 ) Skips the only prefix of slice s that can be a value of TLB type T
void interpret_tlb_skip(vm::Stack& stack) {
auto tp = pop_tlb_type(stack);
auto cs = stack.pop_cellslice();
bool ok = (*tp)->skip(cs.write());
if (ok) {
stack.push(std::move(cs));
}
stack.push_bool(ok);
}
// ( s T -- s' -1 or 0 ) Checks whether a prefix of slice s is a valid value of TLB type T, and skips it
void interpret_tlb_validate_skip(vm::Stack& stack) {
auto tp = pop_tlb_type(stack);
auto cs = stack.pop_cellslice();
bool ok = (*tp)->validate_skip(cs.write());
if (ok) {
stack.push(std::move(cs));
}
stack.push_bool(ok);
}
void init_words_tlb(fift::Dictionary& d) {
tlb_dict.register_types(block::gen::register_simple_types);
d.def_stack_word("tlb-type-lookup ", interpret_tlb_type_lookup);
d.def_stack_word("tlb-type-name ", interpret_tlb_type_name);
d.def_stack_word("tlb. ", interpret_print_tlb_type);
d.def_stack_word("tlb-dump-as ", interpret_tlb_dump_as);
d.def_stack_word("(tlb-dump-str?) ", interpret_tlb_dump_to_str);
d.def_stack_word("tlb-skip ", interpret_tlb_skip);
d.def_stack_word("tlb-validate-skip ", interpret_tlb_validate_skip);
}
void usage(const char* progname) {
std::cerr
<< "Creates initial state for a TON blockchain, using configuration defined by Fift-language source files\n";
@ -739,6 +821,7 @@ int main(int argc, char* const argv[]) {
fift::init_words_vm(config.dictionary);
fift::init_words_ton(config.dictionary);
init_words_custom(config.dictionary);
init_words_tlb(config.dictionary);
if (script_mode) {
fift::import_cmdline_args(config.dictionary, source_list.empty() ? "" : source_list[0], argc - optind,

View file

@ -191,7 +191,8 @@ void test2(vm::CellSlice& cs) {
}
void usage() {
std::cout << "usage: dump-block [-S][<boc-file>]\n\tor dump-block -h\n\tDumps specified blockchain block or state "
std::cout << "usage: dump-block [-t<typename>][-S][<boc-file>]\n\tor dump-block -h\n\tDumps specified blockchain "
"block or state "
"from <boc-file>, or runs some tests\n\t-S\tDump a blockchain state instead of a block\n";
std::exit(2);
}
@ -199,15 +200,20 @@ void usage() {
int main(int argc, char* const argv[]) {
int i;
int new_verbosity_level = VERBOSITY_NAME(INFO);
bool dump_state = false, dump_vmcont = false;
const char* tname = nullptr;
const tlb::TLB* type = &block::gen::t_Block;
auto zerostate = std::make_unique<block::ZerostateInfo>();
while ((i = getopt(argc, argv, "CShv:")) != -1) {
while ((i = getopt(argc, argv, "CSt:hv:")) != -1) {
switch (i) {
case 'C':
dump_vmcont = true;
type = &block::gen::t_VmCont;
break;
case 'S':
dump_state = true;
type = &block::gen::t_ShardStateUnsplit;
break;
case 't':
tname = optarg;
type = nullptr;
break;
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
@ -233,13 +239,18 @@ int main(int argc, char* const argv[]) {
vm::CellSlice cs{vm::NoVm(), boc};
cs.print_rec(std::cout);
std::cout << std::endl;
auto& type = !dump_vmcont
? (dump_state ? (const tlb::TLB&)block::gen::t_ShardStateUnsplit : block::gen::t_Block)
: block::gen::t_VmCont;
type.print_ref(std::cout, boc);
if (!type) {
tlb::TypenameLookup dict(block::gen::register_simple_types);
type = dict.lookup(tname);
if (!type) {
std::cerr << "unknown TL-B type " << tname << std::endl;
std::exit(3);
}
}
type->print_ref(std::cout, boc);
std::cout << std::endl;
bool ok = type.validate_ref(boc);
std::cout << "(" << (ok ? "" : "in") << "valid " << type << ")" << std::endl;
bool ok = type->validate_ref(boc);
std::cout << "(" << (ok ? "" : "in") << "valid " << *type << ")" << std::endl;
}
}
if (!done) {

View file

@ -1064,19 +1064,23 @@ x{FFF0} @Defop SETCPX
variable @proccnt
variable @proclist
variable @procdict
variable @gvarcnt
19 constant @procdictkeylen
{ @proclist @ cons @proclist ! } : @proclistadd
{ dup @procdictkeylen fits not abort"procedure index out of range"
1 'nop does swap dup @proclistadd 0 (create)
} : @declproc
{ 1 'nop does swap 0 (create) } : @declglobvar
{ @proccnt @ 1+ dup @proccnt ! @declproc } : @newproc
{ 0 =: main @proclist null! @proccnt 0!
{ @gvarcnt @ 1+ dup @gvarcnt ! @declglobvar } : @newglobvar
{ 0 =: main @proclist null! @proccnt 0! @gvarcnt 0!
{ bl word @newproc } : NEWPROC
{ bl word dup (def?) ' drop ' @newproc cond } : DECLPROC
{ bl word dup find
{ nip execute <> abort"method redefined with different id" }
{ swap @declproc }
cond } : DECLMETHOD
{ bl word @newglobvar } : DECLGLOBVAR
"main" @proclistadd
dictnew @procdict !
} : PROGRAM{

View file

@ -116,3 +116,15 @@ variable base
{ 9 hold } : +tab
{ "" swap { 0 word 2dup $cmp } { rot swap $+ +cr swap } while 2drop } : scan-until-word
{ 0 word -trailing scan-until-word 1 'nop } ::_ $<<
{ 0x40 runvmx } : runvmcode
{ 0x48 runvmx } : gasrunvmcode
{ 0x43 runvmx } : runvmdict
{ 0x4b runvmx } : gasrunvmdict
{ 0x45 runvmx } : runvm
{ 0x4d runvmx } : gasrunvm
{ 0x55 runvmx } : runvmctx
{ 0x5d runvmx } : gasrunvmctx
{ 0x75 runvmx } : runvmctxact
{ 0x7d runvmx } : gasrunvmctxact
{ 0x35 runvmx } : runvmctxactq
{ 0x3d runvmx } : gasrunvmctxactq

View file

@ -1512,11 +1512,12 @@ void interpret_store_dict(vm::Stack& stack) {
}
// val key dict keylen -- dict' ?
void interpret_dict_add_u(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder, bool sgnd) {
void interpret_dict_add(vm::Stack& stack, vm::Dictionary::SetMode mode, bool add_builder, int sgnd) {
int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[vm::Dictionary::max_key_bytes];
vm::BitSlice key = dict.integer_key(stack.pop_int(), n, sgnd, buffer);
vm::BitSlice key =
(sgnd >= 0) ? dict.integer_key(stack.pop_int(), n, sgnd, buffer) : stack.pop_cellslice()->prefetch_bits(n);
if (!key.is_valid()) {
throw IntError{"not enough bits for a dictionary key"};
}
@ -1530,11 +1531,12 @@ void interpret_dict_add_u(vm::Stack& stack, vm::Dictionary::SetMode mode, bool a
stack.push_bool(res);
}
void interpret_dict_get_u(vm::Stack& stack, bool sgnd) {
void interpret_dict_get(vm::Stack& stack, int sgnd) {
int n = stack.pop_smallint_range(vm::Dictionary::max_key_bits);
vm::Dictionary dict{stack.pop_maybe_cell(), n};
unsigned char buffer[vm::Dictionary::max_key_bytes];
vm::BitSlice key = dict.integer_key(stack.pop_int(), n, sgnd, buffer);
vm::BitSlice key =
(sgnd >= 0) ? dict.integer_key(stack.pop_int(), n, sgnd, buffer) : stack.pop_cellslice()->prefetch_bits(n);
if (!key.is_valid()) {
throw IntError{"not enough bits for a dictionary key"};
}
@ -2169,6 +2171,7 @@ class StringLogger : public td::LogInterface {
}
std::string res;
};
class OstreamLogger : public td::LogInterface {
public:
explicit OstreamLogger(std::ostream* stream) : stream_(stream) {
@ -2191,59 +2194,42 @@ std::vector<Ref<vm::Cell>> get_vm_libraries() {
}
}
void interpret_run_vm_code(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
// mode: -1 = pop from stack
// +1 = same_c3 (set c3 to code)
// +2 = push_0 (push an implicit 0 before running the code)
// +4 = load c4 (persistent data) from stack and return its final value
// +8 = load gas limit from stack and return consumed gas
// +16 = load c7 (smart-contract context)
// +32 = return c5 (actions)
// +64 = log vm ops to stderr
void interpret_run_vm(IntCtx& ctx, int mode) {
if (mode < 0) {
mode = ctx.stack.pop_smallint_range(0xff);
}
bool with_data = mode & 4;
Ref<vm::Tuple> c7;
Ref<vm::Cell> data, actions;
long long gas_limit = (mode & 8) ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
if (mode & 16) {
c7 = ctx.stack.pop_tuple();
}
if (with_data) {
data = ctx.stack.pop_cell();
}
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
auto log = create_vm_log((mode & 64) && ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 0, nullptr, log, nullptr, &gas, get_vm_libraries());
ctx.stack.push_smallint(res);
if (with_gas) {
ctx.stack.push_smallint(gas.gas_consumed());
}
}
void interpret_run_vm_dict(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 3, nullptr, log, nullptr, &gas, get_vm_libraries());
ctx.stack.push_smallint(res);
if (with_gas) {
ctx.stack.push_smallint(gas.gas_consumed());
}
}
void interpret_run_vm(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
auto data = ctx.stack.pop_cell();
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries());
int res =
vm::run_vm_code(cs, ctx.stack, mode & 3, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7), &actions);
ctx.stack.push_smallint(res);
if (with_data) {
ctx.stack.push_cell(std::move(data));
if (with_gas) {
ctx.stack.push_smallint(gas.gas_consumed());
}
if (mode & 32) {
ctx.stack.push_cell(std::move(actions));
}
void interpret_run_vm_c7(IntCtx& ctx, bool with_gas) {
long long gas_limit = with_gas ? ctx.stack.pop_long_range(vm::GasLimits::infty) : vm::GasLimits::infty;
auto c7 = ctx.stack.pop_tuple();
auto data = ctx.stack.pop_cell();
auto cs = ctx.stack.pop_cellslice();
OstreamLogger ostream_logger(ctx.error_stream);
auto log = create_vm_log(ctx.error_stream ? &ostream_logger : nullptr);
vm::GasLimits gas{gas_limit};
int res = vm::run_vm_code(cs, ctx.stack, 1, &data, log, nullptr, &gas, get_vm_libraries(), std::move(c7));
ctx.stack.push_smallint(res);
ctx.stack.push_cell(std::move(data));
if (with_gas) {
if (mode & 8) {
ctx.stack.push_smallint(gas.gas_consumed());
}
}
@ -2759,16 +2745,21 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("dict, ", interpret_store_dict);
d.def_stack_word("dict@ ", std::bind(interpret_load_dict, _1, false));
d.def_stack_word("dict@+ ", std::bind(interpret_load_dict, _1, true));
d.def_stack_word("udict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, false, false));
d.def_stack_word("udict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, false, false));
d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, true, false));
d.def_stack_word("b>udict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, true, false));
d.def_stack_word("udict@ ", std::bind(interpret_dict_get_u, _1, false));
d.def_stack_word("idict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, false, true));
d.def_stack_word("idict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, false, true));
d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Add, true, true));
d.def_stack_word("b>idict! ", std::bind(interpret_dict_add_u, _1, vm::Dictionary::SetMode::Set, true, true));
d.def_stack_word("idict@ ", std::bind(interpret_dict_get_u, _1, true));
d.def_stack_word("sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, -1));
d.def_stack_word("sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, -1));
d.def_stack_word("b>sdict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, -1));
d.def_stack_word("b>sdict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, -1));
d.def_stack_word("sdict@ ", std::bind(interpret_dict_get, _1, -1));
d.def_stack_word("udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 0));
d.def_stack_word("udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 0));
d.def_stack_word("b>udict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 0));
d.def_stack_word("b>udict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 0));
d.def_stack_word("udict@ ", std::bind(interpret_dict_get, _1, 0));
d.def_stack_word("idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, false, 1));
d.def_stack_word("idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, false, 1));
d.def_stack_word("b>idict!+ ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Add, true, 1));
d.def_stack_word("b>idict! ", std::bind(interpret_dict_add, _1, vm::Dictionary::SetMode::Set, true, 1));
d.def_stack_word("idict@ ", std::bind(interpret_dict_get, _1, 1));
d.def_stack_word("pfxdict!+ ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Add, false));
d.def_stack_word("pfxdict! ", std::bind(interpret_pfx_dict_add, _1, vm::Dictionary::SetMode::Set, false));
d.def_stack_word("pfxdict@ ", interpret_pfx_dict_get);
@ -2867,14 +2858,9 @@ void init_words_vm(Dictionary& d) {
vm::init_op_cp0();
// vm run
d.def_stack_word("vmlibs ", std::bind(interpret_literal, _1, vm::StackEntry{vm_libraries}));
d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm_code, _1, false));
d.def_ctx_word("gasrunvmcode ", std::bind(interpret_run_vm_code, _1, true));
d.def_ctx_word("runvmdict ", std::bind(interpret_run_vm_dict, _1, false));
d.def_ctx_word("gasrunvmdict ", std::bind(interpret_run_vm_dict, _1, true));
d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, false));
d.def_ctx_word("gasrunvm ", std::bind(interpret_run_vm, _1, true));
d.def_ctx_word("runvmctx ", std::bind(interpret_run_vm_c7, _1, false));
d.def_ctx_word("gasrunvmctx ", std::bind(interpret_run_vm_c7, _1, true));
// d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm, _1, 0x40));
// d.def_ctx_word("runvm ", std::bind(interpret_run_vm, _1, 0x45));
d.def_ctx_word("runvmx ", std::bind(interpret_run_vm, _1, -1));
d.def_ctx_word("dbrunvm ", interpret_db_run_vm);
d.def_ctx_word("dbrunvm-parallel ", interpret_db_run_vm_parallel);
d.def_stack_word("vmcont, ", interpret_store_vm_cont);

View file

@ -343,6 +343,12 @@ void Op::show(std::ostream& os, const std::vector<TmpVar>& vars, std::string pfx
show_var_list(os, left, vars);
os << " := " << (fun_ref ? fun_ref->name() : "(null)") << std::endl;
break;
case _SetGlob:
os << pfx << dis << "SETGLOB ";
os << (fun_ref ? fun_ref->name() : "(null)") << " := ";
show_var_list(os, right, vars);
os << std::endl;
break;
case _Repeat:
os << pfx << dis << "REPEAT ";
show_var_list(os, left, vars);

View file

@ -365,6 +365,13 @@ bool Op::compute_used_vars(const CodeBlob& code, bool edit) {
}
return std_compute_used_vars();
}
case _SetGlob: {
// GLOB = right
if (right.empty() && edit) {
disable();
}
return std_compute_used_vars(right.empty());
}
case _Let: {
// left = right
std::size_t cnt = next_var_info.count_used(left);
@ -531,6 +538,7 @@ bool prune_unreachable(std::unique_ptr<Op>& ops) {
switch (op.cl) {
case Op::_IntConst:
case Op::_GlobVar:
case Op::_SetGlob:
case Op::_Call:
case Op::_CallInd:
case Op::_Import:
@ -694,7 +702,6 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
values.add_newval(left[0]).set_const(int_const);
break;
}
case _GlobVar:
case _Call: {
prepare_args(values);
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
@ -717,12 +724,15 @@ VarDescrList Op::fwd_analyze(VarDescrList values) {
}
break;
}
case _GlobVar:
case _CallInd: {
for (var_idx_t i : left) {
values.add_newval(i);
}
break;
}
case _SetGlob:
break;
case _Let: {
std::vector<VarDescr> old_val;
assert(left.size() == right.size());
@ -832,6 +842,7 @@ bool Op::mark_noreturn() {
case _Import:
case _IntConst:
case _Let:
case _SetGlob:
case _GlobVar:
case _CallInd:
case _Call:

View file

@ -27,8 +27,8 @@ using namespace std::literals::string_literals;
*
*/
int glob_func_cnt, undef_func_cnt;
std::vector<SymDef*> glob_func;
int glob_func_cnt, undef_func_cnt, glob_var_cnt;
std::vector<SymDef*> glob_func, glob_vars;
SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
sym_idx_t name_idx = sym::symbols.lookup(name, 1);
@ -44,30 +44,49 @@ SymDef* predefine_builtin_func(std::string name, TypeExpr* func_type) {
}
template <typename T>
void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, impure};
return def;
}
template <typename T>
void define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const T& func, std::initializer_list<int> arg_order,
std::initializer_list<int> ret_order = {}, bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, func, arg_order, ret_order, impure};
return def;
}
void define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro,
SymDef* define_builtin_func(std::string name, TypeExpr* func_type, const AsmOp& macro,
std::initializer_list<int> arg_order, std::initializer_list<int> ret_order = {},
bool impure = false) {
SymDef* def = predefine_builtin_func(name, func_type);
def->value = new SymValAsmFunc{func_type, make_simple_compile(macro), arg_order, ret_order, impure};
return def;
}
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& in, std::vector<VarDescr>& out) const {
SymDef* force_autoapply(SymDef* def) {
if (def) {
auto val = dynamic_cast<SymVal*>(def->value);
if (val) {
val->auto_apply = true;
}
}
return def;
}
template <typename... Args>
SymDef* define_builtin_const(std::string name, TypeExpr* const_type, Args&&... args) {
return force_autoapply(
define_builtin_func(name, TypeExpr::new_map(TypeExpr::new_unit(), const_type), std::forward<Args>(args)...));
}
bool SymValAsmFunc::compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in) const {
if (simple_compile) {
return dest.append(simple_compile(in, out));
return dest.append(simple_compile(out, in));
} else if (ext_compile) {
return ext_compile(dest, in, out);
return ext_compile(dest, out, in);
} else {
return false;
}
@ -317,6 +336,12 @@ AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv) {
return AsmOp::Custom(os.str(), args, retv);
}
AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv) {
std::ostringstream os;
os << imm1 << ' ' << imm2 << ' ' << op;
return AsmOp::Custom(os.str(), args, retv);
}
AsmOp push_const(td::RefInt256 x) {
return AsmOp::IntConst(std::move(x));
}
@ -918,11 +943,11 @@ void define_builtins() {
define_builtin_func("_<=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 6));
define_builtin_func("_>=_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 3));
define_builtin_func("_<=>_", arith_bin_op, std::bind(compile_cmp_int, _1, _2, 7));
define_builtin_func("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
define_builtin_func("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
define_builtin_const("true", Int, /* AsmOp::Const("TRUE") */ std::bind(compile_bool_const, _1, _2, true));
define_builtin_const("false", Int, /* AsmOp::Const("FALSE") */ std::bind(compile_bool_const, _1, _2, false));
// define_builtin_func("null", Null, AsmOp::Const("PUSHNULL"));
define_builtin_func("nil", Tuple, AsmOp::Const("PUSHNULL"));
define_builtin_func("Nil", Tuple, AsmOp::Const("NIL"));
define_builtin_const("nil", Tuple, AsmOp::Const("PUSHNULL"));
define_builtin_const("Nil", Tuple, AsmOp::Const("NIL"));
define_builtin_func("null?", TypeExpr::new_forall({X}, TypeExpr::new_map(X, Int)), compile_is_null);
define_builtin_func("throw", impure_un_op, compile_throw, true);
define_builtin_func("throw_if", impure_bin_op, std::bind(compile_cond_throw, _1, _2, true), true);

View file

@ -84,7 +84,9 @@ void Stack::forget_const() {
void Stack::issue_pop(int i) {
validate(i);
if (output_enabled()) {
o << AsmOp::Pop(i);
}
at(i) = get(0);
s.pop_back();
modified();
@ -92,7 +94,9 @@ void Stack::issue_pop(int i) {
void Stack::issue_push(int i) {
validate(i);
if (output_enabled()) {
o << AsmOp::Push(i);
}
s.push_back(get(i));
modified();
}
@ -101,7 +105,9 @@ void Stack::issue_xchg(int i, int j) {
validate(i);
validate(j);
if (i != j && get(i) != get(j)) {
if (output_enabled()) {
o << AsmOp::Xchg(i, j);
}
std::swap(at(i), at(j));
modified();
}
@ -183,6 +189,10 @@ void Stack::enforce_state(const StackLayout& req_stack) {
if (i < depth() && s[i].first == x) {
continue;
}
while (depth() > 0 && std::find(req_stack.cbegin(), req_stack.cend(), get(0).first) == req_stack.cend()) {
// current TOS entry is unused in req_stack, drop it
issue_pop(0);
}
int j = find(x);
if (j >= depth() - i) {
issue_push(j);
@ -292,25 +302,59 @@ bool Op::generate_code_step(Stack& stack) {
}
return true;
}
case _GlobVar: {
case _GlobVar:
if (dynamic_cast<const SymValGlobVar*>(fun_ref->value)) {
bool used = false;
for (auto i : left) {
auto p = next_var_info[i];
if (p && !p->is_unused()) {
used = true;
}
}
if (!used || disabled()) {
return true;
}
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " GETGLOB", 0, 1);
if (left.size() != 1) {
assert(left.size() <= 15);
stack.o << exec_arg_op("UNTUPLE", (int)left.size(), 1, (int)left.size());
}
for (auto i : left) {
stack.push_new_var(i);
}
return true;
} else {
assert(left.size() == 1);
auto p = next_var_info[left[0]];
if (!p || p->is_unused() || disabled()) {
return true;
}
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
stack.o << "CONT:<{";
stack.o.indent();
auto func = dynamic_cast<SymValAsmFunc*>(fun_ref->value);
if (func) {
std::vector<VarDescr> res;
res.reserve(left.size());
for (var_idx_t i : left) {
res.emplace_back(i);
// TODO: create and compile a true lambda instead of this (so that arg_order and ret_order would work correctly)
std::vector<VarDescr> args0, res;
TypeExpr::remove_indirect(func->sym_type);
assert(func->get_type()->is_map());
auto wr = func->get_type()->args.at(0)->get_width();
auto wl = func->get_type()->args.at(1)->get_width();
assert(wl >= 0 && wr >= 0);
for (int i = 0; i < wl; i++) {
res.emplace_back(0);
}
func->compile(stack.o, res, args); // compile res := f (args)
for (int i = 0; i < wr; i++) {
args0.emplace_back(0);
}
func->compile(stack.o, res, args0); // compile res := f (args0)
} else {
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " CALLDICT", (int)right.size(), (int)left.size());
}
stack.push_new_var(left[0]);
stack.o.undent();
stack.o << "}>";
stack.push_new_var(left.at(0));
return true;
}
case _Let: {
@ -395,7 +439,9 @@ bool Op::generate_code_step(Stack& stack) {
assert(stack.s[k + i].first == right1[i]);
}
if (cl == _CallInd) {
stack.o << exec_arg_op("CALLARGS", (int)right.size() - 1, (int)right.size(), (int)left.size());
// TODO: replace with exec_arg2_op()
stack.o << exec_arg2_op("CALLXARGS", (int)right.size() - 1, (int)left.size(), (int)right.size(),
(int)left.size());
} else {
auto func = dynamic_cast<const SymValAsmFunc*>(fun_ref->value);
if (func) {
@ -420,6 +466,32 @@ bool Op::generate_code_step(Stack& stack) {
}
return true;
}
case _SetGlob: {
assert(fun_ref && dynamic_cast<const SymValGlobVar*>(fun_ref->value));
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);
for (int i = 0; i < (int)right.size(); i++) {
if (stack.s[k + i].first != right[i]) {
std::cerr << stack.o;
}
assert(stack.s[k + i].first == right[i]);
}
if (right.size() > 1) {
stack.o << exec_arg_op("TUPLE", (int)right.size(), (int)right.size(), 1);
}
if (!right.empty()) {
std::string name = sym::symbols.get_name(fun_ref->sym_idx);
stack.o << AsmOp::Custom(name + " SETGLOB", 1, 0);
}
stack.s.resize(k);
return true;
}
case _If: {
if (block0->is_empty() && block1->is_empty()) {
return true;
@ -448,7 +520,9 @@ bool Op::generate_code_step(Stack& stack) {
}
stack.o << "IF:<{";
stack.o.indent();
Stack stack_copy{stack};
Stack stack_copy{stack}, stack_target{stack};
stack_target.disable_output();
stack_target.drop_vars_except(next->var_info);
block0->generate_code_all(stack_copy);
stack_copy.drop_vars_except(var_info);
stack_copy.opt_show();
@ -457,7 +531,8 @@ bool Op::generate_code_step(Stack& stack) {
stack.o << "}>";
return true;
}
stack_copy.drop_vars_except(next->var_info);
// stack_copy.drop_vars_except(next->var_info);
stack_copy.enforce_state(stack_target.vars());
stack_copy.opt_show();
if (stack_copy.vars() == stack.vars()) {
stack.o.undent();
@ -487,7 +562,9 @@ bool Op::generate_code_step(Stack& stack) {
}
stack.o << "IFNOT:<{";
stack.o.indent();
Stack stack_copy{stack};
Stack stack_copy{stack}, stack_target{stack};
stack_target.disable_output();
stack_target.drop_vars_except(next->var_info);
block1->generate_code_all(stack_copy);
stack_copy.drop_vars_except(var_info);
stack_copy.opt_show();
@ -497,7 +574,8 @@ bool Op::generate_code_step(Stack& stack) {
stack.merge_const(stack_copy);
return true;
}
stack_copy.drop_vars_except(next->var_info);
// stack_copy.drop_vars_except(next->var_info);
stack_copy.enforce_state(stack_target.vars());
stack_copy.opt_show();
if (stack_copy.vars() == stack.vars()) {
stack.o.undent();

View file

@ -130,6 +130,11 @@ int generate_output() {
*outs << func_val->method_id << " DECLMETHOD " << name << "\n";
}
}
for (SymDef* gvar_sym : glob_vars) {
assert(dynamic_cast<SymValGlobVar*>(gvar_sym->value));
std::string name = sym::symbols.get_name(gvar_sym->sym_idx);
*outs << std::string(indent * 2, ' ') << "DECLGLOBVAR " << name << "\n";
}
int errors = 0;
for (SymDef* func_sym : glob_func) {
try {

View file

@ -97,6 +97,7 @@ enum Keyword {
_Forall,
_Asm,
_Impure,
_Global,
_Extern,
_Inline,
_InlineRef,
@ -181,6 +182,9 @@ struct TypeExpr {
bool is_var() const {
return constr == te_Var;
}
bool is_map() const {
return constr == te_Map;
}
bool has_fixed_width() const {
return minw == maxw;
}
@ -498,6 +502,7 @@ struct Op {
_Let,
_IntConst,
_GlobVar,
_SetGlob,
_Import,
_Return,
_If,
@ -694,6 +699,7 @@ struct SymVal : sym::SymValBase {
TypeExpr* sym_type;
td::RefInt256 method_id;
bool impure;
bool auto_apply{false};
short flags; // +1 = inline, +2 = inline_ref
SymVal(int _type, int _idx, TypeExpr* _stype = nullptr, bool _impure = false)
: sym::SymValBase(_type, _idx), sym_type(_stype), impure(_impure), flags(0) {
@ -745,8 +751,20 @@ struct SymValType : sym::SymValBase {
}
};
extern int glob_func_cnt, undef_func_cnt;
extern std::vector<SymDef*> glob_func;
struct SymValGlobVar : sym::SymValBase {
TypeExpr* sym_type;
int out_idx{0};
SymValGlobVar(int val, TypeExpr* gvtype, int oidx = 0)
: sym::SymValBase(_GlobVar, val), sym_type(gvtype), out_idx(oidx) {
}
~SymValGlobVar() override = default;
TypeExpr* get_type() const {
return sym_type;
}
};
extern int glob_func_cnt, undef_func_cnt, glob_var_cnt;
extern std::vector<SymDef*> glob_func, glob_vars;
/*
*
@ -775,6 +793,7 @@ struct Expr {
_Const,
_Var,
_Glob,
_GlobVar,
_Letop,
_LetFirst,
_Hole,
@ -1155,6 +1174,7 @@ struct StackTransform {
bool apply_push(int i);
bool apply_pop(int i = 0);
bool apply_push_newconst();
bool apply_blkpop(int k);
bool apply(const StackTransform& other); // this = this * other
bool preapply(const StackTransform& other); // this = other * this
// c := a * b
@ -1246,6 +1266,11 @@ struct StackTransform {
bool is_nip_seq(int i, int j = 0) const;
bool is_nip_seq(int* i) const;
bool is_nip_seq(int* i, int* j) const;
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_const_rot() const;
void show(std::ostream& os, int mode = 0) const;
@ -1306,14 +1331,20 @@ struct Optimizer {
bool rewrite_const_push_swap();
bool is_const_push_xchgs();
bool rewrite_const_push_xchgs();
bool is_const_rot() const;
bool rewrite_const_rot();
bool simple_rewrite(int p, AsmOp&& new_op);
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2);
bool simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3);
bool simple_rewrite(AsmOp&& new_op) {
return simple_rewrite(p_, std::move(new_op));
}
bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2) {
return simple_rewrite(p_, std::move(new_op1), std::move(new_op2));
}
bool simple_rewrite(AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) {
return simple_rewrite(p_, std::move(new_op1), std::move(new_op2), std::move(new_op3));
}
bool simple_rewrite_nop();
bool is_pred(const std::function<bool(const StackTransform&)>& pred, int min_p = 2);
bool is_same_as(const StackTransform& trans, int min_p = 2);
@ -1345,6 +1376,8 @@ struct Optimizer {
bool is_blkdrop(int* i);
bool is_reverse(int* i, int* j);
bool is_nip_seq(int* i, int* j);
bool is_pop_blkdrop(int* i, int* k);
bool is_2pop_blkdrop(int* i, int* j, int* k);
AsmOpConsList extract_code();
};
@ -1355,7 +1388,7 @@ void optimize_code(AsmOpList& ops);
struct Stack {
StackLayoutExt s;
AsmOpList& o;
enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _Shown = 256, _Garbage = -0x10000 };
enum { _StkCmt = 1, _CptStkCmt = 2, _DisableOpt = 4, _DisableOut = 128, _Shown = 256, _Garbage = -0x10000 };
int mode;
Stack(AsmOpList& _o, int _mode = 0) : o(_o), mode(_mode) {
}
@ -1381,6 +1414,15 @@ struct Stack {
var_const_idx_t get(int i) const {
return at(i);
}
bool output_disabled() const {
return mode & _DisableOut;
}
bool output_enabled() const {
return !output_disabled();
}
void disable_output() {
mode |= _DisableOut;
}
StackLayout vars() const;
int find(var_idx_t var, int from = 0) const;
int find(var_idx_t var, int from, int to) const;
@ -1470,7 +1512,7 @@ struct SymValAsmFunc : SymValFunc {
std::initializer_list<int> ret_order = {}, bool impure = false)
: SymValFunc(-1, ft, arg_order, ret_order, impure), ext_compile(std::move(_compile)) {
}
bool compile(AsmOpList& dest, std::vector<VarDescr>& in, std::vector<VarDescr>& out) const;
bool compile(AsmOpList& dest, std::vector<VarDescr>& out, std::vector<VarDescr>& in) const;
};
// defined in builtins.cpp
@ -1478,6 +1520,7 @@ AsmOp exec_arg_op(std::string op, long long arg);
AsmOp exec_arg_op(std::string op, long long arg, int args, int retv = 1);
AsmOp exec_arg_op(std::string op, td::RefInt256 arg);
AsmOp exec_arg_op(std::string op, td::RefInt256 arg, int args, int retv = 1);
AsmOp exec_arg2_op(std::string op, long long imm1, long long imm2, int args, int retv = 1);
AsmOp push_const(td::RefInt256 x);
void define_builtins();

View file

@ -282,15 +282,22 @@ std::vector<var_idx_t> Expr::pre_compile(CodeBlob& code) const {
code.emplace_back(here, Op::_IntConst, rvect, intval);
return rvect;
}
case _Glob: {
case _Glob:
case _GlobVar: {
auto rvect = new_tmp_vect(code);
code.emplace_back(here, Op::_GlobVar, rvect, std::vector<var_idx_t>{}, sym);
return rvect;
}
case _Letop: {
auto right = args[1]->pre_compile(code);
if (args[0]->cls == Expr::_GlobVar) {
assert(args[0]->sym);
auto& op = code.emplace_back(here, Op::_SetGlob, std::vector<var_idx_t>{}, right, args[0]->sym);
op.flags |= Op::_Impure;
} else {
auto left = args[0]->pre_compile(code);
code.emplace_back(here, Op::_Let, std::move(left), right);
}
return right;
}
case _LetFirst: {

View file

@ -113,6 +113,7 @@ void define_keywords() {
.add_keyword("forall", Kw::_Forall);
sym::symbols.add_keyword("extern", Kw::_Extern)
.add_keyword("global", Kw::_Global)
.add_keyword("asm", Kw::_Asm)
.add_keyword("impure", Kw::_Impure)
.add_keyword("inline", Kw::_Inline)

View file

@ -150,6 +150,21 @@ bool Optimizer::rewrite_const_push_swap() {
return true;
}
bool Optimizer::is_const_rot() const {
return pb_ >= 3 && pb_ <= l2_ && op_[0]->is_gconst() && tr_[pb_ - 1].is_const_rot();
}
bool Optimizer::rewrite_const_rot() {
p_ = pb_;
q_ = 2;
show_left();
oq_[0] = std::move(op_[0]);
oq_[1] = std::move(op_[1]);
*oq_[1] = AsmOp::Custom("ROT", 3, 3);
show_right();
return true;
}
bool Optimizer::is_const_push_xchgs() {
if (!(pb_ >= 2 && pb_ <= l2_ && op_[0]->is_gconst())) {
return false;
@ -260,6 +275,21 @@ bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2) {
return true;
}
bool Optimizer::simple_rewrite(int p, AsmOp&& new_op1, AsmOp&& new_op2, AsmOp&& new_op3) {
assert(p > 2 && p <= l_);
p_ = p;
q_ = 3;
show_left();
oq_[0] = std::move(op_[0]);
*oq_[0] = new_op1;
oq_[1] = std::move(op_[1]);
*oq_[1] = new_op2;
oq_[2] = std::move(op_[2]);
*oq_[2] = new_op3;
show_right();
return true;
}
bool Optimizer::simple_rewrite_nop() {
assert(p_ > 0 && p_ <= l_);
q_ = 0;
@ -402,6 +432,16 @@ bool Optimizer::is_nip_seq(int* i, int* j) {
return is_pred([i, j](const auto& t) { return t.is_nip_seq(i, j) && *i >= 3 && *i <= 15; });
}
bool Optimizer::is_pop_blkdrop(int* i, int* k) {
return is_pred([i, k](const auto& t) { return t.is_pop_blkdrop(i, k) && *i >= *k && *k >= 2 && *k <= 15; }, 3);
}
bool Optimizer::is_2pop_blkdrop(int* i, int* j, int* k) {
return is_pred(
[i, j, k](const auto& t) { return t.is_2pop_blkdrop(i, j, k) && *i >= *k && *j >= *k && *k >= 2 && *k <= 15; },
3);
}
bool Optimizer::compute_stack_transforms() {
StackTransform trans;
for (int i = 0; i < l_; i++) {
@ -450,7 +490,7 @@ bool Optimizer::find_at_least(int pb) {
// show_stack_transforms();
int i = -100, j = -100, k = -100;
return (is_const_push_swap() && 3 >= pb && rewrite_const_push_swap()) || (is_nop() && simple_rewrite_nop()) ||
(is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
(is_const_rot() && rewrite_const_rot()) || (is_const_push_xchgs() && rewrite_const_push_xchgs()) ||
(is_xchg(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j))) || (is_push(&i) && simple_rewrite(AsmOp::Push(i))) ||
(is_pop(&i) && simple_rewrite(AsmOp::Pop(i))) || (is_rot() && simple_rewrite(AsmOp::Custom("ROT", 3, 3))) ||
(is_rotrev() && simple_rewrite(AsmOp::Custom("-ROT", 3, 3))) ||
@ -468,6 +508,10 @@ bool Optimizer::find_at_least(int pb) {
(is_blkdrop(&i) && simple_rewrite(AsmOp::BlkDrop(i))) ||
(is_reverse(&i, &j) && simple_rewrite(AsmOp::BlkReverse(i, j))) ||
(is_nip_seq(&i, &j) && simple_rewrite(AsmOp::Xchg(i, j), AsmOp::BlkDrop(i))) ||
(is_pop_blkdrop(&i, &k) && simple_rewrite(AsmOp::Pop(i), AsmOp::BlkDrop(k))) ||
(is_2pop_blkdrop(&i, &j, &k) && (k >= 3 && k <= 13 && i != j + 1 && i <= 15 && j <= 14
? simple_rewrite(AsmOp::Xchg2(j + 1, i), AsmOp::BlkDrop(k + 2))
: simple_rewrite(AsmOp::Pop(i), AsmOp::Pop(j), AsmOp::BlkDrop(k)))) ||
(is_xchg3(&i, &j, &k) && simple_rewrite(AsmOp::Xchg3(i, j, k))) ||
(is_xc2pu(&i, &j, &k) && simple_rewrite(AsmOp::Xc2Pu(i, j, k))) ||
(is_xcpuxc(&i, &j, &k) && simple_rewrite(AsmOp::XcPuXc(i, j, k))) ||

View file

@ -174,6 +174,53 @@ FormalArg parse_formal_arg(Lexer& lex, int fa_idx) {
return std::make_tuple(arg_type, new_sym_def, loc);
}
void parse_global_var_decl(Lexer& lex) {
TypeExpr* var_type = 0;
SrcLocation loc = lex.cur().loc;
if (lex.tp() == '_') {
lex.next();
var_type = TypeExpr::new_hole();
loc = lex.cur().loc;
} else if (lex.tp() != _Ident) {
var_type = parse_type(lex);
} else {
auto sym = sym::lookup_symbol(lex.cur().val);
if (sym && dynamic_cast<SymValType*>(sym->value)) {
auto val = dynamic_cast<SymValType*>(sym->value);
lex.next();
var_type = val->get_type();
} else {
var_type = TypeExpr::new_hole();
}
}
if (lex.tp() != _Ident) {
lex.expect(_Ident, "global variable name");
}
loc = lex.cur().loc;
SymDef* sym_def = sym::define_global_symbol(lex.cur().val, false, loc);
if (!sym_def) {
lex.cur().error_at("cannot define global symbol `", "`");
}
if (sym_def->value) {
auto val = dynamic_cast<SymValGlobVar*>(sym_def->value);
if (!val) {
lex.cur().error_at("symbol `", "` cannot be redefined as a global variable");
}
try {
unify(var_type, val->sym_type);
} catch (UnifyError& ue) {
std::ostringstream os;
os << "cannot unify new type " << var_type << " of global variable `" << sym_def->name()
<< "` with its previous type " << val->sym_type << ": " << ue;
lex.cur().error(os.str());
}
} else {
sym_def->value = new SymValGlobVar{glob_var_cnt++, var_type};
glob_vars.push_back(sym_def);
}
lex.next();
}
FormalArgList parse_formal_args(Lexer& lex) {
FormalArgList args;
lex.expect('(', "formal argument list");
@ -205,6 +252,18 @@ TypeExpr* extract_total_arg_type(const FormalArgList& arg_list) {
return TypeExpr::new_tensor(std::move(type_list));
}
void parse_global_var_decls(Lexer& lex) {
lex.expect(_Global);
while (true) {
parse_global_var_decl(lex);
if (lex.tp() != ',') {
break;
}
lex.expect(',');
}
lex.expect(';');
}
SymValCodeFunc* make_new_glob_func(SymDef* func_sym, TypeExpr* func_type, bool impure = false) {
SymValCodeFunc* res = new SymValCodeFunc{glob_func_cnt, func_type, impure};
func_sym->value = res;
@ -239,6 +298,22 @@ bool check_global_func(const Lexem& cur, sym_idx_t func_name = 0) {
}
}
Expr* make_func_apply(Expr* fun, Expr* x) {
Expr* res;
if (fun->cls == Expr::_Glob) {
if (x->cls == Expr::_Tuple) {
res = new Expr{Expr::_Apply, fun->sym, x->args};
} else {
res = new Expr{Expr::_Apply, fun->sym, {x}};
}
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
} else {
res = new Expr{Expr::_VarApply, {fun, x}};
res->flags = Expr::_IsRvalue;
}
return res;
}
Expr* parse_expr(Lexer& lex, CodeBlob& code, bool nv = false);
// parse ( E { , E } ) | () | id | num | _
@ -323,6 +398,16 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
lex.next();
return res;
}
if (sym && dynamic_cast<SymValGlobVar*>(sym->value)) {
auto val = dynamic_cast<SymValGlobVar*>(sym->value);
Expr* res = new Expr{Expr::_GlobVar, lex.cur().loc};
res->e_type = val->get_type();
res->sym = sym;
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | Expr::_IsImpure;
lex.next();
return res;
}
bool auto_apply = false;
Expr* res = new Expr{Expr::_Var, lex.cur().loc};
if (nv) {
res->val = ~lex.cur().val;
@ -344,6 +429,7 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
} else if (val->type == SymVal::_Func) {
res->e_type = val->get_type();
res->cls = Expr::_Glob;
auto_apply = val->auto_apply;
} else if (val->idx < 0) {
lex.cur().error_at("accessing variable `", "` being defined");
} else {
@ -354,6 +440,12 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
// std::cerr << "accessing symbol " << lex.cur().str << " : " << res->e_type << (val->impure ? " (impure)" : " (pure)") << std::endl;
res->flags = Expr::_IsLvalue | Expr::_IsRvalue | (val->impure ? Expr::_IsImpure : 0);
}
if (auto_apply) {
int impure = res->flags & Expr::_IsImpure;
delete res;
res = new Expr{Expr::_Apply, sym, {}};
res->flags = Expr::_IsRvalue | impure;
}
res->deduce_type(lex.cur());
lex.next();
return res;
@ -362,22 +454,6 @@ Expr* parse_expr100(Lexer& lex, CodeBlob& code, bool nv) {
return nullptr;
}
Expr* make_func_apply(Expr* fun, Expr* x) {
Expr* res;
if (fun->cls == Expr::_Glob) {
if (x->cls == Expr::_Tuple) {
res = new Expr{Expr::_Apply, fun->sym, x->args};
} else {
res = new Expr{Expr::_Apply, fun->sym, {x}};
}
res->flags = Expr::_IsRvalue | (fun->flags & Expr::_IsImpure);
} else {
res = new Expr{Expr::_VarApply, {fun, x}};
res->flags = Expr::_IsRvalue;
}
return res;
}
// parse E { E }
Expr* parse_expr90(Lexer& lex, CodeBlob& code, bool nv) {
Expr* res = parse_expr100(lex, code, nv);
@ -1193,8 +1269,12 @@ bool parse_source(std::istream* is, const src::FileDescr* fdescr) {
src::SourceReader reader{is, fdescr};
Lexer lex{reader, true};
while (lex.tp() != _Eof) {
if (lex.tp() == _Global) {
parse_global_var_decls(lex);
} else {
parse_func_def(lex);
}
}
return true;
}

View file

@ -334,6 +334,13 @@ bool StackTransform::apply_pop(int i) {
}
}
bool StackTransform::apply_blkpop(int k) {
if (!is_valid() || k < 0) {
return invalidate();
}
return !k || (touch(k - 1) && shift(k));
}
bool StackTransform::equal(const StackTransform &other, bool relaxed) const {
if (!is_valid() || !other.is_valid()) {
return false;
@ -800,6 +807,49 @@ bool StackTransform::is_nip_seq(int *i, int *j) const {
}
}
// POP s(i); BLKDROP k (usually for i >= k >= 0)
bool StackTransform::is_pop_blkdrop(int i, int k) const {
StackTransform t;
return is_valid() && d == k + 1 && t.apply_pop(i) && t.apply_blkpop(k) && t <= *this;
}
// POP s(i); BLKDROP k == XCHG s0,s(i); BLKDROP k+1 for i >= k >= 0
// k+1 k+2 .. i-1 0 i+1 ..
bool StackTransform::is_pop_blkdrop(int *i, int *k) const {
if (is_valid() && n == 1 && d > 0 && !A[0].second) {
*k = d - 1;
*i = A[0].first;
return is_pop_blkdrop(*i, *k);
} else {
return false;
}
}
// POP s(i); POP s(j); BLKDROP k (usually for i<>j >= k >= 0)
bool StackTransform::is_2pop_blkdrop(int i, int j, int k) const {
StackTransform t;
return is_valid() && d == k + 2 && t.apply_pop(i) && t.apply_pop(j) && t.apply_blkpop(k) && t <= *this;
}
// POP s(i); POP s(j); BLKDROP k == XCHG s0,s(i); XCHG s1,s(j+1); BLKDROP k+2 (usually for i<>j >= k >= 2)
// k+2 k+3 .. i-1 0 i+1 ... j 1 j+2 ...
bool StackTransform::is_2pop_blkdrop(int *i, int *j, int *k) const {
if (is_valid() && n == 2 && d >= 2 && A[0].second + A[1].second == 1) {
*k = d - 2;
int t = (A[0].second > 0);
*i = A[t].first;
*j = A[1 - t].first - 1;
return is_2pop_blkdrop(*i, *j, *k);
} else {
return false;
}
}
// PUSHCONST c ; ROT == 1 -1000 0 2 3
bool StackTransform::is_const_rot() const {
return is_valid() && d == -1 && is_trivial_after(3) && get(0) == 1 && get(1) <= c_start && get(2) == 0;
}
void StackTransform::show(std::ostream &os, int mode) const {
if (!is_valid()) {
os << "<invalid>";

28
crypto/func/test/a11.fc Normal file
View file

@ -0,0 +1,28 @@
_ f(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, cell vdict, int elect) {
return (ds, elect, credits);
}
_ g(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, int cnt, cell credits, int elect) {
return (ds, elect, credits);
}
_ h(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) {
return (ds, elect, credits);
}
_ h2(slice ds, int total_weight, int elect_at, cell frozen, int total_stakes, cell credits, int elect) {
return (ds, credits, elect);
}
_ h3(int pubkey, int adnl_addr, int stake, int tot_weight, tuple vinfo) {
return (pubkey, tot_weight, stake, vinfo);
}
_ z(int a, int b) {
return (b, 0, a);
}
_ z2(int a, int b) {
return (0, a, b);
}

20
crypto/func/test/a6_5.fc Normal file
View file

@ -0,0 +1,20 @@
var twice(f, x) {
return f (f x);
}
_ sqr(x) {
return x * x;
}
var main(x) {
var f = sqr;
return twice(f, x) * f(x);
}
var pow6(x) {
return twice(sqr, x) * sqr(x);
}
_ q() {
return false;
}

37
crypto/func/test/c1.fc Normal file
View file

@ -0,0 +1,37 @@
global int x, y, z;
global (cell, slice) y;
global ((int, int) -> int) op;
_ get() {
var t = z + 1;
return x;
}
_ pre_next() {
return x + 1;
}
() init() impure {
;; global x;
x = 0;
}
int next() impure {
;; global x;
return x += 1;
}
_ set_y(x, w) {
y = (w, x);
}
_ get_y() impure {
return y;
}
int main(int c) {
init();
c += next();
return c + pre_next();
}

10
crypto/func/test/c2.fc Normal file
View file

@ -0,0 +1,10 @@
global ((int, int) -> int) op;
int check_assoc(int a, int b, int c) {
return op(op(a, b), c) == op(a, op(b, c));
}
int main() {
op = _+_;
return check_assoc(2, 3, 9);
}

7
crypto/func/test/c2_1.fc Normal file
View file

@ -0,0 +1,7 @@
_ check_assoc(op, a, b, c) {
return op(op(a, b), c) == op(a, op(b, c));
}
int main() {
return check_assoc(_+_, 2, 3, 9);
}

View file

@ -32,7 +32,7 @@ namespace sym {
typedef int var_idx_t;
struct SymValBase {
enum { _Param, _Var, _Func, _Typename };
enum { _Param, _Var, _Func, _Typename, _GlobVar };
int type;
int idx;
SymValBase(int _type, int _idx) : type(_type), idx(_idx) {

View file

@ -242,3 +242,7 @@ dictnew constant special-dict
register_smc
Masterchain swap 6 .Addr cr
} : create-wallet0
{ dup tlb-type-lookup { nip } { "unknown TLB type " swap $+ abort } cond } : $>tlb
{ bl word $>tlb 1 'nop } ::_ tlb:
{ <b swap vmcont, b> <s tlb:VmCont tlb-dump-as } : vmcont.

View file

@ -39,6 +39,7 @@
#include "td/utils/port/path.h"
#include "td/utils/format.h"
#include "td/utils/misc.h"
#include "td/utils/optional.h"
#include "td/utils/tests.h"
#include "td/utils/tl_parsers.h"
#include "td/utils/tl_helpers.h"
@ -529,16 +530,20 @@ TEST(Cell, MerkleProofCombine) {
ASSERT_EQ(exploration_b.log, exploration2.log);
}
Ref<Cell> proof_union;
{
proof_union = MerkleProof::combine(proof1, proof2);
ASSERT_EQ(proof_union->get_hash(), proof12->get_hash());
auto check = [&](auto proof_union) {
auto virtualized_proof = MerkleProof::virtualize(proof_union, 1);
auto exploration_a = CellExplorer::explore(virtualized_proof, exploration1.ops);
auto exploration_b = CellExplorer::explore(virtualized_proof, exploration2.ops);
ASSERT_EQ(exploration_a.log, exploration1.log);
ASSERT_EQ(exploration_b.log, exploration2.log);
};
auto proof_union = MerkleProof::combine(proof1, proof2);
ASSERT_EQ(proof_union->get_hash(), proof12->get_hash());
check(proof_union);
auto proof_union_fast = MerkleProof::combine_fast(proof1, proof2);
check(proof_union_fast);
}
{
auto cell = MerkleProof::virtualize(proof12, 1);
@ -1194,6 +1199,88 @@ TEST(Cell, MerkleProofArrayHands) {
test_boc_deserializer_full(proof).ensure();
test_boc_deserializer_full(CellBuilder::create_merkle_proof(proof)).ensure();
}
TEST(Cell, MerkleProofCombineArray) {
size_t n = 1 << 15;
std::vector<td::uint64> data;
for (size_t i = 0; i < n; i++) {
data.push_back(i / 3);
}
CompactArray arr(data);
td::Ref<vm::Cell> root = vm::CellBuilder::create_merkle_proof(arr.merkle_proof({}));
td::Timer timer;
for (size_t i = 0; i < n; i++) {
auto new_root = vm::CellBuilder::create_merkle_proof(arr.merkle_proof({i}));
root = vm::MerkleProof::combine_fast(root, new_root);
if ((i - 1) % 100 == 0) {
LOG(ERROR) << timer;
timer = {};
}
}
CompactArray arr2(n, vm::MerkleProof::virtualize(root, 1));
for (size_t i = 0; i < n; i++) {
CHECK(arr.get(i) == arr2.get(i));
}
}
TEST(Cell, MerkleProofCombineArray2) {
auto a = vm::CellBuilder().store_long(1, 8).finalize();
auto b = vm::CellBuilder().store_long(2, 8).finalize();
auto c = vm::CellBuilder().store_long(3, 8).finalize();
auto d = vm::CellBuilder().store_long(4, 8).finalize();
auto left = vm::CellBuilder().store_ref(a).store_ref(b).finalize();
auto right = vm::CellBuilder().store_ref(c).store_ref(d).finalize();
auto x = vm::CellBuilder().store_ref(left).store_ref(right).finalize();
size_t n = 18;
//TODO: n = 100, currently TL
for (size_t i = 0; i < n; i++) {
x = vm::CellBuilder().store_ref(x).store_ref(x).finalize();
}
td::Ref<vm::Cell> root;
auto apply_op = [&](auto op) {
auto usage_tree = std::make_shared<CellUsageTree>();
auto usage_cell = UsageCell::create(x, usage_tree->root_ptr());
root = usage_cell;
op();
return MerkleProof::generate(root, usage_tree.get());
};
auto first = apply_op([&] {
auto x = root;
while (true) {
auto cs = vm::load_cell_slice(x);
if (cs.size_refs() == 0) {
break;
}
x = cs.prefetch_ref(0);
}
});
auto second = apply_op([&] {
auto x = root;
while (true) {
auto cs = vm::load_cell_slice(x);
if (cs.size_refs() == 0) {
break;
}
x = cs.prefetch_ref(1);
}
});
{
td::Timer t;
auto x = vm::MerkleProof::combine(first, second);
LOG(ERROR) << "slow " << t;
}
{
td::Timer t;
auto x = vm::MerkleProof::combine_fast(first, second);
LOG(ERROR) << "fast " << t;
}
}
TEST(Cell, MerkleUpdateHands) {
auto data = CellBuilder{}.store_bytes("pruned data").store_ref(CellBuilder{}.finalize()).finalize();
auto node = CellBuilder{}.store_bytes("protected data").store_ref(data).finalize();
@ -1965,6 +2052,222 @@ TEST(Ref, AtomicRef) {
LOG(ERROR) << String::total_strings.sum();
}
class FileMerkleTree {
public:
FileMerkleTree(size_t chunks_count, td::Ref<vm::Cell> root = {}) {
log_n_ = 0;
while ((size_t(1) << log_n_) < chunks_count) {
log_n_++;
}
n_ = size_t(1) << log_n_;
mark_.resize(n_ * 2);
proof_.resize(n_ * 2);
CHECK(n_ == chunks_count); // TODO: support other chunks_count
//auto x = vm::CellBuilder().finalize();
root_ = std::move(root);
}
struct Chunk {
td::size_t index{0};
td::Slice hash;
};
void remove_chunk(td::size_t index) {
CHECK(index < n_);
index += n_;
while (proof_[index].not_null()) {
proof_[index] = {};
index /= 2;
}
}
bool has_chunk(td::size_t index) const {
CHECK(index < n_);
index += n_;
return proof_[index].not_null();
}
void add_chunk(td::size_t index, td::Slice hash) {
CHECK(hash.size() == 32);
CHECK(index < n_);
index += n_;
auto cell = vm::CellBuilder().store_bytes(hash).finalize();
CHECK(proof_[index].is_null());
proof_[index] = std::move(cell);
for (index /= 2; index != 0; index /= 2) {
CHECK(proof_[index].is_null());
auto &left = proof_[index * 2];
auto &right = proof_[index * 2 + 1];
if (left.not_null() && right.not_null()) {
proof_[index] = vm::CellBuilder().store_ref(left).store_ref(right).finalize();
} else {
mark_[index] = mark_id_;
}
}
}
td::Status validate_proof(td::Ref<vm::Cell> new_root) {
// TODO: check structure
return td::Status::OK();
}
td::Status add_proof(td::Ref<vm::Cell> new_root) {
TRY_STATUS(validate_proof(new_root));
auto combined = vm::MerkleProof::combine_fast_raw(root_, new_root);
if (combined.is_null()) {
return td::Status::Error("Can't combine proofs");
}
root_ = std::move(combined);
return td::Status::OK();
}
td::Status try_add_chunks(td::Span<Chunk> chunks) {
for (auto chunk : chunks) {
if (has_chunk(chunk.index)) {
return td::Status::Error("Already has chunk");
}
}
mark_id_++;
for (auto chunk : chunks) {
add_chunk(chunk.index, chunk.hash);
}
auto r_new_root = merge(root_, 1);
if (r_new_root.is_error()) {
for (auto chunk : chunks) {
remove_chunk(chunk.index);
}
return r_new_root.move_as_error();
}
root_ = r_new_root.move_as_ok();
return td::Status::OK();
}
td::Result<td::Ref<vm::Cell>> merge(td::Ref<vm::Cell> root, size_t index) {
const auto &down = proof_[index];
if (down.not_null()) {
if (down->get_hash() != root->get_hash(0)) {
return td::Status::Error("Hash mismatch");
}
return down;
}
if (mark_[index] != mark_id_) {
return root;
}
vm::CellSlice cs(vm::NoVm(), root);
if (cs.is_special()) {
return td::Status::Error("Proof is not enough to validate chunks");
}
CHECK(cs.size_refs() == 2);
vm::CellBuilder cb;
cb.store_bits(cs.fetch_bits(cs.size()));
TRY_RESULT(left, merge(cs.fetch_ref(), index * 2));
TRY_RESULT(right, merge(cs.fetch_ref(), index * 2 + 1));
cb.store_ref(std::move(left)).store_ref(std::move(right));
return cb.finalize();
}
void init_proof() {
CHECK(proof_[1].not_null());
root_ = proof_[1];
}
td::Result<td::Ref<vm::Cell>> gen_proof(size_t l, size_t r) {
auto usage_tree = std::make_shared<vm::CellUsageTree>();
auto usage_cell = vm::UsageCell::create(root_, usage_tree->root_ptr());
TRY_STATUS(do_gen_proof(std::move(usage_cell), 0, n_ - 1, l, r));
auto res = vm::MerkleProof::generate_raw(root_, usage_tree.get());
CHECK(res.not_null());
return res;
}
private:
td::size_t n_; // n = 2^log_n
td::size_t log_n_;
td::size_t mark_id_{0};
std::vector<td::size_t> mark_; // n_ * 2
std::vector<td::Ref<vm::Cell>> proof_; // n_ * 2
td::Ref<vm::Cell> root_;
td::Status do_gen_proof(td::Ref<vm::Cell> node, size_t il, size_t ir, size_t l, size_t r) {
if (ir < l || il > r) {
return td::Status::OK();
}
if (l <= il && ir <= r) {
return td::Status::OK();
}
vm::CellSlice cs(vm::NoVm(), std::move(node));
if (cs.is_special()) {
return td::Status::Error("Can't generate a proof");
}
CHECK(cs.size_refs() == 2);
auto ic = (il + ir) / 2;
TRY_STATUS(do_gen_proof(cs.fetch_ref(), il, ic, l, r));
TRY_STATUS(do_gen_proof(cs.fetch_ref(), ic + 1, ir, l, r));
return td::Status::OK();
}
};
TEST(FileMerkleTree, Manual) {
// create big random file
size_t chunk_size = 768;
// for simplicity numer of chunks in a file is a power of two
size_t chunks_count = 1 << 16;
size_t file_size = chunk_size * chunks_count;
td::Timer timer;
LOG(INFO) << "Generate random string";
const auto file = td::rand_string('a', 'z', td::narrow_cast<int>(file_size));
LOG(INFO) << timer;
timer = {};
LOG(INFO) << "Calculate all hashes";
std::vector<td::UInt256> hashes(chunks_count);
for (size_t i = 0; i < chunks_count; i++) {
td::sha256(td::Slice(file).substr(i * chunk_size, chunk_size), hashes[i].as_slice());
}
LOG(INFO) << timer;
timer = {};
LOG(INFO) << "Init merkle tree";
FileMerkleTree tree(chunks_count);
for (size_t i = 0; i < chunks_count; i++) {
tree.add_chunk(i, hashes[i].as_slice());
}
tree.init_proof();
LOG(INFO) << timer;
auto root_proof = tree.gen_proof(0, chunks_count - 1).move_as_ok();
// first download each chunk one by one
for (size_t stride : {1 << 6, 1}) {
timer = {};
LOG(INFO) << "Gen all proofs, stride = " << stride;
for (size_t i = 0; i < chunks_count; i += stride) {
tree.gen_proof(i, i + stride - 1).move_as_ok();
}
LOG(INFO) << timer;
timer = {};
LOG(INFO) << "Proof size: " << vm::std_boc_serialize(tree.gen_proof(0, stride - 1).move_as_ok()).ok().size();
LOG(INFO) << "Download file, stride = " << stride;
{
FileMerkleTree new_tree(chunks_count, root_proof);
for (size_t i = 0; i < chunks_count; i += stride) {
new_tree.add_proof(tree.gen_proof(i, i + stride - 1).move_as_ok()).ensure();
std::vector<FileMerkleTree::Chunk> chunks;
for (size_t j = 0; j < stride; j++) {
chunks.push_back({i + j, hashes[i + j].as_slice()});
}
new_tree.try_add_chunks(chunks).ensure();
}
}
LOG(INFO) << timer;
}
}
//TEST(Tmp, Boc) {
//LOG(ERROR) << "A";
//auto data = td::read_file("boc");

View file

@ -3257,6 +3257,31 @@ void generate_type_constants(std::ostream& os, int mode) {
}
}
void generate_register_function(std::ostream& os, int mode) {
os << "\n// " << (mode ? "definition" : "declaration") << " of type name registration function\n";
if (!mode) {
os << "extern bool register_simple_types(std::function<bool(const char*, const TLB*)> func);\n";
return;
}
os << "bool register_simple_types(std::function<bool(const char*, const TLB*)> func) {\n";
os << " return ";
int k = 0;
for (int i = builtin_types_num; i < types_num; i++) {
Type& type = types[i];
CppTypeCode& cc = *cpp_type[i];
if (!cc.cpp_type_var_name.empty() && type.type_name) {
if (k++) {
os << "\n && ";
}
os << "func(\"" << type.get_name() << "\", &" << cc.cpp_type_var_name << ")";
}
}
if (!k) {
os << "true";
}
os << ";\n}\n\n";
}
void assign_const_type_cpp_idents() {
const_type_expr_cpp_idents.resize(const_type_expr_num + 1, "");
const_type_expr_simple.resize(const_type_expr_num + 1, false);
@ -3389,6 +3414,7 @@ void generate_cpp_output_to(std::ostream& os, int options = 0, std::vector<std::
cc.generate(os, (options & -4) | pass);
}
generate_type_constants(os, pass - 1);
generate_register_function(os, pass - 1);
}
}
for (auto it = cpp_namespace_list.rbegin(); it != cpp_namespace_list.rend(); ++it) {

View file

@ -346,3 +346,24 @@ bool PrettyPrinter::fetch_uint256_field(vm::CellSlice& cs, int n, std::string na
}
} // namespace tlb
namespace tlb {
bool TypenameLookup::register_types(typename TypenameLookup::register_func_t func) {
return func([this](const char* name, const TLB* tp) { return register_type(name, tp); });
}
bool TypenameLookup::register_type(const char* name, const TLB* tp) {
if (!name || !tp) {
return false;
}
auto res = types.emplace(name, tp);
return res.second;
}
const TLB* TypenameLookup::lookup(std::string str) const {
auto it = types.find(str);
return it != types.end() ? it->second : nullptr;
}
} // namespace tlb

View file

@ -18,6 +18,7 @@
*/
#pragma once
#include <iostream>
#include <map>
#include "vm/cellslice.h"
namespace tlb {
@ -268,6 +269,30 @@ struct TLB_Complex : TLB {
}
};
class TlbTypeHolder : public td::CntObject {
const TLB* type{nullptr};
char* data{nullptr};
public:
TlbTypeHolder() = default;
TlbTypeHolder(const TLB* _type) : type(_type), data(nullptr) {
}
TlbTypeHolder(const TLB* _type, char* _data) : type(_type), data(_data) {
}
~TlbTypeHolder() override {
free(data);
}
const TLB* get() const {
return type;
}
const TLB& operator*() const {
return *type;
}
const TLB* operator->() const {
return type;
}
};
static inline bool add_chk(int x, int y, int z) {
return x + y == z && z >= 0;
}
@ -508,6 +533,25 @@ struct PrettyPrinter {
namespace tlb {
class TypenameLookup {
std::map<std::string, const TLB*> types;
public:
typedef std::function<bool(const char*, const TLB*)> simple_register_func_t;
typedef std::function<bool(simple_register_func_t)> register_func_t;
TypenameLookup() = default;
TypenameLookup(register_func_t func) {
register_types(func);
}
bool register_type(const char* name, const TLB* tp);
bool register_types(register_func_t func);
const TLB* lookup(std::string str) const;
};
} // namespace tlb
namespace tlb {
struct False final : TLB {
int get_size(const vm::CellSlice& cs) const override {
return -1;

View file

@ -143,19 +143,80 @@ Ref<Cell> MerkleProof::virtualize(Ref<Cell> cell, int virtualization) {
return virtualize_raw(r_raw.move_as_ok(), {0 /*level*/, static_cast<td::uint8>(virtualization)});
}
class MerkleProofCombineFast {
public:
MerkleProofCombineFast(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
}
td::Result<Ref<Cell>> run() {
TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
TRY_RESULT(res, run_raw());
return CellBuilder::create_merkle_proof(std::move(res));
}
td::Result<Ref<Cell>> run_raw() {
if (a_->get_hash(0) != b_->get_hash(0)) {
return td::Status::Error("Can't combine MerkleProofs with different roots");
}
return merge(a_, b_, 0);
}
private:
Ref<Cell> a_;
Ref<Cell> b_;
Ref<Cell> merge(Ref<Cell> a, Ref<Cell> b, td::uint32 merkle_depth) {
if (a->get_hash() == b->get_hash()) {
return a;
}
if (a->get_level() == merkle_depth) {
return a;
}
if (b->get_level() == merkle_depth) {
return b;
}
CellSlice csa(NoVm(), a);
CellSlice csb(NoVm(), b);
if (csa.is_special() && csa.special_type() == vm::Cell::SpecialType::PrunnedBranch) {
return b;
}
if (csb.is_special() && csb.special_type() == vm::Cell::SpecialType::PrunnedBranch) {
return a;
}
CHECK(csa.size_refs() != 0);
auto child_merkle_depth = csa.child_merkle_depth(merkle_depth);
CellBuilder cb;
cb.store_bits(csa.fetch_bits(csa.size()));
for (unsigned i = 0; i < csa.size_refs(); i++) {
cb.store_ref(merge(csa.prefetch_ref(i), csb.prefetch_ref(i), child_merkle_depth));
}
return cb.finalize(csa.is_special());
}
};
class MerkleProofCombine {
public:
MerkleProofCombine(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
}
td::Result<Ref<Cell>> run() {
TRY_RESULT(a, unpack_proof(a_));
TRY_RESULT(b, unpack_proof(b_));
if (a->get_hash(0) != b->get_hash(0)) {
TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
TRY_RESULT(res, run_raw());
return CellBuilder::create_merkle_proof(std::move(res));
}
td::Result<Ref<Cell>> run_raw() {
if (a_->get_hash(0) != b_->get_hash(0)) {
return td::Status::Error("Can't combine MerkleProofs with different roots");
}
dfs(a, 0);
dfs(b, 0);
return CellBuilder::create_merkle_proof(create_A(a, 0, 0));
dfs(a_, 0);
dfs(b_, 0);
return create_A(a_, 0, 0);
}
private:
@ -262,6 +323,30 @@ Ref<Cell> MerkleProof::combine(Ref<Cell> a, Ref<Cell> b) {
return res.move_as_ok();
}
Ref<Cell> MerkleProof::combine_fast(Ref<Cell> a, Ref<Cell> b) {
auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run();
if (res.is_error()) {
return {};
}
return res.move_as_ok();
}
Ref<Cell> MerkleProof::combine_raw(Ref<Cell> a, Ref<Cell> b) {
auto res = MerkleProofCombine(std::move(a), std::move(b)).run_raw();
if (res.is_error()) {
return {};
}
return res.move_as_ok();
}
Ref<Cell> MerkleProof::combine_fast_raw(Ref<Cell> a, Ref<Cell> b) {
auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run_raw();
if (res.is_error()) {
return {};
}
return res.move_as_ok();
}
MerkleProofBuilder::MerkleProofBuilder(Ref<Cell> root)
: usage_tree(std::make_shared<CellUsageTree>()), orig_root(std::move(root)) {
usage_root = UsageCell::create(orig_root, usage_tree->root_ptr());

View file

@ -38,12 +38,15 @@ class MerkleProof {
static Ref<Cell> virtualize(Ref<Cell> cell, int virtualization);
static Ref<Cell> combine(Ref<Cell> a, Ref<Cell> b);
static Ref<Cell> combine_fast(Ref<Cell> a, Ref<Cell> b);
// works with upwrapped proofs
// works fine with cell of non-zero level, but this is not supported (yet?) in MerkeProof special cell
static Ref<Cell> generate_raw(Ref<Cell> cell, IsPrunnedFunction is_prunned);
static Ref<Cell> generate_raw(Ref<Cell> cell, CellUsageTree *usage_tree);
static Ref<Cell> virtualize_raw(Ref<Cell> cell, Cell::VirtualizationParameters virt);
static Ref<Cell> combine_raw(Ref<Cell> a, Ref<Cell> b);
static Ref<Cell> combine_fast_raw(Ref<Cell> a, Ref<Cell> b);
};
class MerkleProofBuilder {

View file

@ -268,7 +268,7 @@ class MerkleCombine {
// 1. Create maximum subtrees of X and Z with all cells in A, B, C and D
// Max(V) - such maximum subtree
//
// 2. Max(A) and Max(D) should be merkle update already. But win want to minimize it
// 2. Max(A) and Max(D) should be merkle update already. But we want to minimize it
// So we cut all branches of Max(D) which are in maxA
// When we cut branch q in Max(D) we mark some path to q in Max(A)
// Then we cut all branches of Max(A) which are not marked.

View file

@ -1055,7 +1055,7 @@ ControlRegs* force_cregs(Ref<Continuation>& cont) {
}
int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* data_ptr, VmLog log, long long* steps,
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7) {
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7, Ref<Cell>* actions_ptr) {
VmState vm{code,
std::move(stack),
gas_limits ? *gas_limits : GasLimits{},
@ -1066,8 +1066,11 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
std::move(init_c7)};
int res = vm.run();
stack = vm.get_stack_ref();
if (res == -1 && data_ptr) {
*data_ptr = vm.get_c4();
if (vm.committed() && data_ptr) {
*data_ptr = vm.get_committed_state().c4;
}
if (vm.committed() && actions_ptr) {
*actions_ptr = vm.get_committed_state().c5;
}
if (steps) {
*steps = vm.get_steps_count();
@ -1090,11 +1093,12 @@ int run_vm_code(Ref<CellSlice> code, Ref<Stack>& stack, int flags, Ref<Cell>* da
}
int run_vm_code(Ref<CellSlice> code, Stack& stack, int flags, Ref<Cell>* data_ptr, VmLog log, long long* steps,
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7) {
GasLimits* gas_limits, std::vector<Ref<Cell>> libraries, Ref<Tuple> init_c7, Ref<Cell>* actions_ptr) {
Ref<Stack> stk{true};
stk.unique_write().set_contents(std::move(stack));
stack.clear();
int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7));
int res = run_vm_code(code, stk, flags, data_ptr, log, steps, gas_limits, std::move(libraries), std::move(init_c7),
actions_ptr);
CHECK(stack.is_unique());
if (stk.is_null()) {
stack.clear();

View file

@ -644,12 +644,12 @@ class VmState final : public VmStateInterface {
void init_cregs(bool same_c3 = false, bool push_0 = true);
};
int run_vm_code(Ref<CellSlice> _code, Ref<Stack>& _stack, int flags = 0, Ref<Cell>* data_ptr = 0, VmLog log = {},
int run_vm_code(Ref<CellSlice> _code, Ref<Stack>& _stack, int flags = 0, Ref<Cell>* data_ptr = nullptr, VmLog log = {},
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {},
Ref<Tuple> init_c7 = {});
int run_vm_code(Ref<CellSlice> _code, Stack& _stack, int flags = 0, Ref<Cell>* data_ptr = 0, VmLog log = {},
Ref<Tuple> init_c7 = {}, Ref<Cell>* actions_ptr = nullptr);
int run_vm_code(Ref<CellSlice> _code, Stack& _stack, int flags = 0, Ref<Cell>* data_ptr = nullptr, VmLog log = {},
long long* steps = nullptr, GasLimits* gas_limits = nullptr, std::vector<Ref<Cell>> libraries = {},
Ref<Tuple> init_c7 = {});
Ref<Tuple> init_c7 = {}, Ref<Cell>* actions_ptr = nullptr);
ControlData* force_cdata(Ref<Continuation>& cont);
ControlRegs* force_cregs(Ref<Continuation>& cont);

View file

@ -481,6 +481,18 @@ class Stack : public td::CntObject {
Ref<Atom> pop_atom();
std::string pop_string();
std::string pop_bytes();
template <typename T>
Ref<T> pop_object() {
return pop_chk().as_object<T>();
}
template <typename T>
Ref<T> pop_object_type_chk() {
auto res = pop_object<T>();
if (!res) {
throw VmError{Excno::type_chk, "not an object of required type"};
}
return res;
}
void push_null();
void push_int(td::RefInt256 val);
void push_int_quiet(td::RefInt256 val, bool quiet = true);

View file

@ -1338,8 +1338,10 @@ Fift has several words for {\em hashmap\/} or {\em (TVM) dictionary\/} manipulat
\item {\tt idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$.
\item {\tt b>idict!}, {\tt b>idict!+}, variants of {\tt idict!} and {\tt idict!+} accepting the new value $v$ in a {\em Builder\/} instead of a {\em Slice}.
\item {\tt udict!}, {\tt udict!+}, {\tt b>udict!}, {\tt b>udict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with an unsigned $n$-bit integer $x$ used as a key.
\item {\tt sdict!}, {\tt sdict!+}, {\tt b>sdict!}, {\tt b>sdict!+}, variants of {\tt idict!}, {\tt idict!+}, {\tt b>idict!}, {\tt b>idict!+}, but with the first $n$ data bits of {\em Slice\/}~$x$ used as a key.
\item {\tt idict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key represented by signed big-endian $n$-bit {\em Integer\/}~$x$ in the dictionary represented by {\em Cell\/}~$D$. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt udict@} ($x$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with an {\em un}signed big-endian $n$-bit {\em Integer\/}~$x$ used as a key.
\item {\tt sdict@} ($k$ $D$ $n$ -- $v$ $-1$ or $0$), similar to {\tt idict@}, but with the key provided in the first $n$ bits of {\em Slice\/}~$k$.
\item {\tt dictmap} ($D$ $n$ $e$ -- $s'$), applies execution token $e$ (i.e., an anonymous function) to each of the key-value pairs stored in a dictionary $D$ with $n$-bit keys. The execution token is executed once for each key-value pair, with a {\em Builder\/} $b$ and a {\em Slice\/} $v$ (containing the value) pushed into the stack before executing $e$. After the execution $e$ must leave in the stack either a modified {\em Builder\/} $b'$ (containing all data from~$b$ along with the new value $v'$) and $-1$, or $0$ indicating failure. In the latter case, the corresponding key is omitted from the new dictionary.
\item {\tt dictmerge} ($D$ $D'$ $n$ $e$ -- $D''$), combines two dictionaries $D$ and $D'$ with $n$-bit keys into one dictionary $D''$ with the same keys. If a key is present in only one of the dictionaries $D$ and $D'$, this key and the corresponding value are copied verbatim to the new dictionary $D''$. Otherwise the execution token (anonymous function) $e$ is invoked to merge the two values $v$ and $v'$ corresponding to the same key $k$ in $D$ and $D'$, respectively. Before $e$ is invoked, a {\em Builder\/}~$b$ and two {\em Slice}s $v$ and $v'$ representing the two values to be merged are pushed. After the execution $e$ leaves either a modified {\em Builder\/}~$b'$ (containing the original data from $b$ along with the combined value) and $-1$, or $0$ on failure. In the latter case, the corresponding key is omitted from the new dictionary.
\end{itemize}
@ -2001,10 +2003,12 @@ For example, the active prefix word {\tt B\{}, used for defining {\em Bytes\/} l
\item {\tt b.} ($x$ -- ), prints the binary representation of an {\em Integer\/}~$x$, followed by a single space. Equivalent to {\tt b.\_ space}.
\item {\tt b.\_} ($x$ -- ), prints the binary representation of an {\em Integer\/}~$x$ without any spaces. Equivalent to {\tt (b.)~type}.
\item {\tt b>} ($b$ -- $c$), transforms a {\em Builder\/}~$b$ into a new {\em Cell\/}~$c$ containing the same data as~$b$, cf.~\ptref{p:builder.ops}.
\item {\tt b>idict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned.
\item {\tt b>idict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt b>idict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt b>udict!} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by unsigned big-endian $n$-bit integer $x$ into dictionary $s$ with $n$-bit keys, and returns the new dictionary $s'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $s$ and $0$ are returned.
\item {\tt b>udict!+} ($v$ $x$ $s$ $n$ -- $s'$ $-1$ or $s$ $0$), adds a new key-value pair $(x,v)$ into dictionary $s$ similarly to {\tt b>udict!}, but fails if the key already exists by returning the unchanged dictionary $s$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt b>idict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by signed big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
\item {\tt b>idict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt b>idict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt b>sdict!} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by the first $n$ bits of {\em Slice\/}~$k$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
\item {\tt b>sdict!+} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(k,v)$ into dictionary $D$ similarly to {\tt b>sdict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt b>udict!} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Builder}) with key given by unsigned big-endian $n$-bit integer $x$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
\item {\tt b>udict!+} ($v$ $x$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(x,v)$ into dictionary $D$ similarly to {\tt b>udict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt bbitrefs} ($b$ -- $x$ $y$), returns both the number of data bits $x$ and the number of references $y$ already stored in {\em Builder\/}~$b$, cf.~\ptref{p:builder.ops}.
\item {\tt bbits} ($b$ -- $x$), returns the number of data bits already stored in {\em Builder\/}~$b$. The result $x$ is an {\em Integer\/} in the range $0\dots1023$, cf.~\ptref{p:builder.ops}.
\item {\tt bl} ( -- $x$), pushes the Unicode codepoint of a space, i.e., 32, cf.~\ptref{p:string.ops}.
@ -2129,6 +2133,9 @@ Typical values of $x$ are $x=0$ or $x=2$ for very small bags of cells (e.g., TON
\item {\tt s>c} ($s$ -- $c$), creates a {\em Cell}~$c$ directly from a {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt <b swap s, b>}.
\item {\tt sbitrefs} ($s$ -- $x$ $y$), returns both the number of data bits $x$ and the number of cell references $y$ remaining in {\em Slice}~$s$, cf.~\ptref{p:slice.ops}. Equivalent to {\tt remaining}.
\item {\tt sbits} ($s$ -- $x$), returns the number of data bits $x$ remaining in {\em Slice}~$s$, cf.~\ptref{p:slice.ops}.
\item {\tt sdict!} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new value $v$ (represented by a {\em Slice\/}) with key given by the first $n$ bits of {\em Slice\/}~$k$ into dictionary $D$ with $n$-bit keys, and returns the new dictionary $D'$ and $-1$ on success, cf.~\ptref{p:hashmap.ops}. Otherwise the unchanged dictionary $D$ and $0$ are returned.
\item {\tt sdict!+} ($v$ $k$ $D$ $n$ -- $D'$ $-1$ or $D$ $0$), adds a new key-value pair $(k,v)$ into dictionary $D$ similarly to {\tt sdict!}, but fails if the key already exists by returning the unchanged dictionary $D$ and $0$, cf.~\ptref{p:hashmap.ops}.
\item {\tt sdict@} ($k$ $D$ $n$ -- $v$ $-1$ or $0$), looks up the key given by the first $n$ data bits of {\em Slice\/}~$x$ in the dictionary represented by {\em Cell\/} or {\em Null\/}~$D$, cf.~\ptref{p:hashmap.ops}. If the key is found, returns the corresponding value as a {\em Slice\/}~$v$ and $-1$. Otherwise returns $0$.
\item {\tt second} ($t$ -- $x$), returns the second component of a {\em Tuple}, cf.~\ptref{p:tuples}. Equivalent to {\tt 1 []}.
\item {\tt sgn} ($x$ -- $y$), computes the sign of an {\em Integer\/} $x$ (i.e., pushes $1$ if $x>0$, $-1$ if $x<0$, and $0$ if $x=0$), cf.~\ptref{p:int.comp}. Equivalent to {\tt 0 cmp}.
\item {\tt shash} ($s$ -- $B$), computes the $\Sha$-based representation hash of a {\em Slice\/} by first transforming it into a cell, cf.~\ptref{p:hash.ops}. Equivalent to {\tt s>c hashB}.

View file

@ -101,10 +101,10 @@ TEST(Actor2, locker) {
kill_signal.add_signal(ActorSignals::Kill);
ActorSignals wakeup_signal;
kill_signal.add_signal(ActorSignals::Wakeup);
wakeup_signal.add_signal(ActorSignals::Wakeup);
ActorSignals cpu_signal;
kill_signal.add_signal(ActorSignals::Cpu);
cpu_signal.add_signal(ActorSignals::Cpu);
{
ActorLocker lockerA(&state);

View file

@ -35,6 +35,7 @@ liteServer.blockState id:tonNode.blockIdExt root_hash:int256 file_hash:int256 da
liteServer.blockHeader id:tonNode.blockIdExt mode:# header_proof:bytes = liteServer.BlockHeader;
liteServer.sendMsgStatus status:int = liteServer.SendMsgStatus;
liteServer.accountState id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state:bytes = liteServer.AccountState;
liteServer.runMethodResult mode:# id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes proof:bytes state_proof:bytes init_c7:bytes lib_extras:bytes exit_code:int result:bytes = liteServer.RunMethodResult;
liteServer.shardInfo id:tonNode.blockIdExt shardblk:tonNode.blockIdExt shard_proof:bytes shard_descr:bytes = liteServer.ShardInfo;
liteServer.allShardsInfo id:tonNode.blockIdExt proof:bytes data:bytes = liteServer.AllShardsInfo;
liteServer.transactionInfo id:tonNode.blockIdExt proof:bytes transaction:bytes = liteServer.TransactionInfo;
@ -63,6 +64,7 @@ liteServer.getState id:tonNode.blockIdExt = liteServer.BlockState;
liteServer.getBlockHeader id:tonNode.blockIdExt mode:# = liteServer.BlockHeader;
liteServer.sendMessage body:bytes = liteServer.SendMsgStatus;
liteServer.getAccountState id:tonNode.blockIdExt account:liteServer.accountId = liteServer.AccountState;
liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:int params:bytes = liteServer.RunMethodResult;
liteServer.getShardInfo id:tonNode.blockIdExt workchain:int shard:long exact:Bool = liteServer.ShardInfo;
liteServer.getAllShardsInfo id:tonNode.blockIdExt = liteServer.AllShardsInfo;
liteServer.getOneTransaction id:tonNode.blockIdExt account:liteServer.accountId lt:long = liteServer.TransactionInfo;

Binary file not shown.

View file

@ -79,8 +79,8 @@ fees in_fwd_fee:int53 storage_fee:int53 gas_fee:int53 fwd_fee:int53= Fees;
query.fees source_fees:fees destination_fees:fees = query.Fees;
query.info id:int53 valid_until:int53 body_hash:bytes = query.Info;
tvm.slice bytes:string = tvm.Slice;
tvm.cell bytes:string = tvm.Cell;
tvm.slice bytes:bytes = tvm.Slice;
tvm.cell bytes:bytes = tvm.Cell;
tvm.numberDecimal number:string = tvm.Number;
tvm.tuple elements:vector<tvm.StackEntry> = tvm.Tuple;
tvm.list elements:vector<tvm.StackEntry> = tvm.List;

Binary file not shown.

View file

@ -85,6 +85,7 @@ class ValidatorEngineConsole : public td::actor::Actor {
void add_cmd(td::BufferSlice data) {
ex_mode_ = true;
ex_queries_.push_back(std::move(data));
set_readline_enabled(false);
}
void set_fail_timeout(td::Timestamp ts) {
fail_timeout_ = ts;

View file

@ -129,7 +129,7 @@ void LiteQuery::start_up() {
[&](lite_api::liteServer_getState& q) { this->perform_getState(ton::create_block_id(q.id_)); },
[&](lite_api::liteServer_getAccountState& q) {
this->perform_getAccountState(ton::create_block_id(q.id_), static_cast<WorkchainId>(q.account_->workchain_),
q.account_->id_);
q.account_->id_, 0);
},
[&](lite_api::liteServer_getOneTransaction& q) {
this->perform_getOneTransaction(ton::create_block_id(q.id_),
@ -172,6 +172,10 @@ void LiteQuery::start_up() {
q.mode_ & 1 ? q.start_after_ : td::Bits256::zero(),
q.mode_ & 4 ? q.modified_after_ : 0);
},
[&](lite_api::liteServer_runSmcMethod& q) {
this->perform_runSmcMethod(ton::create_block_id(q.id_), static_cast<WorkchainId>(q.account_->workchain_),
q.account_->id_, q.mode_, q.method_id_, std::move(q.params_));
},
[&](auto& obj) { this->abort_query(td::Status::Error(ErrorCode::protoviolation, "unknown query")); }));
}
@ -604,9 +608,9 @@ bool LiteQuery::request_zero_state(BlockIdExt blkid) {
return true;
}
void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr) {
LOG(INFO) << "started a getAccountState(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex()
<< ") liteserver query";
void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode) {
LOG(INFO) << "started a getAccountState(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ", "
<< mode << ") liteserver query";
if (blkid.id.workchain != masterchainId && blkid.id.workchain != workchain) {
fatal_error("reference block for a getAccountState() must belong to the masterchain");
return;
@ -626,6 +630,7 @@ void LiteQuery::perform_getAccountState(BlockIdExt blkid, WorkchainId workchain,
}
acc_workchain_ = workchain;
acc_addr_ = addr;
mode_ = mode;
if (blkid.id.workchain != masterchainId) {
base_blk_id_ = blkid;
set_continuation([&]() -> void { finish_getAccountState({}); });
@ -659,6 +664,45 @@ void LiteQuery::continue_getAccountState_0(Ref<ton::validator::MasterchainState>
request_mc_block_data(blkid);
}
void LiteQuery::perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode,
int method_id, td::BufferSlice params) {
LOG(INFO) << "started a runSmcMethod(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ", "
<< method_id << ", " << mode << ") liteserver query with " << params.size() << " parameter bytes";
if (params.size() >= 65536) {
fatal_error("more than 64k parameter bytes passed");
return;
}
if (mode & ~0xf) {
fatal_error("unsupported mode in runSmcMethod");
return;
}
stack_.clear();
try {
if (params.size()) {
auto res = vm::std_boc_deserialize(std::move(params));
if (res.is_error()) {
fatal_error("cannot deserialize parameter list boc: "s + res.move_as_error().to_string());
return;
}
auto cs = vm::load_cell_slice(res.move_as_ok());
if (!(vm::Stack::deserialize_to(cs, stack_, 0) && cs.empty_ext())) {
fatal_error("parameter list boc cannot be deserialized as a VmStack");
return;
}
} else {
stack_ = td::make_ref<vm::Stack>();
}
stack_.write().push_smallint(method_id);
} catch (vm::VmError& vme) {
fatal_error("error deserializing parameter list: "s + vme.get_msg());
return;
} catch (vm::VmVirtError& vme) {
fatal_error("virtualization error while deserializing parameter list: "s + vme.get_msg());
return;
}
perform_getAccountState(blkid, workchain, addr, mode | 0x10000);
}
void LiteQuery::perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt) {
LOG(INFO) << "started a getOneTransaction(" << blkid.to_str() << ", " << workchain << ", " << addr.to_hex() << ","
<< lt << ") liteserver query";
@ -934,10 +978,15 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
acc_root = acc_csr->prefetch_ref();
}
auto proof = vm::std_boc_serialize_multi({std::move(proof1), pb.extract_proof()});
pb.clear();
if (proof.is_error()) {
fatal_error(proof.move_as_error());
return;
}
if (mode_ & 0x10000) {
finish_runSmcMethod(std::move(shard_proof), proof.move_as_ok(), std::move(acc_root));
return;
}
td::BufferSlice data;
if (acc_root.not_null()) {
auto res = vm::std_boc_serialize(std::move(acc_root));
@ -954,6 +1003,13 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
finish_query(std::move(b));
}
void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref<vm::Cell> acc_root) {
LOG(INFO) << "completing runSmcMethod() query";
// ... TODO ...
fatal_error("runSmcMethod not implemented");
return;
}
void LiteQuery::continue_getOneTransaction() {
LOG(INFO) << "completing getOneTransaction() query";
CHECK(block_.not_null());

View file

@ -37,6 +37,7 @@ class LiteQuery : public td::actor::Actor {
td::Timestamp timeout_;
td::Promise<td::BufferSlice> promise_;
int pending_{0};
int mode_{0};
WorkchainId acc_workchain_;
StdSmcAddress acc_addr_;
LogicalTime trans_lt_;
@ -55,6 +56,7 @@ class LiteQuery : public td::actor::Actor {
std::vector<Ref<td::CntObject>> aux_objs_;
std::vector<ton::BlockIdExt> blk_ids_;
std::unique_ptr<block::BlockProofChain> chain_;
Ref<vm::Stack> stack_;
public:
enum {
@ -91,10 +93,13 @@ class LiteQuery : public td::actor::Actor {
void continue_getState(BlockIdExt blkid, Ref<ShardState> state);
void continue_getZeroState(BlockIdExt blkid, td::BufferSlice state);
void perform_sendMessage(td::BufferSlice ext_msg);
void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr);
void perform_getAccountState(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode);
void continue_getAccountState_0(Ref<MasterchainState> mc_state, BlockIdExt blkid);
void continue_getAccountState();
void finish_getAccountState(td::BufferSlice shard_proof);
void perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, int mode, int method_id,
td::BufferSlice params);
void finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref<vm::Cell> acc_root);
void perform_getOneTransaction(BlockIdExt blkid, WorkchainId workchain, StdSmcAddress addr, LogicalTime lt);
void continue_getOneTransaction();
void perform_getTransactions(WorkchainId workchain, StdSmcAddress addr, LogicalTime lt, Bits256 hash, unsigned count);