Skip to content

Commit b3b2bd1

Browse files
authored
New extra currency behavior (#1539)
1 parent 1b70e48 commit b3b2bd1

17 files changed

+228
-65
lines changed

common/global-version.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,6 @@
1919
namespace ton {
2020

2121
// See doc/GlobalVersions.md
22-
const int SUPPORTED_VERSION = 9;
22+
constexpr int SUPPORTED_VERSION = 10;
2323

2424
}

crypto/block/block.cpp

+29
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,35 @@ bool CurrencyCollection::clamp(const CurrencyCollection& other) {
13501350
return ok || invalidate();
13511351
}
13521352

1353+
bool CurrencyCollection::check_extra_currency_limit(td::uint32 max_currencies) const {
1354+
td::uint32 count = 0;
1355+
return vm::Dictionary{extra, 32}.check_for_each([&](td::Ref<vm::CellSlice>, td::ConstBitPtr, int) {
1356+
++count;
1357+
return count <= max_currencies;
1358+
});
1359+
}
1360+
1361+
bool CurrencyCollection::remove_zero_extra_currencies(Ref<vm::Cell>& root, td::uint32 max_currencies) {
1362+
td::uint32 count = 0;
1363+
vm::Dictionary dict{root, 32};
1364+
int res = dict.filter([&](const vm::CellSlice& cs, td::ConstBitPtr, int) -> int {
1365+
++count;
1366+
if (count > max_currencies) {
1367+
return -1;
1368+
}
1369+
td::RefInt256 val = tlb::t_VarUInteger_32.as_integer(cs);
1370+
if (val.is_null()) {
1371+
return -1;
1372+
}
1373+
return val->sgn() > 0;
1374+
});
1375+
if (res < 0) {
1376+
return false;
1377+
}
1378+
root = dict.get_root_cell();
1379+
return true;
1380+
}
1381+
13531382
bool CurrencyCollection::operator==(const CurrencyCollection& other) const {
13541383
return is_valid() && other.is_valid() && !td::cmp(grams, other.grams) &&
13551384
(extra.not_null() == other.extra.not_null()) &&

crypto/block/block.h

+2
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,8 @@ struct CurrencyCollection {
391391
CurrencyCollection operator-(CurrencyCollection&& other) const;
392392
CurrencyCollection operator-(td::RefInt256 other_grams) const;
393393
bool clamp(const CurrencyCollection& other);
394+
bool check_extra_currency_limit(td::uint32 max_currencies) const;
395+
static bool remove_zero_extra_currencies(Ref<vm::Cell>& root, td::uint32 max_currencies);
394396
bool store(vm::CellBuilder& cb) const;
395397
bool store_or_zero(vm::CellBuilder& cb) const;
396398
bool fetch(vm::CellSlice& cs);

crypto/block/block.tlb

+1-1
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ size_limits_config#01 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells
801801
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 = SizeLimitsConfig;
802802
size_limits_config_v2#02 max_msg_bits:uint32 max_msg_cells:uint32 max_library_cells:uint32 max_vm_data_depth:uint16
803803
max_ext_msg_size:uint32 max_ext_msg_depth:uint16 max_acc_state_cells:uint32 max_acc_state_bits:uint32
804-
max_acc_public_libraries:uint32 defer_out_queue_size_limit:uint32 = SizeLimitsConfig;
804+
max_acc_public_libraries:uint32 defer_out_queue_size_limit:uint32 max_msg_extra_currencies:uint32 = SizeLimitsConfig;
805805
_ SizeLimitsConfig = ConfigParam 43;
806806

807807
// key is [ wc:int32 addr:uint256 ]

crypto/block/mc-config.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -1960,6 +1960,7 @@ td::Result<SizeLimitsConfig> Config::do_get_size_limits_config(td::Ref<vm::CellS
19601960
limits.max_acc_state_cells = rec.max_acc_state_cells;
19611961
limits.max_acc_public_libraries = rec.max_acc_public_libraries;
19621962
limits.defer_out_queue_size_limit = rec.defer_out_queue_size_limit;
1963+
limits.max_msg_extra_currencies = rec.max_msg_extra_currencies;
19631964
};
19641965
gen::SizeLimitsConfig::Record_size_limits_config rec_v1;
19651966
gen::SizeLimitsConfig::Record_size_limits_config_v2 rec_v2;

crypto/block/mc-config.h

+1
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@ struct SizeLimitsConfig {
397397
td::uint32 max_acc_state_bits = (1 << 16) * 1023;
398398
td::uint32 max_acc_public_libraries = 256;
399399
td::uint32 defer_out_queue_size_limit = 256;
400+
td::uint32 max_msg_extra_currencies = 2;
400401
};
401402

402403
struct CatchainValidatorsConfig {

crypto/block/transaction.cpp

+112-19
Original file line numberDiff line numberDiff line change
@@ -2000,9 +2000,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
20002000
ap.remaining_balance += ap.reserved_balance;
20012001
CHECK(ap.remaining_balance.is_valid());
20022002
if (ap.acc_delete_req) {
2003-
CHECK(ap.remaining_balance.is_zero());
2003+
CHECK(cfg.extra_currency_v2 ? ap.remaining_balance.grams->sgn() == 0 : ap.remaining_balance.is_zero());
20042004
ap.acc_status_change = ActionPhase::acst_deleted;
2005-
acc_status = Account::acc_deleted;
2005+
acc_status = (ap.remaining_balance.is_zero() ? Account::acc_deleted : Account::acc_uninit);
20062006
was_deleted = true;
20072007
}
20082008
ap.success = true;
@@ -2472,6 +2472,20 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
24722472
LOG(DEBUG) << "invalid destination address in a proposed outbound message";
24732473
return check_skip_invalid(36); // invalid destination address
24742474
}
2475+
if (cfg.extra_currency_v2) {
2476+
CurrencyCollection value;
2477+
if (!value.unpack(info.value)) {
2478+
LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message";
2479+
return check_skip_invalid(37); // invalid value:CurrencyCollection
2480+
}
2481+
if (!CurrencyCollection::remove_zero_extra_currencies(value.extra, cfg.size_limits.max_msg_extra_currencies)) {
2482+
LOG(DEBUG) << "invalid value:ExtraCurrencies in a proposed outbound message: too many currencies (max "
2483+
<< cfg.size_limits.max_msg_extra_currencies << ")";
2484+
// Dict should be valid, since it was checked in t_OutListNode.validate_ref, so error here means limit exceeded
2485+
return check_skip_invalid(41); // invalid value:CurrencyCollection : too many extra currencies
2486+
}
2487+
info.value = value.pack();
2488+
}
24752489

24762490
// fetch message pricing info
24772491
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,
25242538
};
25252539
add_used_storage(msg.init, 3); // message init
25262540
add_used_storage(msg.body, 3); // message body (the root cell itself is not counted)
2527-
if (!ext_msg) {
2541+
if (!ext_msg && !cfg.extra_currency_v2) {
25282542
add_used_storage(info.value->prefetch_ref(), 0);
25292543
}
25302544
auto collect_fine = [&] {
@@ -2595,11 +2609,19 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
25952609

25962610
if (act_rec.mode & 0x80) {
25972611
// attach all remaining balance to this message
2598-
req = ap.remaining_balance;
2612+
if (cfg.extra_currency_v2) {
2613+
req.grams = ap.remaining_balance.grams;
2614+
} else {
2615+
req = ap.remaining_balance;
2616+
}
25992617
act_rec.mode &= ~1; // pay fees from attached value
26002618
} else if (act_rec.mode & 0x40) {
26012619
// attach all remaining balance of the inbound message (in addition to the original value)
2602-
req += msg_balance_remaining;
2620+
if (cfg.extra_currency_v2) {
2621+
req.grams += msg_balance_remaining.grams;
2622+
} else {
2623+
req += msg_balance_remaining;
2624+
}
26032625
if (!(act_rec.mode & 1)) {
26042626
req -= ap.action_fine;
26052627
if (compute_phase) {
@@ -2639,6 +2661,11 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
26392661
return check_skip_invalid(37); // not enough grams
26402662
}
26412663

2664+
if (cfg.extra_currency_v2 && !req.check_extra_currency_limit(cfg.size_limits.max_msg_extra_currencies)) {
2665+
LOG(DEBUG) << "too many extra currencies in the message : max " << cfg.size_limits.max_msg_extra_currencies;
2666+
return check_skip_invalid(41); // to many extra currencies
2667+
}
2668+
26422669
Ref<vm::Cell> new_extra;
26432670

26442671
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,
26802707

26812708
// clear msg_balance_remaining if it has been used
26822709
if (act_rec.mode & 0xc0) {
2683-
msg_balance_remaining.set_zero();
2710+
if (cfg.extra_currency_v2) {
2711+
msg_balance_remaining.grams = td::zero_refint();
2712+
} else {
2713+
msg_balance_remaining.set_zero();
2714+
}
26842715
}
26852716

26862717
// update balance
@@ -2754,8 +2785,13 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
27542785
ap.total_fwd_fees += fees_total;
27552786

27562787
if ((act_rec.mode & 0xa0) == 0xa0) {
2757-
CHECK(ap.remaining_balance.is_zero());
2758-
ap.acc_delete_req = ap.reserved_balance.is_zero();
2788+
if (cfg.extra_currency_v2) {
2789+
CHECK(ap.remaining_balance.grams->sgn() == 0);
2790+
ap.acc_delete_req = ap.reserved_balance.grams->sgn() == 0;
2791+
} else {
2792+
CHECK(ap.remaining_balance.is_zero());
2793+
ap.acc_delete_req = ap.reserved_balance.is_zero();
2794+
}
27592795
}
27602796

27612797
ap.tot_msg_bits += sstat.bits + new_msg_bits;
@@ -3026,7 +3062,8 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
30263062
bp.fwd_fees -= bp.fwd_fees_collected;
30273063
total_fees += td::make_refint(bp.fwd_fees_collected);
30283064
// serialize outbound message
3029-
info.created_lt = end_lt++;
3065+
info.created_lt = start_lt + 1 + out_msgs.size();
3066+
end_lt++;
30303067
info.created_at = now;
30313068
vm::CellBuilder cb;
30323069
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 {
31073144
* Tries to update the storage statistics based on the old storage statistics and old account state without fully recomputing it.
31083145
*
31093146
* It succeeds if only root cell of AccountStorage is changed.
3147+
* old_cs and new_cell are AccountStorage without extra currencies (if global_version >= 10).
31103148
*
31113149
* @param old_stat The old storage statistics.
31123150
* @param old_cs The old AccountStorage.
@@ -3140,13 +3178,48 @@ static td::optional<vm::CellStorageStat> try_update_storage_stat(const vm::CellS
31403178
return new_stat;
31413179
}
31423180

3181+
/**
3182+
* Removes extra currencies dict from AccountStorage.
3183+
*
3184+
* This is used for computing account storage stats.
3185+
*
3186+
* @param storage_cs AccountStorage as CellSlice.
3187+
*
3188+
* @returns AccountStorage without extra currencies as Cell.
3189+
*/
3190+
static td::Ref<vm::Cell> storage_without_extra_currencies(td::Ref<vm::CellSlice> storage_cs) {
3191+
block::gen::AccountStorage::Record rec;
3192+
if (!block::gen::csr_unpack(storage_cs, rec)) {
3193+
LOG(ERROR) << "failed to unpack AccountStorage";
3194+
return {};
3195+
}
3196+
if (rec.balance->size_refs() > 0) {
3197+
block::gen::CurrencyCollection::Record balance;
3198+
if (!block::gen::csr_unpack(rec.balance, balance)) {
3199+
LOG(ERROR) << "failed to unpack AccountStorage";
3200+
return {};
3201+
}
3202+
balance.other = vm::CellBuilder{}.store_zeroes(1).as_cellslice_ref();
3203+
if (!block::gen::csr_pack(rec.balance, balance)) {
3204+
LOG(ERROR) << "failed to pack AccountStorage";
3205+
return {};
3206+
}
3207+
}
3208+
td::Ref<vm::Cell> cell;
3209+
if (!block::gen::pack_cell(cell, rec)) {
3210+
LOG(ERROR) << "failed to pack AccountStorage";
3211+
return {};
3212+
}
3213+
return cell;
3214+
}
3215+
31433216
namespace transaction {
31443217
/**
31453218
* Computes the new state of the account.
31463219
*
31473220
* @returns True if the state computation is successful, false otherwise.
31483221
*/
3149-
bool Transaction::compute_state() {
3222+
bool Transaction::compute_state(const SerializeConfig& cfg) {
31503223
if (new_total_state.not_null()) {
31513224
return true;
31523225
}
@@ -3218,13 +3291,27 @@ bool Transaction::compute_state() {
32183291
new_inner_state.clear();
32193292
}
32203293
vm::CellStorageStat& stats = new_storage_stat;
3221-
auto new_stats = try_update_storage_stat(account.storage_stat, account.storage, storage);
3294+
td::Ref<vm::CellSlice> old_storage_for_stat = account.storage;
3295+
td::Ref<vm::Cell> new_storage_for_stat = storage;
3296+
if (cfg.extra_currency_v2) {
3297+
new_storage_for_stat = storage_without_extra_currencies(new_storage);
3298+
if (new_storage_for_stat.is_null()) {
3299+
return false;
3300+
}
3301+
if (old_storage_for_stat.not_null()) {
3302+
old_storage_for_stat = vm::load_cell_slice_ref(storage_without_extra_currencies(old_storage_for_stat));
3303+
if (old_storage_for_stat.is_null()) {
3304+
return false;
3305+
}
3306+
}
3307+
}
3308+
auto new_stats = try_update_storage_stat(account.storage_stat, old_storage_for_stat, storage);
32223309
if (new_stats) {
32233310
stats = new_stats.unwrap();
32243311
} else {
32253312
TD_PERF_COUNTER(transaction_storage_stat_b);
32263313
td::Timer timer;
3227-
stats.add_used_storage(Ref<vm::Cell>(storage)).ensure();
3314+
stats.add_used_storage(new_storage_for_stat).ensure();
32283315
if (timer.elapsed() > 0.1) {
32293316
LOG(INFO) << "Compute used storage took " << timer.elapsed() << "s";
32303317
}
@@ -3260,11 +3347,11 @@ bool Transaction::compute_state() {
32603347
*
32613348
* @returns True if the serialization is successful, False otherwise.
32623349
*/
3263-
bool Transaction::serialize() {
3350+
bool Transaction::serialize(const SerializeConfig& cfg) {
32643351
if (root.not_null()) {
32653352
return true;
32663353
}
3267-
if (!compute_state()) {
3354+
if (!compute_state(cfg)) {
32683355
return false;
32693356
}
32703357
vm::Dictionary dict{15};
@@ -3730,6 +3817,7 @@ bool Account::libraries_changed() const {
37303817
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
37313818
* @param compute_phase_cfg Pointer to store the compute phase configuration.
37323819
* @param action_phase_cfg Pointer to store the action phase configuration.
3820+
* @param serialize_cfg Pointer to store the serialize phase configuration.
37333821
* @param masterchain_create_fee Pointer to store the masterchain create fee.
37343822
* @param basechain_create_fee Pointer to store the basechain create fee.
37353823
* @param wc The workchain ID.
@@ -3738,15 +3826,15 @@ bool Account::libraries_changed() const {
37383826
td::Status FetchConfigParams::fetch_config_params(
37393827
const block::ConfigInfo& config, Ref<vm::Cell>* old_mparams, std::vector<block::StoragePrices>* storage_prices,
37403828
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed, ComputePhaseConfig* compute_phase_cfg,
3741-
ActionPhaseConfig* action_phase_cfg, td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
3742-
ton::WorkchainId wc, ton::UnixTime now) {
3829+
ActionPhaseConfig* action_phase_cfg, SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
3830+
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now) {
37433831
auto prev_blocks_info = config.get_prev_blocks_info();
37443832
if (prev_blocks_info.is_error()) {
37453833
return prev_blocks_info.move_as_error_prefix(
37463834
td::Status::Error(-668, "cannot fetch prev blocks info from masterchain configuration: "));
37473835
}
37483836
return fetch_config_params(config, prev_blocks_info.move_as_ok(), old_mparams, storage_prices, storage_phase_cfg,
3749-
rand_seed, compute_phase_cfg, action_phase_cfg, masterchain_create_fee,
3837+
rand_seed, compute_phase_cfg, action_phase_cfg, serialize_cfg, masterchain_create_fee,
37503838
basechain_create_fee, wc, now);
37513839
}
37523840

@@ -3761,6 +3849,7 @@ td::Status FetchConfigParams::fetch_config_params(
37613849
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
37623850
* @param compute_phase_cfg Pointer to store the compute phase configuration.
37633851
* @param action_phase_cfg Pointer to store the action phase configuration.
3852+
* @param serialize_cfg Pointer to store the serialize phase configuration.
37643853
* @param masterchain_create_fee Pointer to store the masterchain create fee.
37653854
* @param basechain_create_fee Pointer to store the basechain create fee.
37663855
* @param wc The workchain ID.
@@ -3770,8 +3859,8 @@ td::Status FetchConfigParams::fetch_config_params(
37703859
const block::Config& config, td::Ref<vm::Tuple> prev_blocks_info, Ref<vm::Cell>* old_mparams,
37713860
std::vector<block::StoragePrices>* storage_prices, StoragePhaseConfig* storage_phase_cfg,
37723861
td::BitArray<256>* rand_seed, ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
3773-
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee, ton::WorkchainId wc,
3774-
ton::UnixTime now) {
3862+
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
3863+
ton::WorkchainId wc, ton::UnixTime now) {
37753864
*old_mparams = config.get_config_param(9);
37763865
{
37773866
auto res = config.get_storage_prices();
@@ -3843,6 +3932,10 @@ td::Status FetchConfigParams::fetch_config_params(
38433932
action_phase_cfg->disable_custom_fess = config.get_global_version() >= 8;
38443933
action_phase_cfg->reserve_extra_enabled = config.get_global_version() >= 9;
38453934
action_phase_cfg->mc_blackhole_addr = config.get_burning_config().blackhole_addr;
3935+
action_phase_cfg->extra_currency_v2 = config.get_global_version() >= 10;
3936+
}
3937+
{
3938+
serialize_cfg->extra_currency_v2 = config.get_global_version() >= 10;
38463939
}
38473940
{
38483941
// fetch block_grams_created

crypto/block/transaction.h

+11-6
Original file line numberDiff line numberDiff line change
@@ -170,12 +170,17 @@ struct ActionPhaseConfig {
170170
bool message_skip_enabled{false};
171171
bool disable_custom_fess{false};
172172
bool reserve_extra_enabled{false};
173+
bool extra_currency_v2{false};
173174
td::optional<td::Bits256> mc_blackhole_addr;
174175
const MsgPrices& fetch_msg_prices(bool is_masterchain) const {
175176
return is_masterchain ? fwd_mc : fwd_std;
176177
}
177178
};
178179

180+
struct SerializeConfig {
181+
bool extra_currency_v2{false};
182+
};
183+
179184
struct CreditPhase {
180185
td::RefInt256 due_fees_collected;
181186
block::CurrencyCollection credit;
@@ -389,8 +394,8 @@ struct Transaction {
389394
bool prepare_action_phase(const ActionPhaseConfig& cfg);
390395
td::Status check_state_limits(const SizeLimitsConfig& size_limits, bool update_storage_stat = true);
391396
bool prepare_bounce_phase(const ActionPhaseConfig& cfg);
392-
bool compute_state();
393-
bool serialize();
397+
bool compute_state(const SerializeConfig& cfg);
398+
bool serialize(const SerializeConfig& cfg);
394399
td::uint64 gas_used() const {
395400
return compute_phase ? compute_phase->gas_used : 0;
396401
}
@@ -428,14 +433,14 @@ struct FetchConfigParams {
428433
std::vector<block::StoragePrices>* storage_prices,
429434
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed,
430435
ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
431-
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
432-
ton::WorkchainId wc, ton::UnixTime now);
436+
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
437+
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now);
433438
static td::Status fetch_config_params(const block::Config& config, Ref<vm::Tuple> prev_blocks_info,
434439
Ref<vm::Cell>* old_mparams, std::vector<block::StoragePrices>* storage_prices,
435440
StoragePhaseConfig* storage_phase_cfg, td::BitArray<256>* rand_seed,
436441
ComputePhaseConfig* compute_phase_cfg, ActionPhaseConfig* action_phase_cfg,
437-
td::RefInt256* masterchain_create_fee, td::RefInt256* basechain_create_fee,
438-
ton::WorkchainId wc, ton::UnixTime now);
442+
SerializeConfig* serialize_cfg, td::RefInt256* masterchain_create_fee,
443+
td::RefInt256* basechain_create_fee, ton::WorkchainId wc, ton::UnixTime now);
439444
};
440445

441446
} // namespace block

0 commit comments

Comments
 (0)