From a11ffb1637032faabea9119020f6c80ed678d0e7 Mon Sep 17 00:00:00 2001 From: EmelyanenkoK Date: Mon, 29 Jan 2024 16:38:42 +0300 Subject: [PATCH] Add DUEPAYMENT and some others + small fixes of new opcodes (#881) * Changes in TVM v6 * Rename some opcodes * Add due payment to c7 * Add GETORIGINALFWDFEE, GETGASFEESIMPLE, GETFORWARDFEESIMPLE * Bugfix in GETGASFEE * Fix typo --------- Co-authored-by: SpyCheese --- crypto/block/transaction.cpp | 9 +-- crypto/fift/lib/Asm.fif | 10 ++- crypto/smc-envelope/SmartContract.cpp | 1 + crypto/vm/tonops.cpp | 92 ++++++++++++++++++++------- doc/GlobalVersions.md | 28 ++++---- utils/opcode-timing.cpp | 1 + 6 files changed, 99 insertions(+), 42 deletions(-) diff --git a/crypto/block/transaction.cpp b/crypto/block/transaction.cpp index e08407a23..b3aa6c8f4 100644 --- a/crypto/block/transaction.cpp +++ b/crypto/block/transaction.cpp @@ -1339,7 +1339,8 @@ Ref Transaction::prepare_vm_c7(const ComputePhaseConfig& cfg) const { } 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:[...] + : vm::StackEntry()); // unpacked_config_tuple:[...] + tuple.push_back(due_payment.not_null() ? due_payment : td::zero_refint()); // due_payment:Integer } auto tuple_ref = td::make_cnt_ref>(std::move(tuple)); LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple_ref).to_string(); @@ -1938,9 +1939,9 @@ td::uint64 MsgPrices::compute_fwd_fees(td::uint64 cells, td::uint64 bits) const * @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 + return td::make_refint(lump_price) + + td::rshift(td::make_refint(bit_price) * bits + td::make_refint(cell_price) * cells, 16, + 1); // divide by 2^16 with ceil rounding } /** diff --git a/crypto/fift/lib/Asm.fif b/crypto/fift/lib/Asm.fif index 5ad6ce47b..92ceab6db 100644 --- a/crypto/fift/lib/Asm.fif +++ b/crypto/fift/lib/Asm.fif @@ -1302,16 +1302,20 @@ x{F82B} @Defop INCOMINGVALUE x{F82C} @Defop STORAGEFEES x{F82D} @Defop PREVBLOCKSINFOTUPLE x{F82E} @Defop UNPACKEDCONFIGTUPLE +x{F82F} @Defop DUEPAYMENT x{F830} @Defop CONFIGDICT x{F832} @Defop CONFIGPARAM x{F833} @Defop CONFIGOPTPARAM x{F83400} @Defop PREVMCBLOCKS x{F83401} @Defop PREVKEYBLOCK x{F835} @Defop GLOBALID -x{F836} @Defop GETEXECUTIONPRICE -x{F837} @Defop GETSTORAGEPRICE -x{F838} @Defop GETFORWARDPRICE +x{F836} @Defop GETGASFEE +x{F837} @Defop GETSTORAGEFEE +x{F838} @Defop GETFORWARDFEE x{F839} @Defop GETPRECOMPILEDGAS +x{F83A} @Defop GETORIGINALFWDFEE +x{F83B} @Defop GETGASFEESIMPLE +x{F83C} @Defop GETFORWARDFEESIMPLE x{F840} @Defop GETGLOBVAR { dup 1 31 @rangechk prepare_vm_c7(SmartContract::Args args, td::Ref cod } if (args.config && args.config.value()->get_global_version() >= 6) { tuple.push_back(args.config.value()->get_unpacked_config_tuple(now)); // unpacked_config_tuple + tuple.push_back(td::zero_refint()); // due_payment } auto tuple_ref = td::make_cnt_ref>(std::move(tuple)); //LOG(DEBUG) << "SmartContractInfo initialized with " << vm::StackEntry(tuple).to_string(); diff --git a/crypto/vm/tonops.cpp b/crypto/vm/tonops.cpp index 753bbc0a8..9ce7fe9c4 100644 --- a/crypto/vm/tonops.cpp +++ b/crypto/vm/tonops.cpp @@ -276,11 +276,7 @@ int exec_get_global_id(VmState* st) { 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::max(), 0); +static block::GasLimitsPrices get_gas_prices(VmState* st, bool is_masterchain) { Ref 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"}; @@ -289,13 +285,33 @@ int exec_get_execution_price(VmState* st) { if (r_prices.is_error()) { throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()}; } - block::GasLimitsPrices prices = r_prices.move_as_ok(); + return r_prices.move_as_ok(); +} + +static block::MsgPrices get_msg_prices(VmState* st, bool is_masterchain) { + Ref cs = get_unpacked_config_param(st, is_masterchain ? 4 : 5); + if (cs.is_null()) { + throw VmError{Excno::type_chk, "intermediate value is not a slice"}; + } + auto r_prices = block::Config::do_get_msg_prices(*cs, is_masterchain ? 24 : 25); + if (r_prices.is_error()) { + throw VmError{Excno::cell_und, PSTRING() << "cannot parse config: " << r_prices.error().message()}; + } + return r_prices.move_as_ok(); +} + +int exec_get_gas_fee(VmState* st) { + VM_LOG(st) << "execute GETGASFEE"; + Stack& stack = st->get_stack(); + bool is_masterchain = stack.pop_bool(); + td::uint64 gas = stack.pop_long_range(std::numeric_limits::max(), 0); + block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain); stack.push_int(prices.compute_gas_price(gas)); return 0; } -int exec_get_storage_price(VmState* st) { - VM_LOG(st) << "execute GETSTORAGEPRICE"; +int exec_get_storage_fee(VmState* st) { + VM_LOG(st) << "execute GETSTORAGEFEE"; Stack& stack = st->get_stack(); bool is_masterchain = stack.pop_bool(); td::int64 delta = stack.pop_long_range(std::numeric_limits::max(), 0); @@ -325,21 +341,13 @@ int exec_get_storage_price(VmState* st) { return 0; } -int exec_get_forward_price(VmState* st) { - VM_LOG(st) << "execute GETFORWARDPRICE"; +int exec_get_forward_fee(VmState* st) { + VM_LOG(st) << "execute GETFORWARDFEE"; Stack& stack = st->get_stack(); bool is_masterchain = stack.pop_bool(); td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0); td::uint64 cells = stack.pop_long_range(std::numeric_limits::max(), 0); - Ref 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(); + block::MsgPrices prices = get_msg_prices(st, is_masterchain); stack.push_int(prices.compute_fwd_fees256(cells, bits)); return 0; } @@ -351,6 +359,41 @@ int exec_get_precompiled_gas(VmState* st) { return 0; } +int exec_get_original_fwd_fee(VmState* st) { + VM_LOG(st) << "execute GETORIGINALFWDFEE"; + Stack& stack = st->get_stack(); + bool is_masterchain = stack.pop_bool(); + td::RefInt256 fwd_fee = stack.pop_int_finite(); + if (fwd_fee->sgn() < 0) { + throw VmError{Excno::range_chk, "fwd_fee is negative"}; + } + block::MsgPrices prices = get_msg_prices(st, is_masterchain); + stack.push_int(td::muldiv(fwd_fee, td::make_refint(1 << 16), td::make_refint((1 << 16) - prices.first_frac))); + return 0; +} + +int exec_get_gas_fee_simple(VmState* st) { + VM_LOG(st) << "execute GETGASFEESIMPLE"; + Stack& stack = st->get_stack(); + bool is_masterchain = stack.pop_bool(); + td::uint64 gas = stack.pop_long_range(std::numeric_limits::max(), 0); + block::GasLimitsPrices prices = get_gas_prices(st, is_masterchain); + stack.push_int(td::rshift(td::make_refint(prices.gas_price) * gas, 16, 1)); + return 0; +} + +int exec_get_forward_fee_simple(VmState* st) { + VM_LOG(st) << "execute GETFORWARDFEESIMPLE"; + Stack& stack = st->get_stack(); + bool is_masterchain = stack.pop_bool(); + td::uint64 bits = stack.pop_long_range(std::numeric_limits::max(), 0); + td::uint64 cells = stack.pop_long_range(std::numeric_limits::max(), 0); + block::MsgPrices prices = get_msg_prices(st, is_masterchain); + stack.push_int(td::rshift(td::make_refint(prices.bit_price) * bits + td::make_refint(prices.cell_price) * cells, 16, + 1)); // divide by 2^16 with ceil rounding + return 0; +} + void register_ton_config_ops(OpcodeTable& cp0) { using namespace std::placeholders; cp0.insert(OpcodeInstr::mkfixedrange(0xf820, 0xf823, 16, 4, instr::dump_1c("GETPARAM "), exec_get_var_param)) @@ -366,17 +409,20 @@ void register_ton_config_ops(OpcodeTable& cp0) { .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(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(0xf82f, 16, "DUEPAYMENT", std::bind(exec_get_param, _1, 15, "DUEPAYMENT"))) .insert(OpcodeInstr::mksimple(0xf830, 16, "CONFIGDICT", exec_get_config_dict)) .insert(OpcodeInstr::mksimple(0xf832, 16, "CONFIGPARAM", std::bind(exec_get_config_param, _1, false))) .insert(OpcodeInstr::mksimple(0xf833, 16, "CONFIGOPTPARAM", std::bind(exec_get_config_param, _1, true))) .insert(OpcodeInstr::mksimple(0xf83400, 24, "PREVMCBLOCKS", std::bind(exec_get_prev_blocks_info, _1, 0, "PREVMCBLOCKS"))->require_version(4)) .insert(OpcodeInstr::mksimple(0xf83401, 24, "PREVKEYBLOCK", std::bind(exec_get_prev_blocks_info, _1, 1, "PREVKEYBLOCK"))->require_version(4)) .insert(OpcodeInstr::mksimple(0xf835, 16, "GLOBALID", exec_get_global_id)->require_version(4)) - .insert(OpcodeInstr::mksimple(0xf836, 16, "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(0xf836, 16, "GETGASFEE", exec_get_gas_fee)->require_version(6)) + .insert(OpcodeInstr::mksimple(0xf837, 16, "GETSTORAGEFEE", exec_get_storage_fee)->require_version(6)) + .insert(OpcodeInstr::mksimple(0xf838, 16, "GETFORWARDFEE", exec_get_forward_fee)->require_version(6)) .insert(OpcodeInstr::mksimple(0xf839, 16, "GETPRECOMPILEDGAS", exec_get_precompiled_gas)->require_version(6)) + .insert(OpcodeInstr::mksimple(0xf83a, 16, "GETORIGINALFWDFEE", exec_get_original_fwd_fee)->require_version(6)) + .insert(OpcodeInstr::mksimple(0xf83b, 16, "GETGASFEESIMPLE", exec_get_gas_fee_simple)->require_version(6)) + .insert(OpcodeInstr::mksimple(0xf83c, 16, "GETFORWARDFEESIMPLE", exec_get_forward_fee_simple)->require_version(6)) .insert(OpcodeInstr::mksimple(0xf840, 16, "GETGLOBVAR", exec_get_global_var)) .insert(OpcodeInstr::mkfixedrange(0xf841, 0xf860, 16, 5, instr::dump_1c_and(31, "GETGLOB "), exec_get_global)) .insert(OpcodeInstr::mksimple(0xf860, 16, "SETGLOBVAR", exec_set_global_var)) diff --git a/doc/GlobalVersions.md b/doc/GlobalVersions.md index c2d026e4f..7e3b4ce92 100644 --- a/doc/GlobalVersions.md +++ b/doc/GlobalVersions.md @@ -58,23 +58,27 @@ See [this post](https://t.me/tonstatus/88) for details. ## 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). +**c7** tuple extended from 14 to 16 elements: +* **14**: tuple that contains some config parameters as cell slices. If the parameter is absent from the config, the value is null. Asm opcode: `UNPACKEDCONFIGTUPLE`. + * **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). +* **15**: "[due payment](https://github.com/ton-blockchain/ton/blob/8a9ff339927b22b72819c5125428b70c406da631/crypto/block/block.tlb#L237)" - current debt for storage fee (nanotons). Asm opcode: `DUEPAYMENT`. ### New TVM instructions #### Fee calculation -* `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. +* `GETGASFEE` (`gas_used is_mc - price`) - calculates gas fee. +* `GETSTORAGEFEE` (`cells bits seconds is_mc - price`) - calculates storage fees (only current StoragePrices entry is used). +* `GETFORWARDFEE` (`cells bits is_mc - price`) - calculates forward fee. * `GETPRECOMPILEDGAS` (`- null`) - reserved, currently returns `null`. +* `GETORIGINALFWDFEE` (`fwd_fee is_mc - orig_fwd_fee`) - calculate `fwd_fee * 2^16 / first_frac`. Can be used to get the original `fwd_fee` of the message. +* `GETGASFEESIMPLE` (`gas_used is_mc - price`) - same as `GETGASFEE`, but without flat price (just `(gas_used * price) / 2^16`). +* `GETFORWARDFEESIMPLE` (`cells bits is_mc - price`) - same as `GETFORWARDFEE`, but without lump price (just `(bits*bit_price + cells*cell_price) / 2^16`). `gas_used`, `cells`, `bits`, `time_delta` are integers in range `0..2^63-1`. diff --git a/utils/opcode-timing.cpp b/utils/opcode-timing.cpp index d68928d00..876ba109e 100644 --- a/utils/opcode-timing.cpp +++ b/utils/opcode-timing.cpp @@ -57,6 +57,7 @@ void prepare_c7() { } else { tuple.push_back(vm::StackEntry()); } + tuple.push_back(td::zero_refint()); auto tuple_ref = td::make_cnt_ref>(std::move(tuple)); c7 = vm::make_tuple_ref(std::move(tuple_ref)); }