@@ -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+
31433216namespace 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 {
37383826td::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
0 commit comments