1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-02-13 11:42:18 +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 ## 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;`. 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> </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 <p align="center">
[telegram-community-badge]: https://img.shields.io/badge/TON%20Community-2CA5E0?logo=telegram&logoColor=white&style=flat <a href="https://tonresear.ch">
[telegram-tondev-badge]: https://img.shields.io/badge/chat-TONDev-2CA5E0?logo=telegram&logoColor=white&style=flat <img src="https://img.shields.io/badge/TON%20Research-0098EA?style=flat&logo=discourse&label=Forum&labelColor=gray" alt="Ton Research">
[telegram-foundation-url]: https://t.me/tonblockchain </a>
[telegram-community-url]: https://t.me/toncoin <a href="https://t.me/toncoin">
[telegram-tondev-url]: https://t.me/tondev_eng <img src="https://img.shields.io/badge/TON%20Community-0098EA?logo=telegram&logoColor=white&style=flat" alt="Telegram Community Group">
[twitter-badge]: https://img.shields.io/twitter/follow/ton_blockchain </a>
[twitter-url]: https://twitter.com/ton_blockchain <a href="https://t.me/tonblockchain">
[stack-overflow-badge]: https://img.shields.io/badge/-Stack%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white <img src="https://img.shields.io/badge/TON%20Foundation-0098EA?logo=telegram&logoColor=white&style=flat" alt="Telegram Foundation Group">
[stack-overflow-url]: https://stackoverflow.com/questions/tagged/ton </a>
[ton-overflow-badge]: https://img.shields.io/badge/-TON%20Overflow-FE7A16?style=flat&logo=stack-overflow&logoColor=white <a href="https://t.me/tondev_eng">
[ton-overflow-url]: https://answers.ton.org <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 ## 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 else
nix-build linux-arm64-static.nix nix-build linux-arm64-static.nix
fi fi
mkdir artifacts
mkdir -p artifacts/lib
cp ./result/bin/* artifacts/ cp ./result/bin/* artifacts/
test $? -eq 0 || { echo "No artifacts have been built..."; exit 1; }
chmod +x artifacts/* chmod +x artifacts/*
rm -rf result rm -rf result
nix-build linux-arm64-tonlib.nix nix-build linux-arm64-tonlib.nix
cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so cp ./result/lib/libtonlibjson.so.0.5 artifacts/libtonlibjson.so
cp ./result/lib/libemulator.so artifacts/ cp ./result/lib/libemulator.so artifacts/
cp -r crypto/fift/lib artifacts/ cp ./result/lib/fift/* artifacts/lib/
cp -r crypto/smartcont artifacts/ cp -r ./result/share/ton/smartcont artifacts/

View file

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

View file

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

View file

@ -213,6 +213,7 @@ set(BLOCK_SOURCE
block/mc-config.cpp block/mc-config.cpp
block/output-queue-merger.cpp block/output-queue-merger.cpp
block/transaction.cpp block/transaction.cpp
block/precompiled-smc/PrecompiledSmartContract.cpp
${TLB_BLOCK_AUTO} ${TLB_BLOCK_AUTO}
block/block-binlog.h block/block-binlog.h
@ -223,6 +224,8 @@ set(BLOCK_SOURCE
block/check-proof.h block/check-proof.h
block/output-queue-merger.h block/output-queue-merger.h
block/transaction.h block/transaction.h
block/precompiled-smc/PrecompiledSmartContract.h
block/precompiled-smc/common.h
) )
set(SMC_ENVELOPE_SOURCE 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}> 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}/..>) $<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) 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}) add_executable(func func/func-main.cpp ${FUNC_LIB_SOURCE})
target_include_directories(func PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>) 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; suspended_address_list#00 addresses:(HashmapE 288 Unit) suspended_until:uint32 = SuspendedAddressList;
_ SuspendedAddressList = ConfigParam 44; _ 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; 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 71; // Ethereum bridge
_ OracleBridgeParams = ConfigParam 72; // Binance Smart Chain 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)); 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) { td::Result<std::pair<ton::UnixTime, ton::UnixTime>> Config::unpack_validator_set_start_stop(Ref<vm::Cell> vset_root) {
if (vset_root.is_null()) { if (vset_root.is_null()) {
return td::Status::Error("validator set absent"); 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)); 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 Config::get_collator_config(bool need_collator_nodes) const {
CollatorConfig collator_config; CollatorConfig collator_config;
gen::CollatorConfig::Record rec; 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 { struct CollatorNodeDescr {
ton::ShardIdFull shard; ton::ShardIdFull shard;
ton::NodeIdShort adnl_id; ton::NodeIdShort adnl_id;
@ -655,6 +664,7 @@ class Config {
std::unique_ptr<vm::Dictionary> get_suspended_addresses(ton::UnixTime now) const; std::unique_ptr<vm::Dictionary> get_suspended_addresses(ton::UnixTime now) const;
BurningConfig get_burning_config() const; BurningConfig get_burning_config() const;
td::Ref<vm::Tuple> get_unpacked_config_tuple(ton::UnixTime now) 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, static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard, ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time, 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) tuple.push_back(cfg.unpacked_config_tuple.not_null() ? vm::StackEntry(cfg.unpacked_config_tuple)
: vm::StackEntry()); // 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(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)); 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(); 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()); 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. * 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; cp.skip_reason = ComputePhase::sk_bad_state;
return true; 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 // initialize VM
Ref<vm::Stack> stack = prepare_vm_stack(cp); Ref<vm::Stack> stack = prepare_vm_stack(cp);
if (stack.is_null()) { if (stack.is_null()) {
@ -1536,7 +1640,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
} }
// OstreamLogger ostream_logger(error_stream); // OstreamLogger ostream_logger(error_stream);
// auto log = create_vm_log(error_stream ? &ostream_logger : nullptr); // 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"; LOG(DEBUG) << "creating VM";
std::unique_ptr<StringLoggerTail> logger; std::unique_ptr<StringLoggerTail> logger;
@ -1585,6 +1688,15 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
was_activated = true; was_activated = true;
acc_status = Account::acc_active; 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 LOG(INFO) << "steps: " << vm.get_steps_count() << " gas: used=" << gas.gas_consumed() << ", max=" << gas.gas_max
<< ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit; << ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit;
LOG(INFO) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success 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->suspended_addresses = config.get_suspended_addresses(now);
compute_phase_cfg->size_limits = size_limits; compute_phase_cfg->size_limits = size_limits;
compute_phase_cfg->precompiled_contracts = config.get_precompiled_contracts_config();
} }
{ {
// compute action_phase_cfg // compute action_phase_cfg

View file

@ -29,6 +29,7 @@
#include "ton/ton-types.h" #include "ton/ton-types.h"
#include "block/block.h" #include "block/block.h"
#include "block/mc-config.h" #include "block/mc-config.h"
#include "precompiled-smc/PrecompiledSmartContract.h"
namespace block { namespace block {
using td::Ref; using td::Ref;
@ -122,6 +123,8 @@ struct ComputePhaseConfig {
SizeLimitsConfig size_limits; SizeLimitsConfig size_limits;
int vm_log_verbosity = 0; int vm_log_verbosity = 0;
bool stop_on_accept_message = false; 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) { ComputePhaseConfig() : gas_price(0), gas_limit(0), special_gas_limit(0), gas_credit(0) {
compute_threshold(); compute_threshold();
@ -189,6 +192,7 @@ struct ComputePhase {
Ref<vm::Cell> new_data; Ref<vm::Cell> new_data;
Ref<vm::Cell> actions; Ref<vm::Cell> actions;
std::string vm_log; std::string vm_log;
td::optional<td::uint64> precompiled_gas_usage;
}; };
struct ActionPhase { struct ActionPhase {
@ -372,6 +376,7 @@ struct Transaction {
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg); bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp); Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp);
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg); 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_compute_phase(const ComputePhaseConfig& cfg);
bool prepare_action_phase(const ActionPhaseConfig& cfg); bool prepare_action_phase(const ActionPhaseConfig& cfg);
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true); 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}; size_t size_{0};
}; };
struct Normalize {};
template <class Tr = BigIntInfo> template <class Tr = BigIntInfo>
class AnyIntView { class AnyIntView {
public: public:
@ -290,6 +288,7 @@ class BigIntG {
public: public:
enum { word_bits = Tr::word_bits, word_shift = Tr::word_shift, max_bits = len, word_cnt = len / word_shift + 1 }; 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::word_t word_t;
typedef typename Tr::uword_t uword_t;
typedef Tr Traits; typedef Tr Traits;
typedef BigIntG<len * 2, Tr> DoubleInt; typedef BigIntG<len * 2, Tr> DoubleInt;
@ -312,9 +311,6 @@ class BigIntG {
BigIntG() : n(0) { BigIntG() : n(0) {
} }
explicit BigIntG(word_t x) : n(1) { explicit BigIntG(word_t x) : n(1) {
digits[0] = x;
}
BigIntG(Normalize, word_t x) : n(1) {
if (x >= -Tr::Half && x < Tr::Half) { if (x >= -Tr::Half && x < Tr::Half) {
digits[0] = x; digits[0] = x;
} else if (len <= 1) { } else if (len <= 1) {
@ -325,6 +321,25 @@ class BigIntG {
digits[n++] = (x >> Tr::word_shift) + (digits[0] < 0); 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) { BigIntG(const BigIntG& x) : n(x.n) {
std::memcpy(digits, x.digits, n * sizeof(word_t)); std::memcpy(digits, x.digits, n * sizeof(word_t));
///std::cout << "(BiCC " << (const void*)&x << "->" << (void*)this << ")"; ///std::cout << "(BiCC " << (const void*)&x << "->" << (void*)this << ")";
@ -2556,7 +2571,7 @@ typedef BigIntG<257, BigIntInfo> BigInt256;
template <int n = 257> template <int n = 257>
BigIntG<n, BigIntInfo> make_bigint(long long x) { BigIntG<n, BigIntInfo> make_bigint(long long x) {
return BigIntG<n, BigIntInfo>{Normalize(), x}; return BigIntG<n, BigIntInfo>{x};
} }
namespace literals { namespace literals {

View file

@ -261,10 +261,6 @@ int sgn(RefInt256 x) {
return x->sgn(); return x->sgn();
} }
RefInt256 make_refint(long long x) {
return td::RefInt256{true, td::Normalize(), x};
}
RefInt256 zero_refint() { RefInt256 zero_refint() {
// static RefInt256 Zero = td::RefInt256{true, 0}; // static RefInt256 Zero = td::RefInt256{true, 0};
// return Zero; // return Zero;

View file

@ -113,8 +113,6 @@ RefInt256 make_refint(Args&&... args) {
return td::RefInt256{true, std::forward<Args>(args)...}; return td::RefInt256{true, std::forward<Args>(args)...};
} }
extern RefInt256 make_refint(long long x);
extern RefInt256 zero_refint(); extern RefInt256 zero_refint();
extern RefInt256 bits_to_refint(td::ConstBitPtr bits, int n, bool sgnd = false); 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) { 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(args.config.value()->get_unpacked_config_tuple(now)); // unpacked_config_tuple
tuple.push_back(td::zero_refint()); // due_payment 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)); auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
//LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); //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); 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 } // namespace vm

View file

@ -18,10 +18,20 @@
*/ */
#pragma once #pragma once
#include "common/refint.h"
namespace vm { namespace vm {
class OpcodeTable; class OpcodeTable;
void register_arith_ops(OpcodeTable& cp0); 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 } // namespace vm

View file

@ -1544,4 +1544,193 @@ void register_cell_ops(OpcodeTable& cp0) {
register_cell_deserialize_ops(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 } // namespace vm

View file

@ -23,12 +23,42 @@ namespace vm {
class OpcodeTable; 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); 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); 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); 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); 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 } // namespace vm

View file

@ -595,7 +595,7 @@ td::RefInt256 CellSlice::fetch_int256(unsigned bits, bool sgnd) {
if (!have(bits)) { if (!have(bits)) {
return {}; return {};
} else if (bits < td::BigInt256::word_shift) { } 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 { } else {
td::RefInt256 res{true}; td::RefInt256 res{true};
res.unique_write().import_bits(data_bits(), bits, sgnd); 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)) { if (!have(bits)) {
return {}; return {};
} else if (bits < td::BigInt256::word_shift) { } 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 { } else {
td::RefInt256 res{true}; td::RefInt256 res{true};
res.unique_write().import_bits(data_bits(), bits, sgnd); res.unique_write().import_bits(data_bits(), bits, sgnd);

View file

@ -18,6 +18,7 @@
*/ */
#include "vm/memo.h" #include "vm/memo.h"
#include "vm/excno.hpp" #include "vm/excno.hpp"
#include "vm/vm.h"
namespace vm { namespace vm {
using td::Ref; using td::Ref;
@ -30,4 +31,18 @@ bool FakeVmStateLimits::register_op(int op_units) {
return ok; 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 } // namespace vm

View file

@ -20,6 +20,7 @@
#include "common/refcnt.hpp" #include "common/refcnt.hpp"
#include "vm/cells.h" #include "vm/cells.h"
#include "vm/vmstate.h" #include "vm/vmstate.h"
#include "td/utils/optional.h"
namespace vm { namespace vm {
using td::Ref; using td::Ref;
@ -34,4 +35,23 @@ class FakeVmStateLimits : public VmStateInterface {
bool register_op(int op_units = 1) override; 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 } // 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 // 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 tuple = st->get_c7();
auto t1 = tuple_index(tuple, 0).as_tuple_range(255); auto t1 = tuple_index(tuple, 0).as_tuple_range(255);
if (t1.is_null()) { if (t1.is_null()) {
@ -134,7 +134,7 @@ static td::Ref<CellSlice> get_unpacked_config_param(VmState* st, unsigned idx) {
if (t2.is_null()) { if (t2.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a tuple"}; 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) { 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) { int exec_get_global_id(VmState* st) {
VM_LOG(st) << "execute GLOBALID"; VM_LOG(st) << "execute GLOBALID";
if (st->get_global_version() >= 6) { 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()) { if (cs.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a slice"}; throw VmError{Excno::type_chk, "intermediate value is not a slice"};
} }
@ -276,36 +276,12 @@ int exec_get_global_id(VmState* st) {
return 0; 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) { int exec_get_gas_fee(VmState* st) {
VM_LOG(st) << "execute GETGASFEE"; VM_LOG(st) << "execute GETGASFEE";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0); 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)); stack.push_int(prices.compute_gas_price(gas));
return 0; 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::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 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); td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
Ref<CellSlice> cs = get_unpacked_config_param(st, 0); td::optional<block::StoragePrices> maybe_prices =
if (cs.is_null()) { util::get_storage_prices(get_unpacked_config_tuple(st));
// null means tat no StoragePrices is active, so the price is 0 stack.push_int(util::calculate_storage_fee(maybe_prices, is_masterchain, delta, bits, cells));
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));
return 0; return 0;
} }
@ -347,7 +305,7 @@ int exec_get_forward_fee(VmState* st) {
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 bits = 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); 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)); stack.push_int(prices.compute_fwd_fees256(cells, bits));
return 0; return 0;
} }
@ -355,7 +313,7 @@ int exec_get_forward_fee(VmState* st) {
int exec_get_precompiled_gas(VmState* st) { int exec_get_precompiled_gas(VmState* st) {
VM_LOG(st) << "execute GETPRECOMPILEDGAS"; VM_LOG(st) << "execute GETPRECOMPILEDGAS";
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
stack.push_null(); stack.push(get_param(st, 16));
return 0; return 0;
} }
@ -367,7 +325,7 @@ int exec_get_original_fwd_fee(VmState* st) {
if (fwd_fee->sgn() < 0) { if (fwd_fee->sgn() < 0) {
throw VmError{Excno::range_chk, "fwd_fee is negative"}; 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))); stack.push_int(td::muldiv(fwd_fee, td::make_refint(1 << 16), td::make_refint((1 << 16) - prices.first_frac)));
return 0; return 0;
} }
@ -377,7 +335,7 @@ int exec_get_gas_fee_simple(VmState* st) {
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0); 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)); stack.push_int(td::rshift(td::make_refint(prices.gas_price) * gas, 16, 1));
return 0; return 0;
} }
@ -388,7 +346,7 @@ int exec_get_forward_fee_simple(VmState* st) {
bool is_masterchain = stack.pop_bool(); bool is_masterchain = stack.pop_bool();
td::uint64 bits = 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); 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, 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 1)); // divide by 2^16 with ceil rounding
return 0; 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(); Stack& stack = st->get_stack();
auto csr = stack.pop_cellslice(); auto csr = stack.pop_cellslice();
td::RefInt256 x; td::RefInt256 x;
int len; if (util::load_var_integer_q(csr.write(), x, len_bits, sgnd, quiet)) {
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 {
stack.push_int(std::move(x)); stack.push_int(std::move(x));
stack.push_cellslice(std::move(csr)); stack.push_cellslice(std::move(csr));
if (quiet) { if (quiet) {
stack.push_bool(true); stack.push_bool(true);
} }
} else {
stack.push_bool(false);
} }
return 0; return 0;
} }
@ -1376,21 +1329,13 @@ int exec_store_var_integer(VmState* st, int len_bits, bool sgnd, bool quiet) {
stack.check_underflow(2); stack.check_underflow(2);
auto x = stack.pop_int(); auto x = stack.pop_int();
auto cbr = stack.pop_builder(); auto cbr = stack.pop_builder();
unsigned len = (((unsigned)x->bit_size(sgnd) + 7) >> 3); if (util::store_var_integer(cbr.write(), x, len_bits, sgnd, quiet)) {
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 {
stack.push_builder(std::move(cbr)); stack.push_builder(std::move(cbr));
if (quiet) { if (quiet) {
stack.push_bool(true); stack.push_bool(true);
} }
} else {
stack.push_bool(false);
} }
return 0; return 0;
} }
@ -1433,22 +1378,17 @@ bool skip_message_addr(CellSlice& cs) {
int exec_load_message_addr(VmState* st, bool quiet) { int exec_load_message_addr(VmState* st, bool quiet) {
VM_LOG(st) << "execute LDMSGADDR" << (quiet ? "Q" : ""); VM_LOG(st) << "execute LDMSGADDR" << (quiet ? "Q" : "");
Stack& stack = st->get_stack(); Stack& stack = st->get_stack();
auto csr = stack.pop_cellslice(), csr_copy = csr; auto csr = stack.pop_cellslice();
auto& cs = csr.write(); td::Ref<CellSlice> addr{true};
if (!(skip_message_addr(cs) && csr_copy.write().cut_tail(cs))) { if (util::load_msg_addr_q(csr.write(), addr.write(), quiet)) {
csr.clear(); stack.push_cellslice(std::move(addr));
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));
stack.push_cellslice(std::move(csr)); stack.push_cellslice(std::move(csr));
if (quiet) { if (quiet) {
stack.push_bool(true); stack.push_bool(true);
} }
} else {
stack.push_cellslice(std::move(csr));
stack.push_bool(false);
} }
return 0; 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); bool is_masterchain = parse_addr_workchain(*my_addr) == -1 || (!ext_msg && parse_addr_workchain(*dest) == -1);
td::Ref<CellSlice> prices_cs; td::Ref<CellSlice> prices_cs;
if (st->get_global_version() >= 6) { 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 { } else {
Ref<Cell> config_dict = get_param(st, 9).as_cell(); Ref<Cell> config_dict = get_param(st, 9).as_cell();
Dictionary config{config_dict, 32}; 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) // bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
td::uint64 max_cells; td::uint64 max_cells;
if (st->get_global_version() >= 6) { 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()) { if (r_size_limits_config.is_error()) {
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_size_limits_config.error().message()}; 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); 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 } // namespace vm

View file

@ -17,6 +17,9 @@
Copyright 2017-2020 Telegram Systems LLP Copyright 2017-2020 Telegram Systems LLP
*/ */
#pragma once #pragma once
#include "vm/vm.h"
#include "ton/ton-types.h"
#include "mc-config.h"
namespace vm { namespace vm {
@ -24,4 +27,30 @@ class OpcodeTable;
void register_ton_ops(OpcodeTable& cp0); 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 } // namespace vm

View file

@ -58,7 +58,7 @@ See [this post](https://t.me/tonstatus/88) for details.
## Version 6 ## Version 6
### c7 tuple ### 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`. * **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). * **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). * **1**: `ConfigParam 19` (global id).
@ -68,6 +68,7 @@ See [this post](https://t.me/tonstatus/88) for details.
* **5**: `ConfigParam 25` (fwd fees). * **5**: `ConfigParam 25` (fwd fees).
* **6**: `ConfigParam 43` (size limits). * **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`. * **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 ### 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. * `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). * `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. * `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. * `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`). * `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`). * `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)); 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())); 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)); 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(); 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)); 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(); 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)); 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 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) { 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) { if (exit_code != 0) {
LOG(ERROR) << "VM terminated with error code " << exit_code;
out << "result: error " << exit_code << std::endl; out << "result: error " << exit_code << std::endl;
promise.set_error(td::Status::Error(PSLICE() << "VM terminated with non-zero exit code " << exit_code)); } else {
return; stack = vm.get_stack_ref();
}
stack = vm.get_stack_ref();
{
std::ostringstream os; std::ostringstream os;
os << "result: "; os << "result: ";
stack->dump(os, 3); stack->dump(os, 3);
out << os.str(); out << os.str();
} }
if (mode & 4) { if (!(mode & 4)) {
if (remote_result.empty()) { if (exit_code != 0) {
out << "remote result: <none>, exit code " << remote_exit_code; 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 { } else {
auto res = vm::std_boc_deserialize(std::move(remote_result)); auto res = vm::std_boc_deserialize(std::move(remote_result));
if (res.is_error()) { 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): "; os << "remote result (not to be trusted): ";
remote_stack->dump(os, 3); remote_stack->dump(os, 3);
out << os.str(); out << os.str();
promise.set_value(remote_stack->extract_contents());
} }
} }
out.flush(); out.flush();
promise.set_result(stack->extract_contents());
} catch (vm::VmVirtError& err) { } catch (vm::VmVirtError& err) {
out << "virtualization error while parsing runSmcMethod result: " << err.get_msg(); out << "virtualization error while parsing runSmcMethod result: " << err.get_msg();
promise.set_error( 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 abce
Test_Bigint_main_default 0327a04f1252c37f77b6706b902ab2c3235c47738bca3f183c837a2c5d22bb6f Test_Bigint_main_default 76f38492ec19464a1d0eac51d389023a31ce10396b3894061361d159567ce8cd
Test_Bitstrings_main_default a8b08af3116923c4c2a14e138d168375abd0c059f2f780d3267b294929a1110e Test_Bitstrings_main_default a8b08af3116923c4c2a14e138d168375abd0c059f2f780d3267b294929a1110e
Test_Cells_simple_default 832502642fe4fe5db70de82681aedb7d54d7f3530e0069861fff405fe6f6cf23 Test_Cells_simple_default 832502642fe4fe5db70de82681aedb7d54d7f3530e0069861fff405fe6f6cf23
Test_Fift_bug_div_default 1ac42861ce96b2896001c587f65e9afe1617db48859f19c2f4e3063a20ea60b0 Test_Fift_bug_div_default 1ac42861ce96b2896001c587f65e9afe1617db48859f19c2f4e3063a20ea60b0

View file

@ -1216,7 +1216,7 @@ class RemoteRunSmcMethod : public td::actor::Actor {
client_.send_query( client_.send_query(
//liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:long params:bytes = liteServer.RunMethodResult; //liteServer.runSmcMethod mode:# id:tonNode.blockIdExt account:liteServer.accountId method_id:long params:bytes = liteServer.RunMethodResult;
ton::lite_api::liteServer_runSmcMethod( 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), ton::create_tl_object<ton::lite_api::liteServer_accountId>(query_.address.workchain, query_.address.addr),
method_id, std::move(serialized_stack)), method_id, std::move(serialized_stack)),
[self = this](auto r_state) { self->with_run_method_result(std::move(r_state)); }, [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-auto.h"
#include "block-parse.h" #include "block-parse.h"
#include "common/delay.h" #include "common/delay.h"
#include "block/precompiled-smc/PrecompiledSmartContract.h"
Config::Config() { Config::Config() {
out_port = 3278; 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); }); acts.push_back([&x, v]() { td::actor::send_closure(x, &ValidatorEngine::set_archive_preload_period, v); });
return td::Status::OK(); 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); auto S = p.run(argc, argv);
if (S.is_error()) { if (S.is_error()) {
LOG(ERROR) << "failed to parse options: " << S.move_as_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(); 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")); return promise.set_error(td::Status::Error("duplicate external message broadcast"));
} }
if (config_.ext_messages_broadcast_disabled_) { 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, td::actor::send_closure(validator_manager_, &ValidatorManagerInterface::check_external_message,
std::move(q->message_->data_), std::move(q->message_->data_),
promise.wrap([](td::Ref<ExtMessage>) { return td::Unit(); })); promise.wrap([](td::Ref<ExtMessage>) { return td::Unit(); }));
@ -821,9 +827,11 @@ void FullNodeShardImpl::send_external_message(td::BufferSlice data) {
}); });
return; 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; return;
} }
my_ext_msg_broadcasts_.insert(hash);
auto B = create_serialize_tl_object<ton_api::tonNode_externalMessageBroadcast>( auto B = create_serialize_tl_object<ton_api::tonNode_externalMessageBroadcast>(
create_tl_object<ton_api::tonNode_externalMessage>(std::move(data))); create_tl_object<ton_api::tonNode_externalMessage>(std::move(data)));
if (B.size() <= overlay::Overlays::max_simple_broadcast_size()) { 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()) { if (cleanup_processed_ext_msg_at_ && cleanup_processed_ext_msg_at_.is_in_past()) {
processed_ext_msg_broadcasts_.clear(); processed_ext_msg_broadcasts_.clear();
my_ext_msg_broadcasts_.clear();
cleanup_processed_ext_msg_at_ = td::Timestamp::in(60.0); cleanup_processed_ext_msg_at_ = td::Timestamp::in(60.0);
} }
alarm_timestamp().relax(sync_completed_at_); alarm_timestamp().relax(sync_completed_at_);

View file

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

View file

@ -157,7 +157,7 @@ void LiteQuery::start_up() {
}); });
return; return;
} }
use_cache_ = !cache_.empty() && query_obj_->get_id() == lite_api::liteServer_runSmcMethod::ID; use_cache_ = use_cache();
if (use_cache_) { if (use_cache_) {
cache_key_ = td::sha256_bits256(query_); cache_key_ = td::sha256_bits256(query_);
td::actor::send_closure( 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() { void LiteQuery::perform() {
td::actor::send_closure(manager_, &ValidatorManager::add_lite_query_stats, query_obj_->get_id()); td::actor::send_closure(manager_, &ValidatorManager::add_lite_query_stats, query_obj_->get_id());
lite_api::downcast_call( 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"); fatal_error("more than 64k parameter bytes passed");
return; return;
} }
if (mode & ~0x1f) { if (mode & ~0x3f) {
fatal_error("unsupported mode in runSmcMethod"); fatal_error("unsupported mode in runSmcMethod");
return; return;
} }
@ -1245,8 +1261,24 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
return; return;
} }
if (mode_ & 0x10000) { if (mode_ & 0x10000) {
finish_runSmcMethod(std::move(shard_proof), proof.move_as_ok(), std::move(acc_root), sstate.gen_utime, if (!mc_state_.is_null()) {
sstate.gen_lt); 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; return;
} }
td::BufferSlice data; td::BufferSlice data;
@ -1283,25 +1315,45 @@ void LiteQuery::finish_getAccountState(td::BufferSlice shard_proof) {
// same as in lite-client/lite-client-common.cpp // 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, 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::BitArray<256> rand_seed;
td::RefInt256 rand_seed_int{true}; td::RefInt256 rand_seed_int{true};
td::Random::secure_bytes(rand_seed.as_slice()); td::Random::secure_bytes(rand_seed.as_slice());
if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) { if (!rand_seed_int.unique_write().import_bits(rand_seed.cbits(), 256, false)) {
return {}; return {};
} }
auto tuple = vm::make_tuple_ref(td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea std::vector<vm::StackEntry> tuple = {
td::make_refint(0), // actions:Integer td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
td::make_refint(0), // msgs_sent:Integer td::make_refint(0), // actions:Integer
td::make_refint(now), // unixtime:Integer td::make_refint(0), // msgs_sent:Integer
td::make_refint(lt), // block_lt:Integer td::make_refint(now), // unixtime:Integer
td::make_refint(lt), // trans_lt:Integer td::make_refint(lt), // block_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer td::make_refint(lt), // trans_lt:Integer
balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)] std::move(rand_seed_int), // rand_seed:Integer
my_addr, // myself:MsgAddressInt balance.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
vm::StackEntry()); // global_config:(Maybe Cell) ] = SmartContractInfo; my_addr, // myself:MsgAddressInt
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); config ? config->get_root_cell() : vm::StackEntry() // global_config:(Maybe Cell) ] = SmartContractInfo;
return vm::make_tuple_ref(std::move(tuple)); };
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, 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)}; vm::MerkleProofBuilder pb{std::move(acc_root)};
block::gen::Account::Record_account acc; block::gen::Account::Record_account acc;
block::gen::StorageInfo::Record storage_info;
block::gen::AccountStorage::Record store; block::gen::AccountStorage::Record store;
block::CurrencyCollection balance; block::CurrencyCollection balance;
block::gen::StateInit::Record state_init; block::gen::StateInit::Record state_init;
if (!(tlb::unpack_cell(pb.root(), acc) && tlb::csr_unpack(std::move(acc.storage), store) && 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 && 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"; LOG(INFO) << "error unpacking account state, or account is frozen or uninitialized";
td::Result<td::BufferSlice> proof_boc; td::Result<td::BufferSlice> proof_boc;
if (mode & 2) { 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 code = state_init.code->prefetch_ref();
auto data = state_init.data->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; 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; LOG(DEBUG) << "creating VM with gas limit " << gas_limit;
// **** INIT VM **** // **** 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::GasLimits gas{gas_limit, gas_limit};
vm::VmState vm{std::move(code), std::move(stack_), gas, 1, std::move(data), vm::VmLog::Null()}; 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); 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.set_c7(c7); // tuple with SmartContractInfo
// vm.incr_stack_trace(1); // enable stack dump after each step // 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(); 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; td::BufferSlice c7_info, result;
if (mode & 8) { if (mode & 8) {
// serialize c7 // 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; vm::CellBuilder cb;
if (!(vm::StackEntry{std::move(c7)}.serialize(cb) && cb.finalize_to(cell))) { if (!(vm::StackEntry{std::move(c7)}.serialize(cb) && cb.finalize_to(cell))) {
fatal_error("cannot serialize c7"); fatal_error("cannot serialize c7");

View file

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

View file

@ -129,6 +129,15 @@ td::Status ShardStateQ::init() {
" contains BlockId " + hdr_id.to_str() + " contains BlockId " + hdr_id.to_str() +
" different from the one originally required"); " 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(); return td::Status::OK();
} }

View file

@ -41,6 +41,7 @@ class ShardStateQ : virtual public ShardState {
bool before_split_{false}; bool before_split_{false};
bool fake_split_{false}; bool fake_split_{false};
bool fake_merge_{false}; bool fake_merge_{false};
td::optional<BlockIdExt> master_ref;
protected: protected:
friend class Ref<ShardStateQ>; friend class Ref<ShardStateQ>;
@ -80,6 +81,9 @@ class ShardStateQ : virtual public ShardState {
LogicalTime get_logical_time() const override { LogicalTime get_logical_time() const override {
return lt; return lt;
} }
td::optional<BlockIdExt> get_master_ref() const override {
return master_ref;
}
td::Status validate_deep() const override; td::Status validate_deep() const override;
ShardStateQ* make_copy() const override; ShardStateQ* make_copy() const override;
td::Result<Ref<MessageQueue>> message_queue() 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_.suspended_addresses = config_->get_suspended_addresses(now_);
compute_phase_cfg_.size_limits = size_limits; compute_phase_cfg_.size_limits = size_limits;
compute_phase_cfg_.precompiled_contracts = config_->get_precompiled_contracts_config();
} }
{ {
// compute action_phase_cfg // compute action_phase_cfg

View file

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