# Incorrect usage of get_num_verifiers in send_operator_asserts_if_ready breaks state ma- chine

Clementine operator state machine compares spent watchtower UTXOs against get_num_verifiers instead of get_num_watchtowers, deadlocking assert submission and causing operator slashing.

- Payout: Contest reward
- Severity: high
- Program: Clementine
- Credited: n4nika
- Disclosed: 2025-10-29
- Write-up quality: 6/10
- Original: https://cdn.cantina.xyz/reports/cantina_competition_clementine_aug2025.pdf#3.1.1
- Library page: https://bugbountylibrary.com/r/cantina%3Aclementine_aug2025%3A3.1.1
- Tags: domain:web3 impact:dos impact:funds-loss kind:contest platform:cantina sev:high tech:rust technique:source-review vuln:business-logic vuln:dos

**Target:** Bridge operator state machine (Rust, send_operator_asserts_if_ready)
**Root cause:** send_operator_asserts_if_ready gates on spent_watchtower_utxos.len() == get_num_verifiers() while the preceding latest-blockhash matcher gates on get_num_watchtowers(); when explicit watchtowers are set, the counts diverge and the equality never holds.
**Steps:**
1. Aggregator creates a deposit_data with explicit watchtowers so get_num_watchtowers() > get_num_verifiers().
2. Operator receives all watchtower UTXOs; create_matcher_for_latest_blockhash_if_ready passes since len == get_num_watchtowers().
3. LatestBlockHashSent event fires and calls send_operator_asserts_if_ready.
4. Condition spent_watchtower_utxos.len() == get_num_verifiers() is false (len equals watchtower count), so Duty::SendOperatorAsserts is never dispatched.
5. AssertTimeout elapses and operator is slashed.
**How found:** Code reading of the state machine: reviewer traced the call tree from LatestBlockHashSent and noticed the two readiness checks use different counters (get_num_watchtowers vs get_num_verifiers), then reasoned the equality breaks when explicit watchtowers are configured.
**Exploitation:** A malicious aggregator crafts deposit_data with explicit watchtowers exceeding verifier count, driving the operator into a permanent wait state where asserts are never sent, triggering slashing at AssertTimeout.
**Insight:** When a state machine has multiple readiness gates over the same collection, diff the counters each gate compares against; mismatched accessor names (verifiers vs watchtowers) on the same UTXO set signal a deadlock when the two counts can diverge via optional config fields.
