Centralize renewal mechanics in do_renew_in_memory#515
Conversation
b921b5d to
2754589
Compare
|
/cmd fmt |
| TransactionKind::Renew, | ||
| )?; | ||
| sp_io::transaction_index::renew(extrinsic_index, content_hash); | ||
| let mut transactions = <BlockTransactions<T>>::get(); |
There was a problem hiding this comment.
@rosarp instead of get/put, we should be able to use <BlockTransactions<T>>::try_mutate(|transactions| { pattern - do we have similar places?
There was a problem hiding this comment.
also looks "strange" that "do_renew_in_memory" modifies some "transactions.try_push(new_info).ok()?;, why it cannot mutate directly BlockTransactions? BlockTransactions` are never stored in DB right, it is transient stuff, so the performance shouldnt degradate?
There was a problem hiding this comment.
get/put changed to try_mutate.
There was a problem hiding this comment.
You're correct that no disk write happens. BlockTransactions is cleared via take() in on_finalize, so its value never reaches the on-disk trie at block commit. The overlay holds it in memory for the block's duration. The cost being paid is SCALE encode/decode, not disk I/O.
Why an &mut accumulator avoids it. mutate(|t| { … }) decodes once when entering the closure and encodes once when leaving it. Inside the closure, mutations against &mut BoundedVec are pure in-memory Rust — no codec, no storage layer. So we open the closure, run the whole loop with do_renew_in_memory(t, …) inside it, and close. That's the whole purpose of do_renew_in_memory taking &mut BoundedVec: it's the in-memory shape that both try_mutate (single call) and mutate (loop) hand to their closures, so the same helper serves both paths without forcing a per-call codec round-trip.
e561876 to
cb3cc85
Compare
332d351 to
0a38270
Compare
`renew` and `enable_auto_renew` charge `PermanentStorageUsed` up front in `check_signed`, but the matching `Renew` entry only lands in `Transactions` once the cycle fires at the next retention boundary. The `check_permanent_storage_accounting` invariant now sums paid `AutoRenewals` sizes alongside on-chain `Renew` entries so try_state passes during the prepayment window.
Slot-cap rejection at inherent time is pathological — inherent runs before user extrinsics, and `len(pending) <= MaxBlockTransactions`. Reaching into the current `Authorizations` entry on the refund path would silently apply across auth roll-overs, so leave the per-account `bytes_permanent` / `transactions` increments burned and only restore the chain-wide counter that `try_state` reconciles.
Drops the single-call `refund_chain_wide_renewal_charge` helper and moves the rationale to a comment at the call site in `do_process_auto_renewals`. Switches the two `block_chunks` calculations in `do_renew_in_memory` and `append_to_block_transactions` from raw `+` to `saturating_add`, matching the saturating discipline used elsewhere in the pallet.
Centralize renewal mechanics in a single helper
do_renew_in_memory:stamp
kind = Renew, push ontoBlockTransactions, calltransaction_index::renew, updateTransactionByContentHash. ReturnsNoneat the per-blockMaxBlockTransactionscap.All three entry points go through it:
renew/renew_content_hashviado_renew(BlockTransactions::try_mutate).do_process_auto_renewals(BlockTransactions::mutatewrapping the whole loop, so theBoundedVecis SCALE-encoded once per block instead of n times — preserves the O(n) batching).Hard-cap accounting (per-account
bytes_permanent, chain-widePermanentStorageUsed) is unchanged:check_authorizationhandles it on the extension'spre_dispatchfor manual renewals, and inline indo_process_auto_renewalsfor auto.do_process_auto_renewals's failure path now uniformly drops the registration and emitsAutoRenewalFailedwhether the gate failed on expired/missing auth, per-account cap, chain-wide cap, or block-slot cap; the on-chain data expires normally on its existingRetentionPeriod. When a cycle that had already been charged (apaid = trueprepayment, or the per-cycle charge applied for the failing recurring case) is rejected by the per-block slot cap, the newrefund_renewal_chargehelper reverses the chain-widePermanentStorageUsedbump so the cap does not leak; the per-accountbytes_permanent/transactionsincrements are intentionally left in place — slot-cap rejection at inherent time is pathological (the inherent runs before user txs andlen(pending) <= MaxBlockTransactions), and refunding into the currentAuthorizationsentry would silently apply across auth roll-overs.Also fixes the
check_permanent_storage_accountingtry_stateinvariant:renew/enable_auto_renewchargePermanentStorageUsedup front incheck_signed, but the matchingRenewentry only lands inTransactionsonce the cycle fires at the next retention boundary. The invariant now reconcilesPermanentStorageUsedagainstΣ Renew sizes in Transactions + Σ paid AutoRenewals sizes, so try_state passes during the prepayment window.Adds an
AUTO_RENEWAL_ALREADY_ENABLEDrejection in the extension'svalidate_signedforrenew, mirroring the existingenable_auto_renewguard — duplicate registrations are rejected at pool admission before any charge, instead of charging inpre_dispatchand then erroring in the dispatch body.Lays groundwork for the TODO items in
docs/001_authorizations.md.