Skip to content

feat: proof-bearing validator joins (restores #206, gated on its 4 companions) - #208

Merged
MuncleUscles merged 3 commits into
v2-devfrom
feat/proof-bearing-validator-joins
Aug 18, 2026
Merged

feat: proof-bearing validator joins (restores #206, gated on its 4 companions)#208
MuncleUscles merged 3 commits into
v2-devfrom
feat/proof-bearing-validator-joins

Conversation

@MuncleUscles

@MuncleUscles MuncleUscles commented Aug 11, 2026

Copy link
Copy Markdown
Member

Restores the change from #206, which was reverted in #207 because it merged alone, ahead of the four PRs that make it work.

The code is unchanged — this branch reverts the revert, so the tree is byte-identical to the original 6f127388. Nothing needs re-reviewing on its merits.

Draft on purpose

validatorJoin requires {registration} plus a client-side verifyOperatorRegistration. Every caller must send a proof, and the chain must be able to verify it. Until all five land together, an SDK that demands the proof breaks every consumer — which is exactly what happened: both e2e lanes failed on the shared join as a validator step with Operator registration proof does not match the owner, registrar, chain, or public key, and the deployed chain could not have satisfied it either.

tracks/v0.6/matrix.yaml pins genlayer-js: v2-dev as a moving branch, so anything merged here reaches every downstream build immediately. That is what turned a one-repo merge into a cross-repo outage.

Depends-On — all four must land with this

Depends-On: genlayerlabs/genlayer-cli#405
Depends-On: genlayerlabs/genlayer-e2e#716
Depends-On: genlayerlabs/genlayer-consensus#1290
Depends-On: genlayerlabs/genlayer-explorer#485
Repo / PR Role
genlayer-cli #405 cli sends the proof
genlayer-e2e #716 e2e drivers send the proof
genlayer-consensus #1290 chain verifies the proof
genlayer-explorer #485 explorer sends the proof

The consensus side is the hard constraint: no caller can succeed until the chain can verify the proof. Repointed from #1285 to #1290, which is what the rest of the set now pins — genlayer-node#1707, cli#405, e2e#716, explorer#485 and dev-env#123 all resolve consensus to #1290. Leaving #1285 here also broke dispatch mechanically: the closure allows one ref per repository per track, so #1285 from this body collided with #1290 from the consumers.

Before un-drafting

  • All four companions green and ready to land in the same window.
  • A real e2e verdict on this branch — not the advisory one. gate_policy: release-branches makes e2e advisory on *-dev, so fix: support proof-bearing validator joins #206's E2E Tests check sat permanently pending and it merged on the unit-test job alone. That gate is why nothing objected the first time; treat a pending e2e here as a blocker, not a pass.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 793bc99b-bab1-447a-9466-2111e540f516

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

CON-715 removed ValidatorWalletBlueprint.setOperator in favour of
initiateOperatorTransfer + completeOperatorTransfer. The SDK only spoke the
single-call surface, so rotating an operator against a consensus deployment
that carries the change fails: the selector no longer exists, so viem reverts
with no reason and the Python SDK reports ABIFunctionNotFound.

Adds both calls plus cancelOperatorTransfer and the getPendingOperator view,
and keeps setOperator for deployments that still expose it.

The proof binding is the subtle part. validatorJoin's possession proof is
verified by the ValidatorWalletFactory and is bound to it; rotation is verified
by the wallet, so the registrar is the wallet's own address
(PubKeyUtils.validateWithPossession(pubKey, address(this), owner(), proof)).
createOperatorRegistration already takes the registrar as a parameter, so no
change was needed there — but a join proof silently fails to verify for a
rotation, which is the easy mistake to make. getOperatorTransferContext builds
the wallet-bound context and reads owner() from the wallet rather than assuming
the caller is the owner, so a mismatch surfaces as a clear local error instead
of an onlyOwner revert. A test pins that a join-bound proof does not verify for
rotation while a wallet-bound one does.

Callers still need updating; this only adds the surface they need:
genlayer-e2e's driver interface, genlayer-cli's staking set-operator, and
genlayer-py's staking_set_operator all remain single-call.

Verified: npx tsc --noEmit, npm test -- --run (123/123, no type errors),
npm run build.
CON-715 widened both staking structs — Claim gained `offset`, Commit gained
`outstanding`/`priced`/`fragmented` and narrowed several members — while
keeping the same function names and arguments. Static tuples decode
positionally, so reading a post-CON-715 chain with the shape in STAKING_ABI
does not fail: `commit.input` picks up `claim.commit`. Pending deposits came
back as small indices instead of amounts, which is why the e2e delegator
scenarios asserted 0.1 GEN and got "0" or "2".

Both layouts are deployed — consensus v0.6-dev has the old one, #1290 the new —
so neither shape can simply replace the other. STAKING_COMMIT_VIEWS_CURRENT_ABI
adds the post-CON-715 shape for the four struct-returning views, and
readCommitView resolves which one the chain speaks, once, then caches it for
the client: getStakeInfo loops over every pending entry and must not re-probe
per entry.

The probe only works in one direction, and the tests pin that: decoding a
legacy response with the current shape throws (the response is short), while
decoding a current response with the legacy shape succeeds and lies. So the
current shape is always attempted first and a decode *failure* identifies a
legacy chain. If that asymmetry ever breaks, the probe would quietly start
reporting wrong balances again — hence the test asserting the exact misread
value rather than merely "not equal".

Also drops duplicate ValidatorWalletBlueprint entries: the ABI already carried
initiateOperatorTransfer/completeOperatorTransfer/cancelOperatorTransfer/
getPendingOperator, with initiateOperatorTransfer declared as taking an address
rather than the pubkey and possession proof it actually takes.

Verified: npx tsc --noEmit, npm test -- --run (126/126, no type errors),
npm run build.
@MuncleUscles
MuncleUscles marked this pull request as ready for review August 18, 2026 13:15
@MuncleUscles
MuncleUscles merged commit bae1eee into v2-dev Aug 18, 2026
10 of 13 checks passed
@MuncleUscles
MuncleUscles deleted the feat/proof-bearing-validator-joins branch August 18, 2026 14:13
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.

2 participants