mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
Cheap fee calculations (#878)
* TVM v6 * New tuple with unpacked config parameters in c7 * New instructions for calculating fees * Change unpacked_config_tuple, fix typo --------- Co-authored-by: SpyCheese <mikle98@yandex.ru>
This commit is contained in:
parent
e459aea8e8
commit
64b04e46d7
12 changed files with 384 additions and 73 deletions
|
@ -19,6 +19,6 @@
|
|||
namespace ton {
|
||||
|
||||
// See doc/GlobalVersions.md
|
||||
const int SUPPORTED_VERSION = 5;
|
||||
const int SUPPORTED_VERSION = 6;
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -654,7 +665,6 @@ td::Result<GasLimitsPrices> Config::do_get_gas_limits_prices(td::Ref<vm::Cell> c
|
|||
res.delete_due_limit = r.delete_due_limit;
|
||||
};
|
||||
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
|
||||
vm::CellSlice cs0 = cs;
|
||||
if (tlb::unpack(cs, rec)) {
|
||||
f(rec, rec.special_gas_limit);
|
||||
} else {
|
||||
|
@ -689,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 {
|
||||
|
@ -698,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
|
||||
|
@ -1917,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;
|
||||
|
@ -1939,9 +1959,9 @@ td::Result<SizeLimitsConfig> Config::get_size_limits_config() const {
|
|||
};
|
||||
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");
|
||||
|
@ -1976,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");
|
||||
|
|
|
@ -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;
|
||||
|
@ -604,9 +609,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;
|
||||
|
@ -633,8 +640,10 @@ class Config {
|
|||
ton::CatchainSeqno cc_seqno) const;
|
||||
std::vector<ton::ValidatorDescr> compute_total_validator_set(int next) 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,
|
||||
|
|
|
@ -1337,6 +1337,10 @@ Ref<vm::Tuple> Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const {
|
|||
// may only return tuple or raise Error (See crypto/block/mc-config.cpp#2223)
|
||||
tuple.push_back(cfg.prev_blocks_info.not_null() ? vm::StackEntry(cfg.prev_blocks_info) : vm::StackEntry());
|
||||
}
|
||||
if (cfg.global_version >= 6) {
|
||||
tuple.push_back(cfg.unpacked_config_tuple.not_null() ? vm::StackEntry(cfg.unpacked_config_tuple)
|
||||
: vm::StackEntry()); // unpacked_config_tuple:[...]
|
||||
}
|
||||
auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
|
||||
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string();
|
||||
return vm::make_tuple_ref(std::move(tuple_ref));
|
||||
|
@ -1920,6 +1924,25 @@ td::uint64 MsgPrices::compute_fwd_fees(td::uint64 cells, td::uint64 bits) const
|
|||
.lo();
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the forward fees for a message based on the number of cells and bits.
|
||||
* Return the result as td::RefInt256
|
||||
*
|
||||
* msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms
|
||||
* ihr_fwd_fees = ceil((msg_fwd_fees * ihr_price_factor)/2^16) nanograms
|
||||
* bits in the root cell of a message are not included in msg.bits (lump_price pays for them)
|
||||
*
|
||||
* @param cells The number of cells in the message.
|
||||
* @param bits The number of bits in the message.
|
||||
*
|
||||
* @returns The computed forward fees for the message as td::RefInt256j.
|
||||
*/
|
||||
td::RefInt256 MsgPrices::compute_fwd_fees256(td::uint64 cells, td::uint64 bits) const {
|
||||
return td::rshift(
|
||||
td::make_refint(lump_price) + td::make_refint(bit_price) * bits + td::make_refint(cell_price) * cells, 16,
|
||||
1); // divide by 2^16 with ceil rounding
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes the forward fees and IHR fees for a message with the given number of cells and bits.
|
||||
*
|
||||
|
@ -3539,6 +3562,9 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
if (compute_phase_cfg->global_version >= 4) {
|
||||
compute_phase_cfg->prev_blocks_info = std::move(prev_blocks_info);
|
||||
}
|
||||
if (compute_phase_cfg->global_version >= 6) {
|
||||
compute_phase_cfg->unpacked_config_tuple = config.get_unpacked_config_tuple(now);
|
||||
}
|
||||
compute_phase_cfg->suspended_addresses = config.get_suspended_addresses(now);
|
||||
compute_phase_cfg->size_limits = size_limits;
|
||||
}
|
||||
|
|
|
@ -117,6 +117,7 @@ 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;
|
||||
|
|
|
@ -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
|
||||
|
@ -1295,12 +1295,17 @@ x{F82A} @Defop MYCODE
|
|||
x{F82B} @Defop INCOMINGVALUE
|
||||
x{F82C} @Defop STORAGEFEES
|
||||
x{F82D} @Defop PREVBLOCKSINFOTUPLE
|
||||
x{F82E} @Defop UNPACKEDCONFIGTUPLE
|
||||
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 GETEXECUTIONPRICE
|
||||
x{F837} @Defop GETSTORAGEPRICE
|
||||
x{F838} @Defop GETFORWARDPRICE
|
||||
x{F839} @Defop GETPRECOMPILEDGAS
|
||||
|
||||
x{F840} @Defop GETGLOBVAR
|
||||
{ dup 1 31 @rangechk <b x{F85_} s, swap 5 u, @addopb } : GETGLOB
|
||||
|
|
|
@ -171,6 +171,9 @@ 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
|
||||
}
|
||||
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));
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
#include "openssl/digest.hpp"
|
||||
#include <sodium.h>
|
||||
#include "bls.h"
|
||||
#include "mc-config.h"
|
||||
|
||||
namespace vm {
|
||||
|
||||
|
@ -122,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;
|
||||
|
@ -232,20 +247,107 @@ 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;
|
||||
}
|
||||
|
||||
int exec_get_execution_price(VmState* st) {
|
||||
VM_LOG(st) << "execute GETEXECUTIONPRICE";
|
||||
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);
|
||||
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));
|
||||
block::GasLimitsPrices prices = r_prices.move_as_ok();
|
||||
stack.push_int(prices.compute_gas_price(gas));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int exec_get_storage_price(VmState* st) {
|
||||
VM_LOG(st) << "execute GETSTORAGEPRICE";
|
||||
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_price(VmState* st) {
|
||||
VM_LOG(st) << "execute GETFORWARDPRICE";
|
||||
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);
|
||||
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()};
|
||||
}
|
||||
block::MsgPrices prices = r_prices.move_as_ok();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -263,13 +365,18 @@ 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::mkfixedrange(0xf82f, 0xf830, 16, 4, instr::dump_1c("GETPARAM "), exec_get_var_param))
|
||||
.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, "GETEXECUTIONPRICE", exec_get_execution_price)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf837, 16, "GETSTORAGEPRICE", exec_get_storage_price)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf838, 16, "GETFORWARDPRICE", exec_get_forward_price)->require_version(6))
|
||||
.insert(OpcodeInstr::mksimple(0xf839, 16, "GETPRECOMPILEDGAS", exec_get_precompiled_gas)->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))
|
||||
|
@ -1592,17 +1699,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);
|
||||
|
@ -1650,7 +1779,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};
|
||||
|
|
|
@ -53,4 +53,28 @@ See [this post](https://t.me/tonstatus/88) for details.
|
|||
### Loading libraries
|
||||
* Loading "nested libraries" (i.e. a library cell that points to another library cell) throws an exception.
|
||||
* Loading a library consumes gas for cell load only once (for the library cell), not twice (both for the library cell and the cell in the library).
|
||||
* `XLOAD` now works differently. When it takes a library cell, it returns the cell that it points to. This allows loading "nested libraries", if needed.
|
||||
* `XLOAD` now works differently. When it takes a library cell, it returns the cell that it points to. This allows loading "nested libraries", if needed.
|
||||
|
||||
## Version 6
|
||||
|
||||
### c7 tuple
|
||||
**c7** tuple extended from 14 to 15 elements. The new element is a tuple that contains some config parameters as cell slices.
|
||||
If the parameter is absent from the config, the value is null.
|
||||
* **0**: `StoragePrices` from `ConfigParam 18`. Not the whole dict, but only the one StoragePrices entry (one which corresponds to the current time).
|
||||
* **1**: `ConfigParam 19` (global id).
|
||||
* **2**: `ConfigParam 20` (mc gas prices).
|
||||
* **3**: `ConfigParam 21` (gas prices).
|
||||
* **4**: `ConfigParam 24` (mc fwd fees).
|
||||
* **5**: `ConfigParam 25` (fwd fees).
|
||||
* **6**: `ConfigParam 43` (size limits).
|
||||
|
||||
### New TVM instructions
|
||||
* `GETEXECUTIONPRICE` (`gas_used is_mc - price`) - calculates gas fee.
|
||||
* `GETSTORAGEPRICE` (`cells bits seconds is_mc - price`) - calculates storage fees (only current StoragePrices entry is used).
|
||||
* `GETFORWARDPRICE` (`cells bits is_mc - price`) - calculates forward fee.
|
||||
* `GETPRECOMPILEDGAS` (`- null`) - reserved, currently returns `null`.
|
||||
`gas_used`, `cells`, `bits`, `time_delta` are integers in range `0..2^63-1`.
|
||||
|
||||
### Other changes
|
||||
* `GLOBALID` gets `ConfigParam 19` from the tuple, not from the config dict. This decreases gas usage.
|
||||
* `SENDMSG` gets `ConfigParam 24/25` (message prices) from the tuple, not from the config dict, and also uses `ConfigParam 43` to get max_msg_cells.
|
|
@ -333,8 +333,8 @@ TEST(Tonlib, ConfigParseBug) {
|
|||
unsigned char buff[128];
|
||||
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), literal.begin(), literal.end());
|
||||
CHECK(bits >= 0);
|
||||
auto slice = vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize();
|
||||
block::Config::do_get_gas_limits_prices(std::move(slice), 21).ensure();
|
||||
auto cell = vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize();
|
||||
block::Config::do_get_gas_limits_prices(vm::load_cell_slice(cell), 21).ensure();
|
||||
}
|
||||
|
||||
TEST(Tonlib, EncryptionApi) {
|
||||
|
|
|
@ -11,6 +11,55 @@
|
|||
#include "td/utils/ScopeGuard.h"
|
||||
#include "td/utils/StringBuilder.h"
|
||||
#include "td/utils/Timer.h"
|
||||
#include "block.h"
|
||||
#include "td/utils/filesystem.h"
|
||||
#include "mc-config.h"
|
||||
|
||||
td::Ref<vm::Tuple> c7;
|
||||
|
||||
void prepare_c7() {
|
||||
auto now = (td::uint32)td::Clocks::system();
|
||||
td::Ref<vm::Cell> config_root;
|
||||
auto config_data = td::read_file("config.boc");
|
||||
if (config_data.is_ok()) {
|
||||
LOG(WARNING) << "Reading config from config.boc";
|
||||
auto r_cell = vm::std_boc_deserialize(config_data.move_as_ok());
|
||||
r_cell.ensure();
|
||||
config_root = r_cell.move_as_ok();
|
||||
}
|
||||
|
||||
vm::CellBuilder addr;
|
||||
addr.store_long(4, 3);
|
||||
addr.store_long(0, 8);
|
||||
addr.store_ones(256);
|
||||
std::vector<vm::StackEntry> tuple = {
|
||||
td::make_refint(0x076ef1ea), // [ magic:0x076ef1ea
|
||||
td::zero_refint(), // actions:Integer
|
||||
td::zero_refint(), // msgs_sent:Integer
|
||||
td::make_refint(now), // unixtime:Integer
|
||||
td::make_refint(0), // block_lt:Integer
|
||||
td::make_refint(0), // trans_lt:Integer
|
||||
td::make_refint(123), // rand_seed:Integer
|
||||
block::CurrencyCollection(td::make_refint(10000LL * 1000000000))
|
||||
.as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
|
||||
addr.as_cellslice_ref(), // myself:MsgAddressInt
|
||||
vm::StackEntry::maybe(config_root) // global_config:(Maybe Cell) ] = SmartContractInfo;
|
||||
};
|
||||
tuple.push_back({}); // code:Cell
|
||||
tuple.push_back(block::CurrencyCollection(td::make_refint(2000LL * 1000000000))
|
||||
.as_vm_tuple()); // in_msg_value:[Integer (Maybe Cell)]
|
||||
tuple.push_back(td::make_refint(0)); // storage_fees:Integer
|
||||
tuple.push_back(vm::StackEntry()); // prev_blocks_info
|
||||
if (config_root.not_null()) {
|
||||
block::Config config{config_root};
|
||||
config.unpack().ensure();
|
||||
tuple.push_back(config.get_unpacked_config_tuple(now)); // unpacked_config_tuple
|
||||
} else {
|
||||
tuple.push_back(vm::StackEntry());
|
||||
}
|
||||
auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
|
||||
c7 = vm::make_tuple_ref(std::move(tuple_ref));
|
||||
}
|
||||
|
||||
td::Ref<vm::Cell> to_cell(td::Slice s) {
|
||||
if (s.size() >= 4 && s.substr(0, 4) == "boc:") {
|
||||
|
@ -34,9 +83,11 @@ struct runInfo {
|
|||
long long gasUsage;
|
||||
int vmReturnCode;
|
||||
|
||||
runInfo() : runtime(0.0), gasUsage(0), vmReturnCode(0) {}
|
||||
runInfo(long double runtime, long long gasUsage, int vmReturnCode) :
|
||||
runtime(runtime), gasUsage(gasUsage), vmReturnCode(vmReturnCode) {}
|
||||
runInfo() : runtime(0.0), gasUsage(0), vmReturnCode(0) {
|
||||
}
|
||||
runInfo(long double runtime, long long gasUsage, int vmReturnCode)
|
||||
: runtime(runtime), gasUsage(gasUsage), vmReturnCode(vmReturnCode) {
|
||||
}
|
||||
|
||||
runInfo operator+(const runInfo& addend) const {
|
||||
return {runtime + addend.runtime, gasUsage + addend.gasUsage, vmReturnCode ? vmReturnCode : addend.vmReturnCode};
|
||||
|
@ -45,7 +96,7 @@ struct runInfo {
|
|||
runInfo& operator+=(const runInfo& addend) {
|
||||
runtime += addend.runtime;
|
||||
gasUsage += addend.gasUsage;
|
||||
if(!vmReturnCode && addend.vmReturnCode) {
|
||||
if (!vmReturnCode && addend.vmReturnCode) {
|
||||
vmReturnCode = addend.vmReturnCode;
|
||||
}
|
||||
return *this;
|
||||
|
@ -68,8 +119,8 @@ vm::Stack prepare_stack(td::Slice command) {
|
|||
vm::Stack stack;
|
||||
try {
|
||||
vm::GasLimits gas_limit;
|
||||
int ret = vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/,
|
||||
vm::VmLog{}, nullptr, &gas_limit, {}, {}, nullptr, 4);
|
||||
int ret = vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/, vm::VmLog{}, nullptr,
|
||||
&gas_limit, {}, c7, nullptr, ton::SUPPORTED_VERSION);
|
||||
CHECK(ret == 0);
|
||||
} catch (...) {
|
||||
LOG(FATAL) << "catch unhandled exception";
|
||||
|
@ -83,8 +134,8 @@ runInfo time_run_vm(td::Slice command, td::Ref<vm::Stack> stack) {
|
|||
CHECK(stack.is_unique());
|
||||
try {
|
||||
vm::GasLimits gas_limit;
|
||||
vm::VmState vm{vm::load_cell_slice_ref(cell), std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, {}};
|
||||
vm.set_global_version(4);
|
||||
vm::VmState vm{vm::load_cell_slice_ref(cell), std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7};
|
||||
vm.set_global_version(ton::SUPPORTED_VERSION);
|
||||
std::clock_t cStart = std::clock();
|
||||
int ret = ~vm.run();
|
||||
std::clock_t cEnd = std::clock();
|
||||
|
@ -102,7 +153,7 @@ runtimeStats averageRuntime(td::Slice command, const vm::Stack& stack) {
|
|||
std::vector<runInfo> values;
|
||||
values.reserve(samples);
|
||||
td::Timer t0;
|
||||
for(size_t i = 0; i < samples; ++i) {
|
||||
for (size_t i = 0; i < samples; ++i) {
|
||||
const auto value_empty = time_run_vm(td::Slice(""), td::Ref<vm::Stack>(true, stack));
|
||||
const auto value_code = time_run_vm(command, td::Ref<vm::Stack>(true, stack));
|
||||
runInfo value{value_code.runtime - value_empty.runtime, value_code.gasUsage - value_empty.gasUsage,
|
||||
|
@ -120,18 +171,16 @@ runtimeStats averageRuntime(td::Slice command, const vm::Stack& stack) {
|
|||
long double runtimeDiffSum = 0.0;
|
||||
long double gasDiffSum = 0.0;
|
||||
bool errored = false;
|
||||
for(const auto value : values) {
|
||||
for (const auto value : values) {
|
||||
const auto runtime = value.runtime - runtimeMean;
|
||||
const auto gasUsage = static_cast<long double>(value.gasUsage) - gasMean;
|
||||
runtimeDiffSum += runtime * runtime;
|
||||
gasDiffSum += gasUsage * gasUsage;
|
||||
errored = errored || value.errored();
|
||||
}
|
||||
return {
|
||||
{runtimeMean, sqrtl(runtimeDiffSum / static_cast<long double>(samples))},
|
||||
{gasMean, sqrtl(gasDiffSum / static_cast<long double>(samples))},
|
||||
errored
|
||||
};
|
||||
return {{runtimeMean, sqrtl(runtimeDiffSum / static_cast<long double>(samples))},
|
||||
{gasMean, sqrtl(gasDiffSum / static_cast<long double>(samples))},
|
||||
errored};
|
||||
}
|
||||
|
||||
runtimeStats timeInstruction(const std::string& setupCode, const std::string& toMeasure) {
|
||||
|
@ -141,28 +190,33 @@ runtimeStats timeInstruction(const std::string& setupCode, const std::string& to
|
|||
|
||||
int main(int argc, char** argv) {
|
||||
SET_VERBOSITY_LEVEL(verbosity_ERROR);
|
||||
if(argc != 2 && argc != 3) {
|
||||
std::cerr <<
|
||||
"This utility compares the timing of VM execution against the gas used.\n"
|
||||
"It can be used to discover opcodes or opcode sequences that consume an "
|
||||
"inordinate amount of computational resources relative to their gas cost.\n"
|
||||
"\n"
|
||||
"The utility expects two command line arguments: \n"
|
||||
"The TVM code used to set up the stack and VM state followed by the TVM code to measure.\n"
|
||||
"For example, to test the DIVMODC opcode:\n"
|
||||
"\t$ " << argv[0] << " 80FF801C A90E 2>/dev/null\n"
|
||||
"\tOPCODE,runtime mean,runtime stddev,gas mean,gas stddev\n"
|
||||
"\tA90E,0.0066416,0.00233496,26,0\n"
|
||||
"\n"
|
||||
"Usage: " << argv[0] << " [TVM_SETUP_BYTECODE] TVM_BYTECODE\n"
|
||||
"\tBYTECODE is either:\n"
|
||||
"\t1. hex-encoded string (e.g. A90E for DIVMODC)\n"
|
||||
"\t2. boc:<serialized boc in base64> (e.g. boc:te6ccgEBAgEABwABAogBAAJ7)" << std::endl << std::endl;
|
||||
if (argc != 2 && argc != 3) {
|
||||
std::cerr << "This utility compares the timing of VM execution against the gas used.\n"
|
||||
"It can be used to discover opcodes or opcode sequences that consume an "
|
||||
"inordinate amount of computational resources relative to their gas cost.\n"
|
||||
"\n"
|
||||
"The utility expects two command line arguments: \n"
|
||||
"The TVM code used to set up the stack and VM state followed by the TVM code to measure.\n"
|
||||
"For example, to test the DIVMODC opcode:\n"
|
||||
"\t$ "
|
||||
<< argv[0]
|
||||
<< " 80FF801C A90E 2>/dev/null\n"
|
||||
"\tOPCODE,runtime mean,runtime stddev,gas mean,gas stddev\n"
|
||||
"\tA90E,0.0066416,0.00233496,26,0\n"
|
||||
"\n"
|
||||
"Usage: "
|
||||
<< argv[0]
|
||||
<< " [TVM_SETUP_BYTECODE] TVM_BYTECODE\n"
|
||||
"\tBYTECODE is either:\n"
|
||||
"\t1. hex-encoded string (e.g. A90E for DIVMODC)\n"
|
||||
"\t2. boc:<serialized boc in base64> (e.g. boc:te6ccgEBAgEABwABAogBAAJ7)"
|
||||
<< std::endl
|
||||
<< std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::cout << "OPCODE,runtime mean,runtime stddev,gas mean,gas stddev,error" << std::endl;
|
||||
std::string setup, code;
|
||||
if(argc == 2) {
|
||||
if (argc == 2) {
|
||||
setup = "";
|
||||
code = argv[1];
|
||||
} else {
|
||||
|
@ -170,6 +224,7 @@ int main(int argc, char** argv) {
|
|||
code = argv[2];
|
||||
}
|
||||
vm::init_vm().ensure();
|
||||
prepare_c7();
|
||||
const auto time = timeInstruction(setup, code);
|
||||
std::cout << std::fixed << std::setprecision(9) << code << "," << time.runtime.mean << "," << time.runtime.stddev
|
||||
<< "," << time.gasUsage.mean << "," << time.gasUsage.stddev << "," << (int)time.errored << std::endl;
|
||||
|
|
|
@ -960,6 +960,9 @@ bool ValidateQuery::fetch_config_params() {
|
|||
}
|
||||
compute_phase_cfg_.prev_blocks_info = prev_blocks_info.move_as_ok();
|
||||
}
|
||||
if (compute_phase_cfg_.global_version >= 6) {
|
||||
compute_phase_cfg_.unpacked_config_tuple = config_->get_unpacked_config_tuple(now_);
|
||||
}
|
||||
compute_phase_cfg_.suspended_addresses = config_->get_suspended_addresses(now_);
|
||||
compute_phase_cfg_.size_limits = size_limits;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue