diff --git a/Changelog.md b/Changelog.md
index 81db2c43c..318128c2b 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,8 @@
+## 2024.03 Update
+
+1. Preparatory (not enabled yet) code for pre-compiled smart-contract.
+2. Minor fixes for fee-related opcodes.
+
## 2024.02 Update
1. Improvement of validator synchronisation:
diff --git a/README.md b/README.md
index 653f2f834..96516d447 100644
--- a/README.md
+++ b/README.md
@@ -10,25 +10,33 @@
##
-[![TON Overflow Group][ton-overflow-badge]][ton-overflow-url]
-[![Stack Overflow Group][stack-overflow-badge]][stack-overflow-url]
-[![Telegram Community Chat][telegram-tondev-badge]][telegram-tondev-url]
-[![Telegram Community Group][telegram-community-badge]][telegram-community-url]
-[![Telegram Foundation Group][telegram-foundation-badge]][telegram-foundation-url]
-[![Twitter Group][twitter-badge]][twitter-url]
-
-[telegram-foundation-badge]: https://img.shields.io/badge/TON%20Foundation-2CA5E0?logo=telegram&logoColor=white&style=flat
-[telegram-community-badge]: https://img.shields.io/badge/TON%20Community-2CA5E0?logo=telegram&logoColor=white&style=flat
-[telegram-tondev-badge]: https://img.shields.io/badge/chat-TONDev-2CA5E0?logo=telegram&logoColor=white&style=flat
-[telegram-foundation-url]: https://t.me/tonblockchain
-[telegram-community-url]: https://t.me/toncoin
-[telegram-tondev-url]: https://t.me/tondev_eng
-[twitter-badge]: https://img.shields.io/twitter/follow/ton_blockchain
-[twitter-url]: https://twitter.com/ton_blockchain
-[stack-overflow-badge]: https://img.shields.io/badge/-Stack%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white
-[stack-overflow-url]: https://stackoverflow.com/questions/tagged/ton
-[ton-overflow-badge]: https://img.shields.io/badge/-TON%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white
-[ton-overflow-url]: https://answers.ton.org
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -148,4 +156,4 @@ More examples for other platforms can be found under `assembly/nix`.
## Running tests
-Tests are executed by running `ctest` in the build directory. See `doc/Tests.md` for more information.
\ No newline at end of file
+Tests are executed by running `ctest` in the build directory. See `doc/Tests.md` for more information.
diff --git a/crypto/CMakeLists.txt b/crypto/CMakeLists.txt
index 827c903b6..29b954664 100644
--- a/crypto/CMakeLists.txt
+++ b/crypto/CMakeLists.txt
@@ -213,6 +213,7 @@ set(BLOCK_SOURCE
block/mc-config.cpp
block/output-queue-merger.cpp
block/transaction.cpp
+ block/precompiled-smc/PrecompiledSmartContract.cpp
${TLB_BLOCK_AUTO}
block/block-binlog.h
@@ -223,6 +224,8 @@ set(BLOCK_SOURCE
block/check-proof.h
block/output-queue-merger.h
block/transaction.h
+ block/precompiled-smc/PrecompiledSmartContract.h
+ block/precompiled-smc/common.h
)
set(SMC_ENVELOPE_SOURCE
@@ -376,6 +379,10 @@ add_library(ton_block STATIC ${BLOCK_SOURCE})
target_include_directories(ton_block PUBLIC $
$ $)
target_link_libraries(ton_block PUBLIC ton_crypto tdutils tdactor tl_api)
+if (USE_EMSCRIPTEN)
+ target_link_options(ton_block PRIVATE -fexceptions)
+ target_compile_options(ton_block PRIVATE -fexceptions)
+endif()
add_executable(func func/func-main.cpp ${FUNC_LIB_SOURCE})
target_include_directories(func PUBLIC $)
diff --git a/crypto/block/block.tlb b/crypto/block/block.tlb
index 4b36f13ba..3ae542399 100644
--- a/crypto/block/block.tlb
+++ b/crypto/block/block.tlb
@@ -788,6 +788,10 @@ _ SizeLimitsConfig = ConfigParam 43;
suspended_address_list#00 addresses:(HashmapE 288 Unit) suspended_until:uint32 = SuspendedAddressList;
_ SuspendedAddressList = ConfigParam 44;
+precompiled_smc#b0 gas_usage:uint64 = PrecompiledSmc;
+precompiled_contracts_config#c0 list:(HashmapE 256 PrecompiledSmc) = PrecompiledContractsConfig;
+_ PrecompiledContractsConfig = ConfigParam 45;
+
oracle_bridge_params#_ bridge_address:bits256 oracle_mutlisig_address:bits256 oracles:(HashmapE 256 uint256) external_chain_address:bits256 = OracleBridgeParams;
_ OracleBridgeParams = ConfigParam 71; // Ethereum bridge
_ OracleBridgeParams = ConfigParam 72; // Binance Smart Chain bridge
diff --git a/crypto/block/mc-config.cpp b/crypto/block/mc-config.cpp
index 8b3871551..1dbfeaedc 100644
--- a/crypto/block/mc-config.cpp
+++ b/crypto/block/mc-config.cpp
@@ -2032,6 +2032,17 @@ td::Ref Config::get_unpacked_config_tuple(ton::UnixTime now) const {
return td::make_cnt_ref>(std::move(tuple));
}
+PrecompiledContractsConfig Config::get_precompiled_contracts_config() const {
+ PrecompiledContractsConfig c;
+ td::Ref param = get_config_param(45);
+ gen::PrecompiledContractsConfig::Record rec;
+ if (param.is_null() || !tlb::unpack_cell(param, rec)) {
+ return c;
+ }
+ c.list = vm::Dictionary{rec.list->prefetch_ref(), 256};
+ return c;
+}
+
td::Result> Config::unpack_validator_set_start_stop(Ref vset_root) {
if (vset_root.is_null()) {
return td::Status::Error("validator set absent");
@@ -2316,4 +2327,20 @@ td::Result[> ConfigInfo::get_prev_blocks_info() const {
block_id_to_tuple(last_key_block));
}
+td::optional PrecompiledContractsConfig::get_contract(
+ td::Bits256 code_hash) const {
+ auto list_copy = list;
+ auto cs = list_copy.lookup(code_hash);
+ if (cs.is_null()) {
+ return {};
+ }
+ gen::PrecompiledSmc::Record rec;
+ if (!tlb::csr_unpack(cs, rec)) {
+ return {};
+ }
+ Contract c;
+ c.gas_usage = rec.gas_usage;
+ return c;
+}
+
} // namespace block
diff --git a/crypto/block/mc-config.h b/crypto/block/mc-config.h
index 624e3e03d..2ca893995 100644
--- a/crypto/block/mc-config.h
+++ b/crypto/block/mc-config.h
@@ -526,6 +526,15 @@ struct BurningConfig {
}
};
+struct PrecompiledContractsConfig {
+ struct Contract {
+ td::uint64 gas_usage;
+ };
+ vm::Dictionary list{256};
+
+ td::optional get_contract(td::Bits256 code_hash) const;
+};
+
class Config {
enum {
default_mc_catchain_lifetime = 200,
@@ -644,6 +653,7 @@ class Config {
std::unique_ptr get_suspended_addresses(ton::UnixTime now) const;
BurningConfig get_burning_config() const;
td::Ref get_unpacked_config_tuple(ton::UnixTime now) const;
+ PrecompiledContractsConfig get_precompiled_contracts_config() const;
static std::vector do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time,
diff --git a/crypto/block/precompiled-smc/PrecompiledSmartContract.cpp b/crypto/block/precompiled-smc/PrecompiledSmartContract.cpp
new file mode 100644
index 000000000..6797216df
--- /dev/null
+++ b/crypto/block/precompiled-smc/PrecompiledSmartContract.cpp
@@ -0,0 +1,170 @@
+/*
+ This file is part of TON Blockchain Library.
+
+ TON Blockchain Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ TON Blockchain Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with TON Blockchain Library. If not, see .
+*/
+#include "common.h"
+#include
+#include "vm/memo.h"
+
+namespace block::precompiled {
+
+using namespace vm;
+
+Result PrecompiledSmartContract::run(td::Ref my_address, ton::UnixTime now, ton::LogicalTime cur_lt,
+ CurrencyCollection balance, td::Ref c4, vm::CellSlice msg_body,
+ td::Ref msg, CurrencyCollection msg_balance, bool is_external,
+ std::vector> libraries, int global_version,
+ td::uint16 max_data_depth, td::Ref my_code,
+ td::Ref unpacked_config, td::RefInt256 due_payment,
+ td::uint64 precompiled_gas_usage) {
+ my_address_ = std::move(my_address);
+ now_ = now;
+ cur_lt_ = cur_lt;
+ balance_ = std::move(balance);
+ c4_ = (c4.not_null() ? std::move(c4) : CellBuilder().finalize());
+ in_msg_body_ = std::move(msg_body);
+ in_msg_ = std::move(msg);
+ in_msg_balance_ = std::move(msg_balance);
+ is_external_ = is_external;
+ my_code_ = std::move(my_code);
+ unpacked_config_ = std::move(unpacked_config);
+ due_payment_ = std::move(due_payment);
+ precompiled_gas_usage_ = precompiled_gas_usage;
+
+ vm::DummyVmState vm_state{std::move(libraries), global_version};
+ vm::VmStateInterface::Guard guard{&vm_state};
+
+ Result result;
+ try {
+ result = do_run();
+ } catch (vm::VmError &e) {
+ result = Result::error(e.get_errno(), e.get_arg());
+ } catch (Result &r) {
+ result = std::move(r);
+ }
+
+ if (result.exit_code != 0 && result.exit_code != 1) {
+ // see VmState::try_commit()
+ if (c4_.is_null() || c4_->get_depth() > max_data_depth || c4_->get_level() != 0 || c5_.is_null() ||
+ c5_->get_depth() > max_data_depth || c5_->get_level() != 0) {
+ result = Result::error(Excno::cell_ov, 0);
+ }
+ }
+ return result;
+}
+
+void PrecompiledSmartContract::send_raw_message(const td::Ref] &msg, int mode) {
+ CellBuilder cb;
+ if (!(cb.store_ref_bool(c5_) // out_list$_ {n:#} prev:^(OutList n)
+ && cb.store_long_bool(0x0ec3c86d, 32) // action_send_msg#0ec3c86d
+ && cb.store_long_bool(mode, 8) // mode:(## 8)
+ && cb.store_ref_bool(msg))) {
+ throw VmError{Excno::cell_ov, "cannot serialize raw output message into an output action cell"};
+ }
+ c5_ = cb.finalize_novm();
+}
+
+void PrecompiledSmartContract::raw_reserve(const td::RefInt256 &amount, int mode) {
+ if (amount->sgn() < 0) {
+ throw VmError{Excno::range_chk, "amount of nanograms must be non-negative"};
+ }
+ CellBuilder cb;
+ if (!(cb.store_ref_bool(c5_) // out_list$_ {n:#} prev:^(OutList n)
+ && cb.store_long_bool(0x36e6b809, 32) // action_reserve_currency#36e6b809
+ && cb.store_long_bool(mode, 8) // mode:(## 8)
+ && util::store_coins(cb, std::move(amount), true) //
+ && cb.store_maybe_ref({}))) {
+ throw VmError{Excno::cell_ov, "cannot serialize raw reserved currency amount into an output action cell"};
+ }
+ c5_ = cb.finalize_novm();
+}
+
+td::RefInt256 PrecompiledSmartContract::get_compute_fee(ton::WorkchainId wc, td::uint64 gas_used) {
+ if (gas_used >= (1ULL << 63)) {
+ throw VmError{Excno::range_chk};
+ }
+ block::GasLimitsPrices prices = util::get_gas_prices(unpacked_config_, wc);
+ return util::check_finite(prices.compute_gas_price(gas_used));
+}
+
+td::RefInt256 PrecompiledSmartContract::get_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells) {
+ if (bits >= (1ULL << 63) || cells >= (1ULL << 63)) {
+ throw VmError{Excno::range_chk};
+ }
+ block::MsgPrices prices = util::get_msg_prices(unpacked_config_, wc);
+ return util::check_finite(prices.compute_fwd_fees256(cells, bits));
+}
+
+td::RefInt256 PrecompiledSmartContract::get_storage_fee(ton::WorkchainId wc, td::uint64 duration, td::uint64 bits,
+ td::uint64 cells) {
+ if (bits >= (1ULL << 63) || cells >= (1ULL << 63) || duration >= (1ULL << 63)) {
+ throw VmError{Excno::range_chk};
+ }
+ td::optional maybe_prices = util::get_storage_prices(unpacked_config_);
+ return util::check_finite(util::calculate_storage_fee(maybe_prices, wc, duration, bits, cells));
+}
+
+td::RefInt256 PrecompiledSmartContract::get_simple_compute_fee(ton::WorkchainId wc, td::uint64 gas_used) {
+ if (gas_used >= (1ULL << 63)) {
+ throw VmError{Excno::range_chk};
+ }
+ block::GasLimitsPrices prices = util::get_gas_prices(unpacked_config_, wc);
+ return util::check_finite(td::rshift(td::make_refint(prices.gas_price) * gas_used, 16, 1));
+}
+
+td::RefInt256 PrecompiledSmartContract::get_simple_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells) {
+ if (bits >= (1ULL << 63) || cells >= (1ULL << 63)) {
+ throw VmError{Excno::range_chk};
+ }
+ block::MsgPrices prices = util::get_msg_prices(unpacked_config_, wc);
+ return util::check_finite(
+ td::rshift(td::make_refint(prices.bit_price) * bits + td::make_refint(prices.cell_price) * cells, 16, 1));
+}
+
+td::RefInt256 PrecompiledSmartContract::get_original_fwd_fee(ton::WorkchainId wc, const td::RefInt256 &x) {
+ if (x->sgn() < 0) {
+ throw VmError{Excno::range_chk, "fwd_fee is negative"};
+ }
+ block::MsgPrices prices = util::get_msg_prices(unpacked_config_, wc);
+ return util::check_finite(td::muldiv(x, td::make_refint(1 << 16), td::make_refint((1 << 16) - prices.first_frac)));
+}
+
+static std::atomic_bool precompiled_execution_enabled{false};
+
+std::unique_ptr get_implementation(td::Bits256 code_hash) {
+ if (!precompiled_execution_enabled) {
+ return nullptr;
+ }
+ static std::map (*)()> map = []() {
+ auto from_hex = [](td::Slice s) -> td::Bits256 {
+ td::Bits256 x;
+ CHECK(x.from_hex(s) == 256);
+ return x;
+ };
+ std::map (*)()> map;
+#define CONTRACT(hash, cls) \
+ map[from_hex(hash)] = []() -> std::unique_ptr { return std::make_unique(); };
+ // CONTRACT("CODE_HASH_HEX", ClassName);
+ return map;
+ }();
+ auto it = map.find(code_hash);
+ return it == map.end() ? nullptr : it->second();
+}
+
+void set_precompiled_execution_enabled(bool value) {
+ precompiled_execution_enabled = value;
+}
+
+} // namespace block::precompiled
diff --git a/crypto/block/precompiled-smc/PrecompiledSmartContract.h b/crypto/block/precompiled-smc/PrecompiledSmartContract.h
new file mode 100644
index 000000000..509ebf793
--- /dev/null
+++ b/crypto/block/precompiled-smc/PrecompiledSmartContract.h
@@ -0,0 +1,122 @@
+/*
+ This file is part of TON Blockchain Library.
+
+ TON Blockchain Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ TON Blockchain Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with TON Blockchain Library. If not, see .
+*/
+#pragma once
+#include "common/refcnt.hpp"
+#include "common/refint.h"
+#include "vm/cells.h"
+#include "vm/cellslice.h"
+#include "vm/dict.h"
+#include "vm/boc.h"
+#include
+#include "tl/tlblib.hpp"
+#include "td/utils/bits.h"
+#include "ton/ton-types.h"
+#include "block/block.h"
+#include "block/mc-config.h"
+
+namespace block::precompiled {
+
+struct Result {
+ int exit_code = 0;
+ td::optional exit_arg;
+ bool accepted = true;
+ bool committed = false;
+
+ static Result error(int code, long long arg = 0) {
+ Result res;
+ res.exit_code = code;
+ res.exit_arg = arg;
+ return res;
+ }
+
+ static Result error(vm::Excno code, long long arg = 0) {
+ Result res;
+ res.exit_code = (int)code;
+ res.exit_arg = arg;
+ return res;
+ }
+
+ static Result not_accepted(int code = 0) {
+ Result res;
+ res.exit_code = code;
+ res.accepted = false;
+ return res;
+ }
+
+ static Result success() {
+ Result res;
+ res.committed = true;
+ return res;
+ }
+};
+
+class PrecompiledSmartContract {
+ public:
+ virtual ~PrecompiledSmartContract() = default;
+
+ virtual std::string get_name() const = 0;
+
+ virtual int required_version() const {
+ return 6;
+ }
+
+ Result run(td::Ref my_address, ton::UnixTime now, ton::LogicalTime cur_lt, CurrencyCollection balance,
+ td::Ref c4, vm::CellSlice msg_body, td::Ref msg, CurrencyCollection msg_balance,
+ bool is_external, std::vector> libraries, int global_version, td::uint16 max_data_depth,
+ td::Ref my_code, td::Ref unpacked_config, td::RefInt256 due_payment, td::uint64 precompiled_gas_usage);
+
+ td::Ref get_c4() const {
+ return c4_;
+ }
+ td::Ref get_c5() const {
+ return c5_;
+ }
+
+ protected:
+ td::Ref my_address_;
+ ton::UnixTime now_;
+ ton::LogicalTime cur_lt_;
+ CurrencyCollection balance_;
+ vm::CellSlice in_msg_body_;
+ td::Ref in_msg_;
+ CurrencyCollection in_msg_balance_;
+ bool is_external_;
+ td::Ref my_code_;
+ td::Ref unpacked_config_;
+ td::RefInt256 due_payment_;
+ td::uint64 precompiled_gas_usage_;
+
+ td::Ref c4_;
+ td::Ref c5_ = vm::CellBuilder().finalize_novm();
+
+ void send_raw_message(const td::Ref& msg, int mode);
+ void raw_reserve(const td::RefInt256& amount, int mode);
+
+ td::RefInt256 get_compute_fee(ton::WorkchainId wc, td::uint64 gas_used);
+ td::RefInt256 get_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells);
+ td::RefInt256 get_storage_fee(ton::WorkchainId wc, td::uint64 duration, td::uint64 bits, td::uint64 cells);
+ td::RefInt256 get_simple_compute_fee(ton::WorkchainId wc, td::uint64 gas_used);
+ td::RefInt256 get_simple_forward_fee(ton::WorkchainId wc, td::uint64 bits, td::uint64 cells);
+ td::RefInt256 get_original_fwd_fee(ton::WorkchainId wc, const td::RefInt256& x);
+
+ virtual Result do_run() = 0;
+};
+
+std::unique_ptr get_implementation(td::Bits256 code_hash);
+void set_precompiled_execution_enabled(bool value); // disabled by default
+
+} // namespace block::precompiled
\ No newline at end of file
diff --git a/crypto/block/precompiled-smc/common.h b/crypto/block/precompiled-smc/common.h
new file mode 100644
index 000000000..0406d7dee
--- /dev/null
+++ b/crypto/block/precompiled-smc/common.h
@@ -0,0 +1,21 @@
+/*
+ This file is part of TON Blockchain Library.
+
+ TON Blockchain Library is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ TON Blockchain Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with TON Blockchain Library. If not, see .
+*/
+#pragma once
+#include "PrecompiledSmartContract.h"
+#include "vm/arithops.h"
+#include "vm/cellops.h"
+#include "vm/tonops.h"
\ No newline at end of file
diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp
index b3aa6c8f4..88966179d 100644
--- a/crypto/block/transaction.cpp
+++ b/crypto/block/transaction.cpp
@@ -1341,6 +1341,9 @@ Ref Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const {
tuple.push_back(cfg.unpacked_config_tuple.not_null() ? vm::StackEntry(cfg.unpacked_config_tuple)
: vm::StackEntry()); // unpacked_config_tuple:[...]
tuple.push_back(due_payment.not_null() ? due_payment : td::zero_refint()); // due_payment:Integer
+ tuple.push_back(compute_phase->precompiled_gas_usage
+ ? vm::StackEntry(td::make_refint(compute_phase->precompiled_gas_usage.value()))
+ : vm::StackEntry()); // precompiled_gas_usage:Integer
}
auto tuple_ref = td::make_cnt_ref>(std::move(tuple));
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string();
@@ -1460,6 +1463,80 @@ bool Transaction::check_in_msg_state_hash() {
return account.recompute_tmp_addr(my_addr, d, orig_addr_rewrite.bits());
}
+/**
+ * Runs the precompiled smart contract and prepares the compute phase.
+ *
+ * @param cfg The configuration for the compute phase.
+ * @param impl Implementation of the smart contract
+ *
+ * @returns True if the contract was successfully executed, false otherwise.
+ */
+bool Transaction::run_precompiled_contract(const ComputePhaseConfig& cfg, precompiled::PrecompiledSmartContract& impl) {
+ ComputePhase& cp = *compute_phase;
+ CHECK(cp.precompiled_gas_usage);
+ td::uint64 gas_usage = cp.precompiled_gas_usage.value();
+ td::Timer timer;
+ auto result =
+ impl.run(my_addr, now, start_lt, balance, new_data, *in_msg_body, in_msg, msg_balance_remaining, in_msg_extern,
+ compute_vm_libraries(cfg), cfg.global_version, cfg.max_vm_data_depth, new_code,
+ cfg.unpacked_config_tuple, due_payment.not_null() ? due_payment : td::zero_refint(), gas_usage);
+ double elapsed = timer.elapsed();
+ cp.vm_init_state_hash = td::Bits256::zero();
+ cp.exit_code = result.exit_code;
+ cp.out_of_gas = false;
+ cp.vm_final_state_hash = td::Bits256::zero();
+ cp.vm_steps = 0;
+ cp.gas_used = gas_usage;
+ cp.accepted = result.accepted;
+ cp.success = (cp.accepted && result.committed);
+ LOG(INFO) << "Running precompiled smart contract " << impl.get_name() << ": exit_code=" << result.exit_code
+ << " accepted=" << result.accepted << " success=" << cp.success << " gas_used=" << gas_usage
+ << " time=" << elapsed << "s";
+ if (cp.accepted & use_msg_state) {
+ was_activated = true;
+ acc_status = Account::acc_active;
+ }
+ if (cfg.with_vm_log) {
+ cp.vm_log = PSTRING() << "Running precompiled smart contract " << impl.get_name()
+ << ": exit_code=" << result.exit_code << " accepted=" << result.accepted
+ << " success=" << cp.success << " gas_used=" << gas_usage << " time=" << elapsed << "s";
+ }
+ if (cp.success) {
+ cp.new_data = impl.get_c4();
+ cp.actions = impl.get_c5();
+ int out_act_num = output_actions_count(cp.actions);
+ if (verbosity > 2) {
+ std::cerr << "new smart contract data: ";
+ bool can_be_special = true;
+ load_cell_slice_special(cp.new_data, can_be_special).print_rec(std::cerr);
+ std::cerr << "output actions: ";
+ block::gen::OutList{out_act_num}.print_ref(std::cerr, cp.actions);
+ }
+ }
+ cp.mode = 0;
+ cp.exit_arg = 0;
+ if (!cp.success && result.exit_arg) {
+ auto value = td::narrow_cast_safe(result.exit_arg.value());
+ if (value.is_ok()) {
+ cp.exit_arg = value.ok();
+ }
+ }
+ if (cp.accepted) {
+ if (account.is_special) {
+ cp.gas_fees = td::zero_refint();
+ } else {
+ cp.gas_fees = cfg.compute_gas_price(cp.gas_used);
+ total_fees += cp.gas_fees;
+ balance -= cp.gas_fees;
+ }
+ LOG(DEBUG) << "gas fees: " << cp.gas_fees->to_dec_string() << " = " << cfg.gas_price256->to_dec_string() << " * "
+ << cp.gas_used << " /2^16 ; price=" << cfg.gas_price << "; flat rate=[" << cfg.flat_gas_price << " for "
+ << cfg.flat_gas_limit << "]; remaining balance=" << balance.to_str();
+ CHECK(td::sgn(balance.grams) >= 0);
+ }
+ return true;
+}
+
/**
* Prepares the compute phase of a transaction, which includes running TVM.
*
@@ -1528,6 +1605,33 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
cp.skip_reason = ComputePhase::sk_bad_state;
return true;
}
+
+ td::optional precompiled;
+ if (new_code.not_null() && trans_type == tr_ord) {
+ precompiled = cfg.precompiled_contracts.get_contract(new_code->get_hash().bits());
+ }
+
+ vm::GasLimits gas{(long long)cp.gas_limit, (long long)cp.gas_max, (long long)cp.gas_credit};
+ if (precompiled) {
+ td::uint64 gas_usage = precompiled.value().gas_usage;
+ cp.precompiled_gas_usage = gas_usage;
+ if (gas_usage > cp.gas_limit) {
+ cp.skip_reason = ComputePhase::sk_no_gas;
+ return true;
+ }
+ auto impl = precompiled::get_implementation(new_code->get_hash().bits());
+ if (impl != nullptr && !cfg.dont_run_precompiled_ && impl->required_version() <= cfg.global_version) {
+ return run_precompiled_contract(cfg, *impl);
+ }
+
+ // Contract is marked as precompiled in global config, but implementation is not available
+ // In this case we run TVM and override gas_used
+ LOG(INFO) << "Unknown precompiled contract (code_hash=" << new_code->get_hash().to_hex()
+ << ", gas_usage=" << gas_usage << "), running VM";
+ long long limit = account.is_special ? cfg.special_gas_limit : cfg.gas_limit;
+ gas = vm::GasLimits{limit, limit, gas.gas_credit ? limit : 0};
+ }
+
// initialize VM
Ref stack = prepare_vm_stack(cp);
if (stack.is_null()) {
@@ -1536,7 +1640,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
}
// OstreamLogger ostream_logger(error_stream);
// auto log = create_vm_log(error_stream ? &ostream_logger : nullptr);
- vm::GasLimits gas{(long long)cp.gas_limit, (long long)cp.gas_max, (long long)cp.gas_credit};
LOG(DEBUG) << "creating VM";
std::unique_ptr logger;
@@ -1585,6 +1688,15 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
was_activated = true;
acc_status = Account::acc_active;
}
+ if (precompiled) {
+ cp.gas_used = precompiled.value().gas_usage;
+ cp.vm_steps = 0;
+ cp.vm_init_state_hash = cp.vm_final_state_hash = td::Bits256::zero();
+ if (cp.out_of_gas) {
+ LOG(ERROR) << "Precompiled smc got out_of_gas in TVM";
+ return false;
+ }
+ }
LOG(INFO) << "steps: " << vm.get_steps_count() << " gas: used=" << gas.gas_consumed() << ", max=" << gas.gas_max
<< ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit;
LOG(INFO) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success
@@ -3568,6 +3680,7 @@ td::Status FetchConfigParams::fetch_config_params(
}
compute_phase_cfg->suspended_addresses = config.get_suspended_addresses(now);
compute_phase_cfg->size_limits = size_limits;
+ compute_phase_cfg->precompiled_contracts = config.get_precompiled_contracts_config();
}
{
// compute action_phase_cfg
diff --git a/crypto/block/transaction.h b/crypto/block/transaction.h
index 57defc8c8..42cd84a83 100644
--- a/crypto/block/transaction.h
+++ b/crypto/block/transaction.h
@@ -29,6 +29,7 @@
#include "ton/ton-types.h"
#include "block/block.h"
#include "block/mc-config.h"
+#include "precompiled-smc/PrecompiledSmartContract.h"
namespace block {
using td::Ref;
@@ -122,6 +123,8 @@ struct ComputePhaseConfig {
SizeLimitsConfig size_limits;
int vm_log_verbosity = 0;
bool stop_on_accept_message = false;
+ PrecompiledContractsConfig precompiled_contracts;
+ bool dont_run_precompiled_ = false;
ComputePhaseConfig() : gas_price(0), gas_limit(0), special_gas_limit(0), gas_credit(0) {
compute_threshold();
@@ -189,6 +192,7 @@ struct ComputePhase {
Ref new_data;
Ref actions;
std::string vm_log;
+ td::optional precompiled_gas_usage;
};
struct ActionPhase {
@@ -372,6 +376,7 @@ struct Transaction {
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
Ref prepare_vm_stack(ComputePhase& cp);
std::vector[> compute_vm_libraries(const ComputePhaseConfig& cfg);
+ bool run_precompiled_contract(const ComputePhaseConfig& cfg, precompiled::PrecompiledSmartContract& precompiled);
bool prepare_compute_phase(const ComputePhaseConfig& cfg);
bool prepare_action_phase(const ActionPhaseConfig& cfg);
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true);
diff --git a/crypto/common/bigint.hpp b/crypto/common/bigint.hpp
index 5fa8e2da9..f8756d9f6 100644
--- a/crypto/common/bigint.hpp
+++ b/crypto/common/bigint.hpp
@@ -169,8 +169,6 @@ class PropagateConstSpan {
size_t size_{0};
};
-struct Normalize {};
-
template
class AnyIntView {
public:
@@ -290,6 +288,7 @@ class BigIntG {
public:
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift, max_bits = len, word_cnt = len / word_shift + 1 };
typedef typename Tr::word_t word_t;
+ typedef typename Tr::uword_t uword_t;
typedef Tr Traits;
typedef BigIntG DoubleInt;
@@ -312,9 +311,6 @@ class BigIntG {
BigIntG() : n(0) {
}
explicit BigIntG(word_t x) : n(1) {
- digits[0] = x;
- }
- BigIntG(Normalize, word_t x) : n(1) {
if (x >= -Tr::Half && x < Tr::Half) {
digits[0] = x;
} else if (len <= 1) {
@@ -325,6 +321,25 @@ class BigIntG {
digits[n++] = (x >> Tr::word_shift) + (digits[0] < 0);
}
}
+ explicit BigIntG(uword_t x) : n(1) {
+ if (x < (uword_t)Tr::Half) {
+ digits[0] = x;
+ } else if (len <= 1) {
+ digits[0] = x;
+ normalize_bool();
+ } else {
+ digits[0] = ((x ^ Tr::Half) & (Tr::Base - 1)) - Tr::Half;
+ digits[n++] = (x >> Tr::word_shift) + (digits[0] < 0);
+ }
+ }
+ explicit BigIntG(unsigned x) : BigIntG(uword_t(x)) {
+ }
+ explicit BigIntG(int x) : BigIntG(word_t(x)) {
+ }
+ explicit BigIntG(unsigned long x) : BigIntG(uword_t(x)) {
+ }
+ explicit BigIntG(long x) : BigIntG(word_t(x)) {
+ }
BigIntG(const BigIntG& x) : n(x.n) {
std::memcpy(digits, x.digits, n * sizeof(word_t));
///std::cout << "(BiCC " << (const void*)&x << "->" << (void*)this << ")";
@@ -2556,7 +2571,7 @@ typedef BigIntG<257, BigIntInfo> BigInt256;
template
BigIntG make_bigint(long long x) {
- return BigIntG{Normalize(), x};
+ return BigIntG{x};
}
namespace literals {
diff --git a/crypto/common/refint.cpp b/crypto/common/refint.cpp
index 8e06da5fe..a2ccdaa69 100644
--- a/crypto/common/refint.cpp
+++ b/crypto/common/refint.cpp
@@ -261,10 +261,6 @@ int sgn(RefInt256 x) {
return x->sgn();
}
-RefInt256 make_refint(long long x) {
- return td::RefInt256{true, td::Normalize(), x};
-}
-
RefInt256 zero_refint() {
// static RefInt256 Zero = td::RefInt256{true, 0};
// return Zero;
diff --git a/crypto/common/refint.h b/crypto/common/refint.h
index 488ae97f3..e3305c2f9 100644
--- a/crypto/common/refint.h
+++ b/crypto/common/refint.h
@@ -113,8 +113,6 @@ RefInt256 make_refint(Args&&... args) {
return td::RefInt256{true, std::forward(args)...};
}
-extern RefInt256 make_refint(long long x);
-
extern RefInt256 zero_refint();
extern RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd = false);
diff --git a/crypto/smc-envelope/SmartContract.cpp b/crypto/smc-envelope/SmartContract.cpp
index 1908b9128..13e5c70b8 100644
--- a/crypto/smc-envelope/SmartContract.cpp
+++ b/crypto/smc-envelope/SmartContract.cpp
@@ -174,6 +174,7 @@ td::Ref prepare_vm_c7(SmartContract::Args args, td::Ref cod
if (args.config && args.config.value()->get_global_version() >= 6) {
tuple.push_back(args.config.value()->get_unpacked_config_tuple(now)); // unpacked_config_tuple
tuple.push_back(td::zero_refint()); // due_payment
+ tuple.push_back(vm::StackEntry()); // precompiled_gas_usage:Integer
}
auto tuple_ref = td::make_cnt_ref>(std::move(tuple));
//LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
diff --git a/crypto/vm/arithops.cpp b/crypto/vm/arithops.cpp
index 1d3111b2f..fc482fc4d 100644
--- a/crypto/vm/arithops.cpp
+++ b/crypto/vm/arithops.cpp
@@ -1069,4 +1069,26 @@ void register_arith_ops(OpcodeTable& cp0) {
register_int_cmp_ops(cp0);
}
+namespace util {
+
+const td::RefInt256& check_signed_fits(const td::RefInt256& x, int bits) {
+ if (!x->signed_fits_bits(bits)) {
+ throw VmError{Excno::int_ov};
+ }
+ return x;
+}
+
+const td::RefInt256& check_unsigned_fits(const td::RefInt256& x, int bits) {
+ if (!x->unsigned_fits_bits(bits)) {
+ throw VmError{Excno::int_ov};
+ }
+ return x;
+}
+
+const td::RefInt256& check_finite(const td::RefInt256& x) {
+ return check_signed_fits(x, 257);
+}
+
+} // namespace util
+
} // namespace vm
diff --git a/crypto/vm/arithops.h b/crypto/vm/arithops.h
index 63adcf7c5..c9735798c 100644
--- a/crypto/vm/arithops.h
+++ b/crypto/vm/arithops.h
@@ -18,10 +18,20 @@
*/
#pragma once
+#include "common/refint.h"
namespace vm {
class OpcodeTable;
void register_arith_ops(OpcodeTable& cp0);
+namespace util {
+
+// throw on error
+const td::RefInt256& check_signed_fits(const td::RefInt256& x, int bits);
+const td::RefInt256& check_unsigned_fits(const td::RefInt256& x, int bits);
+const td::RefInt256& check_finite(const td::RefInt256& x);
+
+} // namespace util
+
} // namespace vm
diff --git a/crypto/vm/cellops.cpp b/crypto/vm/cellops.cpp
index 94407231a..61ffe5c55 100644
--- a/crypto/vm/cellops.cpp
+++ b/crypto/vm/cellops.cpp
@@ -1544,4 +1544,193 @@ void register_cell_ops(OpcodeTable& cp0) {
register_cell_deserialize_ops(cp0);
}
+namespace util {
+
+bool load_int256_q(CellSlice& cs, td::RefInt256& res, int len, bool sgnd, bool quiet) {
+ if (!cs.fetch_int256_to(len, res, sgnd)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und};
+ }
+ return true;
+}
+bool load_long_q(CellSlice& cs, td::int64& res, int len, bool quiet) {
+ CHECK(0 <= len && len <= 64);
+ if (!cs.have(len)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und};
+ }
+ res = cs.fetch_long(len);
+ return true;
+}
+bool load_ulong_q(CellSlice& cs, td::uint64& res, int len, bool quiet) {
+ CHECK(0 <= len && len <= 64);
+ if (!cs.have(len)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und};
+ }
+ res = cs.fetch_ulong(len);
+ return true;
+}
+bool load_ref_q(CellSlice& cs, td::Ref]& res, bool quiet) {
+ if (!cs.have_refs(1)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und};
+ }
+ res = cs.fetch_ref();
+ return true;
+}
+bool load_maybe_ref_q(CellSlice& cs, td::Ref& res, bool quiet) {
+ if (!cs.fetch_maybe_ref(res)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und};
+ }
+ return true;
+}
+bool skip_bits_q(CellSlice& cs, int bits, bool quiet) {
+ if (!cs.skip_first(bits)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und};
+ }
+ return true;
+}
+
+td::RefInt256 load_int256(CellSlice& cs, int len, bool sgnd) {
+ td::RefInt256 x;
+ load_int256_q(cs, x, len, sgnd, false);
+ return x;
+}
+td::int64 load_long(CellSlice& cs, int len) {
+ td::int64 x;
+ load_long_q(cs, x, len, false);
+ return x;
+}
+td::uint64 load_ulong(CellSlice& cs, int len) {
+ td::uint64 x;
+ load_ulong_q(cs, x, len, false);
+ return x;
+}
+td::Ref load_ref(CellSlice& cs) {
+ td::Ref x;
+ load_ref_q(cs, x, false);
+ return x;
+}
+td::Ref load_maybe_ref(CellSlice& cs) {
+ td::Ref x;
+ load_maybe_ref_q(cs, x, false);
+ return x;
+}
+void check_have_bits(const CellSlice& cs, int bits) {
+ if (!cs.have(bits)) {
+ throw VmError{Excno::cell_und};
+ }
+}
+void skip_bits(CellSlice& cs, int bits) {
+ skip_bits_q(cs, bits, false);
+}
+void end_parse(CellSlice& cs) {
+ if (cs.size() || cs.size_refs()) {
+ throw VmError{Excno::cell_und, "extra data remaining in deserialized cell"};
+ }
+}
+
+bool store_int256(CellBuilder& cb, const td::RefInt256& x, int len, bool sgnd, bool quiet) {
+ if (!cb.can_extend_by(len)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_ov};
+ }
+ if (!x->fits_bits(len, sgnd)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::range_chk};
+ }
+ cb.store_int256(*x, len, sgnd);
+ return true;
+}
+bool store_long(CellBuilder& cb, td::int64 x, int len, bool quiet) {
+ CHECK(len > 0);
+ if (!cb.can_extend_by(len)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_ov};
+ }
+ if (len < 64 && (x < td::int64(std::numeric_limits::max() << (len - 1)) || x >= (1LL << (len - 1)))) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::range_chk};
+ }
+ if (len > 64) {
+ cb.store_bits_same(len - 64, x < 0);
+ len = 64;
+ }
+ cb.store_long(x, len);
+ return true;
+}
+bool store_ulong(CellBuilder& cb, td::uint64 x, int len, bool quiet) {
+ CHECK(len > 0);
+ if (!cb.can_extend_by(len)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_ov};
+ }
+ if (len < 64 && x >= (1ULL << len)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::range_chk};
+ }
+ if (len > 64) {
+ cb.store_zeroes(len - 64);
+ len = 64;
+ }
+ cb.store_long(x, len);
+ return true;
+}
+bool store_ref(CellBuilder& cb, td::Ref x, bool quiet) {
+ if (!cb.store_ref_bool(std::move(x))) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_ov};
+ }
+ return true;
+}
+bool store_maybe_ref(CellBuilder& cb, td::Ref x, bool quiet) {
+ if (!cb.store_maybe_ref(std::move(x))) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_ov};
+ }
+ return true;
+}
+bool store_slice(CellBuilder& cb, const CellSlice& cs, bool quiet) {
+ if (!cell_builder_add_slice_bool(cb, cs)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_ov};
+ }
+ return true;
+}
+
+} // namespace util
+
} // namespace vm
diff --git a/crypto/vm/cellops.h b/crypto/vm/cellops.h
index 1c08d8e34..0fd62854f 100644
--- a/crypto/vm/cellops.h
+++ b/crypto/vm/cellops.h
@@ -23,12 +23,42 @@ namespace vm {
class OpcodeTable;
-void register_cell_ops(OpcodeTable &cp0);
+void register_cell_ops(OpcodeTable& cp0);
-std::string dump_push_ref(CellSlice &cs, unsigned args, int pfx_bits, std::string name);
-int compute_len_push_ref(const CellSlice &cs, unsigned args, int pfx_bits);
+std::string dump_push_ref(CellSlice& cs, unsigned args, int pfx_bits, std::string name);
+int compute_len_push_ref(const CellSlice& cs, unsigned args, int pfx_bits);
-std::string dump_push_ref2(CellSlice &cs, unsigned args, int pfx_bits, std::string name);
-int compute_len_push_ref2(const CellSlice &cs, unsigned args, int pfx_bits);
+std::string dump_push_ref2(CellSlice& cs, unsigned args, int pfx_bits, std::string name);
+int compute_len_push_ref2(const CellSlice& cs, unsigned args, int pfx_bits);
+
+namespace util {
+
+// "_q" functions throw on error if not quiet, return false if quiet (leaving cs unchanged)
+bool load_int256_q(CellSlice& cs, td::RefInt256& res, int len, bool sgnd, bool quiet);
+bool load_long_q(CellSlice& cs, td::int64& res, int len, bool quiet);
+bool load_ulong_q(CellSlice& cs, td::uint64& res, int len, bool quiet);
+bool load_ref_q(CellSlice& cs, td::Ref& res, bool quiet);
+bool load_maybe_ref_q(CellSlice& cs, td::Ref& res, bool quiet);
+bool skip_bits_q(CellSlice& cs, int bits, bool quiet);
+
+// Non-"_q" functions throw on error
+td::RefInt256 load_int256(CellSlice& cs, int len, bool sgnd);
+td::int64 load_long(CellSlice& cs, int len);
+td::uint64 load_ulong(CellSlice& cs, int len);
+td::Ref load_ref(CellSlice& cs);
+td::Ref load_maybe_ref(CellSlice& cs);
+void check_have_bits(const CellSlice& cs, int bits);
+void skip_bits(CellSlice& cs, int bits);
+void end_parse(CellSlice& cs);
+
+// store_... functions throw on error if not quiet, return false if quiet (leaving cb unchanged)
+bool store_int256(CellBuilder& cb, const td::RefInt256& x, int len, bool sgnd, bool quiet = false);
+bool store_long(CellBuilder& cb, td::int64 x, int len, bool quiet = false);
+bool store_ulong(CellBuilder& cb, td::uint64 x, int len, bool quiet = false);
+bool store_ref(CellBuilder& cb, td::Ref x, bool quiet = false);
+bool store_maybe_ref(CellBuilder& cb, td::Ref x, bool quiet = false);
+bool store_slice(CellBuilder& cb, const CellSlice& cs, bool quiet = false);
+
+} // namespace util
} // namespace vm
diff --git a/crypto/vm/cells/CellSlice.cpp b/crypto/vm/cells/CellSlice.cpp
index ee5f6941a..4be1667e7 100644
--- a/crypto/vm/cells/CellSlice.cpp
+++ b/crypto/vm/cells/CellSlice.cpp
@@ -595,7 +595,7 @@ td::RefInt256 CellSlice::fetch_int256(unsigned bits, bool sgnd) {
if (!have(bits)) {
return {};
} else if (bits < td::BigInt256::word_shift) {
- return td::make_refint(sgnd ? fetch_long(bits) : fetch_ulong(bits));
+ return td::make_refint(td::int64(sgnd ? fetch_long(bits) : fetch_ulong(bits)));
} else {
td::RefInt256 res{true};
res.unique_write().import_bits(data_bits(), bits, sgnd);
@@ -608,7 +608,7 @@ td::RefInt256 CellSlice::prefetch_int256(unsigned bits, bool sgnd) const {
if (!have(bits)) {
return {};
} else if (bits < td::BigInt256::word_shift) {
- return td::make_refint(sgnd ? prefetch_long(bits) : prefetch_ulong(bits));
+ return td::make_refint(td::int64(sgnd ? prefetch_long(bits) : prefetch_ulong(bits)));
} else {
td::RefInt256 res{true};
res.unique_write().import_bits(data_bits(), bits, sgnd);
diff --git a/crypto/vm/memo.cpp b/crypto/vm/memo.cpp
index 576f28f19..015bab6d0 100644
--- a/crypto/vm/memo.cpp
+++ b/crypto/vm/memo.cpp
@@ -18,6 +18,7 @@
*/
#include "vm/memo.h"
#include "vm/excno.hpp"
+#include "vm/vm.h"
namespace vm {
using td::Ref;
@@ -30,4 +31,18 @@ bool FakeVmStateLimits::register_op(int op_units) {
return ok;
}
+Ref DummyVmState::load_library(td::ConstBitPtr hash) {
+ std::unique_ptr tmp_ctx;
+ // install temporary dummy vm state interface to prevent charging for cell load operations during library lookup
+ VmStateInterface::Guard guard{global_version >= 4 ? tmp_ctx.get() : VmStateInterface::get()};
+ for (const auto& lib_collection : libraries) {
+ auto lib = lookup_library_in(hash, lib_collection);
+ if (lib.not_null()) {
+ return lib;
+ }
+ }
+ missing_library = td::Bits256{hash};
+ return {};
+}
+
} // namespace vm
diff --git a/crypto/vm/memo.h b/crypto/vm/memo.h
index 3aee39a6b..db6dc8e20 100644
--- a/crypto/vm/memo.h
+++ b/crypto/vm/memo.h
@@ -20,6 +20,7 @@
#include "common/refcnt.hpp"
#include "vm/cells.h"
#include "vm/vmstate.h"
+#include "td/utils/optional.h"
namespace vm {
using td::Ref;
@@ -34,4 +35,23 @@ class FakeVmStateLimits : public VmStateInterface {
bool register_op(int op_units = 1) override;
};
+class DummyVmState : public VmStateInterface {
+ public:
+ explicit DummyVmState(std::vector[> libraries, int global_version = ton::SUPPORTED_VERSION)
+ : libraries(std::move(libraries)), global_version(global_version) {
+ }
+ Ref] load_library(td::ConstBitPtr hash) override;
+ int get_global_version() const override {
+ return global_version;
+ }
+ td::optional get_missing_library() const {
+ return missing_library;
+ }
+
+ private:
+ std::vector[> libraries;
+ int global_version;
+ td::optional missing_library;
+};
+
} // namespace vm
diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp
index 9ce7fe9c4..4b2d1734e 100644
--- a/crypto/vm/tonops.cpp
+++ b/crypto/vm/tonops.cpp
@@ -124,7 +124,7 @@ static const StackEntry& get_param(VmState* st, unsigned idx) {
}
// ConfigParams: 18 (only one entry), 19, 20, 21, 24, 25, 43
-static td::Ref get_unpacked_config_param(VmState* st, unsigned idx) {
+static td::Ref get_unpacked_config_tuple(VmState* st) {
auto tuple = st->get_c7();
auto t1 = tuple_index(tuple, 0).as_tuple_range(255);
if (t1.is_null()) {
@@ -134,7 +134,7 @@ static td::Ref get_unpacked_config_param(VmState* st, unsigned idx) {
if (t2.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a tuple"};
}
- return tuple_index(t2, idx).as_slice();
+ return t2;
}
int exec_get_param(VmState* st, unsigned idx, const char* name) {
@@ -249,7 +249,7 @@ int exec_get_prev_blocks_info(VmState* st, unsigned idx, const char* name) {
int exec_get_global_id(VmState* st) {
VM_LOG(st) << "execute GLOBALID";
if (st->get_global_version() >= 6) {
- Ref cs = get_unpacked_config_param(st, 1);
+ Ref cs = tuple_index(get_unpacked_config_tuple(st), 1).as_slice();
if (cs.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a slice"};
}
@@ -276,36 +276,12 @@ int exec_get_global_id(VmState* st) {
return 0;
}
-static block::GasLimitsPrices get_gas_prices(VmState* st, bool is_masterchain) {
- Ref cs = get_unpacked_config_param(st, is_masterchain ? 2 : 3);
- if (cs.is_null()) {
- throw VmError{Excno::type_chk, "intermediate value is not a slice"};
- }
- auto r_prices = block::Config::do_get_gas_limits_prices(*cs, is_masterchain ? 20 : 21);
- if (r_prices.is_error()) {
- throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
- }
- return r_prices.move_as_ok();
-}
-
-static block::MsgPrices get_msg_prices(VmState* st, bool is_masterchain) {
- Ref cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5);
- if (cs.is_null()) {
- throw VmError{Excno::type_chk, "intermediate value is not a slice"};
- }
- auto r_prices = block::Config::do_get_msg_prices(*cs, is_masterchain ? 24 : 25);
- if (r_prices.is_error()) {
- throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
- }
- return r_prices.move_as_ok();
-}
-
int exec_get_gas_fee(VmState* st) {
VM_LOG(st) << "execute GETGASFEE";
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits::max(), 0);
- block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain);
+ block::GasLimitsPrices prices = util::get_gas_prices(get_unpacked_config_tuple(st), is_masterchain);
stack.push_int(prices.compute_gas_price(gas));
return 0;
}
@@ -317,27 +293,9 @@ int exec_get_storage_fee(VmState* st) {
td::int64 delta = stack.pop_long_range(std::numeric_limits::max(), 0);
td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits::max(), 0);
- Ref cs = get_unpacked_config_param(st, 0);
- if (cs.is_null()) {
- // null means tat no StoragePrices is active, so the price is 0
- stack.push_smallint(0);
- return 0;
- }
- auto r_prices = block::Config::do_get_one_storage_prices(*cs);
- if (r_prices.is_error()) {
- throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
- }
- block::StoragePrices prices = r_prices.move_as_ok();
- td::RefInt256 total;
- if (is_masterchain) {
- total = td::make_refint(cells) * prices.mc_cell_price;
- total += td::make_refint(bits) * prices.mc_bit_price;
- } else {
- total = td::make_refint(cells) * prices.cell_price;
- total += td::make_refint(bits) * prices.bit_price;
- }
- total *= delta;
- stack.push_int(td::rshift(total, 16, 1));
+ td::optional maybe_prices =
+ util::get_storage_prices(get_unpacked_config_tuple(st));
+ stack.push_int(util::calculate_storage_fee(maybe_prices, is_masterchain, delta, bits, cells));
return 0;
}
@@ -347,7 +305,7 @@ int exec_get_forward_fee(VmState* st) {
bool is_masterchain = stack.pop_bool();
td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits::max(), 0);
- block::MsgPrices prices = get_msg_prices(st, is_masterchain);
+ block::MsgPrices prices = util::get_msg_prices(get_unpacked_config_tuple(st), is_masterchain);
stack.push_int(prices.compute_fwd_fees256(cells, bits));
return 0;
}
@@ -355,7 +313,7 @@ int exec_get_forward_fee(VmState* st) {
int exec_get_precompiled_gas(VmState* st) {
VM_LOG(st) << "execute GETPRECOMPILEDGAS";
Stack& stack = st->get_stack();
- stack.push_null();
+ stack.push(get_param(st, 16));
return 0;
}
@@ -367,7 +325,7 @@ int exec_get_original_fwd_fee(VmState* st) {
if (fwd_fee->sgn() < 0) {
throw VmError{Excno::range_chk, "fwd_fee is negative"};
}
- block::MsgPrices prices = get_msg_prices(st, is_masterchain);
+ block::MsgPrices prices = util::get_msg_prices(get_unpacked_config_tuple(st), is_masterchain);
stack.push_int(td::muldiv(fwd_fee, td::make_refint(1 << 16), td::make_refint((1 << 16) - prices.first_frac)));
return 0;
}
@@ -377,7 +335,7 @@ int exec_get_gas_fee_simple(VmState* st) {
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits::max(), 0);
- block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain);
+ block::GasLimitsPrices prices = util::get_gas_prices(get_unpacked_config_tuple(st), is_masterchain);
stack.push_int(td::rshift(td::make_refint(prices.gas_price) * gas, 16, 1));
return 0;
}
@@ -388,7 +346,7 @@ int exec_get_forward_fee_simple(VmState* st) {
bool is_masterchain = stack.pop_bool();
td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits::max(), 0);
- block::MsgPrices prices = get_msg_prices(st, is_masterchain);
+ block::MsgPrices prices = util::get_msg_prices(get_unpacked_config_tuple(st), is_masterchain);
stack.push_int(td::rshift(td::make_refint(prices.bit_price) * bits + td::make_refint(prices.cell_price) * cells, 16,
1)); // divide by 2^16 with ceil rounding
return 0;
@@ -1349,19 +1307,14 @@ int exec_load_var_integer(VmState* st, int len_bits, bool sgnd, bool quiet) {
Stack& stack = st->get_stack();
auto csr = stack.pop_cellslice();
td::RefInt256 x;
- int len;
- if (!(csr.write().fetch_uint_to(len_bits, len) && csr.unique_write().fetch_int256_to(len * 8, x, sgnd))) {
- if (quiet) {
- stack.push_bool(false);
- } else {
- throw VmError{Excno::cell_und, "cannot deserialize a variable-length integer"};
- }
- } else {
+ if (util::load_var_integer_q(csr.write(), x, len_bits, sgnd, quiet)) {
stack.push_int(std::move(x));
stack.push_cellslice(std::move(csr));
if (quiet) {
stack.push_bool(true);
}
+ } else {
+ stack.push_bool(false);
}
return 0;
}
@@ -1376,21 +1329,13 @@ int exec_store_var_integer(VmState* st, int len_bits, bool sgnd, bool quiet) {
stack.check_underflow(2);
auto x = stack.pop_int();
auto cbr = stack.pop_builder();
- unsigned len = (((unsigned)x->bit_size(sgnd) + 7) >> 3);
- if (len >= (1u << len_bits)) {
- throw VmError{Excno::range_chk};
- }
- if (!(cbr.write().store_long_bool(len, len_bits) && cbr.unique_write().store_int256_bool(*x, len * 8, sgnd))) {
- if (quiet) {
- stack.push_bool(false);
- } else {
- throw VmError{Excno::cell_ov, "cannot serialize a variable-length integer"};
- }
- } else {
+ if (util::store_var_integer(cbr.write(), x, len_bits, sgnd, quiet)) {
stack.push_builder(std::move(cbr));
if (quiet) {
stack.push_bool(true);
}
+ } else {
+ stack.push_bool(false);
}
return 0;
}
@@ -1433,22 +1378,17 @@ bool skip_message_addr(CellSlice& cs) {
int exec_load_message_addr(VmState* st, bool quiet) {
VM_LOG(st) << "execute LDMSGADDR" << (quiet ? "Q" : "");
Stack& stack = st->get_stack();
- auto csr = stack.pop_cellslice(), csr_copy = csr;
- auto& cs = csr.write();
- if (!(skip_message_addr(cs) && csr_copy.write().cut_tail(cs))) {
- csr.clear();
- if (quiet) {
- stack.push_cellslice(std::move(csr_copy));
- stack.push_bool(false);
- } else {
- throw VmError{Excno::cell_und, "cannot load a MsgAddress"};
- }
- } else {
- stack.push_cellslice(std::move(csr_copy));
+ auto csr = stack.pop_cellslice();
+ td::Ref addr{true};
+ if (util::load_msg_addr_q(csr.write(), addr.write(), quiet)) {
+ stack.push_cellslice(std::move(addr));
stack.push_cellslice(std::move(csr));
if (quiet) {
stack.push_bool(true);
}
+ } else {
+ stack.push_cellslice(std::move(csr));
+ stack.push_bool(false);
}
return 0;
}
@@ -1747,7 +1687,7 @@ int exec_send_message(VmState* st) {
bool is_masterchain = parse_addr_workchain(*my_addr) == -1 || (!ext_msg && parse_addr_workchain(*dest) == -1);
td::Ref prices_cs;
if (st->get_global_version() >= 6) {
- prices_cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5);
+ prices_cs = tuple_index(get_unpacked_config_tuple(st), is_masterchain ? 4 : 5).as_slice();
} else {
Ref] config_dict = get_param(st, 9).as_cell();
Dictionary config{config_dict, 32};
@@ -1769,7 +1709,8 @@ int exec_send_message(VmState* st) {
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
td::uint64 max_cells;
if (st->get_global_version() >= 6) {
- auto r_size_limits_config = block::Config::do_get_size_limits_config(get_unpacked_config_param(st, 6));
+ auto r_size_limits_config =
+ block::Config::do_get_size_limits_config(tuple_index(get_unpacked_config_tuple(st), 6).as_slice());
if (r_size_limits_config.is_error()) {
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_size_limits_config.error().message()};
}
@@ -2020,4 +1961,158 @@ void register_ton_ops(OpcodeTable& cp0) {
register_ton_message_ops(cp0);
}
+namespace util {
+
+bool load_var_integer_q(CellSlice& cs, td::RefInt256& res, int len_bits, bool sgnd, bool quiet) {
+ CellSlice cs0 = cs;
+ int len;
+ if (!(cs.fetch_uint_to(len_bits, len) && cs.fetch_int256_to(len * 8, res, sgnd))) {
+ cs = std::move(cs0);
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und, "cannot deserialize a variable-length integer"};
+ }
+ return true;
+}
+bool load_coins_q(CellSlice& cs, td::RefInt256& res, bool quiet) {
+ return load_var_integer_q(cs, res, 4, false, quiet);
+}
+bool load_msg_addr_q(CellSlice& cs, CellSlice& res, bool quiet) {
+ res = cs;
+ if (!skip_message_addr(cs)) {
+ cs = res;
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und, "cannot load a MsgAddress"};
+ }
+ res.cut_tail(cs);
+ return true;
+}
+bool parse_std_addr_q(CellSlice cs, ton::WorkchainId& res_wc, ton::StdSmcAddress& res_addr, bool quiet) {
+ // Like exec_rewrite_message_addr, but for std address case
+ std::vector tuple;
+ if (!(parse_message_addr(cs, tuple) && cs.empty_ext())) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und, "cannot parse a MsgAddress"};
+ }
+ int t = (int)std::move(tuple[0]).as_int()->to_long();
+ if (t != 2 && t != 3) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und, "cannot parse a MsgAddressInt"};
+ }
+ auto addr = std::move(tuple[3]).as_slice();
+ auto prefix = std::move(tuple[1]).as_slice();
+ if (addr->size() != 256) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_und, "MsgAddressInt is not a standard 256-bit address"};
+ }
+ res_wc = (int)tuple[2].as_int()->to_long();
+ CHECK(addr->prefetch_bits_to(res_addr) &&
+ (prefix.is_null() || prefix->prefetch_bits_to(res_addr.bits(), prefix->size())));
+ return true;
+}
+
+td::RefInt256 load_var_integer(CellSlice& cs, int len_bits, bool sgnd) {
+ td::RefInt256 x;
+ load_var_integer_q(cs, x, len_bits, sgnd, false);
+ return x;
+}
+td::RefInt256 load_coins(CellSlice& cs) {
+ return load_var_integer(cs, 4, false);
+}
+CellSlice load_msg_addr(CellSlice& cs) {
+ CellSlice addr;
+ load_msg_addr_q(cs, addr, false);
+ return addr;
+}
+std::pair parse_std_addr(CellSlice cs) {
+ std::pair res;
+ parse_std_addr_q(std::move(cs), res.first, res.second, false);
+ return res;
+}
+
+bool store_var_integer(CellBuilder& cb, const td::RefInt256& x, int len_bits, bool sgnd, bool quiet) {
+ unsigned len = (((unsigned)x->bit_size(sgnd) + 7) >> 3);
+ if (len >= (1u << len_bits)) {
+ throw VmError{Excno::range_chk}; // throw even if quiet
+ }
+ if (!cb.can_extend_by(len_bits + len * 8)) {
+ if (quiet) {
+ return false;
+ }
+ throw VmError{Excno::cell_ov, "cannot serialize a variable-length integer"};
+ }
+ CHECK(cb.store_long_bool(len, len_bits) && cb.store_int256_bool(*x, len * 8, sgnd));
+ return true;
+}
+bool store_coins(CellBuilder& cb, const td::RefInt256& x, bool quiet) {
+ return store_var_integer(cb, x, 4, false, quiet);
+}
+
+block::GasLimitsPrices get_gas_prices(const Ref& unpacked_config, bool is_masterchain) {
+ Ref cs = tuple_index(unpacked_config, is_masterchain ? 2 : 3).as_slice();
+ if (cs.is_null()) {
+ throw VmError{Excno::type_chk, "intermediate value is not a slice"};
+ }
+ auto r_prices = block::Config::do_get_gas_limits_prices(*cs, is_masterchain ? 20 : 21);
+ if (r_prices.is_error()) {
+ throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
+ }
+ return r_prices.move_as_ok();
+}
+
+block::MsgPrices get_msg_prices(const Ref& unpacked_config, bool is_masterchain) {
+ Ref cs = tuple_index(unpacked_config, is_masterchain ? 4 : 5).as_slice();
+ if (cs.is_null()) {
+ throw VmError{Excno::type_chk, "intermediate value is not a slice"};
+ }
+ auto r_prices = block::Config::do_get_msg_prices(*cs, is_masterchain ? 24 : 25);
+ if (r_prices.is_error()) {
+ throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
+ }
+ return r_prices.move_as_ok();
+}
+
+td::optional get_storage_prices(const Ref& unpacked_config) {
+ Ref cs = tuple_index(unpacked_config, 0).as_slice();
+ if (cs.is_null()) {
+ // null means tat no StoragePrices is active, so the price is 0
+ return {};
+ }
+ auto r_prices = block::Config::do_get_one_storage_prices(*cs);
+ if (r_prices.is_error()) {
+ throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
+ }
+ return r_prices.move_as_ok();
+}
+
+td::RefInt256 calculate_storage_fee(const td::optional& maybe_prices, bool is_masterchain,
+ td::uint64 delta, td::uint64 bits, td::uint64 cells) {
+ if (!maybe_prices) {
+ // no StoragePrices is active, so the price is 0
+ return td::zero_refint();
+ }
+ const block::StoragePrices& prices = maybe_prices.value();
+ td::RefInt256 total;
+ if (is_masterchain) {
+ total = td::make_refint(cells) * prices.mc_cell_price;
+ total += td::make_refint(bits) * prices.mc_bit_price;
+ } else {
+ total = td::make_refint(cells) * prices.cell_price;
+ total += td::make_refint(bits) * prices.bit_price;
+ }
+ total *= delta;
+ return td::rshift(total, 16, 1);
+}
+
+} // namespace util
+
} // namespace vm
diff --git a/crypto/vm/tonops.h b/crypto/vm/tonops.h
index 5b3e14d99..bbac078f2 100644
--- a/crypto/vm/tonops.h
+++ b/crypto/vm/tonops.h
@@ -17,6 +17,9 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
+#include "vm/vm.h"
+#include "ton/ton-types.h"
+#include "mc-config.h"
namespace vm {
@@ -24,4 +27,30 @@ class OpcodeTable;
void register_ton_ops(OpcodeTable& cp0);
+namespace util {
+
+// "_q" functions throw on error if not quiet, return false if quiet (leaving cs unchanged)
+bool load_var_integer_q(CellSlice& cs, td::RefInt256& res, int len_bits, bool sgnd, bool quiet);
+bool load_coins_q(CellSlice& cs, td::RefInt256& res, bool quiet);
+bool load_msg_addr_q(CellSlice& cs, CellSlice& res, bool quiet);
+bool parse_std_addr_q(CellSlice cs, ton::WorkchainId& res_wc, ton::StdSmcAddress& res_addr, bool quiet);
+
+// Non-"_q" functions throw on error
+td::RefInt256 load_var_integer(CellSlice& cs, int len_bits, bool sgnd);
+td::RefInt256 load_coins(CellSlice& cs);
+CellSlice load_msg_addr(CellSlice& cs);
+std::pair parse_std_addr(CellSlice cs);
+
+// store_... functions throw on error if not quiet, return false if quiet (leaving cb unchanged)
+bool store_var_integer(CellBuilder& cb, const td::RefInt256& x, int len_bits, bool sgnd, bool quiet = false);
+bool store_coins(CellBuilder& cb, const td::RefInt256& x, bool quiet = false);
+
+block::GasLimitsPrices get_gas_prices(const td::Ref& unpacked_config, bool is_masterchain);
+block::MsgPrices get_msg_prices(const td::Ref& unpacked_config, bool is_masterchain);
+td::optional get_storage_prices(const td::Ref& unpacked_config);
+td::RefInt256 calculate_storage_fee(const td::optional& maybe_prices, bool is_masterchain,
+ td::uint64 delta, td::uint64 bits, td::uint64 cells);
+
+} // namespace util
+
} // namespace vm
diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md
index 7e3b4ce92..6c176552f 100644
--- a/doc/GlobalVersions.md
+++ b/doc/GlobalVersions.md
@@ -58,7 +58,7 @@ See [this post](https://t.me/tonstatus/88) for details.
## Version 6
### c7 tuple
-**c7** tuple extended from 14 to 16 elements:
+**c7** tuple extended from 14 to 17 elements:
* **14**: tuple that contains some config parameters as cell slices. If the parameter is absent from the config, the value is null. Asm opcode: `UNPACKEDCONFIGTUPLE`.
* **0**: `StoragePrices` from `ConfigParam 18`. Not the whole dict, but only the one StoragePrices entry (one which corresponds to the current time).
* **1**: `ConfigParam 19` (global id).
@@ -68,6 +68,7 @@ See [this post](https://t.me/tonstatus/88) for details.
* **5**: `ConfigParam 25` (fwd fees).
* **6**: `ConfigParam 43` (size limits).
* **15**: "[due payment](https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L237)" - current debt for storage fee (nanotons). Asm opcode: `DUEPAYMENT`.
+* **16**: "precompiled gas usage" - gas usage for the current contract if it is precompiled (see `ConfigParam 45`), `null` otherwise. Asm opcode: `GETPRECOMPILEDGAS`.
### New TVM instructions
@@ -75,7 +76,7 @@ See [this post](https://t.me/tonstatus/88) for details.
* `GETGASFEE` (`gas_used is_mc - price`) - calculates gas fee.
* `GETSTORAGEFEE` (`cells bits seconds is_mc - price`) - calculates storage fees (only current StoragePrices entry is used).
* `GETFORWARDFEE` (`cells bits is_mc - price`) - calculates forward fee.
-* `GETPRECOMPILEDGAS` (`- null`) - reserved, currently returns `null`.
+* `GETPRECOMPILEDGAS` (`- x`) - returns gas usage for the current contract if it is precompiled, `null` otherwise.
* `GETORIGINALFWDFEE` (`fwd_fee is_mc - orig_fwd_fee`) - calculate `fwd_fee * 2^16 / first_frac`. Can be used to get the original `fwd_fee` of the message.
* `GETGASFEESIMPLE` (`gas_used is_mc - price`) - same as `GETGASFEE`, but without flat price (just `(gas_used * price) / 2^16`).
* `GETFORWARDFEESIMPLE` (`cells bits is_mc - price`) - same as `GETFORWARDFEE`, but without lump price (just `(bits*bit_price + cells*cell_price) / 2^16`).
diff --git a/recent_changelog.md b/recent_changelog.md
index e4ed12fc5..e741a3289 100644
--- a/recent_changelog.md
+++ b/recent_changelog.md
@@ -1,17 +1,6 @@
-## 2024.02 Update
+## 2024.03 Update
-1. Improvement of validator synchronisation:
- * Better handling of block broadcasts -> faster sync
- * Additional separate overlay among validators as second option for synchronisation
-2. Improvements in LS:
- * c7 and library context is fully filled up for server-side rungetmethod
- * Cache for runmethods and successfull external messages
- * Logging of LS requests statistic
-3. Precise control of open files:
- * almost instantaneous validator start
- * `--max-archive-fd` option
- * autoremoval of not used temp archive files
- * `--archive-preload-period` option
-4. Preparatory (not enabled yet) code for addition on new TVM instructions for cheaper fee calculation onchain.
+1. Preparatory (not enabled yet) code for pre-compiled smart-contract.
+2. Minor fixes for fee-related opcodes.
diff --git a/test/regression-tests.ans b/test/regression-tests.ans
index ec013bf70..96117ee9e 100644
--- a/test/regression-tests.ans
+++ b/test/regression-tests.ans
@@ -1,5 +1,5 @@
abce
-Test_Bigint_main_default 0327a04f1252c37f77b6706b902ab2c3235c47738bca3f183c837a2c5d22bb6f
+Test_Bigint_main_default 76f38492ec19464a1d0eac51d389023a31ce10396b3894061361d159567ce8cd
Test_Bitstrings_main_default a8b08af3116923c4c2a14e138d168375abd0c059f2f780d3267b294929a1110e
Test_Cells_simple_default 832502642fe4fe5db70de82681aedb7d54d7f3530e0069861fff405fe6f6cf23
Test_Fift_bug_div_default 1ac42861ce96b2896001c587f65e9afe1617db48859f19c2f4e3063a20ea60b0
diff --git a/validator-engine/validator-engine.cpp b/validator-engine/validator-engine.cpp
index b8d428f3a..96103cd0b 100644
--- a/validator-engine/validator-engine.cpp
+++ b/validator-engine/validator-engine.cpp
@@ -72,6 +72,7 @@
#include "block-auto.h"
#include "block-parse.h"
#include "common/delay.h"
+#include "block/precompiled-smc/PrecompiledSmartContract.h"
Config::Config() {
out_port = 3278;
@@ -3812,6 +3813,9 @@ int main(int argc, char *argv[]) {
acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_archive_preload_period, v); });
return td::Status::OK();
});
+ p.add_option('\0', "enable-precompiled-smc",
+ "enable exectuion of precompiled contracts (experimental, disabled by default)",
+ []() { block::precompiled::set_precompiled_execution_enabled(true); });
auto S = p.run(argc, argv);
if (S.is_error()) {
LOG(ERROR) << "failed to parse options: " << S.move_as_error();
diff --git a/validator/impl/validate-query.cpp b/validator/impl/validate-query.cpp
index 44b49226b..d925fce63 100644
--- a/validator/impl/validate-query.cpp
+++ b/validator/impl/validate-query.cpp
@@ -965,6 +965,7 @@ bool ValidateQuery::fetch_config_params() {
}
compute_phase_cfg_.suspended_addresses = config_->get_suspended_addresses(now_);
compute_phase_cfg_.size_limits = size_limits;
+ compute_phase_cfg_.precompiled_contracts = config_->get_precompiled_contracts_config();
}
{
// compute action_phase_cfg
| | | | | | | | | | | | | | | | | |