1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-13 03:32:22 +00:00

Merge branch 'testnet' into block-generation

# Conflicts:
#	crypto/block/mc-config.cpp
#	crypto/block/mc-config.h
This commit is contained in:
SpyCheese 2024-03-11 18:08:08 +03:00
commit e216651112
41 changed files with 1233 additions and 192 deletions

View file

@ -1,3 +1,24 @@
## 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:
* 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.
## 2024.01 Update
1. Fixes in how gas in transactions on special accounts is accounted in block limit. Previously, gas was counted as usual, so to conduct elections that costs >30m gas block limit in masterchain was set to 37m gas. To lower the limit for safety reasons it is proposed to caunt gas on special accounts separately. Besides `gas_max` is set to `special_gas_limit` for all types of transactions on special accounts. New behavior is activated through setting `version >= 5` in `ConfigParam 8;`.

View file

@ -10,25 +10,33 @@
</div>
##
[![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
<p align="center">
<a href="https://tonresear.ch">
<img src="https://img.shields.io/badge/TON%20Research-0098EA?style=flat&logo=discourse&label=Forum&labelColor=gray" alt="Ton Research">
</a>
<a href="https://t.me/toncoin">
<img src="https://img.shields.io/badge/TON%20Community-0098EA?logo=telegram&logoColor=white&style=flat" alt="Telegram Community Group">
</a>
<a href="https://t.me/tonblockchain">
<img src="https://img.shields.io/badge/TON%20Foundation-0098EA?logo=telegram&logoColor=white&style=flat" alt="Telegram Foundation Group">
</a>
<a href="https://t.me/tondev_eng">
<img src="https://img.shields.io/badge/chat-TONDev-0098EA?logo=telegram&logoColor=white&style=flat" alt="Telegram Community Chat">
</a>
</p>
<p align="center">
<a href="https://twitter.com/ton_blockchain">
<img src="https://img.shields.io/twitter/follow/ton_blockchain" alt="Twitter Group">
</a>
<a href="https://answers.ton.org">
<img src="https://img.shields.io/badge/-TON%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white" alt="TON Overflow Group">
</a>
<a href="https://stackoverflow.com/questions/tagged/ton">
<img src="https://img.shields.io/badge/-Stack%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white" alt="Stack Overflow Group">
</a>
</p>
@ -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.
Tests are executed by running `ctest` in the build directory. See `doc/Tests.md` for more information.

View file

@ -24,12 +24,14 @@ if [ "$with_tests" = true ]; then
else
nix-build linux-arm64-static.nix
fi
mkdir artifacts
mkdir -p artifacts/lib
cp ./result/bin/* artifacts/
test $? -eq 0 || { echo "No artifacts have been built..."; exit 1; }
chmod +x artifacts/*
rm -rf result
nix-build linux-arm64-tonlib.nix
cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so
cp ./result/lib/libemulator.so artifacts/
cp -r crypto/fift/lib artifacts/
cp -r crypto/smartcont artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/

View file

@ -25,12 +25,13 @@ else
nix-build linux-x86-64-static.nix
fi
mkdir artifacts
mkdir -p artifacts/lib
cp ./result/bin/* artifacts/
test $? -eq 0 || { echo "No artifacts have been built..."; exit 1; }
chmod +x artifacts/*
rm -rf result
nix-build linux-x86-64-tonlib.nix
cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so
cp ./result/lib/libemulator.so artifacts/
cp -r crypto/fift/lib artifacts/
cp -r crypto/smartcont artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/

View file

@ -22,12 +22,14 @@ if [ "$with_tests" = true ]; then
else
nix-build macos-static.nix
fi
mkdir artifacts
mkdir -p artifacts/lib
cp ./result-bin/bin/* artifacts/
test $? -eq 0 || { echo "No artifacts have been built..."; exit 1; }
chmod +x artifacts/*
rm -rf result-bin
nix-build macos-tonlib.nix
cp ./result/lib/libtonlibjson.dylib artifacts/
cp ./result/lib/libemulator.dylib artifacts/
cp -r crypto/fift/lib artifacts/
cp -r crypto/smartcont artifacts/
cp ./result/lib/fift/* artifacts/lib/
cp -r ./result/share/ton/smartcont artifacts/

View file

@ -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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/block> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)

View file

@ -797,6 +797,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

View file

@ -2032,6 +2032,17 @@ td::Ref<vm::Tuple> Config::get_unpacked_config_tuple(ton::UnixTime now) const {
return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
}
PrecompiledContractsConfig Config::get_precompiled_contracts_config() const {
PrecompiledContractsConfig c;
td::Ref<vm::Cell> 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<std::pair<ton::UnixTime, ton::UnixTime>> Config::unpack_validator_set_start_stop(Ref<vm::Cell> vset_root) {
if (vset_root.is_null()) {
return td::Status::Error("validator set absent");
@ -2316,6 +2327,22 @@ td::Result<Ref<vm::Tuple>> ConfigInfo::get_prev_blocks_info() const {
block_id_to_tuple(last_key_block));
}
td::optional<PrecompiledContractsConfig::Contract> 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;
}
CollatorConfig Config::get_collator_config(bool need_collator_nodes) const {
CollatorConfig collator_config;
gen::CollatorConfig::Record rec;

View file

@ -526,6 +526,15 @@ struct BurningConfig {
}
};
struct PrecompiledContractsConfig {
struct Contract {
td::uint64 gas_usage;
};
vm::Dictionary list{256};
td::optional<Contract> get_contract(td::Bits256 code_hash) const;
};
struct CollatorNodeDescr {
ton::ShardIdFull shard;
ton::NodeIdShort adnl_id;
@ -655,6 +664,7 @@ class Config {
std::unique_ptr<vm::Dictionary> get_suspended_addresses(ton::UnixTime now) const;
BurningConfig get_burning_config() const;
td::Ref<vm::Tuple> get_unpacked_config_tuple(ton::UnixTime now) const;
PrecompiledContractsConfig get_precompiled_contracts_config() const;
static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time,

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "common.h"
#include <memory>
#include "vm/memo.h"
namespace block::precompiled {
using namespace vm;
Result PrecompiledSmartContract::run(td::Ref<vm::CellSlice> my_address, ton::UnixTime now, ton::LogicalTime cur_lt,
CurrencyCollection balance, td::Ref<vm::Cell> c4, vm::CellSlice msg_body,
td::Ref<vm::Cell> msg, CurrencyCollection msg_balance, bool is_external,
std::vector<td::Ref<vm::Cell>> libraries, int global_version,
td::uint16 max_data_depth, td::Ref<vm::Cell> my_code,
td::Ref<vm::Tuple> 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<Cell> &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<block::StoragePrices> 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<PrecompiledSmartContract> get_implementation(td::Bits256 code_hash) {
if (!precompiled_execution_enabled) {
return nullptr;
}
static std::map<td::Bits256, std::unique_ptr<PrecompiledSmartContract> (*)()> map = []() {
auto from_hex = [](td::Slice s) -> td::Bits256 {
td::Bits256 x;
CHECK(x.from_hex(s) == 256);
return x;
};
std::map<td::Bits256, std::unique_ptr<PrecompiledSmartContract> (*)()> map;
#define CONTRACT(hash, cls) \
map[from_hex(hash)] = []() -> std::unique_ptr<PrecompiledSmartContract> { return std::make_unique<cls>(); };
// 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

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#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 <ostream>
#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<long long> 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<vm::CellSlice> my_address, ton::UnixTime now, ton::LogicalTime cur_lt, CurrencyCollection balance,
td::Ref<vm::Cell> c4, vm::CellSlice msg_body, td::Ref<vm::Cell> msg, CurrencyCollection msg_balance,
bool is_external, std::vector<td::Ref<vm::Cell>> libraries, int global_version, td::uint16 max_data_depth,
td::Ref<vm::Cell> my_code, td::Ref<vm::Tuple> unpacked_config, td::RefInt256 due_payment, td::uint64 precompiled_gas_usage);
td::Ref<vm::Cell> get_c4() const {
return c4_;
}
td::Ref<vm::Cell> get_c5() const {
return c5_;
}
protected:
td::Ref<vm::CellSlice> my_address_;
ton::UnixTime now_;
ton::LogicalTime cur_lt_;
CurrencyCollection balance_;
vm::CellSlice in_msg_body_;
td::Ref<vm::Cell> in_msg_;
CurrencyCollection in_msg_balance_;
bool is_external_;
td::Ref<vm::Cell> my_code_;
td::Ref<vm::Tuple> unpacked_config_;
td::RefInt256 due_payment_;
td::uint64 precompiled_gas_usage_;
td::Ref<vm::Cell> c4_;
td::Ref<vm::Cell> c5_ = vm::CellBuilder().finalize_novm();
void send_raw_message(const td::Ref<vm::Cell>& 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<PrecompiledSmartContract> get_implementation(td::Bits256 code_hash);
void set_precompiled_execution_enabled(bool value); // disabled by default
} // namespace block::precompiled

View file

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "PrecompiledSmartContract.h"
#include "vm/arithops.h"
#include "vm/cellops.h"
#include "vm/tonops.h"

View file

@ -1341,6 +1341,9 @@ Ref<vm::Tuple> 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::vector<vm::StackEntry>>(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<td::int32>(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<PrecompiledContractsConfig::Contract> 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<vm::Stack> 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<StringLoggerTail> 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

View file

@ -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<vm::Cell> new_data;
Ref<vm::Cell> actions;
std::string vm_log;
td::optional<td::uint64> precompiled_gas_usage;
};
struct ActionPhase {
@ -372,6 +376,7 @@ struct Transaction {
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp);
std::vector<Ref<vm::Cell>> 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);

View file

@ -169,8 +169,6 @@ class PropagateConstSpan {
size_t size_{0};
};
struct Normalize {};
template <class Tr = BigIntInfo>
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<len * 2, Tr> 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 <int n = 257>
BigIntG<n, BigIntInfo> make_bigint(long long x) {
return BigIntG<n, BigIntInfo>{Normalize(), x};
return BigIntG<n, BigIntInfo>{x};
}
namespace literals {

View file

@ -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;

View file

@ -113,8 +113,6 @@ RefInt256 make_refint(Args&&... args) {
return td::RefInt256{true, std::forward<Args>(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);

View file

@ -174,6 +174,7 @@ td::Ref<vm::Tuple> prepare_vm_c7(SmartContract::Args args, td::Ref<vm::Cell> 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::vector<vm::StackEntry>>(std::move(tuple));
//LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();

View file

@ -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

View file

@ -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

View file

@ -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<Cell>& 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<Cell>& 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<Cell> load_ref(CellSlice& cs) {
td::Ref<Cell> x;
load_ref_q(cs, x, false);
return x;
}
td::Ref<Cell> load_maybe_ref(CellSlice& cs) {
td::Ref<Cell> 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<td::uint64>::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<Cell> 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<Cell> 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

View file

@ -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<Cell>& res, bool quiet);
bool load_maybe_ref_q(CellSlice& cs, td::Ref<Cell>& 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<Cell> load_ref(CellSlice& cs);
td::Ref<Cell> 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<Cell> x, bool quiet = false);
bool store_maybe_ref(CellBuilder& cb, td::Ref<Cell> x, bool quiet = false);
bool store_slice(CellBuilder& cb, const CellSlice& cs, bool quiet = false);
} // namespace util
} // namespace vm

View file

@ -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);

View file

@ -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<Cell> DummyVmState::load_library(td::ConstBitPtr hash) {
std::unique_ptr<VmStateInterface> 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

View file

@ -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<Ref<Cell>> libraries, int global_version = ton::SUPPORTED_VERSION)
: libraries(std::move(libraries)), global_version(global_version) {
}
Ref<Cell> load_library(td::ConstBitPtr hash) override;
int get_global_version() const override {
return global_version;
}
td::optional<td::Bits256> get_missing_library() const {
return missing_library;
}
private:
std::vector<Ref<Cell>> libraries;
int global_version;
td::optional<td::Bits256> missing_library;
};
} // namespace vm

View file

@ -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<CellSlice> get_unpacked_config_param(VmState* st, unsigned idx) {
static td::Ref<Tuple> 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<CellSlice> 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<CellSlice> cs = get_unpacked_config_param(st, 1);
Ref<CellSlice> 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<CellSlice> 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<CellSlice> 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<td::int64>::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<td::int64>::max(), 0);
td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
Ref<CellSlice> 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<block::StoragePrices> 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<td::int64>::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::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<td::int64>::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<td::int64>::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::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<CellSlice> 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<CellSlice> 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<Cell> 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<StackEntry> 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<ton::WorkchainId, ton::StdSmcAddress> parse_std_addr(CellSlice cs) {
std::pair<ton::WorkchainId, ton::StdSmcAddress> 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<Tuple>& unpacked_config, bool is_masterchain) {
Ref<CellSlice> 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<Tuple>& unpacked_config, bool is_masterchain) {
Ref<CellSlice> 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<block::StoragePrices> get_storage_prices(const Ref<Tuple>& unpacked_config) {
Ref<CellSlice> 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<block::StoragePrices>& 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

View file

@ -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<ton::WorkchainId, ton::StdSmcAddress> 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<Tuple>& unpacked_config, bool is_masterchain);
block::MsgPrices get_msg_prices(const td::Ref<Tuple>& unpacked_config, bool is_masterchain);
td::optional<block::StoragePrices> get_storage_prices(const td::Ref<Tuple>& unpacked_config);
td::RefInt256 calculate_storage_fee(const td::optional<block::StoragePrices>& maybe_prices, bool is_masterchain,
td::uint64 delta, td::uint64 bits, td::uint64 cells);
} // namespace util
} // namespace vm

View file

@ -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`).

View file

@ -1408,7 +1408,7 @@ bool TestNode::after_parse_run_method(ton::WorkchainId workchain, ton::StdSmcAdd
}
}
});
return start_run_method(workchain, addr, ref_blkid, method_name, std::move(params), ext_mode ? 0x1f : 0,
return start_run_method(workchain, addr, ref_blkid, method_name, std::move(params), ext_mode ? 0x17 : 0,
std::move(P));
}
@ -1582,7 +1582,7 @@ bool TestNode::send_past_vset_query(ton::StdSmcAddress elector_addr) {
}
register_past_vset_info(std::move(S.back()));
});
return start_run_method(ton::masterchainId, elector_addr, mc_last_id_, "past_elections_list", std::move(params), 0x1f,
return start_run_method(ton::masterchainId, elector_addr, mc_last_id_, "past_elections_list", std::move(params), 0x17,
std::move(P));
}
@ -1643,7 +1643,7 @@ void TestNode::send_get_complaints_query(unsigned elect_id, ton::StdSmcAddress e
LOG(ERROR) << "vm virtualization error: " << err.get_msg();
}
});
start_run_method(ton::masterchainId, elector_addr, mc_last_id_, "get_past_complaints", std::move(params), 0x1f,
start_run_method(ton::masterchainId, elector_addr, mc_last_id_, "get_past_complaints", std::move(params), 0x17,
std::move(P));
}
@ -1740,7 +1740,7 @@ void TestNode::send_compute_complaint_price_query(ton::StdSmcAddress elector_add
LOG(ERROR) << "vm virtualization error: " << err.get_msg();
}
});
start_run_method(ton::masterchainId, elector_addr, mc_last_id_, "complaint_storage_price", std::move(params), 0x1f,
start_run_method(ton::masterchainId, elector_addr, mc_last_id_, "complaint_storage_price", std::move(params), 0x17,
std::move(P));
}
@ -1837,7 +1837,7 @@ bool TestNode::dns_resolve_send(ton::WorkchainId workchain, ton::StdSmcAddress a
}
return dns_resolve_finish(workchain, addr, blkid, domain, qdomain, cat, mode, (int)x->to_long(), std::move(cell));
});
return start_run_method(workchain, addr, blkid, "dnsresolve", std::move(params), 0x1f, std::move(P));
return start_run_method(workchain, addr, blkid, "dnsresolve", std::move(params), 0x17, std::move(P));
}
bool TestNode::show_dns_record(std::ostream& os, td::Bits256 cat, Ref<vm::CellSlice> value, bool raw_dump) {
@ -2269,21 +2269,29 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
}
}
if (exit_code != 0) {
LOG(ERROR) << "VM terminated with error code " << exit_code;
out << "result: error " << exit_code << std::endl;
promise.set_error(td::Status::Error(PSLICE() << "VM terminated with non-zero exit code " << exit_code));
return;
}
stack = vm.get_stack_ref();
{
} else {
stack = vm.get_stack_ref();
std::ostringstream os;
os << "result: ";
stack->dump(os, 3);
out << os.str();
}
if (mode & 4) {
if (remote_result.empty()) {
out << "remote result: <none>, exit code " << remote_exit_code;
if (!(mode & 4)) {
if (exit_code != 0) {
LOG(ERROR) << "VM terminated with error code " << exit_code;
promise.set_error(td::Status::Error(PSLICE() << "VM terminated with non-zero exit code " << exit_code));
} else {
promise.set_result(stack->extract_contents());
}
} else {
if (remote_exit_code != 0) {
out << "remote result: error " << remote_exit_code << std::endl;
LOG(ERROR) << "VM terminated with error code " << exit_code;
promise.set_error(td::Status::Error(PSLICE() << "VM terminated with non-zero exit code " << exit_code));
} else if (remote_result.empty()) {
out << "remote result: <none>" << std::endl;
promise.set_value({});
} else {
auto res = vm::std_boc_deserialize(std::move(remote_result));
if (res.is_error()) {
@ -2304,10 +2312,10 @@ void TestNode::run_smc_method(int mode, ton::BlockIdExt ref_blk, ton::BlockIdExt
os << "remote result (not to be trusted): ";
remote_stack->dump(os, 3);
out << os.str();
promise.set_value(remote_stack->extract_contents());
}
}
out.flush();
promise.set_result(stack->extract_contents());
} catch (vm::VmVirtError& err) {
out << "virtualization error while parsing runSmcMethod result: " << err.get_msg();
promise.set_error(

View file

@ -1,17 +1,6 @@
## 2024.01 Update
## 2024.03 Update
1. Preparatory (not enabled yet) code for pre-compiled smart-contract.
2. Minor fixes for fee-related opcodes.
1. Fixes in how gas in transactions on special accounts is accounted in block limit. Previously, gas was counted as usual, so to conduct elections that costs >30m gas block limit in masterchain was set to 37m gas. To lower the limit for safety reasons it is proposed to caunt gas on special accounts separately. Besides `gas_max` is set to `special_gas_limit` for all types of transactions on special accounts. New behavior is activated through setting `version >= 5` in `ConfigParam 8;`.
* Besides update of config temporally increases gas limit on `EQD_v9j1rlsuHHw2FIhcsCFFSD367ldfDdCKcsNmNpIRzUlu` to `special_gas_limit`, see [details](https://t.me/tonstatus/88).
2. Improvements in LS behavior
* Improved detection of the state with all shards applied to decrease rate of `Block is not applied` error
* Better error logs: `block not in db` and `block is not applied` separation
* Fix error in proof generation for blocks after merge
* Fix most of `block is not applied` issues related to sending too recent block in Proofs
* LS now check external messages till `accept_message` (`set_gas`).
3. Improvements in DHT work and storage, CellDb, config.json ammendment, peer misbehavior detection, validator session stats collection, emulator.
4. Change in CTOS and XLOAD behavior activated through setting `version >= 5` in `ConfigParam 8;`:
* Loading "nested libraries" (i.e. a library cell that points to another library cell) throws an exception.
* Loading a library consumes gas for cell load only once (for the library cell), not twice (both for the library cell and the cell in the library).
* `XLOAD` now works differently. When it takes a library cell, it returns the cell that it points to. This allows loading "nested libraries", if needed.
Besides the work of the Core team, this update is based on the efforts of @XaBbl4 (peer misbehavior detection) and @akifoq (CTOS behavior and gas limit scheme for special accounts).

View file

@ -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

View file

@ -1216,7 +1216,7 @@ class RemoteRunSmcMethod : public td::actor::Actor {
client_.send_query(
//liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:long params:bytes = liteServer.RunMethodResult;
ton::lite_api::liteServer_runSmcMethod(
0x1f, ton::create_tl_lite_block_id(query_.block_id.value()),
0x17, ton::create_tl_lite_block_id(query_.block_id.value()),
ton::create_tl_object<ton::lite_api::liteServer_accountId>(query_.address.workchain, query_.address.addr),
method_id, std::move(serialized_stack)),
[self = this](auto r_state) { self->with_run_method_result(std::move(r_state)); },

View file

@ -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;
@ -4123,6 +4124,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();

View file

@ -136,7 +136,8 @@ void FullNodeShardImpl::check_broadcast(PublicKeyHash src, td::BufferSlice broad
}
auto q = B.move_as_ok();
if (!processed_ext_msg_broadcasts_.insert(td::sha256_bits256(q->message_->data_)).second) {
auto hash = td::sha256_bits256(q->message_->data_);
if (!processed_ext_msg_broadcasts_.insert(hash).second) {
return promise.set_error(td::Status::Error("duplicate external message broadcast"));
}
if (config_.ext_messages_broadcast_disabled_) {
@ -147,6 +148,11 @@ void FullNodeShardImpl::check_broadcast(PublicKeyHash src, td::BufferSlice broad
}
};
}
if (my_ext_msg_broadcasts_.count(hash)) {
// Don't re-check messages that were sent by us
promise.set_result(td::Unit());
return;
}
td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::check_external_message,
std::move(q->message_->data_),
promise.wrap([](td::Ref<ExtMessage>) { return td::Unit(); }));
@ -821,9 +827,11 @@ void FullNodeShardImpl::send_external_message(td::BufferSlice data) {
});
return;
}
if (!processed_ext_msg_broadcasts_.insert(td::sha256_bits256(data)).second) {
td::Bits256 hash = td::sha256_bits256(data);
if (processed_ext_msg_broadcasts_.count(hash)) {
return;
}
my_ext_msg_broadcasts_.insert(hash);
auto B = create_serialize_tl_object<ton_api::tonNode_externalMessageBroadcast>(
create_tl_object<ton_api::tonNode_externalMessage>(std::move(data)));
if (B.size() <= overlay::Overlays::max_simple_broadcast_size()) {
@ -1016,6 +1024,7 @@ void FullNodeShardImpl::alarm() {
}
if (cleanup_processed_ext_msg_at_ && cleanup_processed_ext_msg_at_.is_in_past()) {
processed_ext_msg_broadcasts_.clear();
my_ext_msg_broadcasts_.clear();
cleanup_processed_ext_msg_at_ = td::Timestamp::in(60.0);
}
alarm_timestamp().relax(sync_completed_at_);

View file

@ -280,6 +280,7 @@ class FullNodeShardImpl : public FullNodeShard {
FullNodeConfig config_;
std::set<td::Bits256> my_ext_msg_broadcasts_;
std::set<td::Bits256> processed_ext_msg_broadcasts_;
td::Timestamp cleanup_processed_ext_msg_at_;
};

View file

@ -157,7 +157,7 @@ void LiteQuery::start_up() {
});
return;
}
use_cache_ = !cache_.empty() && query_obj_->get_id() == lite_api::liteServer_runSmcMethod::ID;
use_cache_ = use_cache();
if (use_cache_) {
cache_key_ = td::sha256_bits256(query_);
td::actor::send_closure(
@ -173,6 +173,22 @@ void LiteQuery::start_up() {
}
}
bool LiteQuery::use_cache() {
if (cache_.empty()) {
return false;
}
bool use = false;
lite_api::downcast_call(
*query_obj_,
td::overloaded(
[&](lite_api::liteServer_runSmcMethod& q) {
// wc=-1, seqno=-1 means "use latest mc block"
use = q.id_->workchain_ != masterchainId || q.id_->seqno_ != -1;
},
[&](auto& obj) { use = false; }));
return use;
}
void LiteQuery::perform() {
td::actor::send_closure(manager_, &ValidatorManager::add_lite_query_stats, query_obj_->get_id());
lite_api::downcast_call(
@ -848,7 +864,7 @@ void LiteQuery::perform_runSmcMethod(BlockIdExt blkid, WorkchainId workchain, St
fatal_error("more than 64k parameter bytes passed");
return;
}
if (mode & ~0x1f) {
if (mode & ~0x3f) {
fatal_error("unsupported mode in runSmcMethod");
return;
}
@ -1245,8 +1261,24 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
return;
}
if (mode_ & 0x10000) {
finish_runSmcMethod(std::move(shard_proof), proof.move_as_ok(), std::move(acc_root), sstate.gen_utime,
sstate.gen_lt);
if (!mc_state_.is_null()) {
finish_runSmcMethod(std::move(shard_proof), proof.move_as_ok(), std::move(acc_root), sstate.gen_utime,
sstate.gen_lt);
return;
}
shard_proof_ = std::move(shard_proof);
proof_ = proof.move_as_ok();
set_continuation(
[&, base_blk_id = base_blk_id_, acc_root, utime = sstate.gen_utime, lt = sstate.gen_lt]() mutable -> void {
base_blk_id_ = base_blk_id; // It gets overridden by request_mc_block_data_state
finish_runSmcMethod(std::move(shard_proof_), std::move(proof_), std::move(acc_root), utime, lt);
});
td::optional<BlockIdExt> master_ref = state_->get_master_ref();
if (!master_ref) {
fatal_error("masterchain ref block is not available");
return;
}
request_mc_block_data_state(master_ref.value());
return;
}
td::BufferSlice data;
@ -1283,25 +1315,45 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
// same as in lite-client/lite-client-common.cpp
static td::Ref<vm::Tuple> prepare_vm_c7(ton::UnixTime now, ton::LogicalTime lt, td::Ref<vm::CellSlice> my_addr,
const block::CurrencyCollection& balance) {
const block::CurrencyCollection& balance,
const block::ConfigInfo* config = nullptr, td::Ref<vm::Cell> my_code = {},
td::RefInt256 due_payment = td::zero_refint()) {
td::BitArray<256> rand_seed;
td::RefInt256 rand_seed_int{true};
td::Random::secure_bytes(rand_seed.as_slice());
if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) {
return {};
}
auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::make_refint(now), // unixtime:Integer
td::make_refint(lt), // block_lt:Integer
td::make_refint(lt), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
my_addr, // myself:MsgAddressInt
vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo;
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
return vm::make_tuple_ref(std::move(tuple));
std::vector<vm::StackEntry> tuple = {
td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // actions:Integer
td::make_refint(0), // msgs_sent:Integer
td::make_refint(now), // unixtime:Integer
td::make_refint(lt), // block_lt:Integer
td::make_refint(lt), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
my_addr, // myself:MsgAddressInt
config ? config->get_root_cell() : vm::StackEntry() // global_config:(Maybe Cell) ] = SmartContractInfo;
};
if (config && config->get_global_version() >= 4) {
tuple.push_back(my_code); // code:Cell
tuple.push_back(block::CurrencyCollection::zero().as_vm_tuple()); // in_msg_value:[Integer (Maybe Cell)]
tuple.push_back(td::zero_refint()); // storage_fees:Integer
// [ wc:Integer shard:Integer seqno:Integer root_hash:Integer file_hash:Integer] = BlockId;
// [ last_mc_blocks:[BlockId...]
// prev_key_block:BlockId ] : PrevBlocksInfo
auto info = config->get_prev_blocks_info();
tuple.push_back(info.is_ok() ? info.move_as_ok() : vm::StackEntry());
}
if (config && config->get_global_version() >= 6) {
tuple.push_back(config->get_unpacked_config_tuple(now)); // unpacked_config_tuple:[...]
tuple.push_back(due_payment); // due_payment:Integer
}
auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string();
return vm::make_tuple_ref(std::move(tuple_ref));
}
void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice state_proof, Ref<vm::Cell> acc_root,
@ -1320,12 +1372,14 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice
}
vm::MerkleProofBuilder pb{std::move(acc_root)};
block::gen::Account::Record_account acc;
block::gen::StorageInfo::Record storage_info;
block::gen::AccountStorage::Record store;
block::CurrencyCollection balance;
block::gen::StateInit::Record state_init;
if (!(tlb::unpack_cell(pb.root(), acc) && tlb::csr_unpack(std::move(acc.storage), store) &&
balance.validate_unpack(store.balance) && store.state->prefetch_ulong(1) == 1 &&
store.state.write().advance(1) && tlb::csr_unpack(std::move(store.state), state_init))) {
store.state.write().advance(1) && tlb::csr_unpack(std::move(store.state), state_init) &&
tlb::csr_unpack(std::move(acc.storage_stat), storage_info))) {
LOG(INFO) << "error unpacking account state, or account is frozen or uninitialized";
td::Result<td::BufferSlice> proof_boc;
if (mode & 2) {
@ -1346,12 +1400,35 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice
}
auto code = state_init.code->prefetch_ref();
auto data = state_init.data->prefetch_ref();
auto acc_libs = state_init.library->prefetch_ref();
long long gas_limit = client_method_gas_limit;
td::RefInt256 due_payment;
if (storage_info.due_payment.write().fetch_long(1)) {
due_payment = block::tlb::t_Grams.as_integer(storage_info.due_payment);
} else {
due_payment = td::zero_refint();
}
LOG(DEBUG) << "creating VM with gas limit " << gas_limit;
// **** INIT VM ****
auto r_config = block::ConfigInfo::extract_config(
mc_state_->root_cell(),
block::ConfigInfo::needLibraries | block::ConfigInfo::needCapabilities | block::ConfigInfo::needPrevBlocks);
if (r_config.is_error()) {
fatal_error(r_config.move_as_error());
return;
}
auto config = r_config.move_as_ok();
std::vector<td::Ref<vm::Cell>> libraries;
if (config->get_libraries_root().not_null()) {
libraries.push_back(config->get_libraries_root());
}
if (acc_libs.not_null()) {
libraries.push_back(acc_libs);
}
vm::GasLimits gas{gas_limit, gas_limit};
vm::VmState vm{std::move(code), std::move(stack_), gas, 1, std::move(data), vm::VmLog::Null()};
auto c7 = prepare_vm_c7(gen_utime, gen_lt, td::make_ref<vm::CellSlice>(acc.addr->clone()), balance);
vm::VmState vm{code, std::move(stack_), gas, 1, std::move(data), vm::VmLog::Null(), std::move(libraries)};
auto c7 = prepare_vm_c7(gen_utime, gen_lt, td::make_ref<vm::CellSlice>(acc.addr->clone()), balance, config.get(),
std::move(code), due_payment);
vm.set_c7(c7); // tuple with SmartContractInfo
// vm.incr_stack_trace(1); // enable stack dump after each step
LOG(INFO) << "starting VM to run GET-method of smart contract " << acc_workchain_ << ":" << acc_addr_.to_hex();
@ -1367,6 +1444,9 @@ void LiteQuery::finish_runSmcMethod(td::BufferSlice shard_proof, td::BufferSlice
td::BufferSlice c7_info, result;
if (mode & 8) {
// serialize c7
if (!(mode & 32)) {
c7 = prepare_vm_c7(gen_utime, gen_lt, td::make_ref<vm::CellSlice>(acc.addr->clone()), balance);
}
vm::CellBuilder cb;
if (!(vm::StackEntry{std::move(c7)}.serialize(cb) && cb.finalize_to(cell))) {
fatal_error("cannot serialize c7");

View file

@ -62,7 +62,7 @@ class LiteQuery : public td::actor::Actor {
td::BufferSlice buffer_;
std::function<void()> continuation_;
bool cont_set_{false};
td::BufferSlice shard_proof_;
td::BufferSlice shard_proof_, proof_;
std::vector<Ref<vm::Cell>> roots_;
std::vector<Ref<td::CntObject>> aux_objs_;
std::vector<ton::BlockIdExt> blk_ids_;
@ -98,6 +98,7 @@ class LiteQuery : public td::actor::Actor {
bool finish_query(td::BufferSlice result, bool skip_cache_update = false);
void alarm() override;
void start_up() override;
bool use_cache();
void perform();
void perform_getTime();
void perform_getVersion();

View file

@ -129,6 +129,15 @@ td::Status ShardStateQ::init() {
" contains BlockId " + hdr_id.to_str() +
" different from the one originally required");
}
if (info.r1.master_ref.write().fetch_long(1)) {
BlockIdExt mc_id;
if (!block::tlb::t_ExtBlkRef.unpack(info.r1.master_ref, mc_id, nullptr)) {
return td::Status::Error(-668, "cannot unpack master_ref in shardchain state of "s + blkid.to_str());
}
master_ref = mc_id;
} else {
master_ref = {};
}
return td::Status::OK();
}

View file

@ -41,6 +41,7 @@ class ShardStateQ : virtual public ShardState {
bool before_split_{false};
bool fake_split_{false};
bool fake_merge_{false};
td::optional<BlockIdExt> master_ref;
protected:
friend class Ref<ShardStateQ>;
@ -80,6 +81,9 @@ class ShardStateQ : virtual public ShardState {
LogicalTime get_logical_time() const override {
return lt;
}
td::optional<BlockIdExt> get_master_ref() const override {
return master_ref;
}
td::Status validate_deep() const override;
ShardStateQ* make_copy() const override;
td::Result<Ref<MessageQueue>> message_queue() const override;

View file

@ -972,6 +972,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

View file

@ -44,6 +44,7 @@ class ShardState : public td::CntObject {
virtual BlockIdExt get_block_id() const = 0;
virtual RootHash root_hash() const = 0;
virtual td::Ref<vm::Cell> root_cell() const = 0;
virtual td::optional<BlockIdExt> get_master_ref() const = 0;
virtual td::Status validate_deep() const = 0;