You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
register_bls collapses two different situations into one message:
let status = cc3.get_attestor_status(chain_key).await?;if status != Some(AttestorStatus::Idle){
tracing::info!(?status, %account_id,"ℹ️ skipping attest() — already registered");returnOk(());}
Some(Waiting) / Some(Active) — the attestor really is already registered, and the message is right.
None — the attestor is not in the pool at all, and the message says exactly the opposite of the truth.
The behaviour is correct either way — attest() is only callable from Idle, and an account reaches Idle only through register_attestor, which a stash must call; an attestor cannot enrol itself. So skipping is right. Only the diagnosis is wrong.
Why it is worth fixing
Bringing an attestor up against a new source chain produces a log that looks entirely healthy:
🔍 balance ok account_id=5FHneW46… balance=1000000000000000000000000
ℹ️ skipping attest() — already registered status=None account_id=5FHneW46…
⏲️ waiting on election...
⏲️ waiting on election...
Balance fine, "already registered", waiting for an election — nothing suggests a problem. In reality the attestor is not registered, no election will ever seat it, and it will wait forever. status=None is printed, but it reads as a detail rather than a contradiction of the sentence next to it.
This cost us the better part of a day while integrating a new source chain, and it is the kind of thing that costs every operator the same day once.
What this changes
Replaces the equality check with a match that names the None case honestly, warns instead of informing, and says what to do about it:
⚠️ not registered as an attestor for this chain — skipping attest(). A stash account
must call register_attestor(chain_key, attestor_id) first, using an account other
than the attestor itself.
No behaviour change: every branch still returns Ok(()) exactly as before, and the Idle path is untouched. One file, logging only.
Context
Found while bringing up an attestor for exSat's EVM layer (chain id 7200) as part of a BUIDL CTC 2026 Fall project. A separate, more serious finding from the same work — the attestor cannot follow any chain that does not maintain a receipts trie, and reports that as a reorg — is filed as #1355.
Low Risk
Logging-only change in attestor startup with no change to registration or attestation behavior.
Overview Fixes misleading startup logs when an attestor account is not in the chain’s attestor pool at all.
register_bls used to treat any non-Idle status (including None) as “already registered” and log at info level. The diff splits that into a match: None now emits a warning that the account is not registered for that chain, that attest() is skipped for that reason, and that a stash must call register_attestor first; non-Idle statuses like Waiting/Active still log the existing “already registered” info message. Control flow is unchanged—every skip path still returns Ok(()), and the Idle path that submits attest() is untouched.
Reviewed by Cursor Bugbot for commit 93d6375. Bugbot is set up for automated code reviews on this repo. Configure here.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's wrong
register_blscollapses two different situations into one message:Some(Waiting)/Some(Active)— the attestor really is already registered, and the message is right.None— the attestor is not in the pool at all, and the message says exactly the opposite of the truth.The behaviour is correct either way —
attest()is only callable fromIdle, and an account reachesIdleonly throughregister_attestor, which a stash must call; an attestor cannot enrol itself. So skipping is right. Only the diagnosis is wrong.Why it is worth fixing
Bringing an attestor up against a new source chain produces a log that looks entirely healthy:
Balance fine, "already registered", waiting for an election — nothing suggests a problem. In reality the attestor is not registered, no election will ever seat it, and it will wait forever.
status=Noneis printed, but it reads as a detail rather than a contradiction of the sentence next to it.This cost us the better part of a day while integrating a new source chain, and it is the kind of thing that costs every operator the same day once.
What this changes
Replaces the equality check with a
matchthat names theNonecase honestly, warns instead of informing, and says what to do about it:No behaviour change: every branch still returns
Ok(())exactly as before, and theIdlepath is untouched. One file, logging only.Context
Found while bringing up an attestor for exSat's EVM layer (chain id 7200) as part of a BUIDL CTC 2026 Fall project. A separate, more serious finding from the same work — the attestor cannot follow any chain that does not maintain a receipts trie, and reports that as a reorg — is filed as #1355.