Skip to content

refactor(xmss): restructure subspec into focused modules and clean APIs#783

Merged
tcoratger merged 9 commits into
leanEthereum:mainfrom
tcoratger:refactor/xmss-modular-structure
May 27, 2026
Merged

refactor(xmss): restructure subspec into focused modules and clean APIs#783
tcoratger merged 9 commits into
leanEthereum:mainfrom
tcoratger:refactor/xmss-modular-structure

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

Reorganizes the XMSS subspec around single-responsibility modules and tightens the public APIs, with no change to cryptographic behavior. Net: 35 files changed, +1693 / −2628.

Structure

  • Split the low-level primitives into focused files: field.py (base-P decomposition + secure sampling), poseidon.py (Poseidon1 engine + tweakable hash), prf.py (SHAKE128 PRF). hashing.py is removed.
  • tweak_hash and hash_chain are now methods on PoseidonXmss (every call already threaded poseidon as the first arg); the TreeTweak / ChainTweak NamedTuples move to types.py.
  • Merge message_hash.py + target_sum.pyencoding.py, subtree.pymerkle.py; delete the rand.py / utils.py grab-bags.
  • Resulting import graph is acyclic: field/prf at the bottom, poseidon above field, merkle/encoding/interface on top.

APIs

  • TypeOneMultiSignature.aggregate is now a @classmethod taking (validator_index, pubkey, signature) tuples — the optional participant bitfield and its runtime guards are gone (the index travels with each fresh signer, so the mismatch is unrepresentable).
  • The greedy set-cover selection moves out of the multisig type into forks/lstar/aggregation_select.py.
  • Replace three inline # noqa: N802 on uppercase config properties with one file-scoped ruff ignore.

Docs

  • Docstrings tightened to the project /doc rules: one sentence per line, no backticks, WHY-focused comments, and a worked example for the encoding pipeline.

Test plan

  • just check passes (ruff lint + format, ty typecheck, codespell, mdformat)
  • uv run pytest tests/lean_spec/subspecs/xmss/ — 104 passed
  • CI green on the full suite

🤖 Generated with Claude Code

tcoratger and others added 9 commits May 27, 2026 14:45
Reorganize the XMSS subspec around single-responsibility files and tighten
the public APIs while preserving all cryptographic behavior.

Structure:
- Split low-level primitives into field.py (base-P decomposition + secure
  sampling), poseidon.py (Poseidon1 engine + tweakable hash), and prf.py
  (SHAKE128 PRF); hashing.py is removed.
- tweak_hash and hash_chain become methods on PoseidonXmss; the tweak
  NamedTuples move to types.py.
- Merge message_hash.py + target_sum.py into encoding.py, subtree.py into
  merkle.py, and delete the rand.py/utils.py grab-bags.

APIs:
- TypeOneMultiSignature.aggregate becomes a classmethod taking
  (validator_index, pubkey, signature) tuples, dropping the optional
  participant bitfield and its runtime guards.
- Move the greedy set-cover selection out of the multisig type into
  forks/lstar/aggregation_select.py.
- Replace inline noqa for uppercase config properties with a file-scoped
  ruff ignore.

Docs:
- Tighten docstrings to the project /doc rules: one sentence per line, no
  backticks, WHY-focused comments, worked examples for the encoding.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Add a LEAVES_PER_BOTTOM_TREE property to XmssConfig and route the four
recomputations of 2^(LOG_LIFETIME / 2) through it. Sign now reuses
get_prepared_interval for its prepared-window bound check, removing the
duplicated window arithmetic, and drops a redundant int() cast on slot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rework the docstrings and inline comments of the interface module to follow
the project documentation rules: one sentence per line, no backticks, and
WHY-focused comments rather than restatements of the code.

- _expand_activation_time: conceptual overview of the bottom-tree model plus
  phase-labeled comments explaining the round-down/round-up bit tricks.
- key_gen: lead with the memory-bound rationale; fix the phase numbering so
  the inline labels match execution order.
- sign: explain each phase from the scheme's perspective (synchronized
  one-time keys, the target-sum layer giving incomparability, Winternitz
  chain release, and the Merkle opening).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Give the subtree builder an optional highest_layer bound so a bottom tree
stops at depth/2 instead of building the full tree to the global root and
discarding the upper half. The single-node root is then taken from the top
built layer, keeping the absolute-index selection that handles odd bottom
tree indices. The constructed layers are byte-identical to before.

Also simplify the authentication-path index math to plain int arithmetic,
matching the verifier, and drop the redundant Uint64 wraps.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A parallel worktree under .claude/worktrees was accidentally staged and
committed as a gitlink. Remove it from tracking and ignore the directory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rewrite the merkle documentation to the project doc standard: a module
header explaining the top-bottom split and sliding-window memory bound,
overview-only docstrings backed by phase-labeled bodies, and concrete
layout and climb traces.

Move the signer's internal tree representation next to its only user:
the layer container, its list, and the padding logic now live in the
merkle module as a named constructor, while the signature-facing opening
type stays among the base types. Inline the single-use layer cap.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the master key type next to the pseudorandom function and expose its
derivations as methods: a fresh-key constructor, a chain-start derivation,
and a signing-randomness derivation. Update all call sites accordingly.

Tighten the base types: type the PRF domain separator as a fixed-length
byte string, inline the single-use digest-length alias, and express the
layer node cap in terms of the leaves-per-bottom-tree property. Clarify
the docs across the PRF and base-type modules per the project doc rules.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Reorganize the XMSS unit tests so each source module has one matching
test file, redistributing the orphan files into the module they exercise.

Drive every module to full line and branch coverage apart from three
intentionally unreachable arcs, deduplicate overlapping cases, split
bundled assertions into single-behavior tests, parametrize scenario
families across sizes and edges, and match the complete text of every
error message the spec itself raises.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new xmss test module mirroring the field source shares a basename with
the koalabear field test. Without a package marker, pytest's prepend import
mode maps both to the same module name and aborts collection. Adding an
empty package init namespaces the xmss test modules and resolves the clash,
matching how other test directories in the tree are already packaged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 32fbfe3 into leanEthereum:main May 27, 2026
13 checks passed
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