Skip to content

feat: Solidity-compatible packed storage lowering - #2249

Open
Th0rgal wants to merge 1 commit into
mainfrom
feat/packed-storage-lowering-2060
Open

feat: Solidity-compatible packed storage lowering#2249
Th0rgal wants to merge 1 commit into
mainfrom
feat/packed-storage-lowering-2060

Conversation

@Th0rgal

@Th0rgal Th0rgal commented Aug 9, 2026

Copy link
Copy Markdown
Member

Summary

  • expose packed narrow scalar storage declarations in the DSL and compute Solidity-compatible slot/bit offsets automatically
  • lower Uint16, Uint32, and Uint128 packed writes plus fixed Uint128 array reads/writes with emitted index-access helpers
  • extend layout/ABI handling, proof bridges, smoke coverage, and generated property/axiom artifacts

Validation

  • make checks (641 tests): pass
  • make test-evmyullean-fork: pass
  • lake build Contracts.Smoke.Storage: pass
  • lake build Compiler.Proofs.IRGeneration.GenericInduction.Storage: pass
  • lake build Compiler.Proofs.IRGeneration.Contract: pass
  • lake build PrintAxioms Compiler Contracts: all Lean modules compile, but the aggregate exits on the existing Lake Contracts: some modules have bad imports job-computation issue from the current library glob surface

No sorry, admit, source axiom, or new unsafe declarations.

Closes #2060


Note

Medium Risk
Changes contract storage layout and emitted sload/sstore bit-packing for new field types; mistakes could corrupt packed words or break upgrade compatibility, though validation, layout reports, and proofs are extended accordingly.

Overview
Adds Solidity-style packed storage to the contract DSL: narrow scalars (Uint16, Uint32, Uint128, etc.) and FixedArray Uint128 N are accepted in storage declarations instead of being rejected or forced to full-word encodings.

applyAutomaticPackedLayout runs after parsing and assigns LSB-first bit offsets for consecutive narrow fields that share the same explicit slot anchor, including spill to the next word when a slot fills (smoke-tested in PackedStorageSpillSmoke).

The compiler gains FieldType.fixedArrayUint128, maps it to uint128[N] in ABI/storage JSON, and lowers indexed reads/writes for fixed uint128 arrays as two elements per storage word (read-modify-write on write). A new Yul helper storage_array_index_access_uint128 is emitted when storage array element access is used, alongside the existing dynamic-array helper.

Layout validation and compatibility reports treat fixed arrays as occupying (size+1)/2 packed words. Proof bridges, reserved helper prefixes, smoke contracts, Foundry property stubs, and PrintAxioms entries are updated to cover the new paths.

Reviewed by Cursor Bugbot for commit 3d7a295. Bugbot is set up for automated code reviews on this repo. Configure here.

@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
verity Ready Ready Preview Aug 9, 2026 3:16am

Request Review

@cursor

cursor Bot commented Aug 9, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: serverGenReqId_630bcaad-5b54-41e7-9981-5f90bbf0d715)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3d7a295453

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

| .scalar .bytes => throwError "storage field cannot be Bytes; use Uint256 encoding"
| .scalar (.array _) => throwError "storage field cannot be Array; use mapping encodings"
| .scalar (.fixedArray _ _) => throwError "storage field cannot be FixedArray; use mapping encodings"
| .scalar (.fixedArray (.uintN 128) _) => `(List Uint128)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Give fixed arrays fixed-length source semantics

Mapping FixedArray Uint128 N to List Uint128 routes its executable contract body through the dynamic-array helpers in Verity/Core.lean, which use the current list length and default to an empty list. Consequently, a valid access such as index 0 of a newly deployed nonempty fixed array reverts in the Lean/source model, while the generated Yul bounds-checks against N and returns the zero-initialized packed element. This can make proofs and executable specifications disagree with the deployed contract unless every test manually seeds an N-element list.

Useful? React with 👍 / 👎.

Comment on lines +48 to +49
let sameAnchor := anchor? == some field.slotNum
let nextOffset := if sameAnchor then offset else 0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep persistent and transient packing layouts separate

When adjacent narrow persistent and transient fields use the same numeric slot, sameAnchor treats them as sharing one packing sequence because it compares only slotNum. Since sload/sstore and tload/tstore are separate storage spaces, each field should start at offset 0 in its own layout; currently the second declaration is shifted after the first, producing an incorrect external layout without triggering a slot conflict.

Useful? React with 👍 / 👎.

Comment on lines +175 to +176
let packedSlot := YulExpr.call "add" [YulExpr.lit slot,
YulExpr.call "div" [YulExpr.ident "__array_index", YulExpr.lit 2]]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Mirror fixed-array element writes to alias slots

For a fixedArrayUint128 field with compatibility aliasSlots, this address calculation and the subsequent store use only the canonical slot. The Field.aliasSlots contract says writes are mirrored, and layout validation now reserves the full fixed-array footprint at every alias, but setStorageArrayElement leaves all alias copies stale. Iterate the packed read-modify-write over the canonical base and each alias base, as scalar and mapping compatibility writes already do.

Useful? React with 👍 / 👎.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OpenCodeReview first-pass review

🟡 Scout triage only — not a full review. no findings; a human or Codex must still cover the unflagged hunks and proof obligations.

Lean packet budget exceeded: 26 Lean file(s), 371 changed supported line(s).

Warnings

  • routing : Diff exceeded bounded packet review capability; full OCR not attempted.
OCR pilot metrics & packet coverage

OCR pilot metrics

  • Routing: large-lean-hotspots (router-v10)
  • Changed files: 29 supported / 29 total; Lean 26, trust docs 0, workflow/scripts 1, contracts 2, docs 0
  • Changed lines: 371 supported; thresholds large Lean >=3 files or >800 lines
  • OCR: status large-lean-hotspots; comments 0; files 0; tokens 0; tool calls 0; warnings 1; duration 0s
  • Largest changed files: Contracts/Smoke/Storage.lean (+55/-0), Compiler/CompilationModel/StorageWrites.lean (+36/-18), artifacts/macro_property_tests/PropertyPackedStorageLoweringSmoke.t.sol (+53/-0), Verity/Macro/Translate/Parsing.lean (+32/-0), Compiler/Proofs/Storage/StructArrayStorage.lean (+24/-0)

Packet coverage

  • Packet review: not used; selected 0/8 packet(s)
  • Scout: configured; status skipped_no_packets; model builtin/assistant
  • Scout lenses: provenance, verification-independence, environment-determinism, proof-soundness; rubric items checked 3
  • Strong review: required; status blocked_packet_input
  • Residual risk: Diff exceeded packet budget; use top changed files and deterministic signals as required Codex/human review checklist.
  • Strong packet-review blocker: OpenCodeReview 1.7.9 supports --from/--to full diff ranges, but this workflow does not have a safe packet/window input bridge for Lean hunks yet.

Pilot mode: advisory only. Codex Review remains the merge gate.

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.

feat(storage): Solidity-compatible packed storage lowering for Midnight Yul identity

1 participant