Skip to content

fix(staking): read the joined validator set a page at a time - #412

Draft
kirilaa wants to merge 1 commit into
v0.40-devfrom
fix/staking-paged-reads
Draft

fix(staking): read the joined validator set a page at a time#412
kirilaa wants to merge 1 commit into
v0.40-devfrom
fix/staking-paged-reads

Conversation

@kirilaa

@kirilaa kirilaa commented Aug 25, 2026

Copy link
Copy Markdown

What broke

The consensus resolution-kernel train withdraws the unpaged validator reads this CLI is built on. Three separate footholds went at once:

Surface Status on the train CLI caller
activeValidators() removed staking active-validators, staking validators
validatorsRoot() removed (survives only in a test-helper mock) StakingAction.getAllValidatorsFromTree()
validatorView(address) tuple shrank 12 → 9 fields; left / right / parent dropped the same tree walk

None of these degrade gracefully. The unpaged read reverts rather than truncating, so the command exits non-zero rather than returning a short list.

Evidence — composed e2e run against the train, shard retry job 97932864617:

genlayer staking active-validators --staking-address 0xdE14C2233118651c4140F959aC4d27E1AA925cFf --rpc http://127.0.0.1:9168
exited 1

What this changes

The joined validator set is read from the append-only registry, one page at a time — validatorsJoinedCount() bounds the walk, getValidatorsJoined(start, pageSize) returns each page.

  • The count is read first, so a set that grows underneath the walk cannot spin the loop; a short page means it shrank instead, so the walk stops and lets the next read see the settled set.
  • Page size is 64 — the convention the contract's own paged reads are written around. Committee capacity is 1,543 seats, which is why an address[] that long had to go: it overruns the return-size limit.
  • getAllValidatorsFromTree() becomes getJoinedValidators(). The registry is the same superset the tree walk was after (all joined validators, primed or not), so callers keep their semantics.
  • staking validators loses its one-call answer for "in the current draw". The active marker is derived from what is still readable: joined, and neither banned nor quarantined.

This mirrors the two reference adaptations, both of which also map "active validators" onto the paged joined registry: genlayerlabs/genlayer-node#1800 (pagedValidatorsJoined, same page size of 64) and genlayerlabs/genlayer-dev-env#138 (VALIDATORS_JOINED_PAGE_SIZE).

What this does NOT fix

The reads above are ones the CLI issues itself, against its own ABI fragment, so they are fixable here. The rest of the CLI's train exposure is inside genlayer-js (pinned github:genlayerlabs/genlayer-js#v2-dev) and has to be fixed there — this PR alone does not turn the CLI e2e green:

  • getTransactionData(bytes32,uint256) was removed from ConsensusData (successors: getStoredTransactionData(bytes32), getStoredTransactionStatus(bytes32), getTransactionAllData(bytes32)). transactionActions.getTransaction still calls it, so waitForTransactionReceipt / getTransactionReceipt fail — this is the same Function "getTransactionData" not found on ABI seen in the explorer and wallet lanes. Blocks 024_cli_counter_single_inc.
  • activeValidatorsCount() was removed, and getEpochInfo() reads it inside a Promise.all, so the whole call reverts. Blocks staking epoch-info (090_cli_network_and_balances) and every client.getEpochInfo() caller.
  • validatorView output drift (above) also breaks the SDK's getValidatorInfo(), so staking validator-info and the detail rows of staking validators stay blocked.
  • BalancesAction.getKnownValidatorSet() calls client.getActiveValidators() via the vesting client. Left alone deliberately: once the SDK pages internally (as node#1800 and dev-env#138 both do while keeping the method name), that call site is correct as written.

Also worth a decision: .github/e2e-track in this repo pins v0.5, while the train work runs on the v0.6 track. The CLI's own e2e will not exercise the train until that moves.

Validation

  • npm run build — clean
  • npx vitest run — 796 passed, 72 files
  • npx tsc --noEmit — 37 errors, all pre-existing (38 before this change; one implicit any went away with the deleted tree walk)

Not yet run against a live train deployment — that is what the acceptance below is for.

Acceptance

genlayer staking active-validators and staking validators return the validator set against a deployment carrying the train, and the CLI e2e lane passes once the genlayer-js items above land.

Depends-On: genlayerlabs/genlayer-consensus#1307

The staking contract withdrew the unpaged validator reads the CLI was
built on. `activeValidators()` is gone outright, and the balanced-tree
walk lost both of its footholds: `validatorsRoot()` no longer exists and
`validatorView()` no longer carries the left/right/parent links. None of
this degrades gracefully -- the calls revert rather than truncating, so
`genlayer staking active-validators` exits non-zero against a deployment
carrying the change.

Read the append-only joined registry instead, one page at a time:
`validatorsJoinedCount()` bounds the walk and `getValidatorsJoined(start,
pageSize)` returns each page. The count is read first so a set that grows
underneath the walk cannot spin the loop, and a short page means it shrank
instead -- stop there and let the next read see the settled set. Page size
is 64, matching the convention the contract's own paged reads are written
around; committee capacity is 1,543 seats, which is why the unpaged read
had to go in the first place.

`staking validators` loses its one-call answer for "in the current draw",
so the active marker is now derived from what is still readable: joined,
and neither banned nor quarantined.
@coderabbitai

coderabbitai Bot commented Aug 25, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true

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.

@kirilaa

kirilaa commented Aug 25, 2026

Copy link
Copy Markdown
Author

The four SDK-side items under What this does NOT fix are now covered by
genlayerlabs/genlayer-js#212 — leaving a note rather than editing the body,
since that PR is yours.

Item listed here #212
getTransactionData successors detected against the chain's own ABI, so a testnet keeps the old call
activeValidatorsCount() inside getEpochInfo() reads validatorsJoinedCount() instead
validatorView drift breaking getValidatorInfo() ABI resynced to the train's nine fields
getKnownValidatorSet()client.getActiveValidators() paged walk over getValidatorsJoined

One correction worth carrying across: the paged reads are not train-only.
Pre-train StakingReadFacet already exposes validatorsJoinedCount and
getValidatorsJoined (and activeValidatorsCount), so moving to them is safe
on both surfaces — activeValidators, the one the train removed, lived on
StakingConfig. That narrows the shared compat risk to validatorView 12→9,
which is the one read with no chain ABI to detect against.

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