Skip to content

fix: support proof-bearing validator joins - #206

Merged
MuncleUscles merged 2 commits into
v2-devfrom
fix/vesting-operator-proof
Aug 9, 2026
Merged

fix: support proof-bearing validator joins#206
MuncleUscles merged 2 commits into
v2-devfrom
fix/vesting-operator-proof

Conversation

@MuncleUscles

@MuncleUscles MuncleUscles commented Aug 9, 2026

Copy link
Copy Markdown
Member

Summary

  • replace both retired validator-join ABI surfaces with proof-bearing calls:
    • validatorJoin(uint256[2],bytes)
    • vestingValidatorJoin(uint256[2],bytes,uint256)
  • export a private-key-local operator registration helper matching the consensus EIP-191 domain and Solidity ABI encoding
  • resolve the live chain ID and ValidatorWalletFactory registrar, bind each proof to the joining owner, and verify it before submission
  • accept only proof packages in the SDK actions; raw operator keys are never retained

Root cause

Consensus C4 requires a secp256k1 public key and a chain/registrar/owner-bound proof of possession whenever a validator wallet is created. The SDK still encoded the removed address-only and zero-argument join surfaces, which caused Explorer vesting joins to revert and left JS/CLI/Python tooling unable to join validators.

Validation

  • npm test -- --run — 122/122, no type errors
  • npm run build
  • targeted ESLint on the changed source/tests
  • git diff --check
  • deterministic proof vector matches the exact consensus helper; wrong-key/domain and invalid-registration coverage included

Companion consumers

Depends-On: https://github.com/genlayerlabs/genlayer-consensus/pull/1285

@coderabbitai

coderabbitai Bot commented Aug 9, 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: 26229a0a-688b-473b-90bc-b2e27030d436

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.

@MuncleUscles
MuncleUscles force-pushed the fix/vesting-operator-proof branch from cdcbdf7 to ca7a788 Compare August 9, 2026 09:20
@MuncleUscles MuncleUscles changed the title fix: support proof-bearing vesting validator joins fix: support proof-bearing validator joins Aug 9, 2026
@MuncleUscles
MuncleUscles marked this pull request as ready for review August 9, 2026 15:11
@MuncleUscles
MuncleUscles merged commit 6f12738 into v2-dev Aug 9, 2026
12 of 14 checks passed
MuncleUscles added a commit that referenced this pull request Aug 11, 2026
MuncleUscles added a commit that referenced this pull request Aug 18, 2026
…mpanions) (#208)

* Reapply "fix: support proof-bearing validator joins (#206)" (#207)

This reverts commit 7666f0b.

* feat(staking): support the two-step operator rotation

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.

* fix(staking): resolve the Claim/Commit layout from the chain

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.

---------

Co-authored-by: kirilaa <kirilantevski@gmail.com>
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