Skip to content

fix: write only defined bytes into v1 tree and queue accounts - #2392

Merged
ananas-block merged 1 commit into
mainfrom
fix/program-libs-defined-bytes
Sep 24, 2026
Merged

ananas-block merged 1 commit into
mainfrom
fix/program-libs-defined-bytes

Conversation

@ananas-block

@ananas-block ananas-block commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The v1 concurrent Merkle tree changelog and the v1 hash set queues copied undefined bytes into account data. Those bytes came from the stack and can hold VM pointers, so account state diverges when the virtual_address_space_adjustments feature changes the address layout.

This PR contains only the program-libs part of #2389, squashed into one commit and rebased on main.

Changes

  • Concurrent Merkle tree changelog: all pushes go through push_changelog_entry, which zeroes the slot and then writes only the index and the Some nodes. Before, CyclicBoundedVec::push copied the undefined value bytes of None nodes and the repr(C) padding before index.
  • Hash set buckets: buckets are written through a repr(C) RawHashSetCell that defines all 48 bytes. Before, writing None or Some(HashSetCell { sequence_number: None, .. }) left the unused enum payload bytes undefined.
  • Hash set zero-copy: buffers that are missing the reserved 8-byte gap before the buckets are now rejected instead of being read past the end.
  • Layout pins: compile-time assertions for the changelog node tags, entry sizes and index offsets (heights 22/26/32/40), the hash set bucket layout and niche tags, and the v1 indexed changelog entry.

Types, account sizes and on-chain layout are unchanged. HashSet::size_in_account returns the same value as before, since the old formula always added 8 bytes.

CI fix compared to #2389

#2389 failed in just programs build: the platform-tools rustc reports MARKED_BUCKET_TAG and bucket_tag as dead code under -D warnings, because older rustc versions don't count uses inside const _: () = { .. } blocks. Both now live inside the assertion block. Reproduced and verified with rustc 1.84.1.

Test plan

  • cargo test -p light-concurrent-merkle-tree -p light-hash-set -p light-indexed-merkle-tree (Rust 1.91, RUSTFLAGS="-D warnings")
  • cargo clippy on the three crates with --all-targets -- -D warnings
  • cargo +1.84.1 rustc --lib -- -D warnings on the three crates (reproduces the fix: write only defined bytes into concurrent Merkle tree changelog #2389 failure before the fix)
  • RUSTFLAGS="-D warnings" cargo build-sbf --features 'test, migrate-state' in programs/account-compression
  • With the source fixes reverted, the new defined-bytes tests fail in both debug and release builds

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Merkle tree changelogs and hash-set buckets now write deterministic, fully initialized account bytes, including padding and unused fields.
    • Hash-set initialization now rejects undersized account buffers instead of accepting incomplete storage.
  • Compatibility
    • Pinned stored layouts for Merkle tree changelogs and hash-set buckets to preserve their existing account byte formats.

The v1 concurrent Merkle tree changelog and the v1 hash set queues copied
undefined bytes into account data, so account bytes depended on runtime
stack contents. Those bytes can hold VM pointers, which makes account
state diverge when the virtual_address_space_adjustments feature changes
the address layout.

Concurrent Merkle tree changelog:
- CyclicBoundedVec::push copies a ChangelogEntry with ptr::write,
  including the undefined value bytes of None nodes and the repr(C)
  padding between path and index. Route all pushes through
  push_changelog_entry, which zeroes the slot and then writes only the
  index and the Some nodes.

Hash set buckets:
- Writing None or Some(HashSetCell { sequence_number: None, .. }) left the
  unused enum payload bytes undefined. Write buckets through a repr(C)
  RawHashSetCell that defines all 48 bytes.
- Reject zero-copy buffers that are missing the reserved 8-byte gap
  before the buckets instead of reading past the end.

Pin the deployed layouts with compile-time assertions: changelog node tag
values, entry sizes and index offsets for heights 22/26/32/40, the hash
set bucket layout and niche tag values, and the v1 indexed changelog
entry. Items used only by these assertions live inside the const blocks,
because older rustc versions (platform-tools) report them as dead code.

Types, account sizes and on-chain layout are unchanged.

Add tests that prefill account buffers with marker bytes and check that
every written byte is defined.
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: Lightprotocol/light-protocol/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: e76090dd-7391-452d-bae1-35dbea450c13

📥 Commits

Reviewing files that changed from the base of the PR and between 790d01a and db2839f.

📒 Files selected for processing (8)
  • program-libs/concurrent-merkle-tree/src/changelog.rs
  • program-libs/concurrent-merkle-tree/src/lib.rs
  • program-libs/concurrent-merkle-tree/tests/tests.rs
  • program-libs/hash-set/src/lib.rs
  • program-libs/hash-set/src/zero_copy.rs
  • program-libs/hash-set/tests/defined_bytes.rs
  • program-libs/indexed-merkle-tree/src/changelog.rs
  • program-libs/indexed-merkle-tree/tests/defined_bytes.rs

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The concurrent Merkle tree, hash set, and indexed Merkle tree add account-layout assertions and deterministic byte-write checks. Tests inspect serialized changelog entries and hash-set buckets, including buffer-size validation.

Changes

Defined account bytes

Layer / File(s) Summary
Concurrent changelog writes
program-libs/concurrent-merkle-tree/src/changelog.rs, program-libs/concurrent-merkle-tree/src/lib.rs, program-libs/concurrent-merkle-tree/tests/tests.rs
Layout assertions pin changelog entry sizes and offsets. Changelog writes clear each slot and copy the index and defined path nodes. Tests check path tags, absent-node bytes, and padding.
Hash-set bucket layout and writes
program-libs/hash-set/src/lib.rs, program-libs/hash-set/src/zero_copy.rs, program-libs/hash-set/tests/defined_bytes.rs
Layout assertions pin bucket tags and field offsets. Bucket writes use raw storage helpers, and bucket offsets and account-size checks use shared calculations. Tests check bucket bytes across state changes and reject undersized buffers.
Indexed changelog layout and writes
program-libs/indexed-merkle-tree/src/changelog.rs, program-libs/indexed-merkle-tree/tests/defined_bytes.rs
Layout assertions pin indexed changelog entry size, alignment, and field offsets. Tests compare written entry bytes with expected serialization across different buffer prefills.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to db283

This change makes v1 tree and queue account bytes deterministic without changing account sizes or on-chain layout. The new layout assertions and tests check the written bytes, and no remaining issue blocks merging.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 60.71% which is insufficient. The required threshold is 70.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 28 functions across 8 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: preventing undefined bytes in v1 tree and queue account data. It is concise and specific.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Warning

Some tools did not complete. Review the errors below.

🔧 Clippy (1.98.1)

Clippy execution failed


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.

@ananas-block
ananas-block merged commit a67a427 into main Sep 24, 2026
34 checks passed
@ananas-block
ananas-block deleted the fix/program-libs-defined-bytes branch September 24, 2026 11:14
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.

2 participants