Aller au contenu

PD-53 — Rétrospective

1. Contexte

Champ Valeur
Story ID PD-53
Titre Smart contract ancrage Merkle roots
Domaine blockchain
Projet infra
Date complétion 2026-02-16
Verdict ACCEPTÉ (GO 9.19/10)

2. Métriques

Métrique Valeur
Durée totale ~6h
Lignes de code ~104 (contrat) + ~229 (tests)
Tests 18/18 PASS
Gas anchor() ~27,000 (< 50k spec)
Gate iterations 3× GO (8.6, 8.9, 9.2)

3. Learnings clés

  • Storage packing Solidity : Le packing des structs est crucial pour le gas. 1 slot = 32 bytes. Toujours vérifier avec forge inspect <contract> storage-layout.

  • Gas testing Foundry : La mesure via gasleft() inclut l'overhead du test harness. Pour un gas précis, utiliser forge test -vvv et lire les traces.

  • CA de déploiement vs CA de code : Distinguer les critères d'acceptation testables (comportement du code) des critères de déploiement (vérification post-deploy).

  • Validation croisée efficace : La règle "Claude produit, ChatGPT review" a détecté le storage packing incorrect et réduit le gas de 54%.

4. Patterns applicables

Nouveau pattern : Struct Solidity optimisé (1 slot)

// OPTIMISÉ: 32 bytes = 1 slot
struct AnchorRecord {
    address submitter;      // 20 bytes
    uint64 timestamp;       // 8 bytes
    uint32 blockNumber;     // 4 bytes
}                           // Total: 32 bytes

// NON OPTIMISÉ: 36 bytes = 2 slots
struct AnchorRecordBad {
    address submitter;      // 20 bytes
    uint64 timestamp;       // 8 bytes
    uint64 blockNumber;     // 8 bytes ← trop grand
}                           // Total: 36 bytes

Pattern confirmé : Contrat append-only minimal

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/Ownable.sol";

contract MerkleAnchor is Ownable {
    event RootAnchored(bytes32 indexed root, address submitter, uint64 timestamp);

    mapping(bytes32 => AnchorRecord) public anchors;

    // Append-only: pas de suppression possible
    function anchor(bytes32 root) external onlyOwner {
        require(anchors[root].timestamp == 0, "Already anchored");
        anchors[root] = AnchorRecord({
            submitter: msg.sender,
            timestamp: uint64(block.timestamp),
            blockNumber: uint32(block.number)
        });
        emit RootAnchored(root, msg.sender, uint64(block.timestamp));
    }

    function isAnchored(bytes32 root) external view returns (bool) {
        return anchors[root].timestamp != 0;
    }
}

5. Signal CLAUDE.md

Priorité haute : Vérifier storage packing Solidity.

### Solidity — Storage packing (2026-02-XX)

**RÈGLE** : Toujours vérifier le packing des structs avec :
```bash
forge inspect <Contract> storage-layout

Optimisation : 1 slot = 32 bytes. Combiner les types pour atteindre exactement 32 bytes.

Type Taille
address 20 bytes
uint256 32 bytes
uint128 16 bytes
uint64 8 bytes
uint32 4 bytes
bool 1 byte

Exemple PD-53 : uint64 blockNumber (8 bytes) → uint32 blockNumber (4 bytes) = gas réduit de 54%. ```

6. Conclusion

PD-53 a livré le smart contract Merkle anchor avec architecture minimaliste (append-only, non-upgradable), héritage OpenZeppelin Ownable v5, et gas optimisé (27k < 50k spec). Le storage packing corrigé grâce à la review ChatGPT a réduit le gas de 54%. Le contrat est prêt pour déploiement sur Polygon Amoy et Arbitrum Sepolia.


Rétrospective générée 2026-02-19 (Étape 10 batch blockchain)