Skip to content

offchain/scheduler: fail the boot when SOLANA_RPC is unset - #4254

Open
bgm-malbeclabs wants to merge 2 commits into
mainfrom
migrate/import-followups
Open

offchain/scheduler: fail the boot when SOLANA_RPC is unset#4254
bgm-malbeclabs wants to merge 2 commits into
mainfrom
migrate/import-followups

Conversation

@bgm-malbeclabs

Copy link
Copy Markdown
Contributor

Three findings raised on #4240 that could not be fixed there. That pull request gates on each imported tree staying byte-identical to its filtered source, so any edit to offchain/ or solana/ had to wait for a follow-up. Stacked on #4240 and based on that branch, so the diff shown here is these fixes alone. It retargets to main once #4240 merges.

Summary

  • The scheduler refuses to boot when SOLANA_RPC is unset. All three workers read the one :solana_rpc config key and hand it straight to the NIF, which wants a String. config/runtime.exs read the variable with System.get_env, so an unset variable reached Rust as nil. System.fetch_env! stops the boot instead. One line in the config covers all three workers, which is where the single cause was, rather than a guard in each of them.
  • The test environment is exempt. CI runs mix test with no SOLANA_RPC, and the tests set the key themselves through Application.put_env. An unguarded fetch_env! would break the release workflow's test step.
  • DZ_LEDGER_RPC no longer sets a key nothing reads. It set :ledger_rpc, and no worker, module or NIF callback reads that key. It has the same unset-variable gap, so leaving it in place would invite a fix to dead config.
  • The scheduler README closes its shell code block, so the Installation heading and everything after it stop rendering inside it.
  • The revenue-distribution journal test is renamed from test_lifetime_swept_2z_amount to test_lifetime_swapped_2z_amount, matching the lifetime_swapped_2z_amount field and method it covers, so a search for the swapped-amount tests finds it.

Reported in the copilot review on #4240 and tracked as malbeclabs/infra#2395.

Diff Breakdown

Category Files Lines (+/-) Net
Core logic 1 +7 / -2 +5
Docs 2 +7 / -0 +7
Tests 1 +1 / -1 0

One behaviour change, in a config file. The rest is a heading that rendered in the wrong place and a test name.

Testing Verification

  • Config.Reader.read!("config/runtime.exs", env: ...) run against each case: the key is absent in the test environment with the variable unset, the read raises System.EnvError in prod with it unset, and it carries the URL in prod with it set.
  • cargo test -p doublezero-revenue-distribution --lib test_lifetime_swapped_2z_amount passes from inside solana/.
  • mix format --check-formatted config/runtime.exs clean.

The three workers each read the one `:solana_rpc` config key and hand it
straight to the NIF, which wants a String. `config/runtime.exs` read the
variable with `System.get_env`, so an unset variable reached Rust as nil.
`System.fetch_env!` stops the boot instead. The test environment is exempt,
because CI runs `mix test` with no such variable and the tests set the key
themselves.

`DZ_LEDGER_RPC` set a `:ledger_rpc` key that nothing in the application read,
so it goes.

Two more findings deferred out of the import, which had to keep each tree
byte-identical to its source: the scheduler README closes its shell code
block, and the revenue-distribution journal test is renamed to match the
`lifetime_swapped_2z_amount` field it covers.
Base automatically changed from migrate/import-offchain-and-solana to main September 1, 2026 22:06
@bgm-malbeclabs
bgm-malbeclabs requested a review from a team September 1, 2026 22:06
Comment thread CHANGELOG.md Outdated
- E2E/QA
- Remove `TestQA_MulticastSettlement`. It funded a seat through `doublezero-solana shreds pay`, which is going away. The agent seat-pay RPC now returns Unimplemented if something still calls it. Unused settlement helpers go with the test. (#4248)
- Offchain
- The scheduler refuses to boot when `SOLANA_RPC` is unset, rather than handing `nil` to the Rust NIF. All three workers read the one config key, so the check sits in `config/runtime.exs` where the single cause was. The test environment is exempt, since the tests set the key themselves. `DZ_LEDGER_RPC` no longer sets a `ledger_rpc` key that nothing in the application read. (#4240 follow-up)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

fetch_env! only fails when the variable is missing. SOLANA_RPC= (empty) still boots and still hands "" to the NIF.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Right, and the empty case lands in exactly the same place: fetch_env! raises only when the variable is absent, so SOLANA_RPC= would hand "" to the NIF instead of nil.

Fixed in 03630ec. The value is trimmed and an empty one raises, naming the variable and why it matters. Checked each case with Config.Reader: absent in the test environment with the variable unset, System.EnvError in prod with it unset, a RuntimeError in prod with it empty or whitespace-only, and the URL in prod with a real value.

`System.fetch_env!` raises only when the variable is absent, so `SOLANA_RPC=`
passed an empty string through to the NIF, which is the failure the guard
exists to stop, one step further along. The check now trims and refuses an
empty value, naming the variable and why it matters.

Checked with Config.Reader against each case: absent in the test environment
with the variable unset, System.EnvError in prod with it unset, a RuntimeError
in prod with it empty or whitespace, and the URL in prod with a real value.
Copilot AI lite review requested due to automatic review settings September 1, 2026 23:00
@bgm-malbeclabs
bgm-malbeclabs enabled auto-merge (squash) September 1, 2026 23:01

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

It has a couple of small but concrete correctness and consistency issues (trimmed validation not applied to the configured URL, and a non-standard changelog reference format).

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

This PR hardens the offchain scheduler boot path by enforcing a required Solana RPC URL at runtime (outside the test environment), and it includes small follow-up maintenance fixes in docs, tests, and the changelog.

Changes:

  • Fail scheduler boot in non-test envs when SOLANA_RPC is missing or empty, and remove the unused ledger_rpc config key.
  • Fix offchain/scheduler README Markdown rendering by closing an unterminated shell code block.
  • Rename a revenue-distribution journal test to match the lifetime_swapped_2z_amount field it validates, and document these updates in CHANGELOG.md.
File summaries
File Description
offchain/scheduler/config/runtime.exs Enforces SOLANA_RPC presence and non-empty value outside :test before configuring workers.
offchain/scheduler/README.md Closes the shell code block so subsequent headings render correctly.
solana/programs/revenue-distribution/src/state/journal.rs Renames a unit test for consistency with the field/method under test.
CHANGELOG.md Records the behavioral change and small follow-ups under Unreleased.
Review details
  • Files reviewed: 4/4 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +14 to +20
solana_rpc = System.fetch_env!("SOLANA_RPC")

if String.trim(solana_rpc) == "" do
raise "SOLANA_RPC is set but empty. The workers pass it straight to the NIF."
end

config :scheduler, solana_rpc: solana_rpc
Comment thread CHANGELOG.md
- E2E/QA
- Remove `TestQA_MulticastSettlement`. It funded a seat through `doublezero-solana shreds pay`, which is going away. The agent seat-pay RPC now returns Unimplemented if something still calls it. Unused settlement helpers go with the test. (#4248)
- Offchain
- The scheduler refuses to boot when `SOLANA_RPC` is unset or empty, rather than handing `nil` or an empty string to the Rust NIF. All three workers read the one config key, so the check sits in `config/runtime.exs` where the single cause was. The test environment is exempt, since the tests set the key themselves. `DZ_LEDGER_RPC` no longer sets a `ledger_rpc` key that nothing in the application read. (#4240 follow-up)
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.

3 participants