Skip to content

Retry fee receiver validation forever and surface its status via /call - #101

Open
janezpodhostnik wants to merge 1 commit into
mainfrom
janezp/fee-validation-hardening
Open

Retry fee receiver validation forever and surface its status via /call#101
janezpodhostnik wants to merge 1 commit into
mainfrom
janezp/fee-validation-hardening

Conversation

@janezpodhostnik

@janezpodhostnik janezpodhostnik commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

The fee receiver validation added in #100 gave up for good after 5 quick attempts and only logged a single line. A flaky access node at startup plus a stale config could leave the server running with an unvalidated fee set indefinitely. A config mismatch also killed the whole server via log.Fatalf, even though a missing receiver only degrades operation classification, never indexed balances.

Changes

  • Retry validation forever: quick backoff for the first 5 attempts, then a slow one-minute poll. Malformed script results are retried instead of ending validation.
  • Re-check the on-chain receiver list every 10 minutes, so receivers added on chain while the server is running are detected without a restart.
  • Downgrade a config mismatch from a fatal exit to a logged error, surfaced via the new fee_receiver_validation_status /call method (mirroring balance_validation_status).
  • Replace the stringly-typed validation status with a validationStatus enum shared by balance and fee validation.
  • Add tests for the fee validation state machine, including a concurrent access test for the race detector.

Related: #100

Summary by CodeRabbit

  • New Features

    • Added fee-receiver validation status reporting through the /call interface.
    • Validation now retries automatically and periodically rechecks for newly added on-chain receivers.
    • Validation results include status, errors, configured receivers, and missing receivers.
  • Bug Fixes

    • Missing or temporarily unavailable fee receivers no longer stop the server during startup.
    • Improved handling of transient validation failures while preserving definitive results.
  • Documentation

    • Updated fee-receiver validation documentation to describe retrying, status reporting, and ongoing checks.

Previously validateFeeReceivers gave up for good after 5 quick attempts, so
a flaky access node at startup plus a stale config could leave the server
running with an unvalidated fee set indefinitely, and the only signal was a
single log line.

- Retry forever: quick backoff for the first 5 attempts, then a slow
  one-minute poll. Treat malformed script results as retryable instead of
  giving up.
- Re-check every 10 minutes after a definitive result, so receivers added
  on chain while the server is running are also detected.
- Downgrade a config mismatch from a fatal exit to a logged error that is
  surfaced via the new fee_receiver_validation_status /call method,
  matching the balance_validation_status pattern.
- Replace the stringly-typed validation status with a validationStatus
  enum shared by balance and fee validation.
- Add tests for the fee validation state machine, including a concurrent
  access test for the race detector.
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Fee receiver validation now uses typed, synchronized state. It retries Access Node failures indefinitely, rechecks successful validations, preserves terminal results during transient retries, and exposes status details through /call.

Changes

Fee receiver validation

Layer / File(s) Summary
Typed validation state
api/api.go
The server stores typed validation statuses and synchronized fee-validation results, including errors, on-chain receivers, and missing receivers.
Persistent validation loop
api/validate.go, api/validate_test.go
Validation retries transient failures, records success or failure without terminating the server, rechecks successful validations, and tests concurrent access and recovery.
Validation status API
api/api.go, api/call_service.go, README.md
The /call dispatcher exposes fee receiver validation status and the documentation describes retry, revalidation, logging, and response behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant CallService as /call
  participant Validator as validateFeeReceivers
  participant AccessNode
  participant Server

  Validator->>AccessNode: validate configured fee receivers
  AccessNode-->>Validator: receivers or transient error
  Validator->>Server: store validation status
  Client->>CallService: request fee_receiver_validation_status
  CallService->>Server: read validation state
  Server-->>CallService: status and receiver details
  CallService-->>Client: validation response
Loading

Possibly related PRs

  • onflow/rosetta#100: The current PR extends its fee-receiver validation with persistent retries, state tracking, and /call reporting.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the two primary changes: indefinite fee receiver validation retries and status exposure through /call.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch janezp/fee-validation-hardening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (3)
api/call_service.go (1)

215-230: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider reporting the time of the last definitive check.

setFeeValidationRetrying preserves a terminal result, so repeated access node failures do not change the reported status. An operator cannot then distinguish a fresh success from one recorded hours ago while every recheck since has failed. Store the timestamp of the last definitive result and the last attempt, then include them in this response.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/call_service.go` around lines 215 - 230, Update the fee validation status
flow around Server.feeReceiverValidationStatus and setFeeValidationRetrying to
track both the timestamp of the last definitive validation result and the
timestamp of the most recent check attempt, preserving terminal results across
access-node failures. Include both timestamps in the returned result map so
callers can distinguish a fresh status from a stale preserved result.
api/api.go (2)

373-388: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

An unknown validationStatus terminates the process from the /call request path. Both default branches call log.Fatalf, which calls process.Exit(1) (log/log.go:78-81). The branches are unreachable for the current four enum values. If a fifth status is added later without updating both switches, one API request stops the server. Handle the unknown value without exiting.

  • api/api.go#L373-L388: return a fallback string such as fmt.Sprintf("unknown(%d)", int(v)) instead of calling log.Fatalf and panicking.
  • api/call_service.go#L209-L211: replace the fatal default with log.Errorf plus a response that carries v.status.String(), so the handler degrades instead of exiting.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/api.go` around lines 373 - 388, The unknown validationStatus handling
must degrade without terminating the server. In api/api.go at lines 373-388,
update validationStatus.String to return a formatted fallback containing the
numeric value instead of calling log.Fatalf or panicking; in api/call_service.go
at lines 209-211, replace the fatal default with log.Errorf and return a
response carrying v.status.String(), preserving normal handling for known
statuses.

255-270: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Log the mismatch only on a status transition.

checkFeeReceivers re-runs every feeValidateRecheckInterval, so an unresolved mismatch logs the identical error every 10 minutes for the lifetime of the process. setFeeValidationSuccess already logs only on transitions. Apply the same rule here to keep the log volume symmetric. The /call method still reports the persisted failure.

♻️ Proposed change to log only on transitions
-	log.Errorf("%s", msg)
 	s.feeValidationMu.Lock()
-	defer s.feeValidationMu.Unlock()
+	prev := s.feeValidation.status
 	s.feeValidation = &feeValidation{
 		err:     msg,
 		missing: missing,
 		onchain: onchain,
 		status:  validationFailure,
 	}
+	s.feeValidationMu.Unlock()
+	// We only log on transitions so that the periodic re-checks don't flood
+	// the logs.
+	if prev != validationFailure {
+		log.Errorf("%s", msg)
+	}
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@api/api.go` around lines 255 - 270, Update setFeeValidationFailure to log the
mismatch only when the existing fee validation status transitions into
validationFailure, while continuing to persist the latest failure details on
every check. Match the transition-aware behavior of setFeeValidationSuccess and
preserve /call reporting through the stored feeValidation state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@api/api.go`:
- Around line 373-388: The unknown validationStatus handling must degrade
without terminating the server. In api/api.go at lines 373-388, update
validationStatus.String to return a formatted fallback containing the numeric
value instead of calling log.Fatalf or panicking; in api/call_service.go at
lines 209-211, replace the fatal default with log.Errorf and return a response
carrying v.status.String(), preserving normal handling for known statuses.
- Around line 255-270: Update setFeeValidationFailure to log the mismatch only
when the existing fee validation status transitions into validationFailure,
while continuing to persist the latest failure details on every check. Match the
transition-aware behavior of setFeeValidationSuccess and preserve /call
reporting through the stored feeValidation state.

In `@api/call_service.go`:
- Around line 215-230: Update the fee validation status flow around
Server.feeReceiverValidationStatus and setFeeValidationRetrying to track both
the timestamp of the last definitive validation result and the timestamp of the
most recent check attempt, preserving terminal results across access-node
failures. Include both timestamps in the returned result map so callers can
distinguish a fresh status from a stale preserved result.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 8bbec699-e874-4e94-88ec-c2804a4266d8

📥 Commits

Reviewing files that changed from the base of the PR and between 5eab151 and 3ea0b77.

📒 Files selected for processing (5)
  • README.md
  • api/api.go
  • api/call_service.go
  • api/validate.go
  • api/validate_test.go

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