Skip to content

C5 storage flip (étapes 3-4) + inventaire des chantiers en cours — récap de session 2026-08-13 #2330

Description

@Th0rgal

Récapitulatif de l'état au moment de la clôture de la session du 2026-08-13, couvrant la lane C5 (ContractState ≠ storage EVM), la lane forEach/refactor, et les lots orphelins à arbitrer.


Lane C5 — un seul map avec layout Solidity, lenses comme seule API

Fait (mergé)

  • Étape 1 (feat(core): storage-lens API freeze — C5 step 1 #2323) : gel de l'API — gate à cliquet scripts/check_storage_lens_freeze.py dans make check (tout nouveau site brut échoue ; baseline par fichier qui ne peut que décroître).
  • Étape 2 (feat(core): storage lenses off default simp — C5 step 2 #2326) : les lenses ne sont plus @[simp] global ; storage_simps reste laws-only (survivra au flip) ; ~25 fichiers de preuves réparés, zéro énoncé changé.
  • Étape 2.5 (PR feat(core): storage-lens burn-down — C5 step 2.5 (pre-flip) #2329, ⚠️ à merger après CI verte — validation locale complète interrompue par la clôture, tous les modules touchés verts individuellement) : burn-down 304 → 63 sites bruts. Nouvelles lenses : bulk (writeSlots/modifySlots/writeTransientSlots/modifyTransientSlots/writeAddrSlots), withStorageChannel, ContractState.ofChannels. Baseline restante justifiée : Core (39, implémentations), Denote (6, faux positifs + bras find?), Owned/Basic (8, lane composition concurrente), TypedIRTests (3, faux positifs IRState), SourceSemantics (7, writers =-guardés dont le re-guard reshaperait GenericInduction).

Reste — Étape 3 (flip de représentation)

Design arrêté (informé par une recon complète de la dérivation de slots — voir ci-dessous) :

  • StorageKey inductif injectif (| slot n | contractSlot c n | transient n | addr n | map n key | mapUint n k | map2 n k1 k2), DecidableEq ; ContractState.storageWords : StorageKey → Uint256 remplace les 7 champs de canaux. Le layout Solidity vit dans la structure de la clé ; l'effondrement keccak vers Nat reste côté compilateur — c'est ce qui préserve les lois storage_simps inconditionnellement (l'injectivité keccak n'est PAS prouvable et le repo la remplace déjà par des certificats non-alias finis).
  • Les lectures restent stables : accesseurs def storage/storageAddr/storageMap/... aux anciens noms (dot-notation intacte) ⇒ les specs _meets_spec ne changent pas de forme, comme promis par le plan.
  • storageArray (List, pas un mot) et knownAddresses (bookkeeping de preuve) restent des champs ce round.
  • Séquence : flip dans Core + réimplémentation des lenses + repreuve des lois par injectivité de constructeurs → vague de réparation (résidus baseline + les ~60 sites simp [ContractState.writeX] explicites de l'étape 2, greppables) → baseline réduite à Core seul.
  • Prérequis restants avant flip : migrer les writers =-guardés de SourceSemantics (impact GenericInduction), le bras find? de Denote, et statuer sur les 8 sites Owned (lane concurrente).

Reste — Étape 4 (correspondance lens-slot = slot CompilationModel/Yul)

Recon faite (résultats clés à réutiliser) :

  • La dérivation canonique est Compiler.Proofs.abstractMappingSlot (= solidityMappingSlot, keccak kernel-computable, MappingSlot.lean:57) ∘ wordNormalize, + wordOffset pour struct members ; double mapping = composition ; chaînes = foldl.
  • Les mappings sont déjà comparés dans le canal plat à des slots keccak (sourceResultMatchesIRResult ne compare que finalStorage ; finalMappings est une projection dérivée). Les canaux storageMap* sont des ombres write-only — encodeStorageAt ne les lit jamais.
  • Le vrai trou : aucune cohérence prouvée entre les canaux ombre (ce que lisent readMap/les specs) et le canal plat aux slots dérivés. Livrable étape 4 : storageKeySlot : List Field → StorageKey → Option (Channel × Nat) + invariant MappingCoherent (ombre = plat au slot dérivé) + preuve que les write-helpers le préservent + lecture lens = encodeStorageAt sous cohérence.
  • ⚠️ Divergence connue à trancher : les éléments de dynamic array utilisent solidityMappingSlot base idx dans le modèle source (findDynamicArrayElementAtSlot) mais keccak(slot)+i (layout Solidity réel) côté Yul/StructArrayStorage.
  • Déjà prouvé et réutilisable : compiledMappingSlotPointer_eq_sourceMappingSlotRead (SolidityStorage.lean:92), les lemmes encodeStorageAt_write*_singleton_eq_written (GenericInduction/Storage.lean), les ponts packed/struct/array de StructArrayStorage.lean.
  • Toucher la surface layout du compilateur ⇒ coordonner avec la lane forEach (Compile.lean/SupportedFragment.lean).

Lane refactor/forEach (session concurrente)

Arc admission forEach (design complet en mémoire de session) :

  1. PR-A en cours de ship : CompiledNameDiscipline.lean (toolkit préfixes + loopFreeCheck/soundness + gate décidable forEachCompiledBodyChecks). Reste : forEach_loop_coupling (induction sur N−k ; corps via exec_compileStmtList_generic_sizeOf_extraFuel [Helpers.lean:3210], fuel via execIRStmt_boundedFor_stable [feat(proofs): fuel stability for literal-bound forEach loops #2328], shrink de scope via bindingsExactlyMatchIRVarsOnScope_of_included) + compiledStmtStep_forEach_literal (généralise Loops.lean:949/992).
  2. PR-B : constructeur forEachLiteralBounded .literal 0 → .literal n (SupportedFragment.lean:373) + 7 sites consommateurs (Helpers.lean:1718/1866/2204, SupportedSpec.lean:3902/4096/6171/6296, EndToEnd/Base) + prédicat de surface (SupportedSpec.lean:1491) + enregistrement du checker.
  3. PR-C optionnelle : lemme générique de discipline de noms (35 constructeurs SupportedStmtList) pour éliminer le decide du gate.
  4. Limitation documentée : corps loop-free uniquement (les for_ imbriqués sont rejetés par varUntouchedCheck/LoopFree) — extension nested = chantier ultérieur.

Items de rotation restants : 7 (réduction allowlist proof-length, ~15 entrées), 8 (#2000 stores binding-aware pour payloads d'événements dynamiques), 9 (#1987 doc-sync — attention lane invisible active), 11 (identité Yul #2058/#2059), 3 (éclatement IRExecResult.outOfFuel, ~727 usages — à faire en dernier, décision actée), 5 (retrait lock-free erasure — uniquement sur décision explicite).


Lane événements / Lido (contexte)


Lots orphelins / à arbitrer

  • Une session « invisible » (hors ListAgents) travaille dans le même arbre : lane Ownable/OwnedCounterComposed/Specs.Composition/modifiers Macro + refactor owner.slot. Ses fichiers étaient non commités à la clôture.
  • Patches sauvegardés dans le scratchpad de session (orphan-lanes/) : laneA-owned-slot-composition.patch (+ Composition.lean, Smoke-Include.lean), laneC-macro-modifiers.patch (883 lignes).
  • ⚠️ Perte assumée : le lot docsync non commité (docs-site/*.mdx, ROADMAP, VERIFICATION_STATUS, docsync.py) a été écrasé par un checkout groupé dont la sauvegarde avait échoué en silence — en grande partie régénérable par les scripts generate_*.

Ordre suggéré à la reprise

  1. Merger feat(core): storage-lens burn-down — C5 step 2.5 (pre-flip) #2329 (CI verte) puis la PR-A forEach de la lane concurrente.
  2. C5 étape 3 (flip StorageKey) — le plus gros morceau restant, prérequis listés ci-dessus.
  3. C5 étape 4 (cohérence ombre↔plat + storageKeySlot), coordonnée avec la surface layout.
  4. Rotation : items 7/8/11, puis 3 en dernier.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions