1
0
Fork 0
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:
EmelyanenkoK 2024-01-26 15:43:53 +03:00 committed by GitHub
parent e459aea8e8
commit 64b04e46d7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 384 additions and 73 deletions

View file

@ -19,6 +19,6 @@
namespace ton { namespace ton {
// See doc/GlobalVersions.md // See doc/GlobalVersions.md
const int SUPPORTED_VERSION = 5; const int SUPPORTED_VERSION = 6;
} }

View file

@ -621,12 +621,14 @@ td::Result<std::vector<StoragePrices>> Config::get_storage_prices() const {
} }
vm::Dictionary dict{std::move(cell), 32}; vm::Dictionary dict{std::move(cell), 32};
if (!dict.check_for_each([&res](Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n) -> bool { if (!dict.check_for_each([&res](Ref<vm::CellSlice> cs_ref, td::ConstBitPtr key, int n) -> bool {
block::gen::StoragePrices::Record data; auto r_prices = do_get_one_storage_prices(*cs_ref);
if (!tlb::csr_unpack(std::move(cs_ref), data) || data.utime_since != key.get_uint(n)) { 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; 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 true;
})) { })) {
return td::Status::Error("invalid storage prices dictionary in configuration parameter 18"); 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); 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; GasLimitsPrices res;
auto cs = vm::load_cell_slice(cell); vm::CellSlice cs0 = cs;
block::gen::GasLimitsPrices::Record_gas_flat_pfx flat; block::gen::GasLimitsPrices::Record_gas_flat_pfx flat;
if (tlb::unpack(cs, flat)) { if (tlb::unpack(cs, flat)) {
cs = *flat.other; cs = *flat.other;
res.flat_gas_limit = flat.flat_gas_limit; res.flat_gas_limit = flat.flat_gas_limit;
res.flat_gas_price = flat.flat_gas_price; res.flat_gas_price = flat.flat_gas_price;
} else { } else {
cs = vm::load_cell_slice(cell); cs = cs0;
} }
auto f = [&](const auto& r, td::uint64 spec_limit) { auto f = [&](const auto& r, td::uint64 spec_limit) {
res.gas_limit = r.gas_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; res.delete_due_limit = r.delete_due_limit;
}; };
block::gen::GasLimitsPrices::Record_gas_prices_ext rec; block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
vm::CellSlice cs0 = cs;
if (tlb::unpack(cs, rec)) { if (tlb::unpack(cs, rec)) {
f(rec, rec.special_gas_limit); f(rec, rec.special_gas_limit);
} else { } else {
@ -689,7 +699,7 @@ td::Result<GasLimitsPrices> Config::get_gas_limits_prices(bool is_masterchain) c
if (cell.is_null()) { if (cell.is_null()) {
return td::Status::Error(PSLICE() << "configuration parameter " << id << " with gas prices is absent"); 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 { 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()) { if (cell.is_null()) {
return td::Status::Error(PSLICE() << "configuration parameter " << id << " with msg prices is absent"); 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; block::gen::MsgForwardPrices::Record rec;
if (!tlb::unpack(cs, rec)) { if (!tlb::unpack(cs, rec)) {
return td::Status::Error(PSLICE() << "configuration parameter " << id 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 { td::Result<SizeLimitsConfig> Config::get_size_limits_config() const {
SizeLimitsConfig limits;
td::Ref<vm::Cell> param = get_config_param(43); td::Ref<vm::Cell> param = get_config_param(43);
if (param.is_null()) { 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) { auto unpack_v1 = [&](auto& rec) {
limits.max_msg_bits = rec.max_msg_bits; 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 rec_v1;
gen::SizeLimitsConfig::Record_size_limits_config_v2 rec_v2; 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); unpack_v1(rec_v1);
} else if (tlb::unpack_cell(param, rec_v2)) { } else if (tlb::csr_unpack(cs, rec_v2)) {
unpack_v2(rec_v2); unpack_v2(rec_v2);
} else { } else {
return td::Status::Error("configuration parameter 43 is invalid"); return td::Status::Error("configuration parameter 43 is invalid");
@ -1976,6 +1996,42 @@ BurningConfig Config::get_burning_config() const {
return c; 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) { td::Result<std::pair<ton::UnixTime, ton::UnixTime>> Config::unpack_validator_set_start_stop(Ref<vm::Cell> vset_root) {
if (vset_root.is_null()) { if (vset_root.is_null()) {
return td::Status::Error("validator set absent"); return td::Status::Error("validator set absent");

View file

@ -350,7 +350,11 @@ struct GasLimitsPrices {
td::uint64 freeze_due_limit{0}; td::uint64 freeze_due_limit{0};
td::uint64 delete_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 // 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 first_frac;
td::uint32 next_frac; td::uint32 next_frac;
td::uint64 compute_fwd_fees(td::uint64 cells, td::uint64 bits) const; 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, std::pair<td::uint64, td::uint64> compute_fwd_ihr_fees(td::uint64 cells, td::uint64 bits,
bool ihr_disabled = false) const; bool ihr_disabled = false) const;
MsgPrices() = default; MsgPrices() = default;
@ -604,9 +609,11 @@ class Config {
bool is_special_smartcontract(const ton::StdSmcAddress& addr) const; bool is_special_smartcontract(const ton::StdSmcAddress& addr) const;
static td::Result<std::unique_ptr<ValidatorSet>> unpack_validator_set(Ref<vm::Cell> valset_root); static td::Result<std::unique_ptr<ValidatorSet>> unpack_validator_set(Ref<vm::Cell> valset_root);
td::Result<std::vector<StoragePrices>> get_storage_prices() const; 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; 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; 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); static CatchainValidatorsConfig unpack_catchain_validators_config(Ref<vm::Cell> cell);
CatchainValidatorsConfig get_catchain_validators_config() const; CatchainValidatorsConfig get_catchain_validators_config() const;
td::Status visit_validator_params() const; td::Status visit_validator_params() const;
@ -633,8 +640,10 @@ class Config {
ton::CatchainSeqno cc_seqno) const; ton::CatchainSeqno cc_seqno) const;
std::vector<ton::ValidatorDescr> compute_total_validator_set(int next) const; std::vector<ton::ValidatorDescr> compute_total_validator_set(int next) const;
td::Result<SizeLimitsConfig> get_size_limits_config() 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; std::unique_ptr<vm::Dictionary> get_suspended_addresses(ton::UnixTime now) const;
BurningConfig get_burning_config() const; BurningConfig get_burning_config() const;
td::Ref<vm::Tuple> get_unpacked_config_tuple(ton::UnixTime now) const;
static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf, static std::vector<ton::ValidatorDescr> do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard, ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time, const block::ValidatorSet& vset, ton::UnixTime time,

View file

@ -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) // 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()); 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)); auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string(); LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string();
return vm::make_tuple_ref(std::move(tuple_ref)); 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(); .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. * 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) { if (compute_phase_cfg->global_version >= 4) {
compute_phase_cfg->prev_blocks_info = std::move(prev_blocks_info); 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->suspended_addresses = config.get_suspended_addresses(now);
compute_phase_cfg->size_limits = size_limits; compute_phase_cfg->size_limits = size_limits;
} }

View file

@ -117,6 +117,7 @@ struct ComputePhaseConfig {
td::uint16 max_vm_data_depth = 512; td::uint16 max_vm_data_depth = 512;
int global_version = 0; int global_version = 0;
Ref<vm::Tuple> prev_blocks_info; Ref<vm::Tuple> prev_blocks_info;
Ref<vm::Tuple> unpacked_config_tuple;
std::unique_ptr<vm::Dictionary> suspended_addresses; std::unique_ptr<vm::Dictionary> suspended_addresses;
SizeLimitsConfig size_limits; SizeLimitsConfig size_limits;
int vm_log_verbosity = 0; int vm_log_verbosity = 0;

View file

@ -2,7 +2,7 @@ library TVM_Asm
// simple TVM Assembler // simple TVM Assembler
namespace Asm namespace Asm
Asm definitions Asm definitions
"0.4.4" constant asm-fif-version "0.4.5" constant asm-fif-version
variable @atend variable @atend
variable @was-split variable @was-split
@ -1295,12 +1295,17 @@ x{F82A} @Defop MYCODE
x{F82B} @Defop INCOMINGVALUE x{F82B} @Defop INCOMINGVALUE
x{F82C} @Defop STORAGEFEES x{F82C} @Defop STORAGEFEES
x{F82D} @Defop PREVBLOCKSINFOTUPLE x{F82D} @Defop PREVBLOCKSINFOTUPLE
x{F82E} @Defop UNPACKEDCONFIGTUPLE
x{F830} @Defop CONFIGDICT x{F830} @Defop CONFIGDICT
x{F832} @Defop CONFIGPARAM x{F832} @Defop CONFIGPARAM
x{F833} @Defop CONFIGOPTPARAM x{F833} @Defop CONFIGOPTPARAM
x{F83400} @Defop PREVMCBLOCKS x{F83400} @Defop PREVMCBLOCKS
x{F83401} @Defop PREVKEYBLOCK x{F83401} @Defop PREVKEYBLOCK
x{F835} @Defop GLOBALID x{F835} @Defop GLOBALID
x{F836} @Defop GETEXECUTIONPRICE
x{F837} @Defop GETSTORAGEPRICE
x{F838} @Defop GETFORWARDPRICE
x{F839} @Defop GETPRECOMPILEDGAS
x{F840} @Defop GETGLOBVAR x{F840} @Defop GETGLOBVAR
{ dup 1 31 @rangechk <b x{F85_} s, swap 5 u, @addopb } : GETGLOB { dup 1 31 @rangechk <b x{F85_} s, swap 5 u, @addopb } : GETGLOB

View file

@ -171,6 +171,9 @@ td::Ref<vm::Tuple> prepare_vm_c7(SmartContract::Args args, td::Ref<vm::Cell> cod
// prev_key_block:BlockId ] : PrevBlocksInfo // prev_key_block:BlockId ] : PrevBlocksInfo
tuple.push_back(args.prev_blocks_info ? args.prev_blocks_info.value() : vm::StackEntry{}); // prev_block_info 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)); auto tuple_ref = td::make_cnt_ref<std::vector<vm::StackEntry>>(std::move(tuple));
//LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); //LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string();
return vm::make_tuple_ref(std::move(tuple_ref)); return vm::make_tuple_ref(std::move(tuple_ref));

View file

@ -35,6 +35,7 @@
#include "openssl/digest.hpp" #include "openssl/digest.hpp"
#include <sodium.h> #include <sodium.h>
#include "bls.h" #include "bls.h"
#include "mc-config.h"
namespace vm { namespace vm {
@ -122,6 +123,20 @@ static const StackEntry& get_param(VmState* st, unsigned idx) {
return tuple_index(t1, 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) { int exec_get_param(VmState* st, unsigned idx, const char* name) {
if (name) { if (name) {
VM_LOG(st) << "execute " << 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) { int exec_get_global_id(VmState* st) {
Ref<Cell> config = get_param(st, 9).as_cell(); VM_LOG(st) << "execute GLOBALID";
if (config.is_null()) { if (st->get_global_version() >= 6) {
throw VmError{Excno::type_chk, "intermediate value is not a cell"}; 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}; return 0;
Ref<Cell> cell = config_dict.lookup_ref(td::BitArray<32>{19}); }
if (cell.is_null()) {
throw VmError{Excno::unknown, "invalid global-id config"}; 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); auto r_prices = block::Config::do_get_gas_limits_prices(*cs, is_masterchain ? 20 : 21);
if (cs.size() < 32) { if (r_prices.is_error()) {
throw VmError{Excno::unknown, "invalid global-id config"}; 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; 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(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(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::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(0xf830, 16, "CONFIGDICT", exec_get_config_dict))
.insert(OpcodeInstr::mksimple(0xf832, 16, "CONFIGPARAM", std::bind(exec_get_config_param, _1, false))) .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(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(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(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(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::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::mkfixedrange(0xf841, 0xf860, 16, 5, instr::dump_1c_and(31, "GETGLOB "), exec_get_global))
.insert(OpcodeInstr::mksimple(0xf860, 16, "SETGLOBVAR", exec_set_global_var)) .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); 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(); td::Ref<CellSlice> prices_cs;
Dictionary config{config_dict, 32}; if (st->get_global_version() >= 6) {
Ref<Cell> prices_cell = config.lookup_ref(td::BitArray<32>{is_masterchain ? 24 : 25}); prices_cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5);
block::gen::MsgForwardPrices::Record prices; } else {
if (prices_cell.is_null() || !tlb::unpack_cell(std::move(prices_cell), prices)) { 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"}; 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 // 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) // 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); CellSlice cs = load_cell_slice(msg_cell);
cs.skip_first(cs.size()); cs.skip_first(cs.size());
stat.add_storage(cs); stat.add_storage(cs);
@ -1650,7 +1779,7 @@ int exec_send_message(VmState* st) {
if (ihr_disabled) { if (ihr_disabled) {
ihr_fee_short = 0; ihr_fee_short = 0;
} else { } 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}; fwd_fee = td::RefInt256{true, fwd_fee_short};
ihr_fee = td::RefInt256{true, ihr_fee_short}; ihr_fee = td::RefInt256{true, ihr_fee_short};

View file

@ -53,4 +53,28 @@ See [this post](https://t.me/tonstatus/88) for details.
### Loading libraries ### Loading libraries
* Loading "nested libraries" (i.e. a library cell that points to another library cell) throws an exception. * 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). * 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.

View file

@ -333,8 +333,8 @@ TEST(Tonlib, ConfigParseBug) {
unsigned char buff[128]; unsigned char buff[128];
int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), literal.begin(), literal.end()); int bits = (int)td::bitstring::parse_bitstring_hex_literal(buff, sizeof(buff), literal.begin(), literal.end());
CHECK(bits >= 0); CHECK(bits >= 0);
auto slice = vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize(); auto cell = vm::CellBuilder().store_bits(td::ConstBitPtr{buff}, bits).finalize();
block::Config::do_get_gas_limits_prices(std::move(slice), 21).ensure(); block::Config::do_get_gas_limits_prices(vm::load_cell_slice(cell), 21).ensure();
} }
TEST(Tonlib, EncryptionApi) { TEST(Tonlib, EncryptionApi) {

View file

@ -11,6 +11,55 @@
#include "td/utils/ScopeGuard.h" #include "td/utils/ScopeGuard.h"
#include "td/utils/StringBuilder.h" #include "td/utils/StringBuilder.h"
#include "td/utils/Timer.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) { td::Ref<vm::Cell> to_cell(td::Slice s) {
if (s.size() >= 4 && s.substr(0, 4) == "boc:") { if (s.size() >= 4 && s.substr(0, 4) == "boc:") {
@ -34,9 +83,11 @@ struct runInfo {
long long gasUsage; long long gasUsage;
int vmReturnCode; int vmReturnCode;
runInfo() : runtime(0.0), gasUsage(0), vmReturnCode(0) {} runInfo() : runtime(0.0), gasUsage(0), vmReturnCode(0) {
runInfo(long double runtime, long long gasUsage, int vmReturnCode) : }
runtime(runtime), gasUsage(gasUsage), vmReturnCode(vmReturnCode) {} runInfo(long double runtime, long long gasUsage, int vmReturnCode)
: runtime(runtime), gasUsage(gasUsage), vmReturnCode(vmReturnCode) {
}
runInfo operator+(const runInfo& addend) const { runInfo operator+(const runInfo& addend) const {
return {runtime + addend.runtime, gasUsage + addend.gasUsage, vmReturnCode ? vmReturnCode : addend.vmReturnCode}; return {runtime + addend.runtime, gasUsage + addend.gasUsage, vmReturnCode ? vmReturnCode : addend.vmReturnCode};
@ -45,7 +96,7 @@ struct runInfo {
runInfo& operator+=(const runInfo& addend) { runInfo& operator+=(const runInfo& addend) {
runtime += addend.runtime; runtime += addend.runtime;
gasUsage += addend.gasUsage; gasUsage += addend.gasUsage;
if(!vmReturnCode && addend.vmReturnCode) { if (!vmReturnCode && addend.vmReturnCode) {
vmReturnCode = addend.vmReturnCode; vmReturnCode = addend.vmReturnCode;
} }
return *this; return *this;
@ -68,8 +119,8 @@ vm::Stack prepare_stack(td::Slice command) {
vm::Stack stack; vm::Stack stack;
try { try {
vm::GasLimits gas_limit; vm::GasLimits gas_limit;
int ret = vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/, int ret = vm::run_vm_code(vm::load_cell_slice_ref(cell), stack, 0 /*flags*/, nullptr /*data*/, vm::VmLog{}, nullptr,
vm::VmLog{}, nullptr, &gas_limit, {}, {}, nullptr, 4); &gas_limit, {}, c7, nullptr, ton::SUPPORTED_VERSION);
CHECK(ret == 0); CHECK(ret == 0);
} catch (...) { } catch (...) {
LOG(FATAL) << "catch unhandled exception"; 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()); CHECK(stack.is_unique());
try { try {
vm::GasLimits gas_limit; vm::GasLimits gas_limit;
vm::VmState vm{vm::load_cell_slice_ref(cell), std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, {}}; vm::VmState vm{vm::load_cell_slice_ref(cell), std::move(stack), gas_limit, 0, {}, vm::VmLog{}, {}, c7};
vm.set_global_version(4); vm.set_global_version(ton::SUPPORTED_VERSION);
std::clock_t cStart = std::clock(); std::clock_t cStart = std::clock();
int ret = ~vm.run(); int ret = ~vm.run();
std::clock_t cEnd = std::clock(); std::clock_t cEnd = std::clock();
@ -102,7 +153,7 @@ runtimeStats averageRuntime(td::Slice command, const vm::Stack& stack) {
std::vector<runInfo> values; std::vector<runInfo> values;
values.reserve(samples); values.reserve(samples);
td::Timer t0; 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_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)); 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, 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 runtimeDiffSum = 0.0;
long double gasDiffSum = 0.0; long double gasDiffSum = 0.0;
bool errored = false; bool errored = false;
for(const auto value : values) { for (const auto value : values) {
const auto runtime = value.runtime - runtimeMean; const auto runtime = value.runtime - runtimeMean;
const auto gasUsage = static_cast<long double>(value.gasUsage) - gasMean; const auto gasUsage = static_cast<long double>(value.gasUsage) - gasMean;
runtimeDiffSum += runtime * runtime; runtimeDiffSum += runtime * runtime;
gasDiffSum += gasUsage * gasUsage; gasDiffSum += gasUsage * gasUsage;
errored = errored || value.errored(); errored = errored || value.errored();
} }
return { return {{runtimeMean, sqrtl(runtimeDiffSum / static_cast<long double>(samples))},
{runtimeMean, sqrtl(runtimeDiffSum / static_cast<long double>(samples))}, {gasMean, sqrtl(gasDiffSum / static_cast<long double>(samples))},
{gasMean, sqrtl(gasDiffSum / static_cast<long double>(samples))}, errored};
errored
};
} }
runtimeStats timeInstruction(const std::string& setupCode, const std::string& toMeasure) { 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) { int main(int argc, char** argv) {
SET_VERBOSITY_LEVEL(verbosity_ERROR); SET_VERBOSITY_LEVEL(verbosity_ERROR);
if(argc != 2 && argc != 3) { if (argc != 2 && argc != 3) {
std::cerr << std::cerr << "This utility compares the timing of VM execution against the gas used.\n"
"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 "
"It can be used to discover opcodes or opcode sequences that consume an " "inordinate amount of computational resources relative to their gas cost.\n"
"inordinate amount of computational resources relative to their gas cost.\n" "\n"
"\n" "The utility expects two command line arguments: \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"
"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"
"For example, to test the DIVMODC opcode:\n" "\t$ "
"\t$ " << argv[0] << " 80FF801C A90E 2>/dev/null\n" << argv[0]
"\tOPCODE,runtime mean,runtime stddev,gas mean,gas stddev\n" << " 80FF801C A90E 2>/dev/null\n"
"\tA90E,0.0066416,0.00233496,26,0\n" "\tOPCODE,runtime mean,runtime stddev,gas mean,gas stddev\n"
"\n" "\tA90E,0.0066416,0.00233496,26,0\n"
"Usage: " << argv[0] << " [TVM_SETUP_BYTECODE] TVM_BYTECODE\n" "\n"
"\tBYTECODE is either:\n" "Usage: "
"\t1. hex-encoded string (e.g. A90E for DIVMODC)\n" << argv[0]
"\t2. boc:<serialized boc in base64> (e.g. boc:te6ccgEBAgEABwABAogBAAJ7)" << std::endl << std::endl; << " [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; return 1;
} }
std::cout << "OPCODE,runtime mean,runtime stddev,gas mean,gas stddev,error" << std::endl; std::cout << "OPCODE,runtime mean,runtime stddev,gas mean,gas stddev,error" << std::endl;
std::string setup, code; std::string setup, code;
if(argc == 2) { if (argc == 2) {
setup = ""; setup = "";
code = argv[1]; code = argv[1];
} else { } else {
@ -170,6 +224,7 @@ int main(int argc, char** argv) {
code = argv[2]; code = argv[2];
} }
vm::init_vm().ensure(); vm::init_vm().ensure();
prepare_c7();
const auto time = timeInstruction(setup, code); const auto time = timeInstruction(setup, code);
std::cout << std::fixed << std::setprecision(9) << code << "," << time.runtime.mean << "," << time.runtime.stddev 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; << "," << time.gasUsage.mean << "," << time.gasUsage.stddev << "," << (int)time.errored << std::endl;

View file

@ -960,6 +960,9 @@ bool ValidateQuery::fetch_config_params() {
} }
compute_phase_cfg_.prev_blocks_info = prev_blocks_info.move_as_ok(); 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_.suspended_addresses = config_->get_suspended_addresses(now_);
compute_phase_cfg_.size_limits = size_limits; compute_phase_cfg_.size_limits = size_limits;
} }