mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Merge branch 'testnet' into block-generation
# Conflicts: # crypto/block/mc-config.cpp # crypto/block/mc-config.h
This commit is contained in:
commit
e216651112
41 changed files with 1233 additions and 192 deletions
|
@ -797,6 +797,10 @@ _ SizeLimitsConfig = ConfigParam 43;
|
|||
suspended_address_list#00 addresses:(HashmapE 288 Unit) suspended_until:uint32 = SuspendedAddressList;
|
||||
_ SuspendedAddressList = ConfigParam 44;
|
||||
|
||||
precompiled_smc#b0 gas_usage:uint64 = PrecompiledSmc;
|
||||
precompiled_contracts_config#c0 list:(HashmapE 256 PrecompiledSmc) = PrecompiledContractsConfig;
|
||||
_ PrecompiledContractsConfig = ConfigParam 45;
|
||||
|
||||
oracle_bridge_params#_ bridge_address:bits256 oracle_mutlisig_address:bits256 oracles:(HashmapE 256 uint256) external_chain_address:bits256 = OracleBridgeParams;
|
||||
_ OracleBridgeParams = ConfigParam 71; // Ethereum bridge
|
||||
_ OracleBridgeParams = ConfigParam 72; // Binance Smart Chain bridge
|
||||
|
|
|
@ -2032,6 +2032,17 @@ td::Ref<vm::Tuple> Config::get_unpacked_config_tuple(ton::UnixTime now) const {
|
|||
return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
|
||||
}
|
||||
|
||||
PrecompiledContractsConfig Config::get_precompiled_contracts_config() const {
|
||||
PrecompiledContractsConfig c;
|
||||
td::Ref<vm::Cell> param = get_config_param(45);
|
||||
gen::PrecompiledContractsConfig::Record rec;
|
||||
if (param.is_null() || !tlb::unpack_cell(param, rec)) {
|
||||
return c;
|
||||
}
|
||||
c.list = vm::Dictionary{rec.list->prefetch_ref(), 256};
|
||||
return c;
|
||||
}
|
||||
|
||||
td::Result<std::pair<ton::UnixTime, ton::UnixTime>> Config::unpack_validator_set_start_stop(Ref<vm::Cell> vset_root) {
|
||||
if (vset_root.is_null()) {
|
||||
return td::Status::Error("validator set absent");
|
||||
|
@ -2316,6 +2327,22 @@ td::Result<Ref<vm::Tuple>> ConfigInfo::get_prev_blocks_info() const {
|
|||
block_id_to_tuple(last_key_block));
|
||||
}
|
||||
|
||||
td::optional<PrecompiledContractsConfig::Contract> PrecompiledContractsConfig::get_contract(
|
||||
td::Bits256 code_hash) const {
|
||||
auto list_copy = list;
|
||||
auto cs = list_copy.lookup(code_hash);
|
||||
if (cs.is_null()) {
|
||||
return {};
|
||||
}
|
||||
gen::PrecompiledSmc::Record rec;
|
||||
if (!tlb::csr_unpack(cs, rec)) {
|
||||
return {};
|
||||
}
|
||||
Contract c;
|
||||
c.gas_usage = rec.gas_usage;
|
||||
return c;
|
||||
}
|
||||
|
||||
CollatorConfig Config::get_collator_config(bool need_collator_nodes) const {
|
||||
CollatorConfig collator_config;
|
||||
gen::CollatorConfig::Record rec;
|
||||
|
|
|
@ -526,6 +526,15 @@ struct BurningConfig {
|
|||
}
|
||||
};
|
||||
|
||||
struct PrecompiledContractsConfig {
|
||||
struct Contract {
|
||||
td::uint64 gas_usage;
|
||||
};
|
||||
vm::Dictionary list{256};
|
||||
|
||||
td::optional<Contract> get_contract(td::Bits256 code_hash) const;
|
||||
};
|
||||
|
||||
struct CollatorNodeDescr {
|
||||
ton::ShardIdFull shard;
|
||||
ton::NodeIdShort adnl_id;
|
||||
|
@ -655,6 +664,7 @@ class Config {
|
|||
std::unique_ptr<vm::Dictionary> get_suspended_addresses(ton::UnixTime now) const;
|
||||
BurningConfig get_burning_config() const;
|
||||
td::Ref<vm::Tuple> get_unpacked_config_tuple(ton::UnixTime now) const;
|
||||
PrecompiledContractsConfig get_precompiled_contracts_config() const;
|
||||
static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
|
||||
ton::ShardIdFull shard,
|
||||
const block::ValidatorSet& vset, ton::UnixTime time,
|
||||
|
|
170
crypto/block/precompiled-smc/PrecompiledSmartContract.cpp
Normal file
170
crypto/block/precompiled-smc/PrecompiledSmartContract.cpp
Normal 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
|
122
crypto/block/precompiled-smc/PrecompiledSmartContract.h
Normal file
122
crypto/block/precompiled-smc/PrecompiledSmartContract.h
Normal 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
|
21
crypto/block/precompiled-smc/common.h
Normal file
21
crypto/block/precompiled-smc/common.h
Normal 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"
|
|
@ -1341,6 +1341,9 @@ Ref<vm::Tuple> Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const {
|
|||
tuple.push_back(cfg.unpacked_config_tuple.not_null() ? vm::StackEntry(cfg.unpacked_config_tuple)
|
||||
: vm::StackEntry()); // unpacked_config_tuple:[...]
|
||||
tuple.push_back(due_payment.not_null() ? due_payment : td::zero_refint()); // due_payment:Integer
|
||||
tuple.push_back(compute_phase->precompiled_gas_usage
|
||||
? vm::StackEntry(td::make_refint(compute_phase->precompiled_gas_usage.value()))
|
||||
: vm::StackEntry()); // precompiled_gas_usage:Integer
|
||||
}
|
||||
auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
|
||||
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string();
|
||||
|
@ -1460,6 +1463,80 @@ bool Transaction::check_in_msg_state_hash() {
|
|||
return account.recompute_tmp_addr(my_addr, d, orig_addr_rewrite.bits());
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the precompiled smart contract and prepares the compute phase.
|
||||
*
|
||||
* @param cfg The configuration for the compute phase.
|
||||
* @param impl Implementation of the smart contract
|
||||
*
|
||||
* @returns True if the contract was successfully executed, false otherwise.
|
||||
*/
|
||||
bool Transaction::run_precompiled_contract(const ComputePhaseConfig& cfg, precompiled::PrecompiledSmartContract& impl) {
|
||||
ComputePhase& cp = *compute_phase;
|
||||
CHECK(cp.precompiled_gas_usage);
|
||||
td::uint64 gas_usage = cp.precompiled_gas_usage.value();
|
||||
td::Timer timer;
|
||||
auto result =
|
||||
impl.run(my_addr, now, start_lt, balance, new_data, *in_msg_body, in_msg, msg_balance_remaining, in_msg_extern,
|
||||
compute_vm_libraries(cfg), cfg.global_version, cfg.max_vm_data_depth, new_code,
|
||||
cfg.unpacked_config_tuple, due_payment.not_null() ? due_payment : td::zero_refint(), gas_usage);
|
||||
double elapsed = timer.elapsed();
|
||||
cp.vm_init_state_hash = td::Bits256::zero();
|
||||
cp.exit_code = result.exit_code;
|
||||
cp.out_of_gas = false;
|
||||
cp.vm_final_state_hash = td::Bits256::zero();
|
||||
cp.vm_steps = 0;
|
||||
cp.gas_used = gas_usage;
|
||||
cp.accepted = result.accepted;
|
||||
cp.success = (cp.accepted && result.committed);
|
||||
LOG(INFO) << "Running precompiled smart contract " << impl.get_name() << ": exit_code=" << result.exit_code
|
||||
<< " accepted=" << result.accepted << " success=" << cp.success << " gas_used=" << gas_usage
|
||||
<< " time=" << elapsed << "s";
|
||||
if (cp.accepted & use_msg_state) {
|
||||
was_activated = true;
|
||||
acc_status = Account::acc_active;
|
||||
}
|
||||
if (cfg.with_vm_log) {
|
||||
cp.vm_log = PSTRING() << "Running precompiled smart contract " << impl.get_name()
|
||||
<< ": exit_code=" << result.exit_code << " accepted=" << result.accepted
|
||||
<< " success=" << cp.success << " gas_used=" << gas_usage << " time=" << elapsed << "s";
|
||||
}
|
||||
if (cp.success) {
|
||||
cp.new_data = impl.get_c4();
|
||||
cp.actions = impl.get_c5();
|
||||
int out_act_num = output_actions_count(cp.actions);
|
||||
if (verbosity > 2) {
|
||||
std::cerr << "new smart contract data: ";
|
||||
bool can_be_special = true;
|
||||
load_cell_slice_special(cp.new_data, can_be_special).print_rec(std::cerr);
|
||||
std::cerr << "output actions: ";
|
||||
block::gen::OutList{out_act_num}.print_ref(std::cerr, cp.actions);
|
||||
}
|
||||
}
|
||||
cp.mode = 0;
|
||||
cp.exit_arg = 0;
|
||||
if (!cp.success && result.exit_arg) {
|
||||
auto value = td::narrow_cast_safe<td::int32>(result.exit_arg.value());
|
||||
if (value.is_ok()) {
|
||||
cp.exit_arg = value.ok();
|
||||
}
|
||||
}
|
||||
if (cp.accepted) {
|
||||
if (account.is_special) {
|
||||
cp.gas_fees = td::zero_refint();
|
||||
} else {
|
||||
cp.gas_fees = cfg.compute_gas_price(cp.gas_used);
|
||||
total_fees += cp.gas_fees;
|
||||
balance -= cp.gas_fees;
|
||||
}
|
||||
LOG(DEBUG) << "gas fees: " << cp.gas_fees->to_dec_string() << " = " << cfg.gas_price256->to_dec_string() << " * "
|
||||
<< cp.gas_used << " /2^16 ; price=" << cfg.gas_price << "; flat rate=[" << cfg.flat_gas_price << " for "
|
||||
<< cfg.flat_gas_limit << "]; remaining balance=" << balance.to_str();
|
||||
CHECK(td::sgn(balance.grams) >= 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the compute phase of a transaction, which includes running TVM.
|
||||
*
|
||||
|
@ -1528,6 +1605,33 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
cp.skip_reason = ComputePhase::sk_bad_state;
|
||||
return true;
|
||||
}
|
||||
|
||||
td::optional<PrecompiledContractsConfig::Contract> precompiled;
|
||||
if (new_code.not_null() && trans_type == tr_ord) {
|
||||
precompiled = cfg.precompiled_contracts.get_contract(new_code->get_hash().bits());
|
||||
}
|
||||
|
||||
vm::GasLimits gas{(long long)cp.gas_limit, (long long)cp.gas_max, (long long)cp.gas_credit};
|
||||
if (precompiled) {
|
||||
td::uint64 gas_usage = precompiled.value().gas_usage;
|
||||
cp.precompiled_gas_usage = gas_usage;
|
||||
if (gas_usage > cp.gas_limit) {
|
||||
cp.skip_reason = ComputePhase::sk_no_gas;
|
||||
return true;
|
||||
}
|
||||
auto impl = precompiled::get_implementation(new_code->get_hash().bits());
|
||||
if (impl != nullptr && !cfg.dont_run_precompiled_ && impl->required_version() <= cfg.global_version) {
|
||||
return run_precompiled_contract(cfg, *impl);
|
||||
}
|
||||
|
||||
// Contract is marked as precompiled in global config, but implementation is not available
|
||||
// In this case we run TVM and override gas_used
|
||||
LOG(INFO) << "Unknown precompiled contract (code_hash=" << new_code->get_hash().to_hex()
|
||||
<< ", gas_usage=" << gas_usage << "), running VM";
|
||||
long long limit = account.is_special ? cfg.special_gas_limit : cfg.gas_limit;
|
||||
gas = vm::GasLimits{limit, limit, gas.gas_credit ? limit : 0};
|
||||
}
|
||||
|
||||
// initialize VM
|
||||
Ref<vm::Stack> stack = prepare_vm_stack(cp);
|
||||
if (stack.is_null()) {
|
||||
|
@ -1536,7 +1640,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
}
|
||||
// OstreamLogger ostream_logger(error_stream);
|
||||
// auto log = create_vm_log(error_stream ? &ostream_logger : nullptr);
|
||||
vm::GasLimits gas{(long long)cp.gas_limit, (long long)cp.gas_max, (long long)cp.gas_credit};
|
||||
LOG(DEBUG) << "creating VM";
|
||||
|
||||
std::unique_ptr<StringLoggerTail> logger;
|
||||
|
@ -1585,6 +1688,15 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
|
|||
was_activated = true;
|
||||
acc_status = Account::acc_active;
|
||||
}
|
||||
if (precompiled) {
|
||||
cp.gas_used = precompiled.value().gas_usage;
|
||||
cp.vm_steps = 0;
|
||||
cp.vm_init_state_hash = cp.vm_final_state_hash = td::Bits256::zero();
|
||||
if (cp.out_of_gas) {
|
||||
LOG(ERROR) << "Precompiled smc got out_of_gas in TVM";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "steps: " << vm.get_steps_count() << " gas: used=" << gas.gas_consumed() << ", max=" << gas.gas_max
|
||||
<< ", limit=" << gas.gas_limit << ", credit=" << gas.gas_credit;
|
||||
LOG(INFO) << "out_of_gas=" << cp.out_of_gas << ", accepted=" << cp.accepted << ", success=" << cp.success
|
||||
|
@ -3568,6 +3680,7 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
}
|
||||
compute_phase_cfg->suspended_addresses = config.get_suspended_addresses(now);
|
||||
compute_phase_cfg->size_limits = size_limits;
|
||||
compute_phase_cfg->precompiled_contracts = config.get_precompiled_contracts_config();
|
||||
}
|
||||
{
|
||||
// compute action_phase_cfg
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "ton/ton-types.h"
|
||||
#include "block/block.h"
|
||||
#include "block/mc-config.h"
|
||||
#include "precompiled-smc/PrecompiledSmartContract.h"
|
||||
|
||||
namespace block {
|
||||
using td::Ref;
|
||||
|
@ -122,6 +123,8 @@ struct ComputePhaseConfig {
|
|||
SizeLimitsConfig size_limits;
|
||||
int vm_log_verbosity = 0;
|
||||
bool stop_on_accept_message = false;
|
||||
PrecompiledContractsConfig precompiled_contracts;
|
||||
bool dont_run_precompiled_ = false;
|
||||
|
||||
ComputePhaseConfig() : gas_price(0), gas_limit(0), special_gas_limit(0), gas_credit(0) {
|
||||
compute_threshold();
|
||||
|
@ -189,6 +192,7 @@ struct ComputePhase {
|
|||
Ref<vm::Cell> new_data;
|
||||
Ref<vm::Cell> actions;
|
||||
std::string vm_log;
|
||||
td::optional<td::uint64> precompiled_gas_usage;
|
||||
};
|
||||
|
||||
struct ActionPhase {
|
||||
|
@ -372,6 +376,7 @@ struct Transaction {
|
|||
bool compute_gas_limits(ComputePhase& cp, const ComputePhaseConfig& cfg);
|
||||
Ref<vm::Stack> prepare_vm_stack(ComputePhase& cp);
|
||||
std::vector<Ref<vm::Cell>> compute_vm_libraries(const ComputePhaseConfig& cfg);
|
||||
bool run_precompiled_contract(const ComputePhaseConfig& cfg, precompiled::PrecompiledSmartContract& precompiled);
|
||||
bool prepare_compute_phase(const ComputePhaseConfig& cfg);
|
||||
bool prepare_action_phase(const ActionPhaseConfig& cfg);
|
||||
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue