@@ -2000,9 +2000,9 @@ bool Transaction::prepare_action_phase(const ActionPhaseConfig& cfg) {
2000
2000
ap.remaining_balance += ap.reserved_balance ;
2001
2001
CHECK (ap.remaining_balance .is_valid ());
2002
2002
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 ());
2004
2004
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) ;
2006
2006
was_deleted = true ;
2007
2007
}
2008
2008
ap.success = true ;
@@ -2472,6 +2472,20 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
2472
2472
LOG (DEBUG) << " invalid destination address in a proposed outbound message" ;
2473
2473
return check_skip_invalid (36 ); // invalid destination address
2474
2474
}
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
+ }
2475
2489
2476
2490
// fetch message pricing info
2477
2491
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,
2524
2538
};
2525
2539
add_used_storage (msg.init , 3 ); // message init
2526
2540
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 ) {
2528
2542
add_used_storage (info.value ->prefetch_ref (), 0 );
2529
2543
}
2530
2544
auto collect_fine = [&] {
@@ -2595,11 +2609,19 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
2595
2609
2596
2610
if (act_rec.mode & 0x80 ) {
2597
2611
// 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
+ }
2599
2617
act_rec.mode &= ~1 ; // pay fees from attached value
2600
2618
} else if (act_rec.mode & 0x40 ) {
2601
2619
// 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
+ }
2603
2625
if (!(act_rec.mode & 1 )) {
2604
2626
req -= ap.action_fine ;
2605
2627
if (compute_phase) {
@@ -2639,6 +2661,11 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
2639
2661
return check_skip_invalid (37 ); // not enough grams
2640
2662
}
2641
2663
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
+
2642
2669
Ref<vm::Cell> new_extra;
2643
2670
2644
2671
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,
2680
2707
2681
2708
// clear msg_balance_remaining if it has been used
2682
2709
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
+ }
2684
2715
}
2685
2716
2686
2717
// update balance
@@ -2754,8 +2785,13 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
2754
2785
ap.total_fwd_fees += fees_total;
2755
2786
2756
2787
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
+ }
2759
2795
}
2760
2796
2761
2797
ap.tot_msg_bits += sstat.bits + new_msg_bits;
@@ -3026,7 +3062,8 @@ bool Transaction::prepare_bounce_phase(const ActionPhaseConfig& cfg) {
3026
3062
bp.fwd_fees -= bp.fwd_fees_collected ;
3027
3063
total_fees += td::make_refint (bp.fwd_fees_collected );
3028
3064
// serialize outbound message
3029
- info.created_lt = end_lt++;
3065
+ info.created_lt = start_lt + 1 + out_msgs.size ();
3066
+ end_lt++;
3030
3067
info.created_at = now;
3031
3068
vm::CellBuilder cb;
3032
3069
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 {
3107
3144
* Tries to update the storage statistics based on the old storage statistics and old account state without fully recomputing it.
3108
3145
*
3109
3146
* 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).
3110
3148
*
3111
3149
* @param old_stat The old storage statistics.
3112
3150
* @param old_cs The old AccountStorage.
@@ -3140,13 +3178,48 @@ static td::optional<vm::CellStorageStat> try_update_storage_stat(const vm::CellS
3140
3178
return new_stat;
3141
3179
}
3142
3180
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
+
3143
3216
namespace transaction {
3144
3217
/* *
3145
3218
* Computes the new state of the account.
3146
3219
*
3147
3220
* @returns True if the state computation is successful, false otherwise.
3148
3221
*/
3149
- bool Transaction::compute_state () {
3222
+ bool Transaction::compute_state (const SerializeConfig& cfg ) {
3150
3223
if (new_total_state.not_null ()) {
3151
3224
return true ;
3152
3225
}
@@ -3218,13 +3291,27 @@ bool Transaction::compute_state() {
3218
3291
new_inner_state.clear ();
3219
3292
}
3220
3293
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);
3222
3309
if (new_stats) {
3223
3310
stats = new_stats.unwrap ();
3224
3311
} else {
3225
3312
TD_PERF_COUNTER (transaction_storage_stat_b);
3226
3313
td::Timer timer;
3227
- stats.add_used_storage (Ref<vm::Cell>(storage) ).ensure ();
3314
+ stats.add_used_storage (new_storage_for_stat ).ensure ();
3228
3315
if (timer.elapsed () > 0.1 ) {
3229
3316
LOG (INFO) << " Compute used storage took " << timer.elapsed () << " s" ;
3230
3317
}
@@ -3260,11 +3347,11 @@ bool Transaction::compute_state() {
3260
3347
*
3261
3348
* @returns True if the serialization is successful, False otherwise.
3262
3349
*/
3263
- bool Transaction::serialize () {
3350
+ bool Transaction::serialize (const SerializeConfig& cfg ) {
3264
3351
if (root.not_null ()) {
3265
3352
return true ;
3266
3353
}
3267
- if (!compute_state ()) {
3354
+ if (!compute_state (cfg )) {
3268
3355
return false ;
3269
3356
}
3270
3357
vm::Dictionary dict{15 };
@@ -3730,6 +3817,7 @@ bool Account::libraries_changed() const {
3730
3817
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
3731
3818
* @param compute_phase_cfg Pointer to store the compute phase configuration.
3732
3819
* @param action_phase_cfg Pointer to store the action phase configuration.
3820
+ * @param serialize_cfg Pointer to store the serialize phase configuration.
3733
3821
* @param masterchain_create_fee Pointer to store the masterchain create fee.
3734
3822
* @param basechain_create_fee Pointer to store the basechain create fee.
3735
3823
* @param wc The workchain ID.
@@ -3738,15 +3826,15 @@ bool Account::libraries_changed() const {
3738
3826
td::Status FetchConfigParams::fetch_config_params (
3739
3827
const block::ConfigInfo& config, Ref<vm::Cell>* old_mparams, std::vector<block::StoragePrices>* storage_prices,
3740
3828
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) {
3743
3831
auto prev_blocks_info = config.get_prev_blocks_info ();
3744
3832
if (prev_blocks_info.is_error ()) {
3745
3833
return prev_blocks_info.move_as_error_prefix (
3746
3834
td::Status::Error (-668 , " cannot fetch prev blocks info from masterchain configuration: " ));
3747
3835
}
3748
3836
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,
3750
3838
basechain_create_fee, wc, now);
3751
3839
}
3752
3840
@@ -3761,6 +3849,7 @@ td::Status FetchConfigParams::fetch_config_params(
3761
3849
* @param rand_seed Pointer to the random seed. Generates a new seed if the value is `td::Bits256::zero()`.
3762
3850
* @param compute_phase_cfg Pointer to store the compute phase configuration.
3763
3851
* @param action_phase_cfg Pointer to store the action phase configuration.
3852
+ * @param serialize_cfg Pointer to store the serialize phase configuration.
3764
3853
* @param masterchain_create_fee Pointer to store the masterchain create fee.
3765
3854
* @param basechain_create_fee Pointer to store the basechain create fee.
3766
3855
* @param wc The workchain ID.
@@ -3770,8 +3859,8 @@ td::Status FetchConfigParams::fetch_config_params(
3770
3859
const block::Config& config, td::Ref<vm::Tuple> prev_blocks_info, Ref<vm::Cell>* old_mparams,
3771
3860
std::vector<block::StoragePrices>* storage_prices, StoragePhaseConfig* storage_phase_cfg,
3772
3861
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) {
3775
3864
*old_mparams = config.get_config_param (9 );
3776
3865
{
3777
3866
auto res = config.get_storage_prices ();
@@ -3843,6 +3932,10 @@ td::Status FetchConfigParams::fetch_config_params(
3843
3932
action_phase_cfg->disable_custom_fess = config.get_global_version () >= 8 ;
3844
3933
action_phase_cfg->reserve_extra_enabled = config.get_global_version () >= 9 ;
3845
3934
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 ;
3846
3939
}
3847
3940
{
3848
3941
// fetch block_grams_created
0 commit comments