mirror of
https://github.com/ton-blockchain/ton
synced 2025-03-09 15:40:10 +00:00
New extra currency behavior (#1539)
This commit is contained in:
parent
1b70e48327
commit
b3b2bd1c3c
17 changed files with 228 additions and 65 deletions
|
@ -19,6 +19,6 @@
|
|||
namespace ton {
|
||||
|
||||
// See doc/GlobalVersions.md
|
||||
const int SUPPORTED_VERSION = 9;
|
||||
constexpr int SUPPORTED_VERSION = 10;
|
||||
|
||||
}
|
||||
|
|
|
@ -1350,6 +1350,35 @@ bool CurrencyCollection::clamp(const CurrencyCollection& other) {
|
|||
return ok || invalidate();
|
||||
}
|
||||
|
||||
bool CurrencyCollection::check_extra_currency_limit(td::uint32 max_currencies) const {
|
||||
td::uint32 count = 0;
|
||||
return vm::Dictionary{extra, 32}.check_for_each([&](td::Ref<vm::CellSlice>, td::ConstBitPtr, int) {
|
||||
++count;
|
||||
return count <= max_currencies;
|
||||
});
|
||||
}
|
||||
|
||||
bool CurrencyCollection::remove_zero_extra_currencies(Ref<vm::Cell>& root, td::uint32 max_currencies) {
|
||||
td::uint32 count = 0;
|
||||
vm::Dictionary dict{root, 32};
|
||||
int res = dict.filter([&](const vm::CellSlice& cs, td::ConstBitPtr, int) -> int {
|
||||
++count;
|
||||
if (count > max_currencies) {
|
||||
return -1;
|
||||
}
|
||||
td::RefInt256 val = tlb::t_VarUInteger_32.as_integer(cs);
|
||||
if (val.is_null()) {
|
||||
return -1;
|
||||
}
|
||||
return val->sgn() > 0;
|
||||
});
|
||||
if (res < 0) {
|
||||
return false;
|
||||
}
|
||||
root = dict.get_root_cell();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CurrencyCollection::operator==(const CurrencyCollection& other) const {
|
||||
return is_valid() && other.is_valid() && !td::cmp(grams, other.grams) &&
|
||||
(extra.not_null() == other.extra.not_null()) &&
|
||||
|
|
|
@ -391,6 +391,8 @@ struct CurrencyCollection {
|
|||
CurrencyCollection operator-(CurrencyCollection&& other) const;
|
||||
CurrencyCollection operator-(td::RefInt256 other_grams) const;
|
||||
bool clamp(const CurrencyCollection& other);
|
||||
bool check_extra_currency_limit(td::uint32 max_currencies) const;
|
||||
static bool remove_zero_extra_currencies(Ref<vm::Cell>& root, td::uint32 max_currencies);
|
||||
bool store(vm::CellBuilder& cb) const;
|
||||
bool store_or_zero(vm::CellBuilder& cb) const;
|
||||
bool fetch(vm::CellSlice& cs);
|
||||
|
|
|
@ -801,7 +801,7 @@ size_limits_config#01 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells
|
|||
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 = SizeLimitsConfig;
|
||||
size_limits_config_v2#02 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16
|
||||
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 max_acc_state_cells:uint32 max_acc_state_bits:uint32
|
||||
max_acc_public_libraries:uint32 defer_out_queue_size_limit:uint32 = SizeLimitsConfig;
|
||||
max_acc_public_libraries:uint32 defer_out_queue_size_limit:uint32 max_msg_extra_currencies:uint32 = SizeLimitsConfig;
|
||||
_ SizeLimitsConfig = ConfigParam 43;
|
||||
|
||||
// key is [ wc:int32 addr:uint256 ]
|
||||
|
|
|
@ -1960,6 +1960,7 @@ td::Result<SizeLimitsConfig> Config::do_get_size_limits_config(td::Ref<vm::CellS
|
|||
limits.max_acc_state_cells = rec.max_acc_state_cells;
|
||||
limits.max_acc_public_libraries = rec.max_acc_public_libraries;
|
||||
limits.defer_out_queue_size_limit = rec.defer_out_queue_size_limit;
|
||||
limits.max_msg_extra_currencies = rec.max_msg_extra_currencies;
|
||||
};
|
||||
gen::SizeLimitsConfig::Record_size_limits_config rec_v1;
|
||||
gen::SizeLimitsConfig::Record_size_limits_config_v2 rec_v2;
|
||||
|
|
|
@ -397,6 +397,7 @@ struct SizeLimitsConfig {
|
|||
td::uint32 max_acc_state_bits = (1 << 16) * 1023;
|
||||
td::uint32 max_acc_public_libraries = 256;
|
||||
td::uint32 defer_out_queue_size_limit = 256;
|
||||
td::uint32 max_msg_extra_currencies = 2;
|
||||
};
|
||||
|
||||
struct CatchainValidatorsConfig {
|
||||
|
|
|
@ -2000,9 +2000,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
|
|||
ap.remaining_balance += ap.reserved_balance;
|
||||
CHECK(ap.remaining_balance.is_valid());
|
||||
if (ap.acc_delete_req) {
|
||||
CHECK(ap.remaining_balance.is_zero());
|
||||
CHECK(cfg.extra_currency_v2 ? ap.remaining_balance.grams->sgn() == 0 : ap.remaining_balance.is_zero());
|
||||
ap.acc_status_change = ActionPhase::acst_deleted;
|
||||
acc_status = Account::acc_deleted;
|
||||
acc_status = (ap.remaining_balance.is_zero() ? Account::acc_deleted : Account::acc_uninit);
|
||||
was_deleted = true;
|
||||
}
|
||||
ap.success = true;
|
||||
|
@ -2472,6 +2472,20 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
LOG(DEBUG) << "invalid destination address in a proposed outbound message";
|
||||
return check_skip_invalid(36); // invalid destination address
|
||||
}
|
||||
if (cfg.extra_currency_v2) {
|
||||
CurrencyCollection value;
|
||||
if (!value.unpack(info.value)) {
|
||||
LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message";
|
||||
return check_skip_invalid(37); // invalid value:CurrencyCollection
|
||||
}
|
||||
if (!CurrencyCollection::remove_zero_extra_currencies(value.extra, cfg.size_limits.max_msg_extra_currencies)) {
|
||||
LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message: too many currencies (max "
|
||||
<< cfg.size_limits.max_msg_extra_currencies << ")";
|
||||
// Dict should be valid, since it was checked in t_OutListNode.validate_ref, so error here means limit exceeded
|
||||
return check_skip_invalid(41); // invalid value:CurrencyCollection : too many extra currencies
|
||||
}
|
||||
info.value = value.pack();
|
||||
}
|
||||
|
||||
// fetch message pricing info
|
||||
const MsgPrices& msg_prices = cfg.fetch_msg_prices(to_mc || account.is_masterchain());
|
||||
|
@ -2524,7 +2538,7 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
};
|
||||
add_used_storage(msg.init, 3); // message init
|
||||
add_used_storage(msg.body, 3); // message body (the root cell itself is not counted)
|
||||
if (!ext_msg) {
|
||||
if (!ext_msg && !cfg.extra_currency_v2) {
|
||||
add_used_storage(info.value->prefetch_ref(), 0);
|
||||
}
|
||||
auto collect_fine = [&] {
|
||||
|
@ -2595,11 +2609,19 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
|
||||
if (act_rec.mode & 0x80) {
|
||||
// attach all remaining balance to this message
|
||||
req = ap.remaining_balance;
|
||||
if (cfg.extra_currency_v2) {
|
||||
req.grams = ap.remaining_balance.grams;
|
||||
} else {
|
||||
req = ap.remaining_balance;
|
||||
}
|
||||
act_rec.mode &= ~1; // pay fees from attached value
|
||||
} else if (act_rec.mode & 0x40) {
|
||||
// attach all remaining balance of the inbound message (in addition to the original value)
|
||||
req += msg_balance_remaining;
|
||||
if (cfg.extra_currency_v2) {
|
||||
req.grams += msg_balance_remaining.grams;
|
||||
} else {
|
||||
req += msg_balance_remaining;
|
||||
}
|
||||
if (!(act_rec.mode & 1)) {
|
||||
req -= ap.action_fine;
|
||||
if (compute_phase) {
|
||||
|
@ -2639,6 +2661,11 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
return check_skip_invalid(37); // not enough grams
|
||||
}
|
||||
|
||||
if (cfg.extra_currency_v2 && !req.check_extra_currency_limit(cfg.size_limits.max_msg_extra_currencies)) {
|
||||
LOG(DEBUG) << "too many extra currencies in the message : max " << cfg.size_limits.max_msg_extra_currencies;
|
||||
return check_skip_invalid(41); // to many extra currencies
|
||||
}
|
||||
|
||||
Ref<vm::Cell> new_extra;
|
||||
|
||||
if (!block::sub_extra_currency(ap.remaining_balance.extra, req.extra, new_extra)) {
|
||||
|
@ -2680,7 +2707,11 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
|
||||
// clear msg_balance_remaining if it has been used
|
||||
if (act_rec.mode & 0xc0) {
|
||||
msg_balance_remaining.set_zero();
|
||||
if (cfg.extra_currency_v2) {
|
||||
msg_balance_remaining.grams = td::zero_refint();
|
||||
} else {
|
||||
msg_balance_remaining.set_zero();
|
||||
}
|
||||
}
|
||||
|
||||
// update balance
|
||||
|
@ -2754,8 +2785,13 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
|
|||
ap.total_fwd_fees += fees_total;
|
||||
|
||||
if ((act_rec.mode & 0xa0) == 0xa0) {
|
||||
CHECK(ap.remaining_balance.is_zero());
|
||||
ap.acc_delete_req = ap.reserved_balance.is_zero();
|
||||
if (cfg.extra_currency_v2) {
|
||||
CHECK(ap.remaining_balance.grams->sgn() == 0);
|
||||
ap.acc_delete_req = ap.reserved_balance.grams->sgn() == 0;
|
||||
} else {
|
||||
CHECK(ap.remaining_balance.is_zero());
|
||||
ap.acc_delete_req = ap.reserved_balance.is_zero();
|
||||
}
|
||||
}
|
||||
|
||||
ap.tot_msg_bits += sstat.bits + new_msg_bits;
|
||||
|
@ -3026,7 +3062,8 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
|
|||
bp.fwd_fees -= bp.fwd_fees_collected;
|
||||
total_fees += td::make_refint(bp.fwd_fees_collected);
|
||||
// serialize outbound message
|
||||
info.created_lt = end_lt++;
|
||||
info.created_lt = start_lt + 1 + out_msgs.size();
|
||||
end_lt++;
|
||||
info.created_at = now;
|
||||
vm::CellBuilder cb;
|
||||
CHECK(cb.store_long_bool(5, 4) // int_msg_info$0 ihr_disabled:Bool bounce:Bool bounced:Bool
|
||||
|
@ -3107,6 +3144,7 @@ bool Account::store_acc_status(vm::CellBuilder& cb, int acc_status) const {
|
|||
* Tries to update the storage statistics based on the old storage statistics and old account state without fully recomputing it.
|
||||
*
|
||||
* It succeeds if only root cell of AccountStorage is changed.
|
||||
* old_cs and new_cell are AccountStorage without extra currencies (if global_version >= 10).
|
||||
*
|
||||
* @param old_stat The old storage statistics.
|
||||
* @param old_cs The old AccountStorage.
|
||||
|
@ -3140,13 +3178,48 @@ static td::optional<vm::CellStorageStat> try_update_storage_stat(const vm::CellS
|
|||
return new_stat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes extra currencies dict from AccountStorage.
|
||||
*
|
||||
* This is used for computing account storage stats.
|
||||
*
|
||||
* @param storage_cs AccountStorage as CellSlice.
|
||||
*
|
||||
* @returns AccountStorage without extra currencies as Cell.
|
||||
*/
|
||||
static td::Ref<vm::Cell> storage_without_extra_currencies(td::Ref<vm::CellSlice> storage_cs) {
|
||||
block::gen::AccountStorage::Record rec;
|
||||
if (!block::gen::csr_unpack(storage_cs, rec)) {
|
||||
LOG(ERROR) << "failed to unpack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
if (rec.balance->size_refs() > 0) {
|
||||
block::gen::CurrencyCollection::Record balance;
|
||||
if (!block::gen::csr_unpack(rec.balance, balance)) {
|
||||
LOG(ERROR) << "failed to unpack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
balance.other = vm::CellBuilder{}.store_zeroes(1).as_cellslice_ref();
|
||||
if (!block::gen::csr_pack(rec.balance, balance)) {
|
||||
LOG(ERROR) << "failed to pack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
}
|
||||
td::Ref<vm::Cell> cell;
|
||||
if (!block::gen::pack_cell(cell, rec)) {
|
||||
LOG(ERROR) << "failed to pack AccountStorage";
|
||||
return {};
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
namespace transaction {
|
||||
/**
|
||||
* Computes the new state of the account.
|
||||
*
|
||||
* @returns True if the state computation is successful, false otherwise.
|
||||
*/
|
||||
bool Transaction::compute_state() {
|
||||
bool Transaction::compute_state(const SerializeConfig& cfg) {
|
||||
if (new_total_state.not_null()) {
|
||||
return true;
|
||||
}
|
||||
|
@ -3218,13 +3291,27 @@ bool Transaction::compute_state() {
|
|||
new_inner_state.clear();
|
||||
}
|
||||
vm::CellStorageStat& stats = new_storage_stat;
|
||||
auto new_stats = try_update_storage_stat(account.storage_stat, account.storage, storage);
|
||||
td::Ref<vm::CellSlice> old_storage_for_stat = account.storage;
|
||||
td::Ref<vm::Cell> new_storage_for_stat = storage;
|
||||
if (cfg.extra_currency_v2) {
|
||||
new_storage_for_stat = storage_without_extra_currencies(new_storage);
|
||||
if (new_storage_for_stat.is_null()) {
|
||||
return false;
|
||||
}
|
||||
if (old_storage_for_stat.not_null()) {
|
||||
old_storage_for_stat = vm::load_cell_slice_ref(storage_without_extra_currencies(old_storage_for_stat));
|
||||
if (old_storage_for_stat.is_null()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
auto new_stats = try_update_storage_stat(account.storage_stat, old_storage_for_stat, storage);
|
||||
if (new_stats) {
|
||||
stats = new_stats.unwrap();
|
||||
} else {
|
||||
TD_PERF_COUNTER(transaction_storage_stat_b);
|
||||
td::Timer timer;
|
||||
stats.add_used_storage(Ref<vm::Cell>(storage)).ensure();
|
||||
stats.add_used_storage(new_storage_for_stat).ensure();
|
||||
if (timer.elapsed() > 0.1) {
|
||||
LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s";
|
||||
}
|
||||
|
@ -3260,11 +3347,11 @@ bool Transaction::compute_state() {
|
|||
*
|
||||
* @returns True if the serialization is successful, False otherwise.
|
||||
*/
|
||||
bool Transaction::serialize() {
|
||||
bool Transaction::serialize(const SerializeConfig& cfg) {
|
||||
if (root.not_null()) {
|
||||
return true;
|
||||
}
|
||||
if (!compute_state()) {
|
||||
if (!compute_state(cfg)) {
|
||||
return false;
|
||||
}
|
||||
vm::Dictionary dict{15};
|
||||
|
@ -3730,6 +3817,7 @@ bool Account::libraries_changed() const {
|
|||
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
|
||||
* @param compute_phase_cfg Pointer to store the compute phase configuration.
|
||||
* @param action_phase_cfg Pointer to store the action phase configuration.
|
||||
* @param serialize_cfg Pointer to store the serialize phase configuration.
|
||||
* @param masterchain_create_fee Pointer to store the masterchain create fee.
|
||||
* @param basechain_create_fee Pointer to store the basechain create fee.
|
||||
* @param wc The workchain ID.
|
||||
|
@ -3738,15 +3826,15 @@ bool Account::libraries_changed() const {
|
|||
td::Status FetchConfigParams::fetch_config_params(
|
||||
const block::ConfigInfo& config, Ref<vm::Cell>* old_mparams, std::vector<block::StoragePrices>* storage_prices,
|
||||
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed, ComputePhaseConfig* compute_phase_cfg,
|
||||
ActionPhaseConfig* action_phase_cfg, td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now) {
|
||||
ActionPhaseConfig* action_phase_cfg, SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
|
||||
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now) {
|
||||
auto prev_blocks_info = config.get_prev_blocks_info();
|
||||
if (prev_blocks_info.is_error()) {
|
||||
return prev_blocks_info.move_as_error_prefix(
|
||||
td::Status::Error(-668, "cannot fetch prev blocks info from masterchain configuration: "));
|
||||
}
|
||||
return fetch_config_params(config, prev_blocks_info.move_as_ok(), old_mparams, storage_prices, storage_phase_cfg,
|
||||
rand_seed, compute_phase_cfg, action_phase_cfg, masterchain_create_fee,
|
||||
rand_seed, compute_phase_cfg, action_phase_cfg, serialize_cfg, masterchain_create_fee,
|
||||
basechain_create_fee, wc, now);
|
||||
}
|
||||
|
||||
|
@ -3761,6 +3849,7 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
|
||||
* @param compute_phase_cfg Pointer to store the compute phase configuration.
|
||||
* @param action_phase_cfg Pointer to store the action phase configuration.
|
||||
* @param serialize_cfg Pointer to store the serialize phase configuration.
|
||||
* @param masterchain_create_fee Pointer to store the masterchain create fee.
|
||||
* @param basechain_create_fee Pointer to store the basechain create fee.
|
||||
* @param wc The workchain ID.
|
||||
|
@ -3770,8 +3859,8 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
const block::Config& config, td::Ref<vm::Tuple> prev_blocks_info, Ref<vm::Cell>* old_mparams,
|
||||
std::vector<block::StoragePrices>* storage_prices, StoragePhaseConfig* storage_phase_cfg,
|
||||
td::BitArray<256>* rand_seed, ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
|
||||
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee, ton::WorkchainId wc,
|
||||
ton::UnixTime now) {
|
||||
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now) {
|
||||
*old_mparams = config.get_config_param(9);
|
||||
{
|
||||
auto res = config.get_storage_prices();
|
||||
|
@ -3843,6 +3932,10 @@ td::Status FetchConfigParams::fetch_config_params(
|
|||
action_phase_cfg->disable_custom_fess = config.get_global_version() >= 8;
|
||||
action_phase_cfg->reserve_extra_enabled = config.get_global_version() >= 9;
|
||||
action_phase_cfg->mc_blackhole_addr = config.get_burning_config().blackhole_addr;
|
||||
action_phase_cfg->extra_currency_v2 = config.get_global_version() >= 10;
|
||||
}
|
||||
{
|
||||
serialize_cfg->extra_currency_v2 = config.get_global_version() >= 10;
|
||||
}
|
||||
{
|
||||
// fetch block_grams_created
|
||||
|
|
|
@ -170,12 +170,17 @@ struct ActionPhaseConfig {
|
|||
bool message_skip_enabled{false};
|
||||
bool disable_custom_fess{false};
|
||||
bool reserve_extra_enabled{false};
|
||||
bool extra_currency_v2{false};
|
||||
td::optional<td::Bits256> mc_blackhole_addr;
|
||||
const MsgPrices& fetch_msg_prices(bool is_masterchain) const {
|
||||
return is_masterchain ? fwd_mc : fwd_std;
|
||||
}
|
||||
};
|
||||
|
||||
struct SerializeConfig {
|
||||
bool extra_currency_v2{false};
|
||||
};
|
||||
|
||||
struct CreditPhase {
|
||||
td::RefInt256 due_fees_collected;
|
||||
block::CurrencyCollection credit;
|
||||
|
@ -389,8 +394,8 @@ struct Transaction {
|
|||
bool prepare_action_phase(const ActionPhaseConfig& cfg);
|
||||
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true);
|
||||
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
|
||||
bool compute_state();
|
||||
bool serialize();
|
||||
bool compute_state(const SerializeConfig& cfg);
|
||||
bool serialize(const SerializeConfig& cfg);
|
||||
td::uint64 gas_used() const {
|
||||
return compute_phase ? compute_phase->gas_used : 0;
|
||||
}
|
||||
|
@ -428,14 +433,14 @@ struct FetchConfigParams {
|
|||
std::vector<block::StoragePrices>* storage_prices,
|
||||
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed,
|
||||
ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
|
||||
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now);
|
||||
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
|
||||
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now);
|
||||
static td::Status fetch_config_params(const block::Config& config, Ref<vm::Tuple> prev_blocks_info,
|
||||
Ref<vm::Cell>* old_mparams, std::vector<block::StoragePrices>* storage_prices,
|
||||
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed,
|
||||
ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
|
||||
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
|
||||
ton::WorkchainId wc, ton::UnixTime now);
|
||||
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
|
||||
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now);
|
||||
};
|
||||
|
||||
} // namespace block
|
||||
|
|
|
@ -1153,8 +1153,12 @@ td::Result<CellStorageStat::CellInfo> CellStorageStat::add_used_storage(Ref<vm::
|
|||
return ins.first->second;
|
||||
}
|
||||
}
|
||||
vm::CellSlice cs{vm::NoVm{}, std::move(cell)};
|
||||
return add_used_storage(std::move(cs), kill_dup, skip_count_root);
|
||||
vm::CellSlice cs{vm::NoVm{}, cell};
|
||||
TRY_RESULT(res, add_used_storage(std::move(cs), kill_dup, skip_count_root));
|
||||
if (kill_dup) {
|
||||
seen[cell->get_hash()] = res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void NewCellStorageStat::add_cell(Ref<Cell> cell) {
|
||||
|
|
|
@ -1761,6 +1761,10 @@ int exec_send_message(VmState* st) {
|
|||
vm::VmStorageStat stat(max_cells);
|
||||
CellSlice cs = load_cell_slice(msg_cell);
|
||||
cs.skip_first(cs.size());
|
||||
if (st->get_global_version() >= 10 && have_extra_currencies) {
|
||||
// Skip extra currency dict
|
||||
cs.advance_refs(1);
|
||||
}
|
||||
stat.add_storage(cs);
|
||||
|
||||
if (!ext_msg) {
|
||||
|
@ -1773,7 +1777,9 @@ int exec_send_message(VmState* st) {
|
|||
if (value.is_null()) {
|
||||
throw VmError{Excno::type_chk, "invalid param BALANCE"};
|
||||
}
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
if (st->get_global_version() < 10) {
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
}
|
||||
} else if (mode & 64) { // value += value of incoming message
|
||||
Ref<Tuple> balance = get_param(st, 11).as_tuple();
|
||||
if (balance.is_null()) {
|
||||
|
@ -1784,7 +1790,9 @@ int exec_send_message(VmState* st) {
|
|||
throw VmError{Excno::type_chk, "invalid param INCOMINGVALUE"};
|
||||
}
|
||||
value += balance_grams;
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
if (st->get_global_version() < 10) {
|
||||
have_extra_currencies |= !tuple_index(balance, 1).as_cell().is_null();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -134,4 +134,25 @@ Example: if the last masterchain block seqno is `19071` then the list contains b
|
|||
- `PFXDICTADD`, `PFXDICTSET`, `PFXDICTREPLACE`, `PFXDICTDEL`, `GETGASFEE`, `GETSTORAGEFEE`, `GETFORWARDFEE`, `GETORIGINALFWDFEE`, `GETGASFEESIMPLE`, `GETFORWARDFEESIMPLE`, `HASHEXT`
|
||||
- Now setting the contract code to a library cell does not consume additional gas on execution of the code.
|
||||
- Temporary increase gas limit for some accounts (see [this post](https://t.me/tondev_news/129) for details, `override_gas_limit` in `transaction.cpp` for the list of accounts).
|
||||
- Fix recursive jump to continuations with non-null control data.
|
||||
- Fix recursive jump to continuations with non-null control data.
|
||||
|
||||
## Version 10
|
||||
|
||||
### Extra currencies
|
||||
- Internal messages cannot carry more than 2 different extra currencies. The limit can be changed in size limits config (`ConfigParam 43`).
|
||||
- Amount of an extra currency in an output action "send message" can be zero.
|
||||
- In action phase zero values are automatically deleted from the dictionary before sending.
|
||||
- However, the size of the extra currency dictionary in the "send message" action should not be greater than 2 (or the value in size limits config).
|
||||
- Extra currency dictionary is not counted in message size and does not affect message fees.
|
||||
- Message mode `+64` (carry all remaining message balance) is now considered as "carry all remaining TONs from message balance".
|
||||
- Message mode `+128` (carry all remaining account balance) is now considered as "carry all remaining TONs from account balance".
|
||||
- Message mode `+32` (delete account if balance is zero) deletes account if it has zero TONs, regardless of extra currencies.
|
||||
- Deleted accounts with extra currencies become `account_uninit`, extra currencies remain on the account.
|
||||
- `SENDMSG` in TVM calculates message size and fees without extra currencies, uses new `+64` and `+128` mode behavior.
|
||||
- `SENDMSG` does not check the number of extra currencies.
|
||||
- Extra currency dictionary is not counted in the account size and does not affect storage fees.
|
||||
- Accounts with already existing extra currencies will get their sizes recomputed without EC only after modifying `AccountState`.
|
||||
|
||||
### TVM changes
|
||||
- `SENDMSG` calculates messages size and fees without extra currencies, uses new +64 and +128 mode behavior.
|
||||
- `SENDMSG` does not check the number of extra currencies.
|
||||
|
|
|
@ -16,6 +16,7 @@ td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmu
|
|||
block::StoragePhaseConfig storage_phase_cfg{&storage_prices};
|
||||
block::ComputePhaseConfig compute_phase_cfg;
|
||||
block::ActionPhaseConfig action_phase_cfg;
|
||||
block::SerializeConfig serialize_config;
|
||||
td::RefInt256 masterchain_create_fee, basechain_create_fee;
|
||||
|
||||
if (!utime) {
|
||||
|
@ -25,11 +26,9 @@ td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmu
|
|||
utime = (unsigned)std::time(nullptr);
|
||||
}
|
||||
|
||||
auto fetch_res = block::FetchConfigParams::fetch_config_params(*config_, prev_blocks_info_, &old_mparams,
|
||||
&storage_prices, &storage_phase_cfg,
|
||||
&rand_seed_, &compute_phase_cfg,
|
||||
&action_phase_cfg, &masterchain_create_fee,
|
||||
&basechain_create_fee, account.workchain, utime);
|
||||
auto fetch_res = block::FetchConfigParams::fetch_config_params(
|
||||
*config_, prev_blocks_info_, &old_mparams, &storage_prices, &storage_phase_cfg, &rand_seed_, &compute_phase_cfg,
|
||||
&action_phase_cfg, &serialize_config, &masterchain_create_fee, &basechain_create_fee, account.workchain, utime);
|
||||
if(fetch_res.is_error()) {
|
||||
return fetch_res.move_as_error_prefix("cannot fetch config params ");
|
||||
}
|
||||
|
@ -66,7 +65,7 @@ td::Result<std::unique_ptr<TransactionEmulator::EmulationResult>> TransactionEmu
|
|||
return std::make_unique<TransactionEmulator::EmulationExternalNotAccepted>(std::move(vm_log), vm_exit_code, elapsed);
|
||||
}
|
||||
|
||||
if (!trans->serialize()) {
|
||||
if (!trans->serialize(serialize_config)) {
|
||||
return td::Status::Error(-669,"cannot serialize new transaction for smart contract "s + trans->account.addr.to_hex());
|
||||
}
|
||||
|
||||
|
|
|
@ -109,14 +109,11 @@ class Collator final : public td::actor::Actor {
|
|||
return 2;
|
||||
}
|
||||
|
||||
static td::Result<std::unique_ptr<block::transaction::Transaction>>
|
||||
impl_create_ordinary_transaction(Ref<vm::Cell> msg_root,
|
||||
block::Account* acc,
|
||||
UnixTime utime, LogicalTime lt,
|
||||
block::StoragePhaseConfig* storage_phase_cfg,
|
||||
block::ComputePhaseConfig* compute_phase_cfg,
|
||||
block::ActionPhaseConfig* action_phase_cfg,
|
||||
bool external, LogicalTime after_lt);
|
||||
static td::Result<std::unique_ptr<block::transaction::Transaction>> impl_create_ordinary_transaction(
|
||||
Ref<vm::Cell> msg_root, block::Account* acc, UnixTime utime, LogicalTime lt,
|
||||
block::StoragePhaseConfig* storage_phase_cfg, block::ComputePhaseConfig* compute_phase_cfg,
|
||||
block::ActionPhaseConfig* action_phase_cfg, block::SerializeConfig* serialize_cfg, bool external,
|
||||
LogicalTime after_lt);
|
||||
|
||||
private:
|
||||
void start_up() override;
|
||||
|
@ -177,6 +174,7 @@ class Collator final : public td::actor::Actor {
|
|||
block::StoragePhaseConfig storage_phase_cfg_{&storage_prices_};
|
||||
block::ComputePhaseConfig compute_phase_cfg_;
|
||||
block::ActionPhaseConfig action_phase_cfg_;
|
||||
block::SerializeConfig serialize_cfg_;
|
||||
td::RefInt256 masterchain_create_fee_, basechain_create_fee_;
|
||||
std::unique_ptr<block::BlockLimits> block_limits_;
|
||||
std::unique_ptr<block::BlockLimitStatus> block_limit_status_;
|
||||
|
|
|
@ -1995,12 +1995,9 @@ bool Collator::init_lt() {
|
|||
* @returns True if the configuration parameters were successfully fetched and initialized, false otherwise.
|
||||
*/
|
||||
bool Collator::fetch_config_params() {
|
||||
auto res = block::FetchConfigParams::fetch_config_params(*config_,
|
||||
&old_mparams_, &storage_prices_, &storage_phase_cfg_,
|
||||
&rand_seed_, &compute_phase_cfg_, &action_phase_cfg_,
|
||||
&masterchain_create_fee_, &basechain_create_fee_,
|
||||
workchain(), now_
|
||||
);
|
||||
auto res = block::FetchConfigParams::fetch_config_params(
|
||||
*config_, &old_mparams_, &storage_prices_, &storage_phase_cfg_, &rand_seed_, &compute_phase_cfg_,
|
||||
&action_phase_cfg_, &serialize_cfg_, &masterchain_create_fee_, &basechain_create_fee_, workchain(), now_);
|
||||
if (res.is_error()) {
|
||||
return fatal_error(res.move_as_error());
|
||||
}
|
||||
|
@ -2750,7 +2747,7 @@ bool Collator::create_ticktock_transaction(const ton::StdSmcAddress& smc_addr, t
|
|||
return fatal_error(td::Status::Error(
|
||||
-666, std::string{"cannot create action phase of a new transaction for smart contract "} + smc_addr.to_hex()));
|
||||
}
|
||||
if (!trans->serialize()) {
|
||||
if (!trans->serialize(serialize_cfg_)) {
|
||||
return fatal_error(td::Status::Error(
|
||||
-666, std::string{"cannot serialize new transaction for smart contract "} + smc_addr.to_hex()));
|
||||
}
|
||||
|
@ -2834,7 +2831,7 @@ Ref<vm::Cell> Collator::create_ordinary_transaction(Ref<vm::Cell> msg_root,
|
|||
after_lt = std::max(after_lt, it->second);
|
||||
}
|
||||
auto res = impl_create_ordinary_transaction(msg_root, acc, now_, start_lt, &storage_phase_cfg_, &compute_phase_cfg_,
|
||||
&action_phase_cfg_, external, after_lt);
|
||||
&action_phase_cfg_, &serialize_cfg_, external, after_lt);
|
||||
if (res.is_error()) {
|
||||
auto error = res.move_as_error();
|
||||
if (error.code() == -701) {
|
||||
|
@ -2885,6 +2882,7 @@ Ref<vm::Cell> Collator::create_ordinary_transaction(Ref<vm::Cell> msg_root,
|
|||
* @param storage_phase_cfg The configuration for the storage phase of the transaction.
|
||||
* @param compute_phase_cfg The configuration for the compute phase of the transaction.
|
||||
* @param action_phase_cfg The configuration for the action phase of the transaction.
|
||||
* @param serialize_cfg The configuration for the serialization of the transaction.
|
||||
* @param external Flag indicating if the message is external.
|
||||
* @param after_lt The logical time after which the transaction should occur. Used only for external messages.
|
||||
*
|
||||
|
@ -2898,6 +2896,7 @@ td::Result<std::unique_ptr<block::transaction::Transaction>> Collator::impl_crea
|
|||
block::StoragePhaseConfig* storage_phase_cfg,
|
||||
block::ComputePhaseConfig* compute_phase_cfg,
|
||||
block::ActionPhaseConfig* action_phase_cfg,
|
||||
block::SerializeConfig* serialize_cfg,
|
||||
bool external, LogicalTime after_lt) {
|
||||
if (acc->last_trans_end_lt_ >= lt && acc->transactions.empty()) {
|
||||
return td::Status::Error(-669, PSTRING() << "last transaction time in the state of account " << acc->workchain
|
||||
|
@ -2965,7 +2964,7 @@ td::Result<std::unique_ptr<block::transaction::Transaction>> Collator::impl_crea
|
|||
return td::Status::Error(
|
||||
-669, "cannot create bounce phase of a new transaction for smart contract "s + acc->addr.to_hex());
|
||||
}
|
||||
if (!trans->serialize()) {
|
||||
if (!trans->serialize(*serialize_cfg)) {
|
||||
return td::Status::Error(-669, "cannot serialize new transaction for smart contract "s + acc->addr.to_hex());
|
||||
}
|
||||
return std::move(trans);
|
||||
|
|
|
@ -136,13 +136,12 @@ td::Status ExtMessageQ::run_message_on_account(ton::WorkchainId wc,
|
|||
td::BitArray<256> rand_seed_;
|
||||
block::ComputePhaseConfig compute_phase_cfg_;
|
||||
block::ActionPhaseConfig action_phase_cfg_;
|
||||
block::SerializeConfig serialize_config_;
|
||||
td::RefInt256 masterchain_create_fee, basechain_create_fee;
|
||||
|
||||
auto fetch_res = block::FetchConfigParams::fetch_config_params(*config, &old_mparams,
|
||||
&storage_prices_, &storage_phase_cfg_,
|
||||
&rand_seed_, &compute_phase_cfg_,
|
||||
&action_phase_cfg_, &masterchain_create_fee,
|
||||
&basechain_create_fee, wc, utime);
|
||||
auto fetch_res = block::FetchConfigParams::fetch_config_params(
|
||||
*config, &old_mparams, &storage_prices_, &storage_phase_cfg_, &rand_seed_, &compute_phase_cfg_,
|
||||
&action_phase_cfg_, &serialize_config_, &masterchain_create_fee, &basechain_create_fee, wc, utime);
|
||||
if(fetch_res.is_error()) {
|
||||
auto error = fetch_res.move_as_error();
|
||||
LOG(DEBUG) << "Cannot fetch config params: " << error.message();
|
||||
|
@ -152,10 +151,9 @@ td::Status ExtMessageQ::run_message_on_account(ton::WorkchainId wc,
|
|||
compute_phase_cfg_.with_vm_log = true;
|
||||
compute_phase_cfg_.stop_on_accept_message = true;
|
||||
|
||||
auto res = Collator::impl_create_ordinary_transaction(msg_root, acc, utime, lt,
|
||||
&storage_phase_cfg_, &compute_phase_cfg_,
|
||||
&action_phase_cfg_,
|
||||
true, lt);
|
||||
auto res =
|
||||
Collator::impl_create_ordinary_transaction(msg_root, acc, utime, lt, &storage_phase_cfg_, &compute_phase_cfg_,
|
||||
&action_phase_cfg_, &serialize_config_, true, lt);
|
||||
if(res.is_error()) {
|
||||
auto error = res.move_as_error();
|
||||
LOG(DEBUG) << "Cannot run message on account: " << error.message();
|
||||
|
|
|
@ -1004,6 +1004,10 @@ bool ValidateQuery::fetch_config_params() {
|
|||
action_phase_cfg_.disable_custom_fess = config_->get_global_version() >= 8;
|
||||
action_phase_cfg_.reserve_extra_enabled = config_->get_global_version() >= 9;
|
||||
action_phase_cfg_.mc_blackhole_addr = config_->get_burning_config().blackhole_addr;
|
||||
action_phase_cfg_.extra_currency_v2 = config_->get_global_version() >= 10;
|
||||
}
|
||||
{
|
||||
serialize_cfg_.extra_currency_v2 = config_->get_global_version() >= 10;
|
||||
}
|
||||
{
|
||||
// fetch block_grams_created
|
||||
|
@ -5608,7 +5612,7 @@ bool ValidateQuery::check_one_transaction(block::Account& account, ton::LogicalT
|
|||
return reject_query(PSTRING() << "cannot re-create bounce phase of transaction " << lt << " for smart contract "
|
||||
<< addr.to_hex());
|
||||
}
|
||||
if (!trs->serialize()) {
|
||||
if (!trs->serialize(serialize_cfg_)) {
|
||||
return reject_query(PSTRING() << "cannot re-create the serialization of transaction " << lt
|
||||
<< " for smart contract " << addr.to_hex());
|
||||
}
|
||||
|
|
|
@ -205,6 +205,7 @@ class ValidateQuery : public td::actor::Actor {
|
|||
block::StoragePhaseConfig storage_phase_cfg_{&storage_prices_};
|
||||
block::ComputePhaseConfig compute_phase_cfg_;
|
||||
block::ActionPhaseConfig action_phase_cfg_;
|
||||
block::SerializeConfig serialize_cfg_;
|
||||
td::RefInt256 masterchain_create_fee_, basechain_create_fee_;
|
||||
|
||||
std::vector<block::McShardDescr> neighbors_;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue