1
0
Fork 0
mirror of https://github.com/ton-blockchain/ton synced 2025-03-09 15:40:10 +00:00

Merge branch 'testnet' into block-generation

This commit is contained in:
SpyCheese 2024-02-01 19:29:25 +03:00
commit f4fd3ff3be
246 changed files with 7895 additions and 5430 deletions

View file

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
if (NOT OPENSSL_FOUND)
find_package(OpenSSL REQUIRED)
@ -236,6 +236,7 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/SmartContractCode.cpp
smc-envelope/WalletInterface.cpp
smc-envelope/WalletV3.cpp
smc-envelope/WalletV4.cpp
smc-envelope/GenericAccount.h
smc-envelope/HighloadWallet.h
@ -246,6 +247,7 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/SmartContractCode.h
smc-envelope/WalletInterface.h
smc-envelope/WalletV3.h
smc-envelope/WalletV4.h
)
set(ED25519_TEST_SOURCE
@ -323,23 +325,20 @@ endif()
if (MSVC)
find_package(Sodium REQUIRED)
target_compile_definitions(ton_crypto PUBLIC SODIUM_STATIC)
target_include_directories(ton_crypto_core PUBLIC $<BUILD_INTERFACE:${SECP256K1_INCLUDE_DIRS}>)
target_link_libraries(ton_crypto PUBLIC ${SECP256K1_LIBRARIES})
elseif (ANDROID)
target_include_directories(ton_crypto_core PUBLIC $<BUILD_INTERFACE:${SECP256K1_INCLUDE_DIR}>)
target_link_libraries(ton_crypto PUBLIC ${SECP256K1_LIBRARY})
elseif (ANDROID OR EMSCRIPTEN)
target_include_directories(ton_crypto_core PUBLIC $<BUILD_INTERFACE:${SECP256K1_INCLUDE_DIR}>)
target_link_libraries(ton_crypto PUBLIC $<BUILD_INTERFACE:${SECP256K1_LIBRARY}>)
else()
if (NOT USE_EMSCRIPTEN)
if (NOT SODIUM_FOUND)
find_package(Sodium REQUIRED)
target_include_directories(ton_crypto_core PUBLIC $<BUILD_INTERFACE:${SECP256K1_INCLUDE_DIRS}>)
target_link_libraries(ton_crypto PUBLIC ${SECP256K1_LIBRARIES})
else()
target_include_directories(ton_crypto_core PUBLIC $<BUILD_INTERFACE:${SECP256K1_INCLUDE_DIR}>)
target_link_libraries(ton_crypto PUBLIC ${SECP256K1_LIBRARY})
endif()
if (NOT APPLE AND NOT USE_EMSCRIPTEN)
target_link_libraries(ton_crypto_core PUBLIC secp256k1)
message(STATUS "Using Sodium ${SODIUM_LIBRARY_RELEASE}")
endif()
target_compile_definitions(ton_crypto PUBLIC SODIUM_STATIC)
target_include_directories(ton_crypto_core PUBLIC $<BUILD_INTERFACE:${SECP256K1_INCLUDE_DIR}>)
target_link_libraries(ton_crypto PUBLIC ${SECP256K1_LIBRARY})
endif()
target_include_directories(ton_crypto_core PUBLIC $<BUILD_INTERFACE:${SODIUM_INCLUDE_DIR}>)
@ -354,7 +353,7 @@ add_executable(test-ed25519-crypto test/test-ed25519-crypto.cpp)
target_include_directories(test-ed25519-crypto PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(test-ed25519-crypto PUBLIC ton_crypto)
add_library(fift-lib ${FIFT_SOURCE})
add_library(fift-lib STATIC ${FIFT_SOURCE})
target_include_directories(fift-lib PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(fift-lib PUBLIC ton_crypto ton_db tdutils ton_block)
if (USE_EMSCRIPTEN)
@ -398,6 +397,7 @@ if (USE_EMSCRIPTEN)
target_link_options(funcfiftlib PRIVATE -sIGNORE_MISSING_MAIN=1)
target_link_options(funcfiftlib PRIVATE -sAUTO_NATIVE_LIBRARIES=0)
target_link_options(funcfiftlib PRIVATE -sMODULARIZE=1)
target_link_options(funcfiftlib PRIVATE -sTOTAL_MEMORY=33554432)
target_link_options(funcfiftlib PRIVATE -sALLOW_MEMORY_GROWTH=1)
target_link_options(funcfiftlib PRIVATE -sALLOW_TABLE_GROWTH=1)
target_link_options(funcfiftlib PRIVATE --embed-file ${CMAKE_CURRENT_SOURCE_DIR}/fift/lib@/fiftlib)
@ -504,7 +504,7 @@ if (NOT CMAKE_CROSSCOMPILING OR USE_EMSCRIPTEN)
GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext)
endif()
add_library(smc-envelope ${SMC_ENVELOPE_SOURCE})
add_library(smc-envelope STATIC ${SMC_ENVELOPE_SOURCE})
target_include_directories(smc-envelope PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>)
target_link_libraries(smc-envelope PUBLIC ton_crypto PRIVATE tdutils ton_block)
if (NOT CMAKE_CROSSCOMPILING)

View file

@ -734,7 +734,7 @@ td::uint64 BlockLimitStatus::estimate_block_size(const vm::NewCellStorageStat::S
sum += *extra;
}
return 2000 + (sum.bits >> 3) + sum.cells * 12 + sum.internal_refs * 3 + sum.external_refs * 40 + accounts * 200 +
transactions * 200 + (extra ? 200 : 0) + extra_out_msgs * 300;
transactions * 200 + (extra ? 200 : 0) + extra_out_msgs * 300 + public_library_diff * 700;
}
int BlockLimitStatus::classify() const {
@ -1030,8 +1030,8 @@ td::Status ShardState::merge_with(ShardState& sib) {
return td::Status::OK();
}
td::Result<std::unique_ptr<vm::AugmentedDictionary>> ShardState::compute_split_out_msg_queue(
ton::ShardIdFull subshard) {
td::Result<std::unique_ptr<vm::AugmentedDictionary>> ShardState::compute_split_out_msg_queue(ton::ShardIdFull subshard,
td::uint32* queue_size) {
auto shard = id_.shard_full();
if (!ton::shard_is_parent(shard, subshard)) {
return td::Status::Error(-666, "cannot split subshard "s + subshard.to_str() + " from state of " + id_.to_str() +
@ -1039,7 +1039,7 @@ td::Result<std::unique_ptr<vm::AugmentedDictionary>> ShardState::compute_split_o
}
CHECK(out_msg_queue_);
auto subqueue = std::make_unique<vm::AugmentedDictionary>(*out_msg_queue_);
int res = block::filter_out_msg_queue(*subqueue, shard, subshard);
int res = block::filter_out_msg_queue(*subqueue, shard, subshard, queue_size);
if (res < 0) {
return td::Status::Error(-666, "error splitting OutMsgQueue of "s + id_.to_str());
}
@ -1061,7 +1061,7 @@ td::Result<std::shared_ptr<block::MsgProcessedUptoCollection>> ShardState::compu
return std::move(sub_processed_upto);
}
td::Status ShardState::split(ton::ShardIdFull subshard) {
td::Status ShardState::split(ton::ShardIdFull subshard, td::uint32* queue_size) {
if (!ton::shard_is_parent(id_.shard_full(), subshard)) {
return td::Status::Error(-666, "cannot split subshard "s + subshard.to_str() + " from state of " + id_.to_str() +
" because it is not a parent");
@ -1079,7 +1079,7 @@ td::Status ShardState::split(ton::ShardIdFull subshard) {
auto shard1 = id_.shard_full();
CHECK(ton::shard_is_parent(shard1, subshard));
CHECK(out_msg_queue_);
int res1 = block::filter_out_msg_queue(*out_msg_queue_, shard1, subshard);
int res1 = block::filter_out_msg_queue(*out_msg_queue_, shard1, subshard, queue_size);
if (res1 < 0) {
return td::Status::Error(-666, "error splitting OutMsgQueue of "s + id_.to_str());
}
@ -1119,8 +1119,12 @@ td::Status ShardState::split(ton::ShardIdFull subshard) {
return td::Status::OK();
}
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard) {
return out_queue.filter([subshard, old_shard](vm::CellSlice& cs, td::ConstBitPtr key, int key_len) -> int {
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard,
td::uint32* queue_size) {
if (queue_size) {
*queue_size = 0;
}
return out_queue.filter([=](vm::CellSlice& cs, td::ConstBitPtr key, int key_len) -> int {
CHECK(key_len == 352);
LOG(DEBUG) << "scanning OutMsgQueue entry with key " << key.to_hex(key_len);
block::tlb::MsgEnvelope::Record_std env;
@ -1143,7 +1147,11 @@ int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull ol
<< " does not contain current address belonging to shard " << old_shard.to_str();
return -1;
}
return ton::shard_contains(subshard, cur_prefix);
bool res = ton::shard_contains(subshard, cur_prefix);
if (res && queue_size) {
++*queue_size;
}
return res;
});
}

View file

@ -275,6 +275,7 @@ struct BlockLimitStatus {
vm::NewCellStorageStat st_stat;
unsigned accounts{}, transactions{}, extra_out_msgs{};
vm::ProofStorageStat collated_data_stat;
unsigned public_library_diff{};
BlockLimitStatus(const BlockLimits& limits_, ton::LogicalTime lt = 0)
: limits(limits_), cur_lt(std::max(limits_.start_lt, lt)) {
}
@ -284,6 +285,7 @@ struct BlockLimitStatus {
transactions = accounts = 0;
gas_used = 0;
extra_out_msgs = 0;
public_library_diff = 0;
collated_data_stat = {};
}
td::uint64 estimate_block_size(const vm::NewCellStorageStat::Stat* extra = nullptr) const;
@ -445,10 +447,11 @@ struct ShardState {
ton::BlockSeqno prev_mc_block_seqno, bool after_split, bool clear_history,
std::function<bool(ton::BlockSeqno)> for_each_mcseqno);
td::Status merge_with(ShardState& sib);
td::Result<std::unique_ptr<vm::AugmentedDictionary>> compute_split_out_msg_queue(ton::ShardIdFull subshard);
td::Result<std::unique_ptr<vm::AugmentedDictionary>> compute_split_out_msg_queue(ton::ShardIdFull subshard,
td::uint32* queue_size = nullptr);
td::Result<std::shared_ptr<block::MsgProcessedUptoCollection>> compute_split_processed_upto(
ton::ShardIdFull subshard);
td::Status split(ton::ShardIdFull subshard);
td::Status split(ton::ShardIdFull subshard, td::uint32* queue_size = nullptr);
td::Status unpack_out_msg_queue_info(Ref<vm::Cell> out_msg_queue_info);
bool clear_load_history() {
overload_history_ = underload_history_ = 0;
@ -668,7 +671,8 @@ class MtCarloComputeShare {
void gen_vset();
};
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard);
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard,
td::uint32* queue_size = nullptr);
std::ostream& operator<<(std::ostream& os, const ShardId& shard_id);

View file

@ -789,7 +789,8 @@ _ CollatorConfig = ConfigParam 41;
size_limits_config#01 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 = SizeLimitsConfig;
size_limits_config_v2#02 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 max_acc_state_cells:uint32 max_acc_state_bits:uint32 = SizeLimitsConfig;
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 max_acc_state_cells:uint32 max_acc_state_bits:uint32
max_acc_public_libraries:uint32 = SizeLimitsConfig;
_ SizeLimitsConfig = ConfigParam 43;
// key is [ wc:int32 addr:uint256 ]

View file

@ -621,12 +621,14 @@ td::Result<std::vector<StoragePrices>> Config::get_storage_prices() const {
}
vm::Dictionary dict{std::move(cell), 32};
if (!dict.check_for_each([&res](Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n) -> bool {
block::gen::StoragePrices::Record data;
if (!tlb::csr_unpack(std::move(cs_ref), data) || data.utime_since != key.get_uint(n)) {
auto r_prices = do_get_one_storage_prices(*cs_ref);
if (r_prices.is_error()) {
return false;
}
res.push_back(r_prices.move_as_ok());
if (res.back().valid_since != key.get_uint(n)) {
return false;
}
res.emplace_back(data.utime_since, data.bit_price_ps, data.cell_price_ps, data.mc_bit_price_ps,
data.mc_cell_price_ps);
return true;
})) {
return td::Status::Error("invalid storage prices dictionary in configuration parameter 18");
@ -634,16 +636,25 @@ td::Result<std::vector<StoragePrices>> Config::get_storage_prices() const {
return std::move(res);
}
td::Result<GasLimitsPrices> Config::do_get_gas_limits_prices(td::Ref<vm::Cell> cell, int id) {
td::Result<StoragePrices> Config::do_get_one_storage_prices(vm::CellSlice cs) {
block::gen::StoragePrices::Record data;
if (!tlb::unpack(cs, data)) {
return td::Status::Error("invalid storage prices dictionary in configuration parameter 18");
}
return StoragePrices{data.utime_since, data.bit_price_ps, data.cell_price_ps, data.mc_bit_price_ps,
data.mc_cell_price_ps};
}
td::Result<GasLimitsPrices> Config::do_get_gas_limits_prices(vm::CellSlice cs, int id) {
GasLimitsPrices res;
auto cs = vm::load_cell_slice(cell);
vm::CellSlice cs0 = cs;
block::gen::GasLimitsPrices::Record_gas_flat_pfx flat;
if (tlb::unpack(cs, flat)) {
cs = *flat.other;
res.flat_gas_limit = flat.flat_gas_limit;
res.flat_gas_price = flat.flat_gas_price;
} else {
cs = vm::load_cell_slice(cell);
cs = cs0;
}
auto f = [&](const auto& r, td::uint64 spec_limit) {
res.gas_limit = r.gas_limit;
@ -658,7 +669,7 @@ td::Result<GasLimitsPrices> Config::do_get_gas_limits_prices(td::Ref<vm::Cell> c
f(rec, rec.special_gas_limit);
} else {
block::gen::GasLimitsPrices::Record_gas_prices rec0;
if (tlb::unpack(cs, rec0)) {
if (tlb::unpack(cs = cs0, rec0)) {
f(rec0, rec0.gas_limit);
} else {
return td::Status::Error(PSLICE() << "configuration parameter " << id
@ -688,7 +699,7 @@ td::Result<GasLimitsPrices> Config::get_gas_limits_prices(bool is_masterchain) c
if (cell.is_null()) {
return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent");
}
return do_get_gas_limits_prices(std::move(cell), id);
return do_get_gas_limits_prices(vm::load_cell_slice(cell), id);
}
td::Result<MsgPrices> Config::get_msg_prices(bool is_masterchain) const {
@ -697,7 +708,10 @@ td::Result<MsgPrices> Config::get_msg_prices(bool is_masterchain) const {
if (cell.is_null()) {
return td::Status::Error(PSLICE() << "configuration parameter " << id << " with msg prices is absent");
}
auto cs = vm::load_cell_slice(std::move(cell));
return do_get_msg_prices(vm::load_cell_slice(cell), id);
}
td::Result<MsgPrices> Config::do_get_msg_prices(vm::CellSlice cs, int id) {
block::gen::MsgForwardPrices::Record rec;
if (!tlb::unpack(cs, rec)) {
return td::Status::Error(PSLICE() << "configuration parameter " << id
@ -1916,10 +1930,17 @@ std::vector<ton::ValidatorDescr> Config::compute_total_validator_set(int next) c
}
td::Result<SizeLimitsConfig> Config::get_size_limits_config() const {
SizeLimitsConfig limits;
td::Ref<vm::Cell> param = get_config_param(43);
if (param.is_null()) {
return limits;
return do_get_size_limits_config({});
}
return do_get_size_limits_config(vm::load_cell_slice_ref(param));
}
td::Result<SizeLimitsConfig> Config::do_get_size_limits_config(td::Ref<vm::CellSlice> cs) {
SizeLimitsConfig limits;
if (cs.is_null()) {
return limits; // default values
}
auto unpack_v1 = [&](auto& rec) {
limits.max_msg_bits = rec.max_msg_bits;
@ -1934,12 +1955,13 @@ td::Result<SizeLimitsConfig> Config::get_size_limits_config() const {
unpack_v1(rec);
limits.max_acc_state_bits = rec.max_acc_state_bits;
limits.max_acc_state_cells = rec.max_acc_state_cells;
limits.max_acc_public_libraries = rec.max_acc_public_libraries;
};
gen::SizeLimitsConfig::Record_size_limits_config rec_v1;
gen::SizeLimitsConfig::Record_size_limits_config_v2 rec_v2;
if (tlb::unpack_cell(param, rec_v1)) {
if (tlb::csr_unpack(cs, rec_v1)) {
unpack_v1(rec_v1);
} else if (tlb::unpack_cell(param, rec_v2)) {
} else if (tlb::csr_unpack(cs, rec_v2)) {
unpack_v2(rec_v2);
} else {
return td::Status::Error("configuration parameter 43 is invalid");
@ -1974,6 +1996,42 @@ BurningConfig Config::get_burning_config() const {
return c;
}
td::Ref<vm::Tuple> Config::get_unpacked_config_tuple(ton::UnixTime now) const {
auto get_param = [&](td::int32 idx) -> vm::StackEntry {
auto cell = get_config_param(idx);
if (cell.is_null()) {
return {};
}
return vm::load_cell_slice_ref(cell);
};
auto get_current_storage_prices = [&]() -> vm::StackEntry {
auto cell = get_config_param(18);
if (cell.is_null()) {
return {};
}
vm::StackEntry res;
vm::Dictionary dict{std::move(cell), 32};
dict.check_for_each([&](Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n) -> bool {
auto utime_since = key.get_uint(n);
if (now >= utime_since) {
res = std::move(cs_ref);
return true;
}
return false;
});
return res;
};
std::vector<vm::StackEntry> tuple;
tuple.push_back(get_current_storage_prices()); // storage_prices
tuple.push_back(get_param(19)); // global_id
tuple.push_back(get_param(20)); // config_mc_gas_prices
tuple.push_back(get_param(21)); // config_gas_prices
tuple.push_back(get_param(24)); // config_mc_fwd_prices
tuple.push_back(get_param(25)); // config_fwd_prices
tuple.push_back(get_param(43)); // size_limits_config
return td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
}
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");

View file

@ -350,7 +350,11 @@ struct GasLimitsPrices {
td::uint64 freeze_due_limit{0};
td::uint64 delete_due_limit{0};
td::RefInt256 compute_gas_price(td::uint64 gas_used) const;
td::RefInt256 compute_gas_price(td::uint64 gas_used) const {
return gas_used <= flat_gas_limit
? td::make_refint(flat_gas_price)
: td::rshift(td::make_refint(gas_price) * (gas_used - flat_gas_limit), 16, 1) + flat_gas_price;
}
};
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
@ -365,6 +369,7 @@ struct MsgPrices {
td::uint32 first_frac;
td::uint32 next_frac;
td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const;
td::RefInt256 compute_fwd_fees256(td::uint64 cells, td::uint64 bits) const;
std::pair<td::uint64, td::uint64> compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits,
bool ihr_disabled = false) const;
MsgPrices() = default;
@ -389,6 +394,7 @@ struct SizeLimitsConfig {
ExtMsgLimits ext_msg_limits;
td::uint32 max_acc_state_cells = 1 << 16;
td::uint32 max_acc_state_bits = (1 << 16) * 1023;
td::uint32 max_acc_public_libraries = 256;
};
struct CatchainValidatorsConfig {
@ -614,9 +620,11 @@ class Config {
bool is_special_smartcontract(const ton::StdSmcAddress& addr) const;
static td::Result<std::unique_ptr<ValidatorSet>> unpack_validator_set(Ref<vm::Cell> valset_root);
td::Result<std::vector<StoragePrices>> get_storage_prices() const;
static td::Result<StoragePrices> do_get_one_storage_prices(vm::CellSlice cs);
td::Result<GasLimitsPrices> get_gas_limits_prices(bool is_masterchain = false) const;
static td::Result<GasLimitsPrices> do_get_gas_limits_prices(td::Ref<vm::Cell> cell, int id);
static td::Result<GasLimitsPrices> do_get_gas_limits_prices(vm::CellSlice cs, int id);
td::Result<MsgPrices> get_msg_prices(bool is_masterchain = false) const;
static td::Result<MsgPrices> do_get_msg_prices(vm::CellSlice cs, int id);
static CatchainValidatorsConfig unpack_catchain_validators_config(Ref<vm::Cell> cell);
CatchainValidatorsConfig get_catchain_validators_config() const;
td::Status visit_validator_params() const;
@ -644,8 +652,10 @@ class Config {
std::vector<ton::ValidatorDescr> compute_total_validator_set(int next) const;
CollatorConfig get_collator_config(bool need_collator_nodes) const;
td::Result<SizeLimitsConfig> get_size_limits_config() const;
static td::Result<SizeLimitsConfig> do_get_size_limits_config(td::Ref<vm::CellSlice> cs);
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;
static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time,

File diff suppressed because it is too large Load diff

View file

@ -104,6 +104,8 @@ struct ComputePhaseConfig {
td::uint64 gas_credit;
td::uint64 flat_gas_limit = 0;
td::uint64 flat_gas_price = 0;
bool special_gas_full = false;
block::GasLimitsPrices mc_gas_prices;
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
td::RefInt256 gas_price256;
td::RefInt256 max_gas_threshold;
@ -115,15 +117,13 @@ struct ComputePhaseConfig {
td::uint16 max_vm_data_depth = 512;
int global_version = 0;
Ref<vm::Tuple> prev_blocks_info;
Ref<vm::Tuple> unpacked_config_tuple;
std::unique_ptr<vm::Dictionary> suspended_addresses;
SizeLimitsConfig size_limits;
int vm_log_verbosity = 0;
bool stop_on_accept_message = false;
ComputePhaseConfig(td::uint64 _gas_price = 0, td::uint64 _gas_limit = 0, td::uint64 _gas_credit = 0)
: gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_gas_limit), gas_credit(_gas_credit) {
compute_threshold();
}
ComputePhaseConfig(td::uint64 _gas_price, td::uint64 _gas_limit, td::uint64 _spec_gas_limit, td::uint64 _gas_credit)
: gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_spec_gas_limit), gas_credit(_gas_credit) {
ComputePhaseConfig() : gas_price(0), gas_limit(0), special_gas_limit(0), gas_credit(0) {
compute_threshold();
}
void compute_threshold();
@ -270,7 +270,7 @@ struct Account {
return balance;
}
bool set_address(ton::WorkchainId wc, td::ConstBitPtr new_addr);
bool unpack(Ref<vm::CellSlice> account, Ref<vm::CellSlice> extra, ton::UnixTime now, bool special = false);
bool unpack(Ref<vm::CellSlice> account, ton::UnixTime now, bool special);
bool init_new(ton::UnixTime now);
bool deactivate();
bool recompute_tmp_addr(Ref<vm::CellSlice>& tmp_addr, int split_depth, td::ConstBitPtr orig_addr_rewrite) const;
@ -361,18 +361,20 @@ struct Transaction {
std::unique_ptr<ActionPhase> action_phase;
std::unique_ptr<BouncePhase> bounce_phase;
vm::CellStorageStat new_storage_stat;
bool gas_limit_overridden{false};
Transaction(const Account& _account, int ttype, ton::LogicalTime req_start_lt, ton::UnixTime _now,
Ref<vm::Cell> _inmsg = {});
bool unpack_input_msg(bool ihr_delivered, const ActionPhaseConfig* cfg);
bool check_in_msg_state_hash();
bool prepare_storage_phase(const StoragePhaseConfig& cfg, bool force_collect = true, bool adjust_msg_value = false);
bool prepare_credit_phase();
td::uint64 gas_bought_for(const ComputePhaseConfig& cfg, td::RefInt256 nanograms);
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 prepare_compute_phase(const ComputePhaseConfig& cfg);
bool prepare_action_phase(const ActionPhaseConfig& cfg);
td::Status check_state_limits(const ActionPhaseConfig& cfg);
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true);
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
bool compute_state();
bool serialize();
@ -382,9 +384,7 @@ struct Transaction {
td::Result<vm::NewCellStorageStat::Stat> estimate_block_storage_profile_incr(
const vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
bool update_block_storage_profile(vm::NewCellStorageStat& store_stat, const vm::CellUsageTree* usage_tree) const;
bool would_fit(unsigned cls, const block::BlockLimitStatus& blk_lim_st) const;
bool update_limits(block::BlockLimitStatus& blk_lim_st, bool with_size = true) const;
bool update_limits(block::BlockLimitStatus& blk_lim_st, bool with_gas = true, bool with_size = true) const;
Ref<vm::Cell> commit(Account& _account); // _account should point to the same account
LtCellRef extract_out_msg(unsigned i);
@ -406,7 +406,7 @@ struct Transaction {
bool serialize_compute_phase(vm::CellBuilder& cb);
bool serialize_action_phase(vm::CellBuilder& cb);
bool serialize_bounce_phase(vm::CellBuilder& cb);
bool unpack_msg_state(bool lib_only = false);
bool unpack_msg_state(const ComputePhaseConfig& cfg, bool lib_only = false, bool forbid_public_libs = false);
};
} // namespace transaction

View file

@ -130,7 +130,7 @@ void bits_memcpy(unsigned char* to, int to_offs, const unsigned char* from, int
from_offs &= 7;
to_offs &= 7;
//fprintf(stderr, "bits_memcpy: from=%p (%02x) to=%p (%02x) from_offs=%d to_offs=%d count=%lu\n", from, *from, to, *to, from_offs, to_offs, bit_count);
int sz = (int)bit_count;
int sz = static_cast<int>(bit_count);
bit_count += from_offs;
if (from_offs == to_offs) {
if (bit_count < 8) {
@ -206,7 +206,7 @@ void bits_memset(unsigned char* to, int to_offs, bool val, std::size_t bit_count
}
to += (to_offs >> 3);
to_offs &= 7;
int sz = (int)bit_count;
int sz = static_cast<int>(bit_count);
bit_count += to_offs;
int c = *to;
if (bit_count <= 8) {

View file

@ -2,7 +2,7 @@ library TVM_Asm
// simple TVM Assembler
namespace Asm
Asm definitions
"0.4.4" constant asm-fif-version
"0.4.5" constant asm-fif-version
variable @atend
variable @was-split
@ -461,19 +461,109 @@ x{B7A3} @Defop QNEGATE
x{B7A4} @Defop QINC
x{B7A5} @Defop QDEC
x{B7A8} @Defop QMUL
x{B7A904} @Defop QDIV
x{B7A905} @Defop QDIVR
x{B7A906} @Defop QDIVC
x{B7A908} @Defop QMOD
x{B7A909} @Defop QMODR
x{B7A90A} @Defop QMODC
x{B7A90C} @Defop QDIVMOD
x{B7A90D} @Defop QDIVMODR
x{B7A90E} @Defop QDIVMODC
x{B7A900} @Defop QADDDIVMOD
x{B7A901} @Defop QADDDIVMODR
x{B7A902} @Defop QADDDIVMODC
x{B7A925} @Defop QRSHIFTR
x{B7A926} @Defop QRSHIFTC
x{B7A928} @Defop QMODPOW2
x{B7A929} @Defop QMODPOW2R
x{B7A92A} @Defop QMODPOW2C
x{B7A92C} @Defop QRSHIFTMOD
x{B7A92D} @Defop QRSHIFTMODR
x{B7A92E} @Defop QRSHIFTMODC
x{B7A920} @Defop QADDRSHIFTMOD
x{B7A921} @Defop QADDRSHIFTMODR
x{B7A922} @Defop QADDRSHIFTMODC
x{B7A935} @Defop(8u+1) QRSHIFTR#
x{B7A936} @Defop(8u+1) QRSHIFTC#
x{B7A938} @Defop(8u+1) QMODPOW2#
x{B7A939} @Defop(8u+1) QMODPOW2R#
x{B7A93A} @Defop(8u+1) QMODPOW2C#
x{B7A93C} @Defop(8u+1) QRSHIFT#MOD
x{B7A93D} @Defop(8u+1) QRSHIFTR#MOD
x{B7A93E} @Defop(8u+1) QRSHIFTC#MOD
x{B7A930} @Defop(8u+1) QADDRSHIFT#MOD
x{B7A931} @Defop(8u+1) QADDRSHIFTR#MOD
x{B7A932} @Defop(8u+1) QADDRSHIFTC#MOD
x{B7A984} @Defop QMULDIV
x{B7A985} @Defop QMULDIVR
x{B7A986} @Defop QMULDIVC
x{B7A988} @Defop QMULMOD
x{B7A989} @Defop QMULMODR
x{B7A98A} @Defop QMULMODC
x{B7A98C} @Defop QMULDIVMOD
x{B7A980} @Defop QADDMULDIVMOD
x{B7A98D} @Defop QMULDIVMODR
x{B7A98E} @Defop QMULDIVMODC
x{B7A980} @Defop QMULADDDIVMOD
x{B7A981} @Defop QMULADDDIVMODR
x{B7A982} @Defop QMULADDDIVMODC
x{B7A9A4} @Defop QMULRSHIFT
x{B7A9A5} @Defop QMULRSHIFTR
x{B7A9A6} @Defop QMULRSHIFTC
x{B7A9A8} @Defop QMULMODPOW2
x{B7A9A9} @Defop QMULMODPOW2R
x{B7A9AA} @Defop QMULMODPOW2C
x{B7A9AC} @Defop QMULRSHIFTMOD
x{B7A9AD} @Defop QMULRSHIFTRMOD
x{B7A9AE} @Defop QMULRSHIFTCMOD
x{B7A9A0} @Defop QMULADDRSHIFTMOD
x{B7A9A1} @Defop QMULADDRSHIFTRMOD
x{B7A9A2} @Defop QMULADDRSHIFTCMOD
x{B7A9B4} @Defop(8u+1) QMULRSHIFT#
x{B7A9B5} @Defop(8u+1) QMULRSHIFTR#
x{B7A9B6} @Defop(8u+1) QMULRSHIFTC#
x{B7A9B8} @Defop(8u+1) QMULMODPOW2#
x{B7A9B9} @Defop(8u+1) QMULMODPOW2R#
x{B7A9BA} @Defop(8u+1) QMULMODPOW2C#
x{B7A9BC} @Defop(8u+1) QMULRSHIFT#MOD
x{B7A9BD} @Defop(8u+1) QMULRSHIFTR#MOD
x{B7A9BE} @Defop(8u+1) QMULRSHIFTC#MOD
x{B7A9B0} @Defop(8u+1) QMULADDRSHIFT#MOD
x{B7A9B1} @Defop(8u+1) QMULADDRSHIFTR#MOD
x{B7A9B2} @Defop(8u+1) QMULADDRSHIFTC#MOD
x{B7A9C4} @Defop QLSHIFTDIV
x{B7A9C5} @Defop QLSHIFTDIVR
x{B7A9C6} @Defop QLSHIFTDIVC
x{B7A9C8} @Defop QLSHIFTMOD
x{B7A9C9} @Defop QLSHIFTMODR
x{B7A9CA} @Defop QLSHIFTMODC
x{B7A9CC} @Defop QLSHIFTDIVMOD
x{B7A9CD} @Defop QLSHIFTDIVMODR
x{B7A9CE} @Defop QLSHIFTDIVMODC
x{B7A9C0} @Defop QLSHIFTADDDIVMOD
x{B7A9C1} @Defop QLSHIFTADDDIVMODR
x{B7A9C2} @Defop QLSHIFTADDDIVMODC
x{B7A9D4} @Defop(8u+1) QLSHIFT#DIV
x{B7A9D5} @Defop(8u+1) QLSHIFT#DIVR
x{B7A9D6} @Defop(8u+1) QLSHIFT#DIVC
x{B7A9D8} @Defop(8u+1) QLSHIFT#MOD
x{B7A9D9} @Defop(8u+1) QLSHIFT#MODR
x{B7A9DA} @Defop(8u+1) QLSHIFT#MODC
x{B7A9DC} @Defop(8u+1) QLSHIFT#DIVMOD
x{B7A9DD} @Defop(8u+1) QLSHIFT#DIVMODR
x{B7A9DE} @Defop(8u+1) QLSHIFT#DIVMODC
x{B7A9D0} @Defop(8u+1) QLSHIFT#ADDDIVMOD
x{B7A9D1} @Defop(8u+1) QLSHIFT#ADDDIVMODR
x{B7A9D2} @Defop(8u+1) QLSHIFT#ADDDIVMODC
x{B7AC} @Defop QLSHIFT
x{B7AD} @Defop QRSHIFT
x{B7AE} @Defop QPOW2
@ -723,6 +813,12 @@ x{D761} @Defop LDONES
x{D762} @Defop LDSAME
x{D764} @Defop SDEPTH
x{D765} @Defop CDEPTH
x{D766} @Defop CLEVEL
x{D767} @Defop CLEVELMASK
{ <b x{D76A_} s, swap 2 u, @addopb } : CHASHI
{ <b x{D76E_} s, swap 2 u, @addopb } : CDEPTHI
x{D770} @Defop CHASHIX
x{D771} @Defop CDEPTHIX
//
// continuation / flow control primitives
x{D8} dup @Defop EXECUTE @Defop CALLX
@ -1185,7 +1281,7 @@ x{F4BF} @Defop DICTUGETEXECZ
x{F800} @Defop ACCEPT
x{F801} @Defop SETGASLIMIT
x{F802} @Defop GASCONSUMED
x{F807} @Defop GASCONSUMED
x{F80F} @Defop COMMIT
x{F810} @Defop RANDU256
@ -1205,12 +1301,21 @@ x{F82A} @Defop MYCODE
x{F82B} @Defop INCOMINGVALUE
x{F82C} @Defop STORAGEFEES
x{F82D} @Defop PREVBLOCKSINFOTUPLE
x{F82E} @Defop UNPACKEDCONFIGTUPLE
x{F82F} @Defop DUEPAYMENT
x{F830} @Defop CONFIGDICT
x{F832} @Defop CONFIGPARAM
x{F833} @Defop CONFIGOPTPARAM
x{F83400} @Defop PREVMCBLOCKS
x{F83401} @Defop PREVKEYBLOCK
x{F835} @Defop GLOBALID
x{F836} @Defop GETGASFEE
x{F837} @Defop GETSTORAGEFEE
x{F838} @Defop GETFORWARDFEE
x{F839} @Defop GETPRECOMPILEDGAS
x{F83A} @Defop GETORIGINALFWDFEE
x{F83B} @Defop GETGASFEESIMPLE
x{F83C} @Defop GETFORWARDFEESIMPLE
x{F840} @Defop GETGLOBVAR
{ dup 1 31 @rangechk <b x{F85_} s, swap 5 u, @addopb } : GETGLOB

View file

@ -3512,7 +3512,7 @@ void init_words_ton(Dictionary& d) {
void init_words_vm(Dictionary& d, bool enable_debug) {
using namespace std::placeholders;
vm::init_op_cp0(enable_debug);
vm::init_vm(enable_debug).ensure();
// vm run
d.def_word("vmlibs ", LitCont::literal(vm_libraries));
// d.def_ctx_word("runvmcode ", std::bind(interpret_run_vm, _1, 0x40));

View file

@ -8,37 +8,37 @@ import shutil
add_pragmas = [] #["allow-post-modification", "compute-asm-ltr"];
tests = [
# note, that deployed version of elector,config and multisig differ since it is compilled with func-0.1.0.
# Newer compillators optimize arithmetic and logic expression that can be calculated at the compile time
["elector/elector-code.fc", 115226404411715505328583639896096915745686314074575650766750648324043316883483],
["config/config-code.fc", 10913070768607625342121305745084703121685937915388357634624451844356456145601],
["eth-bridge-multisig/multisig-code.fc", 101509909129354488841890823627011033360100627957439967918234053299675481277954],
# note, that deployed version of elector,config and multisig differ since it is compilled with func-0.1.0.
# Newer compillators optimize arithmetic and logic expression that can be calculated at the compile time
["elector/elector-code.fc", 115226404411715505328583639896096915745686314074575650766750648324043316883483],
["config/config-code.fc", 10913070768607625342121305745084703121685937915388357634624451844356456145601],
["eth-bridge-multisig/multisig-code.fc", 101509909129354488841890823627011033360100627957439967918234053299675481277954],
["bsc-bridge-collector/votes-collector.fc", 62190447221288642706570413295807615918589884489514159926097051017036969900417],
["uni-lock-wallet/uni-lockup-wallet.fc", 61959738324779104851267145467044677651344601417998258530238254441977103654381],
["nft-collection/nft-collection-editable.fc", 45561997735512210616567774035540357815786262097548276229169737015839077731274],
["dns-collection/nft-collection.fc", 107999822699841936063083742021519765435859194241091312445235370766165379261859],
["bsc-bridge-collector/votes-collector.fc", 62190447221288642706570413295807615918589884489514159926097051017036969900417],
["uni-lock-wallet/uni-lockup-wallet.fc", 61959738324779104851267145467044677651344601417998258530238254441977103654381],
["nft-collection/nft-collection-editable.fc", 45561997735512210616567774035540357815786262097548276229169737015839077731274],
["dns-collection/nft-collection.fc", 107999822699841936063083742021519765435859194241091312445235370766165379261859],
# note, that deployed version of tele-nft-item differs since it is compilled with func-0.3.0.
# After introducing of try/catch construction, c2 register is not always the default one.
# Thus it is necessary to save it upon jumps, differences of deployed and below compilled is that
# "c2 SAVE" is added to the beginning of recv_internal. It does not change behavior.
["tele-nft-item/nft-item.fc", 69777543125381987786450436977742010705076866061362104025338034583422166453344],
# note, that deployed version of tele-nft-item differs since it is compilled with func-0.3.0.
# After introducing of try/catch construction, c2 register is not always the default one.
# Thus it is necessary to save it upon jumps, differences of deployed and below compilled is that
# "c2 SAVE" is added to the beginning of recv_internal. It does not change behavior.
["tele-nft-item/nft-item.fc", 69777543125381987786450436977742010705076866061362104025338034583422166453344],
["storage/storage-contract.fc", 91377830060355733016937375216020277778264560226873154627574229667513068328151],
["storage/storage-provider.fc", 13618336676213331164384407184540461509022654507176709588621016553953760588122],
["nominator-pool/pool.fc", 69767057279163099864792356875696330339149706521019810113334238732928422055375],
["jetton-minter/jetton-minter.fc", 9028309926287301331466371999814928201427184114165428257502393474125007156494],
["gg-marketplace/nft-marketplace-v2.fc", 92199806964112524639740773542356508485601908152150843819273107618799016205930],
["jetton-wallet/jetton-wallet.fc", 86251125787443633057458168028617933212663498001665054651523310772884328206542],
["whales-nominators/nominators.fc", 8941364499854379927692172316865293429893094891593442801401542636695127885153],
["storage/storage-contract.fc", 91377830060355733016937375216020277778264560226873154627574229667513068328151],
["storage/storage-provider.fc", 13618336676213331164384407184540461509022654507176709588621016553953760588122],
["nominator-pool/pool.fc", 69767057279163099864792356875696330339149706521019810113334238732928422055375],
["jetton-minter/jetton-minter.fc", 9028309926287301331466371999814928201427184114165428257502393474125007156494],
["gg-marketplace/nft-marketplace-v2.fc", 92199806964112524639740773542356508485601908152150843819273107618799016205930],
["jetton-wallet/jetton-wallet.fc", 86251125787443633057458168028617933212663498001665054651523310772884328206542],
["whales-nominators/nominators.fc", 8941364499854379927692172316865293429893094891593442801401542636695127885153],
["tact-examples/treasure_Treasure.code.fc", 13962538639825790677138656603323869918938565499584297120566680287245364723897],
["tact-examples/jetton_SampleJetton.code.fc", 94076762218493729104783735200107713211245710256802265203823917715299139499110],
["tact-examples/jetton_JettonDefaultWallet.code.fc", 29421313492520031238091587108198906058157443241743283101866538036369069620563],
["tact-examples/maps_MapTestContract.code.fc", 22556550222249123835909180266811414538971143565993192846012583552876721649744],
["tact-examples/treasure_Treasure.code.fc", 13962538639825790677138656603323869918938565499584297120566680287245364723897],
["tact-examples/jetton_SampleJetton.code.fc", 94076762218493729104783735200107713211245710256802265203823917715299139499110],
["tact-examples/jetton_JettonDefaultWallet.code.fc", 29421313492520031238091587108198906058157443241743283101866538036369069620563],
["tact-examples/maps_MapTestContract.code.fc", 22556550222249123835909180266811414538971143565993192846012583552876721649744],
]
def getenv(name, default=None):
@ -51,7 +51,6 @@ def getenv(name, default=None):
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
FIFT_LIBS = getenv("FIFTPATH")
TMP_DIR = tempfile.mkdtemp()
COMPILED_FIF = os.path.join(TMP_DIR, "compiled.fif")
@ -63,49 +62,49 @@ class ExecutionError(Exception):
pass
def pre_process_func(f):
shutil.copyfile(f, f+"_backup")
with open(f, "r") as src:
sources = src.read()
with open(f, "w") as src:
for pragma in add_pragmas:
src.write("#pragma %s;\n"%pragma)
src.write(sources)
shutil.copyfile(f, f+"_backup")
with open(f, "r") as src:
sources = src.read()
with open(f, "w") as src:
for pragma in add_pragmas:
src.write("#pragma %s;\n"%pragma)
src.write(sources)
def post_process_func(f):
shutil.move(f+"_backup", f)
shutil.move(f+"_backup", f)
def compile_func(f):
res = None
try:
pre_process_func(f)
if "storage-provider.fc" in f :
# This contract requires building of storage-contract to include it as ref
with open(f, "r") as src:
sources = src.read()
# This contract requires building of storage-contract to include it as ref
with open(f, "r") as src:
sources = src.read()
COMPILED_ST_BOC = os.path.join(TMP_DIR, "storage-contract-code.boc")
sources = sources.replace("storage-contract-code.boc", COMPILED_ST_BOC)
with open(f, "w") as src:
src.write(sources)
COMPILED_ST_FIF = os.path.join(TMP_DIR, "storage-contract.fif")
COMPILED_ST_BOC = os.path.join(TMP_DIR, "storage-contract-code.boc")
sources = sources.replace("storage-contract-code.boc", COMPILED_ST_BOC)
with open(f, "w") as src:
src.write(sources)
COMPILED_ST_FIF = os.path.join(TMP_DIR, "storage-contract.fif")
COMPILED_ST_BOC = os.path.join(TMP_DIR, "storage-contract-code.boc")
COMPILED_BUILD_BOC = os.path.join(TMP_DIR, "build-boc.fif")
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_ST_FIF, "-SPA", f.replace("storage-provider.fc","storage-contract.fc")], capture_output=False, timeout=10)
with open(COMPILED_BUILD_BOC, "w") as scr:
scr.write("\"%s\" include boc>B \"%s\" B>file "%(COMPILED_ST_FIF, COMPILED_ST_BOC))
res = subprocess.run([FIFT_EXECUTABLE, COMPILED_BUILD_BOC ], capture_output=True, timeout=10)
COMPILED_BUILD_BOC = os.path.join(TMP_DIR, "build-boc.fif")
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_ST_FIF, "-SPA", f.replace("storage-provider.fc","storage-contract.fc")], capture_output=False, timeout=10)
with open(COMPILED_BUILD_BOC, "w") as scr:
scr.write("\"%s\" include boc>B \"%s\" B>file "%(COMPILED_ST_FIF, COMPILED_ST_BOC))
res = subprocess.run([FIFT_EXECUTABLE, COMPILED_BUILD_BOC ], capture_output=True, timeout=10)
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_FIF, "-SPA", f], capture_output=True, timeout=10)
except Exception as e:
post_process_func(f)
raise e
post_process_func(f)
raise e
else:
post_process_func(f)
post_process_func(f)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
def run_runner():
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, RUNNER_FIF], capture_output=True, timeout=10)
res = subprocess.run([FIFT_EXECUTABLE, RUNNER_FIF], capture_output=True, timeout=10)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
s = str(res.stdout, "utf-8")
@ -138,16 +137,15 @@ for ti, t in enumerate(tests):
try:
func_out = run_runner()
if func_out != th:
raise ExecutionError("Error : expected '%d', found '%d'" % (th, func_out))
raise ExecutionError("Error : expected '%d', found '%d'" % (th, func_out))
success += 1
except ExecutionError as e:
print(e, file=sys.stderr)
#print("Compiled:", file=sys.stderr)
#with open(COMPILED_FIF, "r") as f:
# print(f.read(), file=sys.stderr)
#exit(2)
print("Compiled:", file=sys.stderr)
with open(COMPILED_FIF, "r") as f:
print(f.read(), file=sys.stderr)
exit(2)
print(" OK ", file=sys.stderr)
print(get_version())
print("Done: Success %d, Error: %d"%(success, len(tests)-success), file=sys.stderr)
print("Done: Success %d, Error: %d"%(success, len(tests)-success), file=sys.stderr)

View file

@ -4,6 +4,7 @@ import subprocess
import sys
import tempfile
def getenv(name, default=None):
if name in os.environ:
return os.environ[name]
@ -12,10 +13,9 @@ def getenv(name, default=None):
exit(1)
return default
FUNC_EXECUTABLE = getenv("FUNC_EXECUTABLE", "func")
FIFT_EXECUTABLE = getenv("FIFT_EXECUTABLE", "fift")
#FUNC_STDLIB = getenv("FUNC_STDLIB")
FIFT_LIBS = getenv("FIFT_LIBS")
TMP_DIR = tempfile.mkdtemp()
COMPILED_FIF = os.path.join(TMP_DIR, "compiled.fif")
RUNNER_FIF = os.path.join(TMP_DIR, "runner.fif")
@ -25,22 +25,26 @@ if len(sys.argv) != 2:
exit(1)
TESTS_DIR = sys.argv[1]
class ExecutionError(Exception):
pass
def compile_func(f):
res = subprocess.run([FUNC_EXECUTABLE, "-o", COMPILED_FIF, "-SPA", f], capture_output=True, timeout=10)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
def run_runner():
res = subprocess.run([FIFT_EXECUTABLE, "-I", FIFT_LIBS, RUNNER_FIF], capture_output=True, timeout=10)
res = subprocess.run([FIFT_EXECUTABLE, RUNNER_FIF], capture_output=True, timeout=10)
if res.returncode != 0:
raise ExecutionError(str(res.stderr, "utf-8"))
s = str(res.stdout, "utf-8")
s = [x.strip() for x in s.split("\n")]
return [x for x in s if x != ""]
tests = [s for s in os.listdir(TESTS_DIR) if s.endswith(".fc")]
tests.sort()
print("Found", len(tests), "tests", file=sys.stderr)
@ -68,18 +72,18 @@ for ti, tf in enumerate(tests):
# preprocess arithmetics in input
for i in range(len(cases)):
inputs = cases[i][1].split(" ")
processed_inputs = ""
for in_arg in inputs:
if "x{" in in_arg:
processed_inputs += in_arg
continue
# filter and execute
# is it safe enough?
filtered_in = "".join(filter(lambda x: x in "0x123456789()+-*/<>", in_arg))
if(filtered_in):
processed_inputs += str(eval(filtered_in)) + " ";
cases[i][1] = processed_inputs.strip()
inputs = cases[i][1].split(" ")
processed_inputs = ""
for in_arg in inputs:
if "x{" in in_arg:
processed_inputs += in_arg
continue
# filter and execute
# is it safe enough?
filtered_in = "".join(filter(lambda x: x in "0x123456789()+-*/<>", in_arg))
if filtered_in:
processed_inputs += str(eval(filtered_in)) + " "
cases[i][1] = processed_inputs.strip()
with open(RUNNER_FIF, "w") as f:
print("\"%s\" include <s constant code" % COMPILED_FIF, file=f)
@ -103,4 +107,4 @@ for ti, tf in enumerate(tests):
exit(2)
print(" OK, %d cases" % len(cases), file=sys.stderr)
print("Done", file=sys.stderr)
print("Done", file=sys.stderr)

View file

@ -232,6 +232,7 @@ Bignum& Bignum::import_lsb(const unsigned char* buffer, std::size_t size) {
std::string Bignum::to_str() const {
char* ptr = BN_bn2dec(val);
CHECK(ptr);
std::string z(ptr);
OPENSSL_free(ptr);
return z;
@ -239,6 +240,7 @@ std::string Bignum::to_str() const {
std::string Bignum::to_hex() const {
char* ptr = BN_bn2hex(val);
CHECK(ptr);
std::string z(ptr);
OPENSSL_free(ptr);
return z;
@ -255,7 +257,13 @@ std::istream& operator>>(std::istream& is, Bignum& x) {
return is;
}
bool is_prime(const Bignum& p, int nchecks, bool trial_div) {
return BN_is_prime_fasttest_ex(p.bn_ptr(), BN_prime_checks, get_ctx(), trial_div, 0);
bool is_prime(const Bignum& p) {
#if OPENSSL_VERSION_MAJOR >= 3
int result = BN_check_prime(p.bn_ptr(), get_ctx(), nullptr);
LOG_IF(FATAL, result == -1);
return result;
#else
return BN_is_prime_fasttest_ex(p.bn_ptr(), BN_prime_checks, get_ctx(), true, 0);
#endif
}
} // namespace arith

View file

@ -335,7 +335,7 @@ const Bignum sqr(const Bignum& x);
std::ostream& operator<<(std::ostream& os, const Bignum& x);
std::istream& operator>>(std::istream& is, Bignum& x);
bool is_prime(const Bignum& p, int nchecks = 64, bool trial_div = true);
bool is_prime(const Bignum& p);
inline int cmp(const Bignum& x, const Bignum& y) {
return BN_cmp(x.bn_ptr(), y.bn_ptr());

View file

@ -250,7 +250,6 @@ const Lexem& Lexer::next() {
}
if (is_multiline_quote(src.get_ptr(), src.get_end_ptr())) {
src.advance(multiline_quote.size());
const char* begin = src.get_ptr();
const char* end = nullptr;
SrcLocation here = src.here();
std::string body;

View file

@ -3,6 +3,22 @@
;; this version does not use seqno for replay protection; instead, it remembers all recent query_ids
;; in this way several external messages with different query_id can be sent in parallel
;; Note, when dealing with highload-wallet the following limits need to be checked and taken into account:
;; 1) Storage size limit. Currently, size of contract storage should be less than 65535 cells. If size of
;; old_queries will grow above this limit, exception in ActionPhase will be thrown and transaction will fail.
;; Failed transaction may be replayed.
;; 2) Gas limit. Currently, gas limit is 1'000'000 gas units, that means that there is a limit of how much
;; old queries may be cleaned in one tx. If number of expired queries will be higher, contract will stuck.
;; That means that it is not recommended to set too high expiration date:
;; number of queries during expiration timespan should not exceed 1000.
;; Also, number of expired queries cleaned in one transaction should be below 100.
;; Such precautions are not easy to follow, so it is recommended to use highload contract
;; only when strictly necessary and the developer understands the above details.
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}

View file

@ -155,7 +155,7 @@ td::Result<td::uint32> GenericAccount::get_wallet_id(const SmartContract& sc) {
return TRY_VM([&]() -> td::Result<td::uint32> {
auto answer = sc.run_get_method("wallet_id");
if (!answer.success) {
return td::Status::Error("seqno get method failed");
return td::Status::Error("wallet_id get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());

View file

@ -305,7 +305,7 @@ class ManualDns : public ton::SmartContract, public DnsInterface {
if (!info.known_category.insert(action.category).second) {
continue;
}
if (action.category == 0) {
if (action.category.is_zero()) {
info.closed = true;
auto old_actions = std::move(info.actions);
bool is_empty = true;
@ -327,7 +327,7 @@ class ManualDns : public ton::SmartContract, public DnsInterface {
if (info.closed) {
CombinedActions<ActionT> ca;
ca.name = it.first;
ca.category = 0;
ca.category = td::Bits256::zero();
if (!info.actions.empty() || info.non_empty) {
ca.actions = std::move(info.actions);
}

View file

@ -171,6 +171,10 @@ td::Ref<vm::Tuple> prepare_vm_c7(SmartContract::Args args, td::Ref<vm::Cell> cod
// prev_key_block:BlockId ] : PrevBlocksInfo
tuple.push_back(args.prev_blocks_info ? args.prev_blocks_info.value() : vm::StackEntry{}); // prev_block_info
}
if (args.config && args.config.value()->get_global_version() >= 6) {
tuple.push_back(args.config.value()->get_unpacked_config_tuple(now)); // unpacked_config_tuple
tuple.push_back(td::zero_refint()); // due_payment
}
auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
//LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
return vm::make_tuple_ref(std::move(tuple_ref));
@ -181,7 +185,7 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
int vm_log_verbosity, bool debug_enabled,
std::shared_ptr<const block::Config> config) {
auto gas_credit = gas.gas_credit;
vm::init_op_cp0(debug_enabled);
vm::init_vm(debug_enabled).ensure();
vm::DictionaryBase::get_empty_dictionary();
class Logger : public td::LogInterface {

View file

@ -28,6 +28,7 @@ namespace {
// WALLET_REVISION = 2;
// WALLET2_REVISION = 2;
// WALLET3_REVISION = 2;
// WALLET4_REVISION = 2;
// HIGHLOAD_WALLET_REVISION = 2;
// HIGHLOAD_WALLET2_REVISION = 2;
// DNS_REVISION = 1;
@ -92,6 +93,20 @@ const auto& get_map() {
"AAXrc52omhpn5jrhf/AABesePaiaGmPmOuFj8ABDbbYHwR7Z5AOAQm1B1tnkA4BTu1E0IEBQNch0x/"
"0BNEC2zz4J28QAoAg9HtvpTGX+gAwoXC2CZEw4g8AOiGOETGA8/gzIG6SMHCU0NcLH+IB3yGSAaGSW3/iAAzTB9QC+wAAHssfFMsfEsv/yx/"
"0AMntVA==");
with_tvm_code(
"wallet-v4-r2",
"te6cckECFAEAAtQAART/APSkE/S88sgLAQIBIAIDAgFIBAUE+PKDCNcYINMf0x/THwL4I7vyZO1E0NMf0x/T//"
"QE0VFDuvKhUVG68qIF+QFUEGT5EPKj+AAkpMjLH1JAyx9SMMv/"
"UhD0AMntVPgPAdMHIcAAn2xRkyDXSpbTB9QC+wDoMOAhwAHjACHAAuMAAcADkTDjDQOkyMsfEssfy/"
"8QERITAubQAdDTAyFxsJJfBOAi10nBIJJfBOAC0x8hghBwbHVnvSKCEGRzdHK9sJJfBeAD+kAwIPpEAcjKB8v/"
"ydDtRNCBAUDXIfQEMFyBAQj0Cm+hMbOSXwfgBdM/"
"yCWCEHBsdWe6kjgw4w0DghBkc3RyupJfBuMNBgcCASAICQB4AfoA9AQw+CdvIjBQCqEhvvLgUIIQcGx1Z4MesXCAGFAEywUmzxZY+"
"gIZ9ADLaRfLH1Jgyz8gyYBA+wAGAIpQBIEBCPRZMO1E0IEBQNcgyAHPFvQAye1UAXKwjiOCEGRzdHKDHrFwgBhQBcsFUAPPFiP6AhPLassfyz/"
"JgED7AJJfA+ICASAKCwBZvSQrb2omhAgKBrkPoCGEcNQICEekk30pkQzmkD6f+YN4EoAbeBAUiYcVnzGEAgFYDA0AEbjJftRNDXCx+"
"AA9sp37UTQgQFA1yH0BDACyMoHy//J0AGBAQj0Cm+hMYAIBIA4PABmtznaiaEAga5Drhf/AABmvHfaiaEAQa5DrhY/AAG7SB/"
"oA1NQi+QAFyMoHFcv/ydB3dIAYyMsFywIizxZQBfoCFMtrEszMyXP7AMhAFIEBCPRR8qcCAHCBAQjXGPoA0z/"
"IVCBHgQEI9FHyp4IQbm90ZXB0gBjIywXLAlAGzxZQBPoCFMtqEssfyz/Jc/sAAgBsgQEI1xj6ANM/"
"MFIkgQEI9Fnyp4IQZHN0cnB0gBjIywXLAlAFzxZQA/oCE8tqyx8Syz/Jc/sAAAr0AMntVGliJeU=");
return map;
}();
return map;
@ -137,9 +152,12 @@ td::Span<int> SmartContractCode::get_revisions(Type type) {
static int res[] = {1};
return res;
}
case Type::WalletV4: {
static int res[] = {2};
return res;
}
}
UNREACHABLE();
return {};
}
td::Result<int> SmartContractCode::validate_revision(Type type, int revision) {
@ -179,9 +197,10 @@ td::Ref<vm::Cell> SmartContractCode::get_code(Type type, int ext_revision) {
return "payment-channel";
case Type::RestrictedWallet:
return "restricted-wallet3";
case Type::WalletV4:
return "wallet-v4";
}
UNREACHABLE();
return "";
}(type);
if (revision == -1) {
return load(basename).move_as_ok();

View file

@ -26,7 +26,16 @@ class SmartContractCode {
public:
static td::Result<td::Ref<vm::Cell>> load(td::Slice name);
enum Type { WalletV3 = 4, HighloadWalletV1, HighloadWalletV2, ManualDns, Multisig, PaymentChannel, RestrictedWallet };
enum Type {
WalletV3 = 4,
HighloadWalletV1,
HighloadWalletV2,
ManualDns,
Multisig,
PaymentChannel,
RestrictedWallet,
WalletV4
};
static td::Span<int> get_revisions(Type type);
static td::Result<int> validate_revision(Type type, int revision);
static td::Ref<vm::Cell> get_code(Type type, int revision = 0);

View file

@ -0,0 +1,71 @@
/*
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 "WalletV4.h"
#include "GenericAccount.h"
#include "SmartContractCode.h"
#include "vm/boc.h"
#include "vm/cells/CellString.h"
#include "td/utils/base64.h"
#include <limits>
namespace ton {
td::Result<td::Ref<vm::Cell>> WalletV4::make_a_gift_message(const td::Ed25519::PrivateKey& private_key,
td::uint32 valid_until, td::Span<Gift> gifts) const {
CHECK(gifts.size() <= get_max_gifts_size());
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
vm::CellBuilder cb;
cb.store_long(wallet_id, 32).store_long(valid_until, 32).store_long(seqno, 32);
cb.store_long(0, 8); // The only difference with wallet-v3
for (auto& gift : gifts) {
td::int32 send_mode = 3;
if (gift.gramms == -1) {
send_mode += 128;
}
if (gift.send_mode > -1) {
send_mode = gift.send_mode;
}
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Ref<vm::Cell> WalletV4::get_init_data(const InitData& init_data) noexcept {
return vm::CellBuilder()
.store_long(init_data.seqno, 32)
.store_long(init_data.wallet_id, 32)
.store_bytes(init_data.public_key)
.store_zeroes(1) // plugins dict
.finalize();
}
td::Result<td::uint32> WalletV4::get_wallet_id() const {
return TRY_VM([&]() -> td::Result<td::uint32> {
auto answer = run_get_method("get_subwallet_id");
if (!answer.success) {
return td::Status::Error("get_subwallet_id get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());
}
} // namespace ton

View file

@ -0,0 +1,46 @@
/*
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 "smc-envelope/SmartContract.h"
#include "smc-envelope/WalletInterface.h"
#include "vm/cells.h"
#include "Ed25519.h"
#include "block/block.h"
#include "vm/cells/CellString.h"
namespace ton {
struct WalletV4Traits {
using InitData = WalletInterface::DefaultInitData;
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static constexpr unsigned max_gifts_size = 4;
static constexpr auto code_type = SmartContractCode::WalletV4;
};
class WalletV4 : public WalletBase<WalletV4, WalletV4Traits> {
public:
explicit WalletV4(State state) : WalletBase(std::move(state)) {
}
td::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
td::Span<Gift> gifts) const override;
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) noexcept;
td::Result<td::uint32> get_wallet_id() const override;
};
} // namespace ton

View file

@ -33,7 +33,7 @@ std::string load_test(std::string name) {
return td::read_file_str(current_dir() + "fift/" + name).move_as_ok();
}
td::Status run_fift(std::string name, bool expect_error = false, bool preload_fift = true) {
td::Status run_fift(std::string name, bool expect_error = false) {
auto res = fift::mem_run_fift(load_test(name));
if (expect_error) {
res.ensure_error();
@ -163,3 +163,7 @@ TEST(Fift, test_bls) {
TEST(Fift, test_bls_ops) {
run_fift("bls_ops.fif");
}
TEST(Fift, test_levels) {
run_fift("levels.fif");
}

View file

@ -0,0 +1,75 @@
"Asm.fif" include
"FiftExt.fif" include
{
dup
."Cell " .dump cr
dup [[ <{ CLEVEL }>s ]] 0 runvmx abort"exitcode != 0" ."Level = " . cr
dup [[ <{ CLEVELMASK }>s ]] 0 runvmx abort"exitcode != 0" ."Level mask = 0b" b. cr
dup dup [[ <{ 0 CHASHI DUP ROT 0 INT CHASHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Hash_0 = " X. cr
dup dup [[ <{ 1 CHASHI DUP ROT 1 INT CHASHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Hash_1 = " X. cr
dup dup [[ <{ 2 CHASHI DUP ROT 2 INT CHASHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Hash_2 = " X. cr
dup dup [[ <{ 3 CHASHI DUP ROT 3 INT CHASHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Hash_3 = " X. cr
dup dup [[ <{ 0 CDEPTHI DUP ROT 0 INT CDEPTHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Depth_0 = " . cr
dup dup [[ <{ 1 CDEPTHI DUP ROT 1 INT CDEPTHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Depth_1 = " . cr
dup dup [[ <{ 2 CDEPTHI DUP ROT 2 INT CDEPTHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Depth_2 = " . cr
dup dup [[ <{ 3 CDEPTHI DUP ROT 3 INT CDEPTHIX EQUAL 55 THROWIFNOT }>s ]] 0 runvmx abort"exitcode != 0" ."Depth_3 = " . cr
drop
cr
} : print-all
// Ordinary cell of level 0
<b
123 32 u,
<b <b 22 32 u, b> ref, b> ref,
<b b> ref,
b>
print-all
// Prunned branch of level 1
<b
1 8 u,
1 8 u,
0xabcd1111abcd1111abcd1111abcd1111abcd1111abcd1111abcd1111abcd1111 256 u,
14 16 u,
b>spec
print-all
// Prunned branch of level 3
<b
1 8 u,
7 8 u,
0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 256 u,
0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb 256 u,
0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc 256 u,
55 16 u,
44 16 u,
33 16 u,
b>spec
print-all
// Prunned branch of level 3, mask 0b101
<b
1 8 u,
5 8 u,
0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 256 u,
0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc 256 u,
55 16 u,
33 16 u,
b>spec
print-all
// Tree with the previous cell inside
<b
<b
<b
1 8 u,
5 8 u,
0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 256 u,
0xcccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc 256 u,
55 16 u,
33 16 u,
b>spec ref,
b> ref,
b>
print-all

View file

@ -9,6 +9,9 @@
[[ <{ RIST255_MULBASE DUP RIST255_VALIDATE }>s ]] 0 runvmx abort"Exitcode != 0"
@' n . dup (x.) type cr
@' ans <> abort"Invalid result"
@' n
[[ <{ 1 INT RIST255_MULBASE SWAP RIST255_MUL DUP RIST255_VALIDATE }>s ]] 0 runvmx abort"Exitcode != 0"
@' ans <> abort"Invalid result"
} : test-basepoint
0 0x0000000000000000000000000000000000000000000000000000000000000000 test-basepoint

View file

@ -180,7 +180,7 @@ struct MixedRadix {
template <int M>
const MixedRadix<M>& as_shorter() const {
static_assert(M <= N);
static_assert(M <= N,"error");
return *reinterpret_cast<const MixedRadix<M>*>(this);
}
@ -458,7 +458,7 @@ struct ModArray {
}
template <int M>
ModArray(const ModArray<M>& other) {
static_assert(M >= N);
static_assert(M >= N,"error");
std::copy(other.a, other.a + N, a);
}
ModArray(const int* p) : a(p) {
@ -819,7 +819,7 @@ struct ModArray {
template <int M>
const ModArray<M>& as_shorter() const {
static_assert(M <= N);
static_assert(M <= N,"error");
return *reinterpret_cast<const ModArray<M>*>(this);
}

View file

@ -16,12 +16,12 @@
*/
#include <assert.h>
#include <string.h>
#include <unistd.h>
#include <array>
#include <string>
#include <iostream>
#include <sstream>
#include <random>
#include <getopt.h>
#include "common/refcnt.hpp"
#include "common/bigint.hpp"
#include "common/refint.h"
@ -211,7 +211,7 @@ bool coin() {
// returns 0 with probability 1/2, 1 with prob. 1/4, ..., k with prob. 1/2^(k+1)
int randexp(int max = 63, int min = 0) {
return min + __builtin_clzll(Random() | (1ULL << (63 - max + min)));
return min + td::count_leading_zeroes64(Random() | (1ULL << (63 - max + min)));
}
void bin_add_small(unsigned char bin[64], long long val, int shift = 0) {
@ -363,7 +363,7 @@ void check_one_int_repr(td::RefInt256 x, int mode, int in_range, const BInt* val
if (is_small) {
// special check for small (64-bit) values
CHECK(x->to_long() == xval);
CHECK((long long)__builtin_bswap64(*(long long*)(bytes + 64 - 8)) == xval);
CHECK((long long)td::bswap64(*(long long*)(bytes + 64 - 8)) == xval);
CHECK(in_range);
// check sign
CHECK(x->sgn() == (xval > 0 ? 1 : (xval < 0 ? -1 : 0)));

View file

@ -127,12 +127,12 @@ class BenchSha256Low : public td::Benchmark {
void run(int n) override {
int res = 0;
SHA256_CTX ctx;
td::Sha256State ctx;
for (int i = 0; i < n; i++) {
SHA256_Init(&ctx);
SHA256_Update(&ctx, "abcd", 4);
ctx.init();
ctx.feed("abcd");
unsigned char buf[32];
SHA256_Final(buf, &ctx);
ctx.extract(td::MutableSlice{buf, 32});
res += buf[0];
}
td::do_not_optimize_away(res);

View file

@ -35,6 +35,7 @@
#include "smc-envelope/SmartContract.h"
#include "smc-envelope/SmartContractCode.h"
#include "smc-envelope/WalletV3.h"
#include "smc-envelope/WalletV4.h"
#include "smc-envelope/HighloadWallet.h"
#include "smc-envelope/HighloadWalletV2.h"
#include "smc-envelope/PaymentChannel.h"
@ -488,7 +489,7 @@ void do_test_wallet(int revision) {
auto address = std::move(res.address);
auto iwallet = std::move(res.wallet);
auto public_key = priv_key.get_public_key().move_as_ok().as_octet_string();
;
check_wallet_state(iwallet, 1, 123, public_key);
// lets send a lot of messages
@ -526,6 +527,7 @@ void do_test_wallet() {
TEST(Tonlib, Wallet) {
do_test_wallet<ton::WalletV3>();
do_test_wallet<ton::WalletV4>();
do_test_wallet<ton::HighloadWallet>();
do_test_wallet<ton::HighloadWalletV2>();
do_test_wallet<ton::RestrictedWallet>();
@ -956,7 +958,7 @@ class MapDns {
}
return;
}
if (!actions.category.is_zero()) {
if (actions.category.is_zero()) {
entries_.erase(actions.name);
LOG(ERROR) << "CLEAR " << actions.name;
if (!actions.actions) {
@ -1001,7 +1003,7 @@ class CheckedDns {
explicit CheckedDns(bool check_smc = true, bool check_combine = true) {
if (check_smc) {
key_ = td::Ed25519::generate_private_key().move_as_ok();
dns_ = ManualDns::create(ManualDns::create_init_data_fast(key_.value().get_public_key().move_as_ok(), 123));
dns_ = ManualDns::create(ManualDns::create_init_data_fast(key_.value().get_public_key().move_as_ok(), 123), -1);
}
if (check_combine) {
combined_map_dns_ = MapDns();
@ -1024,7 +1026,7 @@ class CheckedDns {
}
return action;
});
auto query = dns_->create_update_query(key_.value(), smc_actions).move_as_ok();
auto query = dns_->create_update_query(key_.value(), smc_actions, query_id_++).move_as_ok();
CHECK(dns_.write().send_external_message(std::move(query)).code == 0);
}
map_dns_.update(entries);
@ -1079,6 +1081,7 @@ class CheckedDns {
using ManualDns = ton::ManualDns;
td::optional<td::Ed25519::PrivateKey> key_;
td::Ref<ManualDns> dns_;
td::uint32 query_id_ = 1; // Query id serve as "valid until", but in tests now() == 0
MapDns map_dns_;
td::optional<MapDns> combined_map_dns_;
@ -1092,9 +1095,10 @@ class CheckedDns {
}
};
static td::Bits256 intToCat(int x) {
td::Bits256 cat = td::Bits256::zero();
cat.as_slice().copy_from(td::Slice((char*)&x, sizeof(x)));
static td::Bits256 intToCat(td::uint32 x) {
auto y = td::make_refint(x);
td::Bits256 cat;
y->export_bytes(cat.data(), 32, false);
return cat;
}
@ -1180,7 +1184,7 @@ TEST(Smartcont, DnsManual) {
auto key = td::Ed25519::generate_private_key().move_as_ok();
auto manual = ManualDns::create(ManualDns::create_init_data_fast(key.get_public_key().move_as_ok(), 123));
auto manual = ManualDns::create(ManualDns::create_init_data_fast(key.get_public_key().move_as_ok(), 123), -1);
CHECK(manual->get_wallet_id().move_as_ok() == 123);
auto init_query = manual->create_init_query(key).move_as_ok();
LOG(ERROR) << "A";

View file

@ -28,7 +28,7 @@
#include "td/utils/StringBuilder.h"
std::string run_vm(td::Ref<vm::Cell> cell) {
vm::init_op_cp0();
vm::init_vm().ensure();
vm::DictionaryBase::get_empty_dictionary();
class Logger : public td::LogInterface {

View file

@ -159,7 +159,6 @@ std::string CppIdentSet::compute_cpp_ident(std::string orig_ident, int count) {
}
if (!cnt) {
os << '_';
prev_skip = true;
}
if (count) {
os << count;

View file

@ -2252,11 +2252,9 @@ TypeExpr* parse_expr10(Lexer& lex, Constructor& cs, int mode) {
}
if (op == '>') {
std::swap(expr, expr2);
op = '<';
op_name = Less_name;
} else if (op == src::_Geq) {
std::swap(expr, expr2);
op = src::_Leq;
op_name = Leq_name;
}
auto sym_def = sym::lookup_symbol(op_name, 2);

View file

@ -285,8 +285,11 @@ int exec_divmod(VmState* st, unsigned args, int quiet) {
typename td::BigInt256::DoubleInt tmp{*x}, quot;
tmp += *w;
tmp.mod_div(*y, quot, round_mode);
stack.push_int_quiet(td::make_refint(quot), quiet);
stack.push_int_quiet(td::make_refint(tmp), quiet);
auto q = td::make_refint(quot), r = td::make_refint(tmp);
q.write().normalize();
r.write().normalize();
stack.push_int_quiet(std::move(q), quiet);
stack.push_int_quiet(std::move(r), quiet);
} else {
switch (d) {
case 1:
@ -399,6 +402,7 @@ std::string dump_shrmod(CellSlice&, unsigned args, int mode) {
if (mode & 1) {
os << 'Q';
}
std::string end;
switch (args & 12) {
case 4:
os << "RSHIFT";
@ -407,17 +411,22 @@ std::string dump_shrmod(CellSlice&, unsigned args, int mode) {
os << "MODPOW2";
break;
case 12:
os << "RSHIFTMOD";
os << "RSHIFT";
end = "MOD";
break;
case 0:
os << "ADDRSHIFTMOD";
os << "ADDRSHIFT";
end = "MOD";
break;
}
if (!(mode & 2)) {
os << end;
}
if (round_mode) {
os << "FRC"[round_mode];
}
if (mode & 2) {
os << ' ' << y;
os << "#" << end << ' ' << y;
}
return os.str();
}
@ -519,7 +528,7 @@ int exec_mulshrmod(VmState* st, unsigned args, int mode) {
if (add) {
tmp = *w;
}
tmp.add_mul(*x, *y);
tmp.add_mul(*x, *y).normalize();
switch (d) {
case 1:
tmp.rshift(z, round_mode).normalize();
@ -553,6 +562,7 @@ std::string dump_mulshrmod(CellSlice&, unsigned args, int mode) {
if (mode & 1) {
os << 'Q';
}
std::string end;
switch (args & 12) {
case 4:
os << "MULRSHIFT";
@ -561,15 +571,21 @@ std::string dump_mulshrmod(CellSlice&, unsigned args, int mode) {
os << "MULMODPOW2";
break;
case 12:
os << "MULRSHIFTMOD";
os << "MULRSHIFT";
end = "MOD";
break;
case 0:
os << "MULADDRSHIFTMOD";
os << "MULADDRSHIFT";
end = "MOD";
break;
}
if (round_mode) {
os << "FRC"[round_mode];
}
if (mode & 2) {
os << "#";
}
os << end;
if (mode & 2) {
os << ' ' << y;
}
@ -644,18 +660,22 @@ std::string dump_shldivmod(CellSlice&, unsigned args, int mode) {
if (mode & 1) {
os << "Q";
}
os << "LSHIFT";
if (mode & 2) {
os << "#";
}
switch (args & 12) {
case 4:
os << "LSHIFTDIV";
os << "DIV";
break;
case 8:
os << "LSHIFTMOD";
os << "MOD";
break;
case 12:
os << "LSHIFTDIVMOD";
os << "DIVMOD";
break;
case 0:
os << "LSHIFTADDDIVMOD";
os << "ADDDIVMOD";
break;
}
if (round_mode) {

View file

@ -930,7 +930,7 @@ unsigned long long BagOfCells::get_idx_entry_raw(int index) {
*
*/
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty, bool allow_nonzero_level) {
if (data.empty() && can_be_empty) {
return Ref<Cell>();
}
@ -946,7 +946,7 @@ td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty) {
if (root.is_null()) {
return td::Status::Error("bag of cells has null root cell (?)");
}
if (root->get_level() != 0) {
if (!allow_nonzero_level && root->get_level() != 0) {
return td::Status::Error("bag of cells has a root with non-zero level");
}
return std::move(root);

View file

@ -53,6 +53,7 @@ class NewCellStorageStat {
bool operator==(const Stat& other) const {
return key() == other.key();
}
Stat(const Stat& other) = default;
Stat& operator=(const Stat& other) = default;
Stat& operator+=(const Stat& other) {
cells += other.cells;
@ -336,7 +337,7 @@ class BagOfCells {
std::vector<td::uint8>* cell_should_cache);
};
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false);
td::Result<Ref<Cell>> std_boc_deserialize(td::Slice data, bool can_be_empty = false, bool allow_nonzero_level = false);
td::Result<td::BufferSlice> std_boc_serialize(Ref<Cell> root, int mode = 0);
td::Result<std::vector<Ref<Cell>>> std_boc_deserialize_multi(td::Slice data,

View file

@ -892,6 +892,40 @@ int exec_load_special_cell(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute XLOAD" << (quiet ? "Q" : "");
auto cell = stack.pop_cell();
if (st->get_global_version() >= 5) {
st->register_cell_load(cell->get_hash());
auto r_loaded_cell = cell->load_cell();
if (r_loaded_cell.is_error()) {
if (quiet) {
stack.push_bool(false);
return 0;
} else {
throw VmError{Excno::cell_und, "failed to load cell"};
}
}
auto loaded_cell = r_loaded_cell.move_as_ok();
if (loaded_cell.data_cell->is_special()) {
if (loaded_cell.data_cell->special_type() != CellTraits::SpecialType::Library) {
if (quiet) {
stack.push_bool(false);
return 0;
} else {
throw VmError{Excno::cell_und, "unexpected special cell"};
}
}
CellSlice cs(std::move(loaded_cell));
DCHECK(cs.size() == Cell::hash_bits + 8);
cell = st->load_library(cs.data_bits() + 8);
if (cell.is_null()) {
if (quiet) {
stack.push_bool(false);
return 0;
} else {
throw VmError{Excno::cell_und, "failed to load library cell"};
}
}
}
}
stack.push_cell(cell);
if (quiet) {
stack.push_bool(true);
@ -1357,6 +1391,55 @@ int exec_slice_depth(VmState* st) {
return 0;
}
int exec_cell_level(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute CLEVEL";
auto cell = stack.pop_cell();
stack.push_smallint(cell->get_level());
return 0;
}
int exec_cell_level_mask(VmState* st) {
Stack& stack = st->get_stack();
VM_LOG(st) << "execute CLEVELMASK";
auto cell = stack.pop_cell();
stack.push_smallint(cell->get_level_mask().get_mask());
return 0;
}
int exec_cell_hash_i(VmState* st, unsigned args, bool var) {
unsigned i;
Stack& stack = st->get_stack();
if (var) {
VM_LOG(st) << "execute CHASHIX";
i = stack.pop_smallint_range(3);
} else {
i = args & 3;
VM_LOG(st) << "execute CHASHI " << i;
}
auto cell = stack.pop_cell();
std::array<unsigned char, 32> hash = cell->get_hash(i).as_array();
td::RefInt256 res{true};
CHECK(res.write().import_bytes(hash.data(), hash.size(), false));
stack.push_int(std::move(res));
return 0;
}
int exec_cell_depth_i(VmState* st, unsigned args, bool var) {
unsigned i;
Stack& stack = st->get_stack();
if (var) {
VM_LOG(st) << "execute CDEPTHIX";
i = stack.pop_smallint_range(3);
} else {
i = args & 3;
VM_LOG(st) << "execute CDEPTHI " << i;
}
auto cell = stack.pop_cell();
stack.push_smallint(cell->get_depth(i));
return 0;
}
void register_cell_deserialize_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
cp0.insert(OpcodeInstr::mksimple(0xd0, 8, "CTOS", exec_cell_to_slice))
@ -1445,7 +1528,13 @@ void register_cell_deserialize_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mksimple(0xd761, 16, "LDONES", std::bind(exec_load_same, _1, "LDONES", 1)))
.insert(OpcodeInstr::mksimple(0xd762, 16, "LDSAME", std::bind(exec_load_same, _1, "LDSAME", -1)))
.insert(OpcodeInstr::mksimple(0xd764, 16, "SDEPTH", exec_slice_depth))
.insert(OpcodeInstr::mksimple(0xd765, 16, "CDEPTH", exec_cell_depth));
.insert(OpcodeInstr::mksimple(0xd765, 16, "CDEPTH", exec_cell_depth))
.insert(OpcodeInstr::mksimple(0xd766, 16, "CLEVEL", exec_cell_level)->require_version(6))
.insert(OpcodeInstr::mksimple(0xd767, 16, "CLEVELMASK", exec_cell_level_mask)->require_version(6))
.insert(OpcodeInstr::mkfixed(0xd768 >> 2, 14, 2, instr::dump_1c_and(3, "CHASHI "), std::bind(exec_cell_hash_i, _1, _2, false))->require_version(6))
.insert(OpcodeInstr::mkfixed(0xd76c >> 2, 14, 2, instr::dump_1c_and(3, "CDEPTHI "), std::bind(exec_cell_depth_i, _1, _2, false))->require_version(6))
.insert(OpcodeInstr::mksimple(0xd770, 16, "CHASHIX ", std::bind(exec_cell_hash_i, _1, 0, true))->require_version(6))
.insert(OpcodeInstr::mksimple(0xd771, 16, "CDEPTHIX ", std::bind(exec_cell_depth_i, _1, 0, true))->require_version(6));
}
void register_cell_ops(OpcodeTable& cp0) {

View file

@ -1056,9 +1056,10 @@ std::ostream& operator<<(std::ostream& os, Ref<CellSlice> cs_ref) {
// If can_be_special is not null, then it is allowed to load special cell
// Flag whether loaded cell is actually special will be stored into can_be_special
VirtualCell::LoadedCell load_cell_slice_impl(Ref<Cell> cell, bool* can_be_special) {
auto* vm_state_interface = VmStateInterface::get();
bool library_loaded = false;
while (true) {
auto* vm_state_interface = VmStateInterface::get();
if (vm_state_interface) {
if (vm_state_interface && !library_loaded) {
vm_state_interface->register_cell_load(cell->get_hash());
}
auto r_loaded_cell = cell->load_cell();
@ -1077,6 +1078,12 @@ VirtualCell::LoadedCell load_cell_slice_impl(Ref<Cell> cell, bool* can_be_specia
} else if (loaded_cell.data_cell->is_special()) {
if (loaded_cell.data_cell->special_type() == DataCell::SpecialType::Library) {
if (vm_state_interface) {
if (vm_state_interface->get_global_version() >= 5) {
if (library_loaded) {
throw VmError{Excno::cell_und, "failed to load library cell: recursive library cells are not allowed"};
}
library_loaded = true;
}
CellSlice cs(std::move(loaded_cell));
DCHECK(cs.size() == Cell::hash_bits + 8);
auto library_cell = vm_state_interface->load_library(cs.data_bits() + 8);

View file

@ -27,16 +27,25 @@ namespace vm {
namespace {
class RefcntCellStorer {
public:
RefcntCellStorer(td::int32 refcnt, const DataCell &cell) : refcnt_(refcnt), cell_(cell) {
RefcntCellStorer(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc)
: refcnt_(refcnt), cell_(cell), as_boc_(as_boc) {
}
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
if (as_boc_) {
td::int32 tag = -1;
store(tag, storer);
store(refcnt_, storer);
td::BufferSlice data = vm::std_boc_serialize(cell_).move_as_ok();
storer.store_slice(data);
return;
}
store(refcnt_, storer);
store(cell_, storer);
for (unsigned i = 0; i < cell_.size_refs(); i++) {
auto cell = cell_.get_ref(i);
store(*cell_, storer);
for (unsigned i = 0; i < cell_->size_refs(); i++) {
auto cell = cell_->get_ref(i);
auto level_mask = cell->get_level_mask();
auto level = level_mask.get_level();
td::uint8 x = static_cast<td::uint8>(level_mask.get_mask());
@ -60,7 +69,8 @@ class RefcntCellStorer {
private:
td::int32 refcnt_;
const DataCell &cell_;
td::Ref<DataCell> cell_;
bool as_boc_;
};
class RefcntCellParser {
@ -69,11 +79,17 @@ class RefcntCellParser {
}
td::int32 refcnt;
Ref<DataCell> cell;
bool stored_boc_;
template <class ParserT>
void parse(ParserT &parser, ExtCellCreator &ext_cell_creator) {
using ::td::parse;
parse(refcnt, parser);
stored_boc_ = false;
if (refcnt == -1) {
stored_boc_ = true;
parse(refcnt, parser);
}
if (!need_data_) {
return;
}
@ -81,6 +97,12 @@ class RefcntCellParser {
TRY_STATUS(parser.get_status());
auto size = parser.get_left_len();
td::Slice data = parser.template fetch_string_raw<td::Slice>(size);
if (stored_boc_) {
TRY_RESULT(boc, vm::std_boc_deserialize(data, false, true));
TRY_RESULT(loaded_cell, boc->load_cell());
cell = std::move(loaded_cell.data_cell);
return td::Status::OK();
}
CellSerializationInfo info;
auto cell_data = data;
TRY_STATUS(info.init(cell_data, 0 /*ref_byte_size*/));
@ -122,7 +144,8 @@ class RefcntCellParser {
};
} // namespace
CellLoader::CellLoader(std::shared_ptr<KeyValueReader> reader) : reader_(std::move(reader)) {
CellLoader::CellLoader(std::shared_ptr<KeyValueReader> reader, std::function<void(const LoadResult &)> on_load_callback)
: reader_(std::move(reader)), on_load_callback_(std::move(on_load_callback)) {
CHECK(reader_);
}
@ -145,7 +168,11 @@ td::Result<CellLoader::LoadResult> CellLoader::load(td::Slice hash, bool need_da
res.refcnt_ = refcnt_cell.refcnt;
res.cell_ = std::move(refcnt_cell.cell);
res.stored_boc_ = refcnt_cell.stored_boc_;
//CHECK(res.cell_->get_hash() == hash);
if (on_load_callback_) {
on_load_callback_(res);
}
return res;
}
@ -157,7 +184,7 @@ td::Status CellStorer::erase(td::Slice hash) {
return kv_.erase(hash);
}
td::Status CellStorer::set(td::int32 refcnt, const DataCell &cell) {
return kv_.set(cell.get_hash().as_slice(), td::serialize(RefcntCellStorer(refcnt, cell)));
td::Status CellStorer::set(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc) {
return kv_.set(cell->get_hash().as_slice(), td::serialize(RefcntCellStorer(refcnt, cell, as_boc)));
}
} // namespace vm

View file

@ -45,19 +45,21 @@ class CellLoader {
Ref<DataCell> cell_;
td::int32 refcnt_{0};
bool stored_boc_{false};
};
CellLoader(std::shared_ptr<KeyValueReader> reader);
CellLoader(std::shared_ptr<KeyValueReader> reader, std::function<void(const LoadResult &)> on_load_callback = {});
td::Result<LoadResult> load(td::Slice hash, bool need_data, ExtCellCreator &ext_cell_creator);
private:
std::shared_ptr<KeyValueReader> reader_;
std::function<void(const LoadResult &)> on_load_callback_;
};
class CellStorer {
public:
CellStorer(KeyValue &kv);
td::Status erase(td::Slice hash);
td::Status set(td::int32 refcnt, const DataCell &cell);
td::Status set(td::int32 refcnt, const td::Ref<DataCell> &cell, bool as_boc);
private:
KeyValue &kv_;

View file

@ -210,6 +210,14 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat
return td::Status::OK();
}
void set_celldb_compress_depth(td::uint32 value) override {
celldb_compress_depth_ = value;
}
vm::ExtCellCreator& as_ext_cell_creator() override {
return *this;
}
private:
std::unique_ptr<CellLoader> loader_;
std::vector<Ref<Cell>> to_inc_;
@ -217,6 +225,7 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat
CellHashTable<CellInfo> hash_table_;
std::vector<CellInfo *> visited_;
Stats stats_diff_;
td::uint32 celldb_compress_depth_{0};
static td::NamedThreadSafeCounter::CounterRef get_thread_safe_counter() {
static auto res = td::NamedThreadSafeCounter::get_default().get_counter("DynamicBagOfCellsDb");
@ -443,7 +452,8 @@ class DynamicBagOfCellsDbImpl : public DynamicBagOfCellsDb, private ExtCellCreat
guard.dismiss();
} else {
auto loaded_cell = info.cell->load_cell().move_as_ok();
storer.set(info.db_refcnt, *loaded_cell.data_cell);
storer.set(info.db_refcnt, loaded_cell.data_cell,
loaded_cell.data_cell->get_depth() == celldb_compress_depth_ && celldb_compress_depth_ != 0);
info.in_db = true;
}
}

View file

@ -64,6 +64,9 @@ class DynamicBagOfCellsDb {
// restart with new loader will also reset stats_diff
virtual td::Status set_loader(std::unique_ptr<CellLoader> loader) = 0;
virtual void set_celldb_compress_depth(td::uint32 value) = 0;
virtual vm::ExtCellCreator& as_ext_cell_creator() = 0;
static std::unique_ptr<DynamicBagOfCellsDb> create();
class AsyncExecutor {

View file

@ -21,6 +21,7 @@
#include "vm/cellslice.h"
#include "vm/stack.hpp"
#include "common/bitstring.h"
#include "td/utils/Random.h"
#include "td/utils/bits.h"
@ -2007,7 +2008,7 @@ bool DictionaryFixed::combine_with(DictionaryFixed& dict2) {
bool DictionaryFixed::dict_check_for_each(Ref<Cell> dict, td::BitPtr key_buffer, int n, int total_key_len,
const DictionaryFixed::foreach_func_t& foreach_func,
bool invert_first) const {
bool invert_first, bool shuffle) const {
if (dict.is_null()) {
return true;
}
@ -2026,26 +2027,29 @@ bool DictionaryFixed::dict_check_for_each(Ref<Cell> dict, td::BitPtr key_buffer,
key_buffer += l + 1;
if (l) {
invert_first = false;
} else if (invert_first) {
}
bool invert = shuffle ? td::Random::fast(0, 1) == 1: invert_first;
if (invert) {
std::swap(c1, c2);
}
key_buffer[-1] = invert_first;
key_buffer[-1] = invert;
// recursive check_foreach applied to both children
if (!dict_check_for_each(std::move(c1), key_buffer, n - l - 1, total_key_len, foreach_func)) {
if (!dict_check_for_each(std::move(c1), key_buffer, n - l - 1, total_key_len, foreach_func, false, shuffle)) {
return false;
}
key_buffer[-1] = !invert_first;
return dict_check_for_each(std::move(c2), key_buffer, n - l - 1, total_key_len, foreach_func);
key_buffer[-1] = !invert;
return dict_check_for_each(std::move(c2), key_buffer, n - l - 1, total_key_len, foreach_func, false, shuffle);
}
bool DictionaryFixed::check_for_each(const foreach_func_t& foreach_func, bool invert_first) {
bool DictionaryFixed::check_for_each(const foreach_func_t& foreach_func, bool invert_first, bool shuffle) {
force_validate();
if (is_empty()) {
return true;
}
int key_len = get_key_bits();
unsigned char key_buffer[max_key_bytes];
return dict_check_for_each(get_root_cell(), td::BitPtr{key_buffer}, key_len, key_len, foreach_func, invert_first);
return dict_check_for_each(get_root_cell(), td::BitPtr{key_buffer}, key_len, key_len, foreach_func, invert_first,
shuffle);
}
static inline bool set_bit(td::BitPtr ptr, bool value = true) {

View file

@ -223,7 +223,7 @@ class DictionaryFixed : public DictionaryBase {
int get_common_prefix(td::BitPtr buffer, unsigned buffer_len);
bool cut_prefix_subdict(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false);
Ref<vm::Cell> extract_prefix_subdict_root(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false);
bool check_for_each(const foreach_func_t& foreach_func, bool invert_first = false);
bool check_for_each(const foreach_func_t& foreach_func, bool invert_first = false, bool shuffle = false);
int filter(filter_func_t check);
bool combine_with(DictionaryFixed& dict2, const combine_func_t& combine_func, int mode = 0);
bool combine_with(DictionaryFixed& dict2, const simple_combine_func_t& simple_combine_func, int mode = 0);
@ -292,7 +292,7 @@ class DictionaryFixed : public DictionaryBase {
std::pair<Ref<Cell>, bool> extract_prefix_subdict_internal(Ref<Cell> dict, td::ConstBitPtr prefix, int prefix_len,
bool remove_prefix = false) const;
bool dict_check_for_each(Ref<Cell> dict, td::BitPtr key_buffer, int n, int total_key_len,
const foreach_func_t& foreach_func, bool invert_first = false) const;
const foreach_func_t& foreach_func, bool invert_first = false, bool shuffle = false) const;
std::pair<Ref<Cell>, int> dict_filter(Ref<Cell> dict, td::BitPtr key, int n, const filter_func_t& check_leaf,
int& skip_rest) const;
Ref<Cell> dict_combine_with(Ref<Cell> dict1, Ref<Cell> dict2, td::BitPtr key_buffer, int n, int total_key_len,

View file

@ -35,6 +35,7 @@
#include "openssl/digest.hpp"
#include <sodium.h>
#include "bls.h"
#include "mc-config.h"
namespace vm {
@ -67,6 +68,10 @@ int exec_set_gas_generic(VmState* st, long long new_gas_limit) {
throw VmNoGas{};
}
st->change_gas_limit(new_gas_limit);
if (st->get_stop_on_accept_message()) {
VM_LOG(st) << "External message is accepted, stopping TVM";
return st->jump(td::Ref<QuitCont>{true, 0});
}
return 0;
}
@ -101,7 +106,7 @@ void register_basic_gas_ops(OpcodeTable& cp0) {
using namespace std::placeholders;
cp0.insert(OpcodeInstr::mksimple(0xf800, 16, "ACCEPT", exec_accept))
.insert(OpcodeInstr::mksimple(0xf801, 16, "SETGASLIMIT", exec_set_gas_limit))
.insert(OpcodeInstr::mksimple(0xf802, 16, "GASCONSUMED", exec_gas_consumed)->require_version(4))
.insert(OpcodeInstr::mksimple(0xf807, 16, "GASCONSUMED", exec_gas_consumed)->require_version(4))
.insert(OpcodeInstr::mksimple(0xf80f, 16, "COMMIT", exec_commit));
}
@ -118,6 +123,20 @@ static const StackEntry& get_param(VmState* st, unsigned idx) {
return tuple_index(t1, idx);
}
// ConfigParams: 18 (only one entry), 19, 20, 21, 24, 25, 43
static td::Ref<CellSlice> get_unpacked_config_param(VmState* st, unsigned idx) {
auto tuple = st->get_c7();
auto t1 = tuple_index(tuple, 0).as_tuple_range(255);
if (t1.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a tuple"};
}
auto t2 = tuple_index(t1, 14).as_tuple_range(255);
if (t2.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a tuple"};
}
return tuple_index(t2, idx).as_slice();
}
int exec_get_param(VmState* st, unsigned idx, const char* name) {
if (name) {
VM_LOG(st) << "execute " << name;
@ -228,20 +247,150 @@ int exec_get_prev_blocks_info(VmState* st, unsigned idx, const char* name) {
}
int exec_get_global_id(VmState* st) {
Ref<Cell> config = get_param(st, 9).as_cell();
if (config.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a cell"};
VM_LOG(st) << "execute GLOBALID";
if (st->get_global_version() >= 6) {
Ref<CellSlice> cs = get_unpacked_config_param(st, 1);
if (cs.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a slice"};
}
if (cs->size() < 32) {
throw VmError{Excno::cell_und, "invalid global-id config"};
}
st->get_stack().push_smallint(cs->prefetch_long(32));
} else {
Ref<Cell> config = get_param(st, 19).as_cell();
if (config.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a cell"};
}
Dictionary config_dict{std::move(config), 32};
Ref<Cell> cell = config_dict.lookup_ref(td::BitArray<32>{19});
if (cell.is_null()) {
throw VmError{Excno::unknown, "invalid global-id config"};
}
CellSlice cs = load_cell_slice(cell);
if (cs.size() < 32) {
throw VmError{Excno::unknown, "invalid global-id config"};
}
st->get_stack().push_smallint(cs.fetch_long(32));
}
Dictionary config_dict{std::move(config), 32};
Ref<Cell> cell = config_dict.lookup_ref(td::BitArray<32>{19});
if (cell.is_null()) {
throw VmError{Excno::unknown, "invalid global-id config"};
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"};
}
CellSlice cs = load_cell_slice(cell);
if (cs.size() < 32) {
throw VmError{Excno::unknown, "invalid global-id config"};
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()};
}
st->get_stack().push_smallint(cs.fetch_long(32));
return r_prices.move_as_ok();
}
static block::MsgPrices get_msg_prices(VmState* st, bool is_masterchain) {
Ref<CellSlice> cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5);
if (cs.is_null()) {
throw VmError{Excno::type_chk, "intermediate value is not a slice"};
}
auto r_prices = block::Config::do_get_msg_prices(*cs, is_masterchain ? 24 : 25);
if (r_prices.is_error()) {
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
}
return r_prices.move_as_ok();
}
int exec_get_gas_fee(VmState* st) {
VM_LOG(st) << "execute GETGASFEE";
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain);
stack.push_int(prices.compute_gas_price(gas));
return 0;
}
int exec_get_storage_fee(VmState* st) {
VM_LOG(st) << "execute GETSTORAGEFEE";
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::int64 delta = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
Ref<CellSlice> cs = get_unpacked_config_param(st, 0);
if (cs.is_null()) {
// null means tat no StoragePrices is active, so the price is 0
stack.push_smallint(0);
return 0;
}
auto r_prices = block::Config::do_get_one_storage_prices(*cs);
if (r_prices.is_error()) {
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
}
block::StoragePrices prices = r_prices.move_as_ok();
td::RefInt256 total;
if (is_masterchain) {
total = td::make_refint(cells) * prices.mc_cell_price;
total += td::make_refint(bits) * prices.mc_bit_price;
} else {
total = td::make_refint(cells) * prices.cell_price;
total += td::make_refint(bits) * prices.bit_price;
}
total *= delta;
stack.push_int(td::rshift(total, 16, 1));
return 0;
}
int exec_get_forward_fee(VmState* st) {
VM_LOG(st) << "execute GETFORWARDFEE";
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
block::MsgPrices prices = get_msg_prices(st, is_masterchain);
stack.push_int(prices.compute_fwd_fees256(cells, bits));
return 0;
}
int exec_get_precompiled_gas(VmState* st) {
VM_LOG(st) << "execute GETPRECOMPILEDGAS";
Stack& stack = st->get_stack();
stack.push_null();
return 0;
}
int exec_get_original_fwd_fee(VmState* st) {
VM_LOG(st) << "execute GETORIGINALFWDFEE";
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::RefInt256 fwd_fee = stack.pop_int_finite();
if (fwd_fee->sgn() < 0) {
throw VmError{Excno::range_chk, "fwd_fee is negative"};
}
block::MsgPrices prices = get_msg_prices(st, is_masterchain);
stack.push_int(td::muldiv(fwd_fee, td::make_refint(1 << 16), td::make_refint((1 << 16) - prices.first_frac)));
return 0;
}
int exec_get_gas_fee_simple(VmState* st) {
VM_LOG(st) << "execute GETGASFEESIMPLE";
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::uint64 gas = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain);
stack.push_int(td::rshift(td::make_refint(prices.gas_price) * gas, 16, 1));
return 0;
}
int exec_get_forward_fee_simple(VmState* st) {
VM_LOG(st) << "execute GETFORWARDFEESIMPLE";
Stack& stack = st->get_stack();
bool is_masterchain = stack.pop_bool();
td::uint64 bits = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
td::uint64 cells = stack.pop_long_range(std::numeric_limits<td::int64>::max(), 0);
block::MsgPrices prices = get_msg_prices(st, is_masterchain);
stack.push_int(td::rshift(td::make_refint(prices.bit_price) * bits + td::make_refint(prices.cell_price) * cells, 16,
1)); // divide by 2^16 with ceil rounding
return 0;
}
@ -259,13 +408,21 @@ void register_ton_config_ops(OpcodeTable& cp0) {
.insert(OpcodeInstr::mksimple(0xf82b, 16, "INCOMINGVALUE", std::bind(exec_get_param, _1, 11, "INCOMINGVALUE")))
.insert(OpcodeInstr::mksimple(0xf82c, 16, "STORAGEFEES", std::bind(exec_get_param, _1, 12, "STORAGEFEES")))
.insert(OpcodeInstr::mksimple(0xf82d, 16, "PREVBLOCKSINFOTUPLE", std::bind(exec_get_param, _1, 13, "PREVBLOCKSINFOTUPLE")))
.insert(OpcodeInstr::mkfixedrange(0xf82e, 0xf830, 16, 4, instr::dump_1c("GETPARAM "), exec_get_var_param))
.insert(OpcodeInstr::mksimple(0xf82e, 16, "UNPACKEDCONFIGTUPLE", std::bind(exec_get_param, _1, 14, "UNPACKEDCONFIGTUPLE")))
.insert(OpcodeInstr::mksimple(0xf82f, 16, "DUEPAYMENT", std::bind(exec_get_param, _1, 15, "DUEPAYMENT")))
.insert(OpcodeInstr::mksimple(0xf830, 16, "CONFIGDICT", exec_get_config_dict))
.insert(OpcodeInstr::mksimple(0xf832, 16, "CONFIGPARAM", std::bind(exec_get_config_param, _1, false)))
.insert(OpcodeInstr::mksimple(0xf833, 16, "CONFIGOPTPARAM", std::bind(exec_get_config_param, _1, true)))
.insert(OpcodeInstr::mksimple(0xf83400, 24, "PREVMCBLOCKS", std::bind(exec_get_prev_blocks_info, _1, 0, "PREVMCBLOCKS"))->require_version(4))
.insert(OpcodeInstr::mksimple(0xf83401, 24, "PREVKEYBLOCK", std::bind(exec_get_prev_blocks_info, _1, 1, "PREVKEYBLOCK"))->require_version(4))
.insert(OpcodeInstr::mksimple(0xf835, 16, "GLOBALID", exec_get_global_id)->require_version(4))
.insert(OpcodeInstr::mksimple(0xf836, 16, "GETGASFEE", exec_get_gas_fee)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf837, 16, "GETSTORAGEFEE", exec_get_storage_fee)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf838, 16, "GETFORWARDFEE", exec_get_forward_fee)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf839, 16, "GETPRECOMPILEDGAS", exec_get_precompiled_gas)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf83a, 16, "GETORIGINALFWDFEE", exec_get_original_fwd_fee)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf83b, 16, "GETGASFEESIMPLE", exec_get_gas_fee_simple)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf83c, 16, "GETFORWARDFEESIMPLE", exec_get_forward_fee_simple)->require_version(6))
.insert(OpcodeInstr::mksimple(0xf840, 16, "GETGLOBVAR", exec_get_global_var))
.insert(OpcodeInstr::mkfixedrange(0xf841, 0xf860, 16, 5, instr::dump_1c_and(31, "GETGLOB "), exec_get_global))
.insert(OpcodeInstr::mksimple(0xf860, 16, "SETGLOBVAR", exec_set_global_var))
@ -620,7 +777,6 @@ int exec_ristretto255_from_hash(VmState* st) {
if (!x2->export_bytes(xb + 32, 32, false)) {
throw VmError{Excno::range_chk, "x2 must fit in an unsigned 256-bit integer"};
}
CHECK(sodium_init() >= 0);
crypto_core_ristretto255_from_hash(rb, xb);
td::RefInt256 r{true};
CHECK(r.write().import_bytes(rb, 32, false));
@ -633,8 +789,7 @@ int exec_ristretto255_validate(VmState* st, bool quiet) {
Stack& stack = st->get_stack();
auto x = stack.pop_int();
st->consume_gas(VmState::rist255_validate_gas_price);
unsigned char xb[64];
CHECK(sodium_init() >= 0);
unsigned char xb[32];
if (!x->export_bytes(xb, 32, false) || !crypto_core_ristretto255_is_valid_point(xb)) {
if (quiet) {
stack.push_bool(false);
@ -656,7 +811,6 @@ int exec_ristretto255_add(VmState* st, bool quiet) {
auto x = stack.pop_int();
st->consume_gas(VmState::rist255_add_gas_price);
unsigned char xb[32], yb[32], rb[32];
CHECK(sodium_init() >= 0);
if (!x->export_bytes(xb, 32, false) || !y->export_bytes(yb, 32, false) || crypto_core_ristretto255_add(rb, xb, yb)) {
if (quiet) {
stack.push_bool(false);
@ -681,7 +835,6 @@ int exec_ristretto255_sub(VmState* st, bool quiet) {
auto x = stack.pop_int();
st->consume_gas(VmState::rist255_add_gas_price);
unsigned char xb[32], yb[32], rb[32];
CHECK(sodium_init() >= 0);
if (!x->export_bytes(xb, 32, false) || !y->export_bytes(yb, 32, false) || crypto_core_ristretto255_sub(rb, xb, yb)) {
if (quiet) {
stack.push_bool(false);
@ -719,17 +872,20 @@ int exec_ristretto255_mul(VmState* st, bool quiet) {
auto n = stack.pop_int() % get_ristretto256_l();
auto x = stack.pop_int();
st->consume_gas(VmState::rist255_mul_gas_price);
unsigned char xb[32], nb[32], rb[32];
memset(rb, 255, sizeof(rb));
CHECK(sodium_init() >= 0);
if (!x->export_bytes(xb, 32, false) || !export_bytes_little(n, nb) || crypto_scalarmult_ristretto255(rb, nb, xb)) {
if (std::all_of(rb, rb + 32, [](unsigned char c) { return c == 255; })) {
if (quiet) {
stack.push_bool(false);
return 0;
}
throw VmError{Excno::range_chk, "invalid x or n"};
if (n->sgn() == 0) {
stack.push_smallint(0);
if (quiet) {
stack.push_bool(true);
}
return 0;
}
unsigned char xb[32], nb[32], rb[32];
if (!x->export_bytes(xb, 32, false) || !export_bytes_little(n, nb) || crypto_scalarmult_ristretto255(rb, nb, xb)) {
if (quiet) {
stack.push_bool(false);
return 0;
}
throw VmError{Excno::range_chk, "invalid x or n"};
}
td::RefInt256 r{true};
CHECK(r.write().import_bytes(rb, 32, false));
@ -747,7 +903,6 @@ int exec_ristretto255_mul_base(VmState* st, bool quiet) {
st->consume_gas(VmState::rist255_mulbase_gas_price);
unsigned char nb[32], rb[32];
memset(rb, 255, sizeof(rb));
CHECK(sodium_init() >= 0);
if (!export_bytes_little(n, nb) || crypto_scalarmult_ristretto255_base(rb, nb)) {
if (std::all_of(rb, rb + 32, [](unsigned char c) { return c == 255; })) {
if (quiet) {
@ -833,7 +988,7 @@ int exec_bls_verify(VmState* st) {
VM_LOG(st) << "execute BLS_VERIFY";
Stack& stack = st->get_stack();
stack.check_underflow(3);
st->consume_gas(st->bls_verify_gas_price);
st->consume_gas(VmState::bls_verify_gas_price);
bls::P2 sig = slice_to_bls_p2(*stack.pop_cellslice());
td::BufferSlice msg = slice_to_bls_msg(*stack.pop_cellslice());
bls::P1 pub = slice_to_bls_p1(*stack.pop_cellslice());
@ -845,8 +1000,7 @@ int exec_bls_aggregate(VmState* st) {
VM_LOG(st) << "execute BLS_AGGREGATE";
Stack& stack = st->get_stack();
int n = stack.pop_smallint_range(stack.depth() - 1, 1);
st->consume_gas(
std::max(0LL, VmState::bls_aggregate_base_gas_price + (long long)n * VmState::bls_aggregate_element_gas_price));
st->consume_gas(VmState::bls_aggregate_base_gas_price + (long long)n * VmState::bls_aggregate_element_gas_price);
std::vector<bls::P2> sigs(n);
for (int i = n - 1; i >= 0; --i) {
sigs[i] = slice_to_bls_p2(*stack.pop_cellslice());
@ -1591,17 +1745,39 @@ int exec_send_message(VmState* st) {
}
bool is_masterchain = parse_addr_workchain(*my_addr) == -1 || (!ext_msg && parse_addr_workchain(*dest) == -1);
Ref<Cell> config_dict = get_param(st, 9).as_cell();
Dictionary config{config_dict, 32};
Ref<Cell> prices_cell = config.lookup_ref(td::BitArray<32>{is_masterchain ? 24 : 25});
block::gen::MsgForwardPrices::Record prices;
if (prices_cell.is_null() || !tlb::unpack_cell(std::move(prices_cell), prices)) {
td::Ref<CellSlice> prices_cs;
if (st->get_global_version() >= 6) {
prices_cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5);
} else {
Ref<Cell> config_dict = get_param(st, 9).as_cell();
Dictionary config{config_dict, 32};
Ref<Cell> prices_cell = config.lookup_ref(td::BitArray<32>{is_masterchain ? 24 : 25});
if (prices_cell.not_null()) {
prices_cs = load_cell_slice_ref(prices_cell);
}
}
if (prices_cs.is_null()) {
throw VmError{Excno::unknown, "invalid prices config"};
}
auto r_prices = block::Config::do_get_msg_prices(*prices_cs, is_masterchain ? 24 : 25);
if (r_prices.is_error()) {
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()};
}
block::MsgPrices prices = r_prices.move_as_ok();
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
// bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
vm::VmStorageStat stat(1 << 13);
td::uint64 max_cells;
if (st->get_global_version() >= 6) {
auto r_size_limits_config = block::Config::do_get_size_limits_config(get_unpacked_config_param(st, 6));
if (r_size_limits_config.is_error()) {
throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_size_limits_config.error().message()};
}
max_cells = r_size_limits_config.ok().max_msg_cells;
} else {
max_cells = 1 << 13;
}
vm::VmStorageStat stat(max_cells);
CellSlice cs = load_cell_slice(msg_cell);
cs.skip_first(cs.size());
stat.add_storage(cs);
@ -1649,7 +1825,7 @@ int exec_send_message(VmState* st) {
if (ihr_disabled) {
ihr_fee_short = 0;
} else {
ihr_fee_short = td::uint128(fwd_fee_short).mult(prices.ihr_price_factor).shr(16).lo();
ihr_fee_short = td::uint128(fwd_fee_short).mult(prices.ihr_factor).shr(16).lo();
}
fwd_fee = td::RefInt256{true, fwd_fee_short};
ihr_fee = td::RefInt256{true, ihr_fee_short};

View file

@ -21,6 +21,8 @@
#include "vm/dict.h"
#include "vm/log.h"
#include "vm/vm.h"
#include "cp0.h"
#include <sodium.h>
namespace vm {
@ -770,4 +772,15 @@ void VmState::restore_parent_vm(int res) {
}
}
td::Status init_vm(bool enable_debug) {
if (!init_op_cp0(enable_debug)) {
return td::Status::Error("Failed to init TVM: failed to init cp0");
}
auto code = sodium_init();
if (code < 0) {
return td::Status::Error(PSTRING() << "Failed to init TVM: sodium_init, code=" << code);
}
return td::Status::OK();
}
} // namespace vm

View file

@ -98,6 +98,7 @@ class VmState final : public VmStateInterface {
td::HashSet<CellHash> loaded_cells;
int stack_trace{0}, debug_off{0};
bool chksig_always_succeed{false};
bool stop_on_accept_message{false};
td::optional<td::Bits256> missing_library;
td::uint16 max_data_depth = 512; // Default value
int global_version{0};
@ -339,7 +340,7 @@ class VmState final : public VmStateInterface {
void preclear_cr(const ControlRegs& save) {
cr &= save;
}
int get_global_version() const {
int get_global_version() const override {
return global_version;
}
void set_global_version(int version) {
@ -381,6 +382,12 @@ class VmState final : public VmStateInterface {
bool get_chksig_always_succeed() const {
return chksig_always_succeed;
}
void set_stop_on_accept_message(bool flag) {
stop_on_accept_message = flag;
}
bool get_stop_on_accept_message() const {
return stop_on_accept_message;
}
Ref<OrdCont> ref_to_cont(Ref<Cell> cell) const {
return td::make_ref<OrdCont>(load_cell_slice_ref(std::move(cell)), get_cp());
}
@ -423,4 +430,6 @@ int run_vm_code(Ref<CellSlice> _code, Stack& _stack, int flags = 0, Ref<Cell>* d
Ref<vm::Cell> lookup_library_in(td::ConstBitPtr key, Ref<vm::Cell> lib_root);
td::Status init_vm(bool enable_debug = false);
} // namespace vm

View file

@ -19,6 +19,7 @@
#pragma once
#include "common/refcnt.hpp"
#include "vm/cells.h"
#include "common/global-version.h"
#include "td/utils/Context.h"
@ -38,6 +39,9 @@ class VmStateInterface : public td::Context<VmStateInterface> {
virtual bool register_op(int op_units = 1) {
return true;
};
virtual int get_global_version() const {
return ton::SUPPORTED_VERSION;
}
};
} // namespace vm