Summary
When a miner deregisters, purge_deregistered_hotkeys calls state_store.delete_hotkey, which clears rate_events, swap_outcomes, and reservation_pins but not pending_confirms. Queued user confirms for that hotkey keep draining and voting until reservation expiry or contract rejection.
Impact
- Wasted work signing
vote_initiate for a miner no longer in the metagraph.
- Stale queue rows survive indefinitely (until
purge_expired_pending_confirms) even though scoring/state for that hotkey was dropped.
- Operators see
PendingConfirm [… UID ? (hotkey…)] after deregistration with no clear cleanup.
Current behavior
delete_hotkey omits the pending table:
def delete_hotkey(self, hotkey: str) -> None:
with self.lock:
conn = self.require_connection()
conn.execute('DELETE FROM rate_events WHERE hotkey = ?', (hotkey,))
conn.execute('DELETE FROM swap_outcomes WHERE miner_hotkey = ?', (hotkey,))
conn.execute('DELETE FROM reservation_pins WHERE miner_hotkey = ?', (hotkey,))
conn.commit()
purge_deregistered_hotkeys only triggers that helper:
def purge_deregistered_hotkeys(self: Validator) -> None:
current_hotkeys = set(self.metagraph.hotkeys)
stale = {hk for (hk, _, _) in self.last_known_rates.keys()} - current_hotkeys
if not stale:
return
for hk in stale:
self.state_store.delete_hotkey(hk)
initialize_pending_user_reservations still iterates all queued rows with no metagraph membership check.
Expected behavior
delete_hotkey (or purge_deregistered_hotkeys) also deletes pending_confirms for the hotkey. Log when a deregistered miner had queued confirms removed.
How to reproduce
- User queues
SwapConfirm for miner M (unconfirmed source tx).
- Miner M deregisters from the subnet before the queue drains.
purge_deregistered_hotkeys runs — rates/pins cleared, pending_confirms row remains.
- Forward steps keep processing
PendingConfirm for UID ? until expiry.
Proposed fix
Add DELETE FROM pending_confirms WHERE miner_hotkey = ? to delete_hotkey. Extend test_state_store.py::test_delete_hotkey_* to assert pending row removal. Optionally skip vote_initiate in initialize_pending when hotkey ∉ metagraph as defense in depth.
Summary
When a miner deregisters,
purge_deregistered_hotkeyscallsstate_store.delete_hotkey, which clearsrate_events,swap_outcomes, andreservation_pinsbut notpending_confirms. Queued user confirms for that hotkey keep draining and voting until reservation expiry or contract rejection.Impact
vote_initiatefor a miner no longer in the metagraph.purge_expired_pending_confirms) even though scoring/state for that hotkey was dropped.PendingConfirm [… UID ? (hotkey…)]after deregistration with no clear cleanup.Current behavior
delete_hotkeyomits the pending table:purge_deregistered_hotkeysonly triggers that helper:initialize_pending_user_reservationsstill iterates all queued rows with no metagraph membership check.Expected behavior
delete_hotkey(orpurge_deregistered_hotkeys) also deletespending_confirmsfor the hotkey. Log when a deregistered miner had queued confirms removed.How to reproduce
SwapConfirmfor miner M (unconfirmed source tx).purge_deregistered_hotkeysruns — rates/pins cleared,pending_confirmsrow remains.PendingConfirmfor UID?until expiry.Proposed fix
Add
DELETE FROM pending_confirms WHERE miner_hotkey = ?todelete_hotkey. Extendtest_state_store.py::test_delete_hotkey_*to assert pending row removal. Optionally skipvote_initiateininitialize_pendingwhen hotkey ∉ metagraph as defense in depth.