Skip to content

fix(cnight-observation): enforce MaxRegistrationsPerCardanoAddress in handle_registration (audit AE / #314)#1488

Draft
m2ux wants to merge 1 commit intomainfrom
fix/314-enforce-cap-handle-registration
Draft

fix(cnight-observation): enforce MaxRegistrationsPerCardanoAddress in handle_registration (audit AE / #314)#1488
m2ux wants to merge 1 commit intomainfrom
fix/314-enforce-cap-handle-registration

Conversation

@m2ux
Copy link
Copy Markdown
Contributor

@m2ux m2ux commented May 11, 2026

Summary

Enforce MaxRegistrationsPerCardanoAddress in pallet-cnight-observation::handle_registration so a single Cardano address cannot accumulate more MappingEntry records than the declared cap, closing the writer-side gap flagged by audit Issue AE.

🐛 Issue 📐 Engineering

Audit reference: Least Authority Node Final Audit Report (10 March 2026) — Issue AE.
Jira: PM-19974.


Motivation

pallet-cnight-observation's handle_registration (pallets/cnight-observation/src/lib.rs:411-452) appends new MappingEntry records to Mappings::<T>::get(cardano_reward_address) without checking the per-address cap. The cap concept is already declared in three places — Error::<T>::MaxRegistrationsExceeded (src/lib.rs:178, currently unused), MaxRegistrationsPerCardanoAddress = 100 in both mock runtimes (mock/src/mock.rs:146, mock_with_capture.rs:134), and a block of commented-out cap-enforcement tests in tests/tests.rs:730+ — but no path reads the cap at write time. A Cardano address that submits N valid registration UTXOs without intervening deregistrations accumulates N entries unchecked. The audit categorises both severity and feasibility as Low (the spam path is rate-limited by Cardano fees), but the gap is a straightforward declaration/enforcement mismatch and a latent panic site for any future reader that converts Mappings to BoundedVec.

This PR closes the gap with a writer-invariant cap inside handle_registration, parameterised on the pallet Config trait so the runtime can bind the value. The cap-rejection is observable via a new Event::MappingCapped variant (indexer-facing) and a log::warn! line (operator-facing). The dead Error::MaxRegistrationsExceeded variant is removed in the same PR.


Changes

Implementation (coming next):

  • Config — Add type MaxRegistrationsPerCardanoAddress: Get<u32> to pallet_cnight_observation::Config (third associated type, alongside MidnightSystemTransactionExecutor and WeightInfo).
  • Writer invariant — Insert a cap-check at the head of handle_registration (before any read of Mappings for write purposes). On mappings.len() as u32 >= T::MaxRegistrationsPerCardanoAddress::get(), emit Event::MappingCapped(new_reg), log::warn!, and return None. No mutation occurs on rejection.
  • Event — Add MappingCapped(MappingEntry) variant to Event<T>. Shape-parallel to MappingAdded / MappingRemoved.
  • Error — Remove the unused Error::MaxRegistrationsExceeded variant.
  • Mock bindings — Widen MaxRegistrationsPerCardanoAddress literal from u8 = 100 to u32 = 100 in both mocks; bind type MaxRegistrationsPerCardanoAddress = MaxRegistrationsPerCardanoAddress; in each impl Config for Test.
  • Runtime binding — Add parameter_types! { pub const MaxRegistrationsPerCardanoAddress: u32 = 100; } and bind type MaxRegistrationsPerCardanoAddress in runtime/src/lib.rs.
  • Tests — Three new unit tests: cap-exact accumulation (cap-th accepted), cap-over rejection (cap+1-th rejected with MappingCapped event), and process_tokens batch continuity (cap-rejected UTXO does not poison sibling UTXOs).
  • Change filechanges/runtime/changed/audit-cnight-observation-cap-enforcement.md mirroring the audit-motion-close-weight-bound.md precedent.
  • Storage typeUnchanged. Mappings remains Vec<MappingEntry>. No BoundedVec migration in this PR. (Path B — typed BoundedVec<_, T::MaxRegistrationsPerCardanoAddress> with OnRuntimeUpgrade — is enumerated as a follow-up in the engineering plan.)

📌 Submission Checklist

  • Changes are backward-compatible (or flagged if breaking)
  • Pull request description explains why the change is needed
  • Self-reviewed the diff
  • I have included a change file, or skipped for this reason: [reason]
  • If the changes introduce a new feature, I have bumped the node minor version
  • Update documentation (if relevant)
  • No new todos introduced

🔱 Fork Strategy

  • Node Runtime Update
  • Node Client Update
  • Other
  • N/A

🗹 TODO before merging

  • Ready for review

…ation

Signed-off-by: Mike Clay <mike.clay@shielded.io>
@m2ux m2ux self-assigned this May 11, 2026
@m2ux m2ux changed the title fix(native-token-observation): enforce MaxRegistrationsPerCardanoAddress in handle_registration (audit AE / shieldedtech#314) fix(cnight-observation): enforce MaxRegistrationsPerCardanoAddress in handle_registration (audit AE / #314) May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant