diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index ec93e11c..e43b9305 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1559,6 +1559,7 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) { vm.set_global_version(cfg.global_version); vm.set_c7(prepare_vm_c7(cfg)); // tuple with SmartContractInfo vm.set_chksig_always_succeed(cfg.ignore_chksig); + vm.set_stop_on_accept_message(cfg.stop_on_accept_message); // vm.incr_stack_trace(1); // enable stack dump after each step LOG(DEBUG) << "starting VM"; diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h index 7539efe0..1ed2dfd3 100644 --- a/crypto/block/transaction.h +++ b/crypto/block/transaction.h @@ -120,6 +120,7 @@ struct ComputePhaseConfig { std::unique_ptr suspended_addresses; SizeLimitsConfig size_limits; int vm_log_verbosity = 0; + bool stop_on_accept_message = false; ComputePhaseConfig() : gas_price(0), gas_limit(0), special_gas_limit(0), gas_credit(0) { compute_threshold(); diff --git a/crypto/vm/cellops.cpp b/crypto/vm/cellops.cpp index 9e10e072..9b702fcd 100644 --- a/crypto/vm/cellops.cpp +++ b/crypto/vm/cellops.cpp @@ -892,6 +892,37 @@ int exec_load_special_cell(VmState* st, bool quiet) { Stack& stack = st->get_stack(); VM_LOG(st) << "execute XLOAD" << (quiet ? "Q" : ""); auto cell = stack.pop_cell(); + auto r_loaded_cell = cell->load_cell(); + if (r_loaded_cell.is_error()) { + if (quiet) { + stack.push_bool(false); + return 0; + } else { + throw VmError{Excno::cell_und, "failed to load cell"}; + } + } + auto loaded_cell = r_loaded_cell.move_as_ok(); + if (loaded_cell.data_cell->is_special()) { + if (loaded_cell.data_cell->special_type() != CellTraits::SpecialType::Library) { + if (quiet) { + stack.push_bool(false); + return 0; + } else { + throw VmError{Excno::cell_und, "unexpected special cell"}; + } + } + CellSlice cs(std::move(loaded_cell)); + DCHECK(cs.size() == Cell::hash_bits + 8); + cell = st->load_library(cs.data_bits() + 8); + if (cell.is_null()) { + if (quiet) { + stack.push_bool(false); + return 0; + } else { + throw VmError{Excno::cell_und, "failed to load library cell"}; + } + } + } stack.push_cell(cell); if (quiet) { stack.push_bool(true); diff --git a/crypto/vm/cells/CellSlice.cpp b/crypto/vm/cells/CellSlice.cpp index e1df5759..cbfc9e50 100644 --- a/crypto/vm/cells/CellSlice.cpp +++ b/crypto/vm/cells/CellSlice.cpp @@ -1056,6 +1056,7 @@ std::ostream& operator<<(std::ostream& os, Ref cs_ref) { // If can_be_special is not null, then it is allowed to load special cell // Flag whether loaded cell is actually special will be stored into can_be_special VirtualCell::LoadedCell load_cell_slice_impl(Ref cell, bool* can_be_special) { + bool library_loaded = false; while (true) { auto* vm_state_interface = VmStateInterface::get(); if (vm_state_interface) { @@ -1076,6 +1077,10 @@ VirtualCell::LoadedCell load_cell_slice_impl(Ref cell, bool* can_be_specia *can_be_special = loaded_cell.data_cell->is_special(); } else if (loaded_cell.data_cell->is_special()) { if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::Library) { + if (library_loaded) { + throw VmError{Excno::cell_und, "failed to load library cell: recursive library cells are not allowed"}; + } + library_loaded = true; if (vm_state_interface) { CellSlice cs(std::move(loaded_cell)); DCHECK(cs.size() == Cell::hash_bits + 8); diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index d150f30b..dce61797 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -67,6 +67,10 @@ int exec_set_gas_generic(VmState* st, long long new_gas_limit) { throw VmNoGas{}; } st->change_gas_limit(new_gas_limit); + if (st->get_stop_on_accept_message()) { + VM_LOG(st) << "External message is accepted, stopping TVM"; + return st->jump(td::Ref{true, 0}); + } return 0; } diff --git a/crypto/vm/vm.h b/crypto/vm/vm.h index 2066db4c..fd18f92d 100644 --- a/crypto/vm/vm.h +++ b/crypto/vm/vm.h @@ -98,6 +98,7 @@ class VmState final : public VmStateInterface { td::HashSet loaded_cells; int stack_trace{0}, debug_off{0}; bool chksig_always_succeed{false}; + bool stop_on_accept_message{false}; td::optional missing_library; td::uint16 max_data_depth = 512; // Default value int global_version{0}; @@ -381,6 +382,12 @@ class VmState final : public VmStateInterface { bool get_chksig_always_succeed() const { return chksig_always_succeed; } + void set_stop_on_accept_message(bool flag) { + stop_on_accept_message = flag; + } + bool get_stop_on_accept_message() const { + return stop_on_accept_message; + } Ref ref_to_cont(Ref cell) const { return td::make_ref(load_cell_slice_ref(std::move(cell)), get_cp()); } diff --git a/validator/impl/external-message.cpp b/validator/impl/external-message.cpp index e7665afc..073e7360 100644 --- a/validator/impl/external-message.cpp +++ b/validator/impl/external-message.cpp @@ -156,6 +156,7 @@ td::Status ExtMessageQ::run_message_on_account(ton::WorkchainId wc, } compute_phase_cfg_.libraries = std::make_unique(config->get_libraries_root(), 256); compute_phase_cfg_.with_vm_log = true; + compute_phase_cfg_.stop_on_accept_message = true; auto res = Collator::impl_create_ordinary_transaction(msg_root, acc, utime, lt, &storage_phase_cfg_, &compute_phase_cfg_,