build(genesis)!: generate keys for leanVM's internalized XMSS - #182
Open
MegaRedHand wants to merge 2 commits into
Open
build(genesis)!: generate keys for leanVM's internalized XMSS#182MegaRedHand wants to merge 2 commits into
MegaRedHand wants to merge 2 commits into
Conversation
leanVM absorbed XMSS into its own crate and the standalone leanSig crate was retired, so the key format clients read changed: public keys are SSZ and 32 bytes rather than 52, and secret keys are postcard-encoded. No published hash-sig-cli tag produced that format, so the keygen image moves to ghcr.io/lambdaclass/hash-sig-cli:0.5.0 (multi-arch, built from hash-sig-cli PR blockblaz#39). Switch back to a blockblaz tag once upstream releases one. The ethlambda client image moves in step, to devnet5-leanvm-main: a client built against leanSig cannot read these keys, so the two pins have to agree. Key file names and the manifest schema did not change across the migration, so a leftover hash-sig-keys/ directory still satisfies the "keys already exist" check and its old-format pubkeys would be baked into config.yaml unnoticed. Report the scheme and public key size found in the manifest, warn when a key set predates 0.5.0, and fail when a manifest's declared pubkey_bytes disagrees with the pubkeys it holds. Also drops the hardcoded scheme label from the keygen log, which named a scheme no released image had produced for some time. The scheme now comes from the manifest, which derives it from the constants the image was built against. Verified by running generate-genesis.sh end to end against the new image: keys generated, manifest verified, config.yaml carries 32-byte attestation and proposal pubkeys, and genesis.ssz built. A 52-byte key directory warns and names the regeneration command. BREAKING CHANGE: existing hash-sig key sets cannot be migrated and must be regenerated with --forceKeyGen; nodes must run a leanVM-XMSS client build.
Contributor
There was a problem hiding this comment.
Pull request overview
This PR updates genesis hash-signature key generation and client/automation pins to match leanVM’s internalized XMSS key format (post-leanSig), including stronger manifest validation to prevent silently reusing incompatible legacy key directories.
Changes:
- Pin hash-sig keygen to
ghcr.io/lambdaclass/hash-sig-cli:0.5.0and update docs to reflect 32-byte SSZ public keys. - Add manifest cross-checks in
generate-genesis.shto reportkey_scheme, validatepubkey_bytes, and warn/fail on legacy or inconsistent key sets. - Bump ethlambda client image pin (and ansible defaults) to
devnet5-leanvm-mainto stay compatible with the new key format.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| README.md | Updates user-facing docs for the new keygen image and manifest fields (key_scheme, pubkey_bytes). |
| generate-genesis.sh | Pins the new keygen image and adds manifest-based validation/reporting to detect legacy or inconsistent key directories. |
| client-cmds/ethlambda-cmd.sh | Updates the default docker image tag for ethlambda to the leanVM-XMSS-compatible build. |
| ansible/roles/ethlambda/tasks/main.yml | Updates fallback default ethlambda docker image to match the new client tag. |
| ansible/roles/ethlambda/defaults/main.yml | Updates role default ethlambda docker image tag for consistency with tasks/client script. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+362
to
+366
| MANIFEST_KEY_SCHEME=$(yq eval '.key_scheme // ""' "$MANIFEST_FILE" 2>/dev/null) | ||
| MANIFEST_PUBKEY_BYTES=$(yq eval '.pubkey_bytes // ""' "$MANIFEST_FILE" 2>/dev/null) | ||
| ATTEST_PUB_BYTES=$(( (${#ATTEST_PUB} - 2) / 2 )) | ||
| echo " Key scheme: ${MANIFEST_KEY_SCHEME:-<unreported>}" | ||
| echo " Public key size: ${ATTEST_PUB_BYTES} bytes" |
| echo " ⚠️ Manifest reports no 'pubkey_bytes' - these keys predate hash-sig-cli 0.5.0" | ||
| echo " and are in the retired leanSig format. Clients built against leanVM's XMSS" | ||
| echo " cannot read them; the genesis below will not start such a devnet." | ||
| echo " Regenerate: ./generate-genesis.sh $GENESIS_DIR --forceKeyGen" |
|
|
||
| 1. **post-quantum secure validator keypairs** in `genesis/hash-sig-keys` unless already generated or forced with `--forceKeyGen` | ||
| 2. **config.yaml** - Updated genesis time, `ATTESTATION_COMMITTEE_COUNT`, and `GENESIS_VALIDATORS` with **attestation** and **proposal** public keys per validator (dual-key layout / `hash-sig-cli:devnet4`) | ||
| 2. **config.yaml** - Updated genesis time, `ATTESTATION_COMMITTEE_COUNT`, and `GENESIS_VALIDATORS` with **attestation** and **proposal** public keys per validator (dual-key layout, 32-byte SSZ pubkeys / `hash-sig-cli:0.5.0`) |
Review follow-ups on the checks added in the previous commit. Requiring whole hex bytes in the pubkey pattern makes the byte count exact instead of rounding an odd-length string down, and it rejects a truncated pubkey where it is already reported rather than several lines later. A non-numeric pubkey_bytes made `[ -ne ]` fail as a *condition*, so bash took the else path and skipped the check entirely instead of reporting anything. Validate it before comparing. Quote the genesis directory in the printed regeneration command so the hint stays copy/paste-safe for paths containing spaces, and use the full image reference in the file list so it matches the pin.
Contributor
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
generate-genesis.sh:383
- In dual-key mode the new
pubkey_bytesconsistency check only derives the byte length from the attester pubkey. If the manifest is corrupted such that attester/proposer pubkeys differ in length, this won’t be detected (and the printed “Public key size” may be misleading). Consider computing and validating both lengths, and fail if they differ or if either mismatchespubkey_bytes.
# Cross-check the pubkey length the manifest declares against the pubkeys it holds.
# Key file names did not change when the format moved to leanVM's XMSS, so a key
# directory left over from an older image passes the "keys already exist" check
# above and would otherwise be baked into config.yaml unnoticed.
MANIFEST_KEY_SCHEME=$(yq eval '.key_scheme // ""' "$MANIFEST_FILE" 2>/dev/null)
MANIFEST_PUBKEY_BYTES=$(yq eval '.pubkey_bytes // ""' "$MANIFEST_FILE" 2>/dev/null)
ATTEST_PUB_BYTES=$(( (${#ATTEST_PUB} - 2) / 2 ))
echo " Key scheme: ${MANIFEST_KEY_SCHEME:-<unreported>}"
echo " Public key size: ${ATTEST_PUB_BYTES} bytes"
if [ -z "$MANIFEST_PUBKEY_BYTES" ]; then
echo " ⚠️ Manifest reports no 'pubkey_bytes' - these keys predate hash-sig-cli 0.5.0"
echo " and are in the retired leanSig format. Clients built against leanVM's XMSS"
echo " cannot read them; the genesis below will not start such a devnet."
echo " Regenerate: ./generate-genesis.sh \"$GENESIS_DIR\" --forceKeyGen"
elif [[ ! "$MANIFEST_PUBKEY_BYTES" =~ ^[0-9]+$ ]]; then
# Guard the arithmetic comparison below: a non-numeric value makes `[ -ne ]` fail
# as a condition, which would skip the check instead of reporting anything.
echo " ❌ Error: manifest 'pubkey_bytes' is not a number: $MANIFEST_PUBKEY_BYTES"
echo " The manifest is inconsistent - regenerate it rather than editing it by hand"
exit 1
elif [ "$ATTEST_PUB_BYTES" -ne "$MANIFEST_PUBKEY_BYTES" ]; then
echo " ❌ Error: manifest declares pubkey_bytes=$MANIFEST_PUBKEY_BYTES but its pubkeys are $ATTEST_PUB_BYTES bytes"
echo " The manifest is inconsistent - regenerate it rather than editing it by hand"
exit 1
fi
README.md:738
- The README now states (correctly) that
key_schemeis derived from the pinned image/leanVM XMSS constants, but the earlier “Signature Scheme” section still says the system uses a fixedSIGTopLevelTargetSumLifetime32Dim64Base8scheme. With the updated generator/image, that fixed-scheme statement is now misleading; consider updating that section to reflect that consumers should trustvalidator-keys-manifest.yaml:key_scheme(and potentiallypubkey_bytes) rather than a hardcoded scheme name.
`key_scheme` and `pubkey_bytes` are derived from the constants of the XMSS revision the image was
built against, so they report the actual format on disk instead of a fixed label. `generate-genesis.sh`
reads `pubkey_bytes` back to detect a key directory left over from an older image.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Moves genesis key generation to the XMSS implementation leanVM internalized when the standalone leanSig crate was retired, and moves the ethlambda client image in step.
Public keys are now SSZ and 32 bytes (they were 52 under leanSig) and secret keys are postcard-encoded. File names and the manifest schema are unchanged, so
parse-vc.sh, the ansible roles and the client--hash-sig-keys-dirflags need no adjustment.Changes
blockblaz/hash-sig-cli:latestghcr.io/lambdaclass/hash-sig-cli:0.5.0ghcr.io/lambdaclass/ethlambda:devnet4ghcr.io/lambdaclass/ethlambda:devnet5-leanvm-mainkey_schemeand pubkey size, warns on a pre-0.5.0key set, fails on apubkey_bytesmismatchSIGTopLevelTargetSumLifetime32Dim64Base8The keygen image is a lambdaclass GHCR build because no published
blockblaz/hash-sig-clitag generates this format: upstream'sauto-release.ymlfires only on a merged release PR, and hash-sig-cli#39 is still open. It is multi-arch (amd64 + arm64) and built from that branch. Once upstream tags a release this is a one-line switch back to Docker Hub.The two image pins have to move together: a leanSig-era client cannot read these keys, and vice versa.
Why the extra manifest checks
Key file names did not change across the migration, so a
hash-sig-keys/directory left from an older image still satisfies the existing "keys already exist" check and its 52-byte pubkeys would be written intoconfig.yamlwith nothing flagging it. The generator now prints what it found and says how to regenerate:The
pubkey_bytesmismatch case is a hard error: it means the manifest was edited or merged incorrectly, which has silently corrupted a key set before (a YAML 1.1 loader coerces unquoted0xpubkeys to integers).Verification
generate-genesis.shrun end to end from an empty genesis dir with--forceKeyGenagainst the new image: keys generated, manifest verified asXmssTargetSumLifetime32Dim42Base8/ 32 bytes, andconfig.yaml,validators.yaml,nodes.yaml,genesis.json,genesis.ssz,annotated_validators.yamlplus node keys all produced. All four pubkeys inconfig.yamlare 32 bytes.bash -nclean on the touched shell scripts; the ansible files parse.Existing hash-sig key sets cannot be migrated. Regenerate with
--forceKeyGen, and run a leanVM-XMSS client build (devnet5-leanvm-mainfor ethlambda). Other clients need their own leanVM-XMSS images before they can join such a devnet.