Draft: (paused) Resolve "Use consumers for identities (instead of sufficients) so identities cannot bypass existential deposit"

Changes

  • Runtime behavior change for identity accounts created after this update:
    • identity lifecycle references now use consumers instead of sufficients on identity creation/removal/key-change paths.
    • as a result, Balances::transfer_all can no longer drain a newly created identity account to zero; the account keeps at least ExistentialDeposit.
  • New guardrail on owner key reuse:
    • introduced OldOwnerKeys storage set (AccountId -> ()) to track keys that have ever been an old_owner_key.
    • create_identity now rejects any receiver key present in OldOwnerKeys (OwnerKeyAlreadyUsed).
    • this prevents creating a new identity with a previously rotated-out owner key.

Migration/compatibility notes:

  • Legacy identities (created before this change) may still carry identity refs in sufficients.
  • Cleanup now uses a unified rule: decrement sufficients when present, otherwise decrement consumers.
  • New identity owner keys must be provider-backed (providers >= 1) for both create_identity and change_owner_key.
  • Genesis compatibility retained:
    • genesis identities use consumers when the account has providers,
    • fallback to sufficients for funding-less/provider-less genesis accounts.
  • OldOwnerKeys is also populated from genesis identities that already contain old_owner_key.

Goal of the changes

Ensure identity accounts cannot bypass account liveness constraints by relying on sufficients, align identity lifecycle refs with the consumer/provider model expected by balances account reaping semantics, and prevent unsafe owner-key reuse after key rotation.

What reviewers need to know

Identity refcount model update

  • pallets/identity/src/lib.rs
    • create_identity: inc_consumers instead of inc_sufficients.
    • change_owner_key:
      • enforce provider-backed new_key,
      • mark previous owner key in OldOwnerKeys,
      • use inc_consumers for the new owner key,
      • cleanup of previous owner refs goes through dec_identity_ref_count.
    • do_remove_identity: owner and old-owner cleanup also go through dec_identity_ref_count.
    • introduced helper:
      • dec_identity_ref_count(account_id):
        • dec_sufficients if sufficients > 0,
        • else dec_consumers if consumers > 0,
        • else no-op.
    • check_create_identity:
      • receiver must have at least one provider,
      • receiver must not be present in OldOwnerKeys.

Owner-key history tracking

  • pallets/identity/src/lib.rs
    • new storage OldOwnerKeys.
    • populated:
      • during genesis build for identities with an old_owner_key,
      • during owner-key rotation when current owner becomes old owner.

Test coverage added/updated

  • runtime/gdev/tests/balance_tests.rs
    • test_transfer_all_identity_cannot_drain validates a newly created identity account keeps ExistentialDeposit after transfer_all(..., false).
  • pallets/identity/src/tests.rs
    • extended assertions around consumers/sufficients in creation/removal/key-change flows.
    • added focused tests for legacy/new cleanup behavior and repeated key changes.
    • added test_old_owner_key_can_transfer_all_and_reach_zero_counters.
    • added test_create_identity_forbidden_with_old_owner_key for the new OldOwnerKeys rule.
  • pallets/identity/src/mock.rs + pallets/identity/Cargo.toml
    • added pallet_balances in identity test runtime/dev-deps to enable unit-level transfer_all coverage.

Closes #350 (closed)

Edited by Éloïs

Merge request reports

Loading