Skip to content

feat: ssz, eth2api add bitfield accessors, synthetic graffiti#469

Open
mskrzypkows wants to merge 3 commits into
mainfrom
bitfield_ops
Open

feat: ssz, eth2api add bitfield accessors, synthetic graffiti#469
mskrzypkows wants to merge 3 commits into
mainfrom
bitfield_ops

Conversation

@mskrzypkows
Copy link
Copy Markdown
Contributor

is_synthetic

  • Add bit_at, set_bit_at, bit_indices, contains, and or combinators to BitList and BitVector, along with a BitfieldError type for length mismatches; export BitfieldError from the ssz crate
  • Replace the synthetic_graffiti() runtime function with a compile-time SYNTHETIC_GRAFFITI constant using a const block
  • Add SignedProposalBlock::graffiti() accessor and VersionedSignedProposal::is_synthetic() using the new constant

is_synthetic

- Add bit_at, set_bit_at, bit_indices, contains, and or combinators to
  BitList and BitVector, along with a BitfieldError type for length
  mismatches; export BitfieldError from the ssz crate
- Replace the synthetic_graffiti() runtime function with a compile-time
  SYNTHETIC_GRAFFITI constant using a const block
- Add SignedProposalBlock::graffiti() accessor and
  VersionedSignedProposal::is_synthetic() using the new constant
@mskrzypkows
Copy link
Copy Markdown
Contributor Author

@claude review

@claude
Copy link
Copy Markdown

claude Bot commented Jun 3, 2026

Claude finished @mskrzypkows's task in 3m 27s —— View job


Review: PR #469 — bitfield accessors, synthetic graffiti const, is_synthetic

  • Read the diff (ssz types, lib, versioned)
  • Cross-check against Charon Go source
  • Review correctness, style, CI
  • Post review feedback

Overall this is a clean, well-tested change. I verified the bitfield semantics against the Go (go-bitfield) behavior used in charon/core/tracker/inclusion.go, and the synthetic-graffiti logic against charon/app/eth2wrap/synthproposer.go. One blocking CI item and a few minor notes below.


🔴 Blocking — CI Linter check (rustfmt) is failing

The only failure relevant to this PR is cargo +nightly fmt --all -- --check. The doc comment above SYNTHETIC_GRAFFITI exceeds the line width and rustfmt wants it wrapped (crates/eth2api/src/versioned.rs:12):

/// 32-byte graffiti used to mark synthetic blocks, left-aligned with zero
/// padding.
pub const SYNTHETIC_GRAFFITI: phase0::Root = {

(The other PR Naming Convention failure is a duplicate/transient — two sibling runs passed. Build and test is still in progress.)

Fix this →


✅ Correctness (verified against Charon)

  • BitList::containsother & self == other per byte matches go-bitfield's Bitlist.Contains (b[i]^(b[i]|c[i]) != 0), and the DifferentLength guard matches the Go length check. ✔
  • BitList::or — byte-wise OR with length guard matches Bitlist.Or. ✔
  • bit_indices / bit_at / set_bit_at — out-of-range reads as false, out-of-range set is a no-op; indices ascending. Matches BitIndices usage in inclusion.go / validatorapi.go. ✔
  • SYNTHETIC_GRAFFITI constphase0::Root is [u8; 32]; the marker is 29 bytes so the while copy can't overflow (and would fail at compile time if it ever did). Equivalent to Go's GetSyntheticGraffiti() (copy(buf[:], syntheticBlockGraffiti)). ✔
  • graffiti() / is_synthetic() — covers all 12 enum variants (incl. the Deneb/Electra/Fulu signed_block nesting) and correctly unifies Go's separate IsSyntheticProposal + IsSyntheticBlindedBlock, since SignedProposalBlock already carries both blinded and full variants. ✔

The trailing-bit invariant that contains/or rely on (bits past len are zero) holds for every constructor (from_ssz_bytes masks the remainder, with_bits only sets in-range bits, set_bit_at guards i >= len), so the byte-level ops are safe.

🟡 Minor / non-blocking

  1. PR description vs. diff. The description says it replaces a synthetic_graffiti() runtime function, but the diff is purely additive (+221/-1) — no such function existed or was removed. Worth correcting the description to avoid confusion for reviewers/changelog.

  2. VersionedSignedBlindedProposal has no is_synthetic(). Go exposes IsSyntheticBlindedBlock on the blinded wrapper too. Here it's only on VersionedSignedProposal. That's fine if all synthetic checks go through VersionedSignedProposal (which can hold blinded variants), but if the blinded wrapper is ever checked directly, it'll need its own accessor. Flagging for awareness — not required for this PR.

  3. BitVector is intentionally asymmetric with BitList (no contains/or). This matches Go usage (only BitIndices is used on committee bit-vectors), so leaving them off is reasonable — just calling it out.

  4. Test coverage is goodbit_at/set_bit_at/bit_indices, the SSZ round-trip alignment test, both contains/or length-mismatch paths, and the graffiti layout/zero-padding test. Nice.
    · branch bitfield_ops

@mskrzypkows mskrzypkows changed the title feat(ssz,eth2api): add bitfield accessors, synthetic graffiti const, and feat: ssz, eth2api add bitfield accessors, synthetic graffiti const, and Jun 3, 2026
@mskrzypkows mskrzypkows changed the title feat: ssz, eth2api add bitfield accessors, synthetic graffiti const, and feat: ssz, eth2api add bitfield accessors, synthetic graffiti Jun 3, 2026
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