Skip to content

refactor(koalabear): make Fp an int subclass and tighten field surface#776

Merged
tcoratger merged 3 commits into
leanEthereum:mainfrom
tcoratger:refactor/koalabear-fp-is-a
May 25, 2026
Merged

refactor(koalabear): make Fp an int subclass and tighten field surface#776
tcoratger merged 3 commits into
leanEthereum:mainfrom
tcoratger:refactor/koalabear-fp-is-a

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

  • Switch Fp from a HAS-A wrapper (self.value: int) to an IS-A scalar (class Fp(int, SSZType)) matching the BaseUint primitive pattern. Drops the .value indirection at every call site.
  • Replace explicit Fermat exponentiation in inverse() with the stdlib pow(a, -1, P) form (modular inverse via extended Euclidean).
  • Move from bare ValueError / TypeError to the project's SSZ exception hierarchy (SSZTypeError, SSZSerializationError, SSZValueError).
  • Strict typing on arithmetic with reverse-operator guards so int + Fp raises instead of silently returning a plain int.
  • Loose equality (Fp(5) == 5 returns False, doesn't raise) so collection membership checks still work; __hash__ mixes in the type to keep Fp(5) and 5 distinct in sets.
  • Add a Pydantic __get_pydantic_core_schema__ hook: instances pass through, raw ints are strict-validated against [0, P), JSON round-trips drop the subtype.

Migration

  • xmss/message_hash.py, poseidon1/permutation.py, and the consensus_testing poseidon fixture drop their fp.value reads in favor of int(fp).
  • types/collections.py collapses the now-dead IntFieldElement protocol into a plain isinstance(item, int) check that also covers BaseUint.

Test coverage

  • tests/lean_spec/subspecs/koalabear/test_field.py: 11 -> 55 tests, branch coverage 84% -> 100%.
  • New parametrized groups cover __new__ rejection of non-ints, modular reduction at boundary inputs, reverse-operator rejection, __pow__ edges (zero base, modular exponent), inverse multiplicative identity, full equality matrix, hash-mixing, Pydantic schema lift/reject/round-trip, and the P - 1 boundary in serialize/deserialize.

Test plan

  • uv run pytest — full suite (2956 tests pass)
  • uv run pytest tests/lean_spec/subspecs/koalabear/ --cov=src/lean_spec/subspecs/koalabear/field --cov-report=term-missing — 100% coverage
  • `just check` — ruff lint, ruff format, ty typecheck, codespell, mdformat all pass

🤖 Generated with Claude Code

tcoratger and others added 3 commits May 26, 2026 00:27
Switches Fp from a HAS-A wrapper around `self.value: int` to an IS-A
scalar inheriting from `int`, matching the BaseUint primitive pattern.
Modular inverse now uses the stdlib `pow(a, -1, P)` form instead of
explicit Fermat exponentiation. Errors route through the project SSZ
exception hierarchy. Strict typing on arithmetic with reverse-operator
guards prevents silent int fallback. A Pydantic core-schema hook lets
Fp sit directly inside Pydantic SSZ containers.

Call sites in xmss and poseidon1 drop their `.value` reads in favor of
`int(fp)`, and the JSON-element helper in types/collections collapses
the `IntFieldElement` protocol into a plain `isinstance(item, int)`
check that also covers BaseUint.

Test coverage of field.py reaches 100% (11 -> 55 tests).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces substring matches with anchored regex assertions so every
pytest.raises captures the exact error string the implementation
produces, not just a prefix or middle slice.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the intermediate `expected` local so every error-message assertion
uses the same inline `match=` shape across the file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit b948f9a into leanEthereum:main May 25, 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