Skip to content

Fix auto-renewal charging tasks for foreign workloads - #12750

Open
Szegoo wants to merge 5 commits into
masterfrom
szegoo-auto-renew-fix
Open

Fix auto-renewal charging tasks for foreign workloads#12750
Szegoo wants to merge 5 commits into
masterfrom
szegoo-auto-renew-fix

Conversation

@Szegoo

@Szegoo Szegoo commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

Summary

A core index doesn't permanently belong to a task. Core assignments are rebuilt every region, and a renewal buys a core in the next sale, so a workload can end up on a different core index each region. Because of this, PotentialRenewals can hold records for the same core index belonging to different tasks at the same time. For example, the pending renewal of the task running on that core in the current bulk period, and the renewal record of the task that will run on it in the next bulk period.

enable_auto_renew didn't account for this. It first checks whether there is any renewable workload at (core, current region begin), and if there is one it renews it immediately and charges the task's sovereign account, without checking whose workload it is and without looking at the hint. So a task following the documented flow (passing its own core and its own renewal record's timeslice as the hint) could end up paying for the renewal of a completely unrelated task. The stored auto-renewal record then keeps charging it for that foreign workload every sale.

This happened on Polkadot Coretime: task 3428 enabled auto-renewal for its twelve cores with correct parameters, but five of those core indices were still carrying other tasks' pending renewals for the current sale. One went through, so task 3428 paid for task 2094's renewal: Link

Fix

  • enable_auto_renew only renews immediately if the expiring workload includes the task. If the core is expiring with another task's workload, we fall through to the workload_end_hint path instead, which must point to the task's own renewal record (otherwise the call fails with the new TaskNotInWorkload error).
  • renew_cores re-checks this before charging the sovereign account. On a mismatch it emits AutoRenewalFailed and drops the record instead of charging. This also cleans up the mismatched record that is currently on-chain.
  • If the core was renewed immediately when enabling, next_renewal is now set to the end of the period that was just renewed. Previously the workload_end_hint was stored instead, so auto-renewal would skip the next renewal and the task would lose its core.

@Szegoo
Szegoo requested a review from a team as a code owner July 29, 2026 06:46
@Szegoo Szegoo added the T2-pallets This PR/Issue is related to a particular pallet. label Jul 29, 2026
@Szegoo
Szegoo requested review from mertwole and seadanda July 29, 2026 08:33
}
impl<Balance> PotentialRenewalRecord<Balance> {
/// Return whether the workload to be renewed is complete and includes the given task.
pub fn complete_workload_includes(&self, task: TaskId) -> bool {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe make this a method on the CompletionStatus itself? Something like fn is_complete_and_contains_task

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

T2-pallets This PR/Issue is related to a particular pallet.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants