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
|
@ -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};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue