PD-275 — Expression de Besoin¶
1. Contexte¶
L'audit de vérification formelle PV Anchor (Prolog, 32 checks) identifie 5 écarts non conformes sur les règles ANCHOR-16 (Finality Depth) et ANCHOR-17 (Signer Revocation). Les 27 autres checks passent à 100%.
Vérification formelle amont : Les modèles TLA+ (13 invariants) et Alloy (12 assertions) valident déjà le design. L'écart est uniquement sur le mapping implémentation — les features ne sont pas encore codées dans le backend.
Références : - Règles Prolog : docs/normes/pv-anchor/formal/pv_anchor_compliance.pl - Rapport d'audit : docs/normes/pv-anchor/formal/AUDIT-REPORT.md - Synthèse : docs/normes/AUDIT-SYNTHESIS.md (29/32 OK → objectif 32/32) - Modèle TLA+ : docs/normes/pv-anchor/formal/PV_ANCHOR.tla
2. Problème¶
ANCHOR-16 : Finality Depth (2 checks KO)¶
| Check | ID Prolog | Description | État actuel |
|---|---|---|---|
| 28 | check_confirmation_count_col | Colonne confirmation_count absente sur anchor_batch | ConfirmationTracker track en mémoire, pas de persistance |
| 29 | check_finalize_confirmations_guard | finalizeBatch() sans garde confirmation_count >= FINALITY_DEPTH | Garde implicite dans ConfirmationTracker.track() mais non contractualisée dans AnchorBatchService |
ANCHOR-17 : Signer Revocation (3 checks KO)¶
| Check | ID Prolog | Description | État actuel |
|---|---|---|---|
| 30 | check_signer_status_mechanism | Mécanisme signer_status absent | Non implémenté |
| 31 | check_signer_revoke_method | Méthode revokeSigner() absente | Non implémenté |
| 32 | check_submit_active_signer_guard | submitBatch() sans garde signer ACTIVE | Non implémenté |
3. Solution attendue¶
3.1 ANCHOR-16 — Finality Depth¶
- Colonne
confirmation_count(integer, default 0) sur l'entitéanchor_batch - Persistance :
ConfirmationTrackermet à jouranchor_batch.confirmation_countà chaque poll - Garde dans
finalizeBatch(): vérifierconfirmation_count >= FINALITY_DEPTHavant transition versFINALIZED. Rejet si insuffisant (fail-closed, cf. learning PD-39) - Migration TypeORM : ajouter la colonne avec valeur par défaut
3.2 ANCHOR-17 — Signer Revocation¶
- Entité
signer_registry(nouvelle) avec colonnes : signerId(UUID, PK)address(EIP-55 checksum, unique)status(enumACTIVE|REVOKED)revokedAt(timestamp, nullable)revokedBy(string, nullable — audit trail)createdAt,updatedAt- Méthode
revokeSigner(address)surAnchorBatchService(ouSignerRegistryService) : - Transition
ACTIVE → REVOKEDatomique - Enregistrement
revokedAt+revokedBy(audit trail) - Validation : impossible de révoquer un signer déjà
REVOKED - Garde dans
submitBatch(): vérifier que le signer estACTIVEdanssigner_registryavant soumission. Rejet immédiat siREVOKED(fail-closed)
3.3 Vérification formelle¶
Après implémentation, régénérer les faits Prolog et relancer :
Objectif : 32/32 OK (0 KO, 0 WARN).4. Critères d'acceptation¶
- CA-01 : La colonne
confirmation_countexiste suranchor_batch(migration TypeORM) - CA-02 :
finalizeBatch()rejette siconfirmation_count < FINALITY_DEPTH(fail-closed) - CA-03 : L'entité
signer_registryexiste avec colonnesaddress,status,revokedAt,revokedBy - CA-04 : La méthode
revokeSigner(address)effectue la transition ACTIVE → REVOKED avec audit trail - CA-05 :
submitBatch()rejette si le signer estREVOKED(fail-closed) - CA-06 : L'audit Prolog passe 32/32 (5 checks corrigés : 28, 29, 30, 31, 32)
- CA-07 : Les tests unitaires couvrent les 5 nouveaux comportements
- CA-08 : Migration TypeORM réversible (up/down)
5. Hors périmètre¶
- Rotation automatique des signers (story future)
- Multi-signature (hors scope ANCHOR-17)
- Interface d'administration des signers (API CRUD complète)
- Modification du modèle TLA+ ou Alloy (déjà conformes)
6. Dépendances¶
- PD-177 (wallet Ethereum) :
signerAddressdéjà présent suranchor_batch— PD-275 s'appuie dessus - PD-52 (Ethereum L2 setup) : infrastructure blockchain opérationnelle
- PD-264 (nonce TSA RFC 3161) : module blockchain adjacent, pas de couplage direct
7. Risques¶
| Risque | Mitigation |
|---|---|
Migration sur table anchor_batch avec données existantes | Valeur par défaut 0, migration non-bloquante |
signer_registry vide au démarrage (cold start) | Seed initial avec les signers actifs connus |
Performance garde submitBatch() (lookup signer_registry) | Index unique sur address, query O(1) |
8. Learnings injectés¶
- PD-39 : Fail-closed obligatoire pour features sécuritaires — pas de mode permissif par défaut
- PD-5 : Contraintes d'irréversibilité pour les états terminaux (Vault Lock pattern)