Skip to content

refactor: compare a CompoundCycles on a part it names - #11515

Closed
mraszyk wants to merge 1 commit into
masterfrom
mraszyk/compound-cycles-explicit-comparison
Closed

refactor: compare a CompoundCycles on a part it names#11515
mraszyk wants to merge 1 commit into
masterfrom
mraszyk/compound-cycles-explicit-comparison

Conversation

@mraszyk

@mraszyk mraszyk commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

CompoundCycles derived Ord and PartialOrd over its fields, so a comparison of two amounts was made on their real parts first and only fell back to the nominal ones for amounts whose real parts are equal.

Which part a comparison is made on matters. The real part of an amount whose use case is free under the free cost schedule is zero (cf. CompoundCycles::new), so a comparison of real parts cannot tell two such amounts apart; the nominal part is independent of the cost schedule. Under the normal cost schedule the two parts coincide, so the distinction is invisible there.

Nothing derived that ordering on purpose, and all four call sites that used it clamp a cost to a prepayment, for which the nominal part is the right one:

  • CyclesAccountManager::refund_unused_execution_cycles
  • CyclesAccountManager::settle_prepayment_for_unexecuted_response
  • CyclesAccountManager::refund_for_response_transmission
  • CanisterManager, which recomputes the same clamp to record the net cycle charge

They were correct only because the real and the nominal parts coincide under the normal cost schedule and the real parts are both zero under the free one, i.e. because the fallback to the nominal part happened to kick in. Reordering the two fields of the struct would have changed what they mean.

This PR drops the two derives and adds CompoundCycles::min_nominal, which says which part it compares, and uses it at those four call sites. It is behavior-preserving: for two amounts derived under the same cost schedule, and for the use cases those call sites deal with, min_nominal picks the same amount as the derived ordering did. The tests that compared two execution costs with assert_gt! now compare their nominal parts.

Why now

Cleanup split out of #11431, which fixes a bug of exactly this shape: the cycles prepaid for a response execution were settled against the requirement by comparing their real parts, which made the adjustment a no-op under the free cost schedule and misreported the consumed cycles metrics. Removing the derived ordering means no call site can compare on a part it did not name.

🤖 Generated with Claude Code

`CompoundCycles` derived `Ord` and `PartialOrd` over its fields, so a comparison of
two amounts was made on their real parts first and only fell back to the nominal ones
for amounts whose real parts are equal. Which part a comparison is made on matters:
the real part of an amount whose use case is free under the free cost schedule is
zero, so a comparison of real parts cannot tell two such amounts apart, whereas the
nominal part is independent of the cost schedule. The two parts coincide under the
normal cost schedule, so the distinction is invisible there.

Nothing derived that ordering on purpose, and the four call sites that used it all
clamp a cost to a prepayment, for which the nominal part is the right one: they were
correct only because the real and the nominal parts coincide under the normal cost
schedule and the real parts are both zero under the free one, i.e. because the
fallback happened to kick in. Reordering the two fields of the struct would have
changed what they mean.

Drop the two derives and add `CompoundCycles::min_nominal`, which says which part it
compares, and use it at those four call sites. This is behavior-preserving: for two
amounts derived under the same cost schedule, and for the use cases those call sites
deal with, it picks the same amount as the derived ordering did. The tests that
compared two execution costs with `assert_gt!` now compare their nominal parts.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

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.

🟢 Approval recommended

The explicit comparisons preserve existing behavior while removing ambiguous implicit ordering.

Pull request overview

Makes CompoundCycles comparisons explicit to prevent accidental ordering by the real component.

Changes:

  • Removes derived ordering and adds min_nominal.
  • Updates four production clamps and affected test assertions.
File summaries
File Description
rs/types/cycles/src/compound_cycles.rs Adds explicit nominal minimum comparison.
rs/cycles_account_manager/src/cycles_account_manager.rs Uses nominal comparisons for refunds and settlement.
rs/execution_environment/src/canister_manager.rs Uses nominal comparison when recording charges.
rs/execution_environment/src/execution/response/tests.rs Compares nominal execution costs explicitly.
rs/execution_environment/tests/hypervisor.rs Compares nominal execution costs explicitly.
Review details
  • Files reviewed: 5/5 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mraszyk mraszyk closed this Sep 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants