Methodologie de verification formelle — ProbatioVault¶
| Version | 1.4.0 |
| Date | 2026-02-28 |
| Statut | Approuve |
| Classification | Confidentiel — diffusion controlee (auditeurs autorises) |
| Auteur | ProbatioVault Engineering |
| Verification formelle | TLA+ (TLC), Alloy (SAT), Z Notation, Prolog (SWI-Prolog) |
Table des matieres¶
- Objet et portee
- Fondement theorique
- Les quatre formalismes
- 3.1 TLA+ — Logique temporelle des actions
- 3.2 Alloy — Analyse structurelle par solveur SAT
- 3.3 Z Notation — Specification mathematique
- 3.4 Prolog — Verification de conformite directe
- Pipeline d'extraction
- Pipeline de verification
- Mecanisme d'ancrage
- Piste d'audit
- Tracabilite
- Limites et perimetre
- 9.5 Feuille de route d'amelioration
- Annexe A — Demonstration complete ANCHOR-16
- Annexe B — Arborescence des fichiers
- Annexe C — Glossaire
1. Objet et portee¶
1.1 Objectif¶
Ce document decrit la methodologie de verification formelle utilisee par ProbatioVault pour verifier formellement que les implementations logicielles sont conformes aux specifications normatives. Il constitue la reference methodologique pour tout audit de conformite et est concu pour etre auto-porteur : un auditeur n'a pas besoin de lire d'autres documents pour comprendre la demarche.
La verification formelle ProbatioVault repose sur quatre systemes formels complementaires, appliques systematiquement a chaque norme. Chaque systeme apporte une garantie distincte, et leur combinaison constitue une defense en profondeur contre les defauts de conception et d'implementation.
1.2 Perimetre de verification¶
La methodologie s'applique a l'ensemble des normes ProbatioVault, internes et externes.
Protocoles internes ProbatioVault :
| Identifiant | Protocole | Objet |
|---|---|---|
pv-anchor | PV Blockchain Anchoring Protocol | Ancrage blockchain, Merkle tree, inclusion proofs |
pv-envelope | PV Zero-Knowledge Key Management | Hierarchie de cles, chiffrement zero-knowledge |
pv-audit | PV Immutable Audit Trail | Journal d'audit immutable, append-only |
pv-proof | PV Composite Proof Envelope | Chaine de preuve a 4 maillons |
pv-pre | PV Proxy Re-Encryption | Re-chiffrement Umbral proxy |
Standards externes de reference :
| Identifiant | Standard | Objet |
|---|---|---|
nf-z42-013 | NF Z42-013 | Archivage electronique — Specifications (AFNOR) |
iso-14641 | ISO 14641 | Archivage electronique — Conception et exploitation |
rfc-3161 | RFC 3161 | Internet X.509 PKI Time-Stamp Protocol |
rfc-5054 | RFC 5054 | SRP (Secure Remote Password) Protocol |
1.3 Garanties apportees¶
La verification formelle ProbatioVault apporte les assurances suivantes (toutes bornees par le scope d'exploration — cf. Section 9 pour les limites) :
- Correction du design — Le modele TLA+ verifie par model checking exhaustif que les proprietes de surete (invariants) sont satisfaites dans tous les etats atteignables du systeme dans le scope configure.
- Coherence structurelle — Le modele Alloy verifie par solveur SAT qu'aucune configuration structurelle ne viole les assertions dans le scope explore. L'absence de contre-exemple constitue une assurance formelle bornee.
- Rigueur mathematique — La specification Z exprime les proprietes en theorie des ensembles et logique des predicats, avec pre/post-conditions explicites pour chaque operation. Les theoremes Z sont formules comme contrats mathematiques auditables (cf. Section 3.3.5).
- Conformite de l'implementation — Les regles Prolog verifient directement que les artefacts du code source reel (entites, colonnes, services, methodes, gardes, triggers, infrastructure) satisfont chaque exigence normative.
- Ancrage au code reel — Les modeles formels sont lies au code source par extraction automatique. Les constantes et structures extraites du code sont injectees dans les modeles. Si le code ne correspond pas au modele, la verification echoue immediatement.
1.4 Hors perimetre¶
Les elements suivants ne sont pas couverts par la verification formelle :
- Performance et scalabilite — Temps de reponse, debit, consommation memoire.
- Correction des primitives cryptographiques — La verification formelle suppose que les bibliotheques cryptographiques sous-jacentes (OpenSSL, AWS CloudHSM, node:crypto) sont correctes. Seule leur utilisation est verifiee.
- Securite reseau — Configuration TLS, pare-feu, regles de routage.
- Interface utilisateur — Comportement de l'application front-end.
- Dependances tierces — Correction des modules npm, drivers de base de donnees.
- Sur-implementation — La verification verifie que le code satisfait les exigences (
RFC ⊆ SOURCE). Elle ne verifie pas que le code ne fait rien de plus (SOURCE ⊆ RFC).
2. Fondement theorique¶
2.1 Principe d'inclusion predicats_RFC ⊆ predicats_SOURCE¶
Le fondement mathematique de la verification ProbatioVault repose sur un principe unique :
Theoreme de conformite : Soit P_RFC l'ensemble des predicats formalisant les exigences d'une norme, et P_SOURCE l'ensemble des predicats extraits du code source. Le systeme est conforme si P_RFC ⊆ P_SOURCE — c'est-a-dire que chaque exigence normative est satisfaite par un artefact du code reel.
Precision : la relation est une condition suffisante de conformite, pas une equivalence. La verification etablit que toutes les exigences du RFC sont satisfaites dans le code (P_RFC ⊆ P_SOURCE), mais ne verifie pas que le code ne contient rien de plus que les exigences (P_SOURCE ⊆ P_RFC). Autrement dit : la conformite est verifiee, pas la minimalite.
Ce principe est applique independamment par chaque formalisme :
| Formalisme | P_RFC | P_SOURCE | Mecanisme de preuve |
|---|---|---|---|
| TLA+ | Invariants, preconditions d'actions, theoremes | ASSUME statements extraits du code | TLC model checker : violation = contre-exemple avec trace |
| Alloy | Facts (contraintes), assertions (proprietes) | one sig Real_* singletons (artefacts reels) | SAT solver : violation = contre-exemple |
| Z | Invariants de schema, pre/post-conditions | _generated-z-lint.yaml (colonnes, schemas) | Lint : required_columns(RFC) ⊆ actual_columns(code) |
| Prolog | Regles check_*/0 (presence et garde) | Faits entity/3, service_method/2, etc. | Resolution : fait absent = echec du check |
La direction de l'inclusion est cruciale :
P_RFC ⊆ P_SOURCE(verifie) : toute exigence du RFC est implementee dans le code.P_SOURCE ⊆ P_RFC(non verifie) : le code pourrait contenir des fonctionnalites non specifiees.
2.2 Defense en profondeur¶
Quatre formalismes sont necessaires car chacun detecte des classes de defauts differentes.
| Formalisme | Nature | Detecte | Ne detecte pas |
|---|---|---|---|
| TLA+ | Temporel / comportemental | Deadlocks, violations d'invariants dans des sequences d'etats, transitions invalides | Relations structurelles statiques |
| Alloy | Structurel / relationnel | Configurations impossibles, relations manquantes, incoherences structurelles | Sequences temporelles, comportements dynamiques |
| Z | Mathematique / formel | Erreurs de typage, violations de pre-conditions, incoherences de schema | Comportements a l'execution |
| Prolog | Factuel / implementation | Artefacts manquants, gardes absentes, colonnes/methodes/services non implementes | Correction algorithmique |
Exemple concret : une exigence stipule que « un batch ne peut etre finalise que si le nombre de confirmations blockchain est suffisant ».
- TLA+ verifie que dans toute sequence d'etats possible, un batch
FINALIZEDa toujoursconfirmations >= FINALITY_DEPTH. - Alloy verifie que dans toute configuration structurelle valide, un batch
Finalizeda un champconfirmations >= 1. - Z verifie que la precondition formelle de l'operation
FinalizeBatchexigeconfirmations >= finalityDepth. - Prolog verifie que dans le code reel, la colonne
confirmation_countexiste ET que la methodefinalizeBatch()contient une garde sur cette colonne.
2.3 Ancrage au code reel¶
La verification formelle traditionnelle opere sur des modeles abstraits, deconnectes du code reel. Un modele peut etre correct alors que l'implementation diverge. ProbatioVault elimine ce risque par un mecanisme d'ancrage automatique :
- Extraction automatique — Le script
extract-facts.pyanalyse le code source reel (TypeScript, SQL, Terraform) et genere des fichiers de faits dans les 4 formats formels. - Regeneration systematique — Les fichiers d'ancrage sont regeneres a chaque build CI. Ils portent la mention
DO NOT EDIT — regenerated at each CI build. - Controle de compatibilite — Pour les types et enumerations, l'ancrage verifie
RealStates ⊆ StatusValues(pas d'etat code hors modele). Pour garantir l'absence d'etats modele non implementes, le pipeline verifie aussiStatusValues ⊆ RealStates— les deux inclusions combinees imposent l'egaliteRealStates = StatusValues. Pour les invariants, la liaison est unidirectionnelle : le modele exige, le code satisfait.
2.4 Deux niveaux de verification¶
La verification Prolog opere a deux niveaux, par ordre de rigueur croissante :
Niveau 1 — Structurel : l'artefact existe.
%% La colonne confirmation_count existe-t-elle dans l'entite anchor_batch ?
check_confirmation_count_col :-
entity_column(anchor_batch, confirmation_count, _), !.
Niveau 2 — Semantique : l'artefact est utilise correctement.
%% La methode finalizeBatch() verifie-t-elle la valeur de confirmation_count ?
check_finalize_confirmations_guard :-
service_method_guard(anchor_batch, finalizeBatch, confirmation_count), !.
Le predicat service_method_guard/3 est extrait du code source par analyse statique des methodes de service. Il represente une precondition metier : la methode finalizeBatch du service anchor_batch contient une garde sur le champ confirmation_count.
Ce double niveau detecte les violations silencieuses : une colonne peut exister dans le schema sans jamais etre verifiee dans la logique metier.
2.5 Diagramme de flux¶
RFC-{NORM}.md Code source
(source normative) (TypeScript, SQL, Terraform)
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ Formalisation │ │ extract-facts.py │
│ manuelle │ │ (automatique) │
└────────┬─────────┘ └────────┬─────────┘
│ │
▼ ▼
┌──────────────────┐ ┌──────────────────┐
│ predicats_RFC │ │ predicats_SOURCE │
│ │ │ │
│ • {Norm}.tla │ │ • _AnchoredFacts │
│ • {Norm}.als │ │ _{MODULE}.tla │
│ • {Norm}.zed │ │ • _AnchoredFacts │
│ • {norm}_ │ │ _{MODULE}.als │
│ compliance.pl │ │ • _generated- │
└────────┬─────────┘ │ z-lint.yaml │
│ │ • _generated- │
│ │ facts.pl │
│ └────────┬─────────┘
│ │
▼ ▼
┌─────────────────────────────────────────────────────────┐
│ verify-norm.sh │
│ │
│ 1. TLC : {Norm}.tla + _AnchoredFacts → 0 violation ? │
│ 2. Prolog: compliance.pl + _generated-facts → 32/32 ? │
│ 3. Alloy: {Norm}.als + _AnchoredFacts → UNSAT ? │
│ 4. z-lint: {Norm}.zed + _generated-z-lint → lint OK ? │
│ 5. Prolog: completeness.pl (informatif) │
│ 6. Score: compliance_scoring.pl (informatif) │
└────────────────────────┬────────────────────────────────┘
│
▼
┌──────────────────┐
│ AUDIT-REPORT.md │
│ Verdict : │
│ CONFORME / │
│ PARTIELLEMENT / │
│ NON CONFORME │
└──────────────────┘
3. Les quatre formalismes¶
3.1 TLA+ — Logique temporelle des actions¶
3.1.1 Nature de la verification¶
TLA+ (Temporal Logic of Actions) est un langage de specification formelle concu par Leslie Lamport pour la modelisation de systemes concurrents et distribues. Le model checker TLC explore exhaustivement l'espace des etats atteignables du systeme et verifie que chaque invariant est satisfait dans chaque etat.
La verification TLA+ apporte une assurance formelle de surete (safety) : si TLC ne trouve aucune violation, alors l'invariant est verifie dans tous les etats atteignables du systeme dans le scope defini par les constantes. Cette assurance est bornee par les constantes du modele (cf. Section 3.1.6).
3.1.2 Comment les exigences RFC deviennent des predicats¶
Chaque exigence normative du RFC est traduite en un ou plusieurs elements du modele TLA+ :
Types et domaines — Les types enumeres du RFC deviennent des ensembles TLA+ :
StatusValues == {"PENDING", "BUILDING", "SUBMITTED",
"PENDING_FINALITY", "FINALIZED", "FAILED"}
SignerStatusValues == {"ACTIVE", "REVOKED"}
Actions — Chaque operation du protocole devient une action TLA+ avec des preconditions et postconditions explicites. Le modele PV Anchor definit 11 actions :
| # | Action | Precondition cle | Postcondition |
|---|---|---|---|
| 1 | CreateBatch(id) | id ∉ batches | batch_status[id] = "BUILDING" |
| 2 | SubmitBatch(id) | status = "BUILDING", signer ACTIVE | status = "SUBMITTED" |
| 3 | ConfirmBatch(id) | status = "SUBMITTED" | status = "PENDING_FINALITY" |
| 4 | FinalizeBatch(id) | status = "PENDING_FINALITY", confirmations >= FINALITY_DEPTH | status = "FINALIZED" |
| 5 | FailBatch(id) | status ∈ {"BUILDING", "SUBMITTED"} | status = "FAILED" |
| 6 | ExportProofArtifact(id) | status = "FINALIZED" | Audit log enrichi |
| 7 | VerifyInclusionProof(id) | status = "FINALIZED" | Audit log enrichi |
| 8 | HandleReorg(id) | status = "PENDING_FINALITY" | status = "FAILED", confirmations = 0 |
| 9 | RetryBatch(id) | status = "FAILED" | Audit log enrichi |
| 10 | RevokeSigner(sa) | signer_status[sa] = "ACTIVE" | signer_status[sa] = "REVOKED" |
| 11 | Tick | — | clock' = clock + 1 |
Invariants — Chaque propriete de surete du RFC devient un invariant TLA+, verifie dans chaque etat atteignable. Exemple (INV-12, ANCHOR-16) :
(* INV-12 : FinalityDepthEnforced — batch FINALIZED a confirmations >= FINALITY_DEPTH *)
FinalityDepthEnforced ==
\A id \in batches :
batch_status[id] = "FINALIZED"
=> batch_confirmations[id] >= FINALITY_DEPTH
Theoremes — Chaque invariant est associe a un theoreme de preservation :
Ce theoreme affirme que l'invariant FinalityDepthEnforced est satisfait dans tous les etats de toutes les executions conformes a la specification Spec.
Le modele PV Anchor comprend 13 invariants (INV-01 a INV-13) et 10 theoremes (THM-01 a THM-10).
3.1.3 Comment le code source devient des predicats¶
Le script extract-facts.py genere automatiquement un module TLA+ d'ancrage (_AnchoredFacts_{MODULE}.tla) qui etend le modele principal et ajoute des contraintes ASSUME derivees du code reel.
Mecanisme EXTENDS + ASSUME :
---- MODULE _AnchoredFacts_ANCHOR ----
EXTENDS PV_Anchor
\* Mapping code enum -> norm states (from norm.yaml state_mapping)
RealStates == {"PENDING", "BUILDING", "SUBMITTED",
"PENDING_FINALITY", "FINALIZED", "FAILED"}
\* ASSUME: real states must match model states (double inclusion = egalite)
ASSUME AnchorAssume_States ==
/\ RealStates \subseteq StatusValues
/\ StatusValues \subseteq RealStates
Categories de faits ancres :
| Categorie | Exemple | Source d'extraction |
|---|---|---|
| Etats enum | RealStates == {"PENDING", ...} | Enum TypeORM dans @Column({type: 'enum'}) |
| Statuts signer | RealSignerStatuses == {"ACTIVE", "REVOKED"} | Enum TypeORM signer_status |
| Infrastructure HSM | RealHasHSM == TRUE | Resource Terraform aws_cloudhsm_v2_cluster |
| Stockage WORM | RealHasWORM == TRUE | S3 bucket avec object_lock_configuration |
| Replication CRR | RealHasCRR == TRUE | Regle de replication cross-region |
| Journal immutable | RealHasJournalTrigger == TRUE | Trigger SQL sur la table audit_log |
| Truncate revoque | RealHasTruncateRevoked == TRUE | REVOKE TRUNCATE dans migration SQL |
| Support finalite | RealHasConfirmationCount == TRUE | Colonne confirmation_count dans entite |
Chaque ASSUME est un axiome : si le fait extrait du code ne satisfait pas la contrainte du modele, TLC s'arrete immediatement avec une erreur d'assumption.
3.1.4 Comment l'inclusion est prouvee¶
Le model checker TLC opere en deux phases :
Phase 1 — Verification des ASSUMEs : TLC charge le module ancre (qui EXTENDS le modele principal) et evalue chaque ASSUME. Si un ASSUME echoue (par exemple, le code contient un etat PROCESSING absent du modele), TLC s'arrete immediatement avec le message :
Phase 2 — Exploration exhaustive : TLC explore l'espace des etats en partant de l'etat initial (Init) et en appliquant toutes les actions possibles (Next) a chaque etat. Pour chaque etat atteint, TLC verifie que chaque invariant est satisfait. Si un invariant est viole, TLC produit un contre-exemple : une trace d'execution complete menant a l'etat violant, permettant de reproduire exactement le probleme.
Resultat : 0 violation = aucune violation trouvee dans l'espace d'etats explore. Ceci constitue une assurance formelle bornee (cf. Section 9.5.6 pour les limites du vocabulaire).
Direction de l'inclusion :
- Egalite imposee pour les types : le pipeline genere
RealStatesdepuis le code et verifieRealStates ⊆ StatusValues(pas d'etat code hors modele) etStatusValues ⊆ RealStates(pas d'etat modele absent du code). Les deuxASSUMEcombines imposentRealStates = StatusValues, une compatibilite stricte. - Unidirectionnelle pour les invariants : le modele exige des proprietes (ex:
FinalityDepthEnforced). Si le code ne les satisfait pas, le model checking ne le detecte pas directement — c'est le role de Prolog.
3.1.5 Configuration¶
Le fichier de configuration TLC (PV_Anchor.cfg) definit les parametres du model checking :
SPECIFICATION Spec
CONSTANTS
MAX_BATCHES = 1
MAX_EVENTS = 2
MAX_ITERATIONS = 6
FINALITY_DEPTH = 1
CONSTRAINT StateConstraint
INVARIANTS
TypeInvariant
ValidTransitions
FinalizedImmutable
EventUniqueness
NoEventsLost
WindowOrdering
AuditCompleteness
MerkleRootPresent
TxIdPresent
SignerAddressAppendOnly
AuditAppendOnly
FinalityDepthEnforced
SignerRevocationValid
Les constantes bornent l'espace d'etats pour rendre le model checking faisable en temps fini. La contrainte StateConstraint limite le nombre de batches et l'horloge logique.
3.1.6 Limites¶
- Model checking borne : les constantes
MAX_BATCHES=1,MAX_EVENTS=2limitent l'exploration. Les proprietes sont prouvees pour ces bornes, pas pour des valeurs arbitrairement grandes. En pratique, les defauts de conception se manifestent generalement dans de petites instances (hypothese du petit scope). - Abstraction des valeurs : les hashes, adresses, et identifiants de transaction sont representes par des entiers abstraits (
MerkleValues == 0..10). La verification ne porte pas sur les valeurs reelles. - Pas de proprietes de vivacite : seules les proprietes de surete (invariants) sont verifiees. Les proprietes de vivacite (« un batch PENDING finit par etre FINALIZED ») ne sont pas modelisees.
- Explosion combinatoire : l'espace d'etats croit exponentiellement avec les constantes. Des bornes trop larges rendent le model checking impraticable.
3.2 Alloy — Analyse structurelle par solveur SAT¶
3.2.1 Nature de la verification¶
Alloy est un langage de modelisation relationnelle cree par Daniel Jackson (MIT). Il utilise un solveur SAT (Satisfiability) pour explorer exhaustivement toutes les configurations possibles d'un modele dans un scope borne, et determiner si une assertion est satisfaite ou si un contre-exemple existe.
La verification Alloy apporte une assurance structurelle bornee : si aucun contre-exemple n'est trouve dans le scope for N, alors l'assertion est satisfaite pour toutes les configurations contenant jusqu'a N atomes par type. Il ne s'agit pas d'une preuve au sens mathematique strict mais d'une verification exhaustive dans un scope fini (hypothese du petit scope, Jackson 2006).
3.2.2 Comment les exigences RFC deviennent des predicats¶
Signatures (sig) — Les structures de donnees du RFC deviennent des signatures Alloy :
sig AnchorBatch {
status : one BatchStatus,
merkleTree : one MerkleTree,
events : some BatchEvent,
window : one TemporalWindow,
txHash : lone HashValue,
chainId : lone Int,
blockNumber : lone Int,
confirmations : lone Int,
signerAddress : lone SignerAddress
}
Les multiplicites (one, lone, some) expriment directement les contraintes de cardinalite du RFC.
Faits (fact) — Les contraintes structurelles du RFC deviennent des faits Alloy, toujours vrais dans toute instance valide. Le modele PV Anchor comprend 12 faits :
-- ANCHOR-16 : Batch FINALIZED a confirmations >= 1 (FINALITY_DEPTH abstrait)
fact FinalityDepthEnforced {
all b : AnchorBatch |
b.status = Finalized implies
(one b.confirmations and b.confirmations >= 1)
}
-- ANCHOR-17 : Batch SUBMITTED+ a un signer ACTIVE
fact SignerRevocationValid {
all b : AnchorBatch |
b.status in (Submitted + PendingFinality + Finalized)
implies (one b.signerAddress and b.signerAddress.signerStatus = Active)
}
Assertions (assert) — Les proprietes a verifier deviennent des assertions, verifiees par commande check. Le modele PV Anchor comprend 12 assertions :
assert FinalityDepthCheck {
all b : AnchorBatch |
b.status = Finalized implies
(one b.confirmations and b.confirmations >= 1)
}
check FinalityDepthCheck for 5
Commandes run — Des scenarios d'exploration permettent de verifier que le modele est satisfiable (pas trivialement vide) :
run ExampleFinalizedBatch {
#AnchorBatch = 1
some b : AnchorBatch | b.status = Finalized
} for 4 but 6 Int
3.2.3 Comment le code source devient des predicats¶
Le script extract-facts.py genere un module Alloy d'ancrage (_AnchoredFacts_{MODULE}.als) contenant des singletons one sig Real_* representant les artefacts reels du code :
module _AnchoredFacts_ANCHOR
open PV_Anchor
-- Real artifact singletons (zero scope cost)
one sig Real_document_secure_file_hash {}
one sig Real_audit_log_entry_hash {}
one sig Real_audit_log_hsm_signature {}
one sig Real_anchor_batch_tx_id {}
one sig Real_anchor_batch_merkle_root {}
one sig Real_anchor_batch_confirmation_count {}
one sig Real_anchor_batch_signer_status {}
one sig Real_WORM_bucket {}
one sig Real_HSM_FIPS {}
one sig Real_journal_trigger {}
Les singletons (one sig) ont un cout de scope nul : ils n'augmentent pas le nombre de configurations a explorer.
Precision sur les one sig : un singleton one sig Real_X {} existe toujours dans toute instance Alloy (exactement un atome). L'expression some Real_X est donc trivialement vraie. L'interet des singletons n'est pas dans la verification some — c'est le mecanisme de generation conditionnelle qui assure la garantie : le module _AnchoredFacts_*.als n'est genere par extract-facts.py que si l'artefact correspondant existe reellement dans le code source. Si la colonne confirmation_count est supprimee du code, le singleton Real_anchor_batch_confirmation_count disparait du module genere, et toute assertion ou fait qui le reference echoue a la compilation Alloy.
Les singletons sont groupes en faits thematiques :
fact AnchoredTSAAndAnchoring {
some Real_anchor_batch_tx_id
some Real_anchor_batch_merkle_root
some Real_anchor_batch_confirmation_count
some Real_anchor_batch_signer_status
}
Et une assertion combinee verifie la completude des artefacts :
assert AnchoredArtifactsConsistent {
some Real_anchor_batch_tx_id and
some Real_anchor_batch_merkle_root and
some Real_anchor_batch_confirmation_count and
some Real_anchor_batch_signer_status and
some Real_WORM_bucket and
some Real_HSM_FIPS and
some Real_journal_trigger
\* ... (tous les singletons)
}
check AnchoredArtifactsConsistent for 4
3.2.4 Comment l'inclusion est verifiee¶
L'analyseur Alloy effectue une verification en deux passes :
Passe 1 — Modele principal : pour chaque commande check AssertionName for N, le solveur SAT cherche un contre-exemple contenant jusqu'a N atomes par type. Si le solveur retourne No counterexample found (souvent note UNSAT), aucun contre-exemple n'existe dans ce scope — l'assertion est satisfaite pour ce scope.
Passe 2 — Artefacts ancres : le module _AnchoredFacts_*.als est genere conditionnellement. Si un artefact requis est absent du code, le singleton correspondant n'est pas genere, et la compilation ou le check echoue.
Resultat : absence de contre-exemple pour toutes les assertions dans le scope explore = assurance formelle bornee que le modele structural est coherent et que le code reel fournit tous les artefacts requis.
3.2.5 Limites¶
- Scope borne : les assertions sont verifiees pour un nombre fini d'atomes (
for 5= jusqu'a 5 instances par type). L'hypothese du petit scope (Jackson, 2006) stipule que la plupart des defauts se manifestent dans de petites instances. - Pas de raisonnement temporel : Alloy analyse des instantanes (snapshots), pas des sequences d'etats. Les proprietes temporelles sont du ressort de TLA+.
- Abstraction de FINALITY_DEPTH : le modele utilise
confirmations >= 1comme abstraction. La valeur configurable reelle est verifiee par TLA+ et Prolog.
3.3 Z Notation — Specification mathematique¶
3.3.1 Nature de la verification¶
La notation Z est un langage de specification formelle base sur la theorie des ensembles ZFC et le calcul des predicats du premier ordre. Developpe a l'Universite d'Oxford, Z est un standard ISO (ISO/IEC 13568:2002). Il fournit la specification la plus rigoureuse mathematiquement, avec des schemas explicites pour les donnees, l'etat, et les operations.
3.3.2 Comment les exigences RFC deviennent des predicats¶
Types enumeres — Les enumerations du RFC deviennent des types libres Z :
BatchStatus ::= PENDING | BUILDING | SUBMITTED | PENDING_FINALITY | FINALIZED | FAILED
SignerStatus ::= ACTIVE | REVOKED
Schemas de donnees — Chaque structure du RFC devient un schema Z avec des contraintes internes :
┌─ AnchorBatch ────────────────────────────────────────
│ batchId : BatchId
│ status : BatchStatus
│ merkleRoot : HashBytes
│ txId : TxHashBytes
│ chainId : ChainIdStr
│ blockNumber : ℕ
│ confirmations : ℕ
│ signerAddress : SignerAddressStr
│ events : ℙ₁ AnchorEvent
│ window : TemporalWindow
├───────────────────────────────────────────────────────────
│ batchId ≠ ∅
│ #events ≥ 1
│ window.windowStart < window.windowEnd
└───────────────────────────────────────────────────────────
Schema d'etat — L'etat global du systeme est un schema contenant tous les invariants. Le schema ANCHOR_State du modele PV Anchor contient 17 invariants (INV-01 a INV-17). Exemples :
┌─ ANCHOR_State ───────────────────────────────────────
│ batches : BatchId ⇸ AnchorBatch
│ finalizedEvents : ℙ EventId
│ signerAddresses : ℙ SignerAddressStr
│ signerStatuses : SignerAddressStr ⇸ SignerStatus
│ finalityDepth : ℕ₁
│ auditLog : seq AuditEntry
├───────────────────────────────────────────────────────────
│ ── INV-12 : FinalityDepthEnforced
│ ∀ bid : dom batches •
│ batches(bid).status = FINALIZED ⇒
│ batches(bid).confirmations ≥ finalityDepth
│
│ ── INV-13 : SignerRevocationValid
│ ∀ bid : dom batches •
│ batches(bid).status ∈ {SUBMITTED, PENDING_FINALITY, FINALIZED} ⇒
│ batches(bid).signerAddress ∈ dom signerStatuses
└───────────────────────────────────────────────────────────
Operations — Chaque operation du protocole est specifiee par un schema Delta (Δ) avec pre-conditions explicites :
┌─ FinalizeBatch ──────────────────────────────────────
│ ΔANCHOR_State
│ batchId? : BatchId
├───────────────────────────────────────────────────────────
│ batchId? ∈ dom batches
│ batches(batchId?).status = PENDING_FINALITY
│ batches(batchId?).merkleRoot ≠ ∅
│ batches(batchId?).txId ≠ ∅
│ batches(batchId?).blockNumber ≥ 1
│ ── ANCHOR-16 : confirmations >= finalityDepth
│ batches(batchId?).confirmations ≥ finalityDepth
└───────────────────────────────────────────────────────────
Theoremes — Des theoremes de preservation garantissent que les operations maintiennent les invariants :
── THM-09 : FinalizeBatch preserve INV-12 (finality depth enforced)
── ∀ batchId? •
── pre(FinalizeBatch) ⇒ confirmations ≥ finalityDepth
Le modele Z PV Anchor comprend 17 invariants, 9 theoremes, et 8 operations.
Invariants Z supplementaires — Z notation permet des invariants structurels supplementaires (INV-14 a INV-17) qui n'ont pas d'equivalent direct dans TLA+ ou Alloy :
| INV | Nom | Objet |
|---|---|---|
| INV-14 | PessimisticLocking | FinalizeBatch exige SELECT ... FOR UPDATE |
| INV-15 | SignerAddressNonEmpty | Batch SUBMITTED+ a une signerAddress non vide |
| INV-16 | BlockchainInInclusionProof | Chaque inclusion proof contient le champ blockchain |
| INV-17 | ReorgHandling | Batch FAILED apres reorg n'a pas d'events finalises |
Ces invariants supplementaires sont traces dans la matrice de tracabilite du RFC (Section 7.2).
3.3.3 Comment le code source devient des predicats¶
Le script extract-facts.py genere un fichier _generated-z-lint.yaml qui mappe les entites TypeORM aux schemas Z :
norm: pv-anchor
version: 1.1.0
entities:
anchor_batch:
z_schemas:
- AnchorBatch
- AnchorEvent
- TemporalWindow
- MerkleTree
- InclusionProof
- ProofArtifact
required_columns:
- merkle_root
- tx_id
- chain_id
- block_number
- status
- signer_address
- window_start
- window_end
- confirmation_count
- signer_status
enums:
batch_status:
z_type: BatchStatus
values: [PENDING, BUILDING, SUBMITTED, PENDING_FINALITY, FINALIZED, FAILED]
signer_status:
z_type: SignerStatus
values: [ACTIVE, REVOKED]
La section anchored: du fichier genere contient les entites et colonnes reelles extraites du code, avec les schemas Z attendus :
anchored:
entities:
- name: anchor_batch
expected_in_schemas: [Fingerprint]
columns: [block_number, chain_id, merkle_root, signer_address, status,
submitted_at, tree_id, tx_id, window_end, window_start]
3.3.4 Comment l'inclusion est prouvee¶
Le linter Z (z-notation-lint.py) effectue les verifications suivantes :
- Colonnes requises : pour chaque entite,
required_columns(RFC) ⊆ actual_columns(code). Si une colonne requise par la specification Z est absente du code, le lint echoue. - Mapping schemas : chaque entite est mappee aux schemas Z attendus. Le lint verifie que les schemas references existent dans la specification.
- Enumerations : les valeurs d'enum extraites du code sont comparees aux types Z correspondants.
Le resultat est un score de lint (ex: 23/23 checks pass).
3.3.5 Role et limites¶
Role dans le pipeline : Z sert de contrat mathematique auditable. La mecanisation actuelle est un lint structurel ; la preuve des theoremes Z est revue par un auditeur humain. Z remplit trois fonctions complementaires aux autres formalismes :
- Specification mathematique de reference — Les schemas Z constituent la definition la plus rigoureuse des structures de donnees, des invariants et des operations. Ils servent de source de verite formelle pour les auditeurs, exprimee en theorie des ensembles et logique des predicats.
- Lint structurel automatise — Le linter Z verifie mecaniquement que les colonnes requises, les enumerations et les schemas du code correspondent a ceux de la specification.
- Tracabilite exigences → artefacts — Les invariants INV-14 a INV-17 (specifiques a Z) enrichissent la matrice de tracabilite avec des proprietes que TLA+ et Alloy n'expriment pas (pessimistic locking, reorg handling).
Limites :
- Pas de prouveur automatique : contrairement a TLA+ (TLC) et Alloy (SAT), Z n'a pas de prouveur automatique dans ce pipeline. La verification mecanisee est structurelle (lint), pas semantique. Les theoremes Z (THM-01 a THM-09) sont formules mais non prouves mecaniquement — ils servent de contrats formels pour review humaine ou outillee ulterieurement (ex: Z/EVES, ProofPower-Z).
- Invariants supplementaires : les invariants INV-14 a INV-17 sont specifiques a Z et ne sont pas verifies mecaniquement. Ils sont neanmoins traces et documentes dans le rapport d'audit.
3.4 Prolog — Verification de conformite directe¶
3.4.1 Nature de la verification¶
SWI-Prolog est utilise comme moteur de verification factuelle directe : les exigences du RFC sont traduites en regles Prolog (check_*/0), et les artefacts du code source sont traduits en faits Prolog (entity/3, service_method/2, etc.). L'evaluation des regles contre les faits determine si chaque exigence est satisfaite.
Prolog apporte la verification la plus concrete : il ne raisonne pas sur des abstractions mais sur les artefacts reels du code — entites de base de donnees, methodes de service, triggers SQL, ressources Terraform.
Deux niveaux de regles sont definis :
- Compliance (
*_compliance.pl) : checks bloquants. Tout echec constitue une non-conformite. - Completeness (
*_completeness.pl) : checks informatifs. Les echecs sont des recommandations, pas des non-conformites.
3.4.2 Comment les exigences RFC deviennent des predicats¶
Chaque exigence du RFC se traduit en un ou plusieurs predicats check_*/0. Un predicat utilise le mecanisme de cut (!) pour produire un verdict unique et lisible : la premiere clause qui reussit est definitive, et le backtracking est coupe. La base de faits peut contenir plusieurs correspondances (ex: variantes de nommage createBatch / create), la regle selectionne la premiere strategie valide et emet un seul [OK] ou [KO].
Check structurel (l'artefact existe) :
%% CHECK 28 : Colonne confirmation_count (ANCHOR-16)
check_confirmation_count_col :-
entity_column(anchor_batch, confirmation_count, _), !,
format(" [OK] final : colonne anchor_batch.confirmation_count (ANCHOR-16)~n"),
record_ok.
check_confirmation_count_col :-
entity_column(anchor_batch, confirmations, _), !,
format(" [OK] final : colonne anchor_batch.confirmations (ANCHOR-16)~n"),
record_ok.
check_confirmation_count_col :-
format(" [KO] final : colonne confirmation_count/confirmations ABSENTE (ANCHOR-16)~n"),
record_ko.
Check semantique (l'artefact est correctement utilise) :
%% CHECK 29 : Garde confirmations dans finalizeBatch (ANCHOR-16 — semantique)
check_finalize_confirmations_guard :-
service_method_guard(anchor_batch, finalizeBatch, confirmation_count), !,
format(" [OK] final : finalizeBatch() verifie confirmation_count (ANCHOR-16)~n"),
record_ok.
check_finalize_confirmations_guard :-
format(" [KO] final : finalizeBatch() SANS garde confirmation_count (ANCHOR-16)~n"),
record_ko.
Tolerance aux variations de nommage : les checks acceptent des variantes courantes (ex: createBatch ou create, finalizeBatch ou finalize, anchor_batch ou anchor_batches). Cette tolerance evite les faux negatifs dus aux conventions de nommage.
Le modele PV Anchor comprend 32 checks de compliance (dont ANCHOR-16 finality depth et ANCHOR-17 signer revocation, ajoutes avec PD-276) et 4 checks de completeness.
3.4.3 Comment le code source devient des predicats¶
Le script extract-facts.py genere un fichier _generated-facts.pl contenant l'ensemble des faits extraits du code source. Les predicats generes sont :
| Predicat | Arite | Source d'extraction | Exemple |
|---|---|---|---|
entity/3 | slug, schema, table | Decorateur @Entity() TypeORM | entity(anchor_batch, 'vault_tsa', 'anchor_batches'). |
entity_column/3 | entity, column, type | Decorateur @Column() TypeORM | entity_column(anchor_batch, merkle_root, 'varchar'). |
entity_enum_values/3 | entity, column, values | Enum TypeScript dans @Column({type: 'enum'}) | entity_enum_values(anchor_batch, status, ['PENDING', ...]). |
entity_index/2 | entity, index_name | Decorateur @Index() TypeORM | entity_index(anchor_batch, 'idx_batch_status'). |
service/2 | slug, file_path | Classe @Injectable() NestJS | service(anchor_batch, 'src/.../anchor-batch.service.ts'). |
service_method/2 | service, method | Methode publique de service | service_method(anchor_batch, finalizeBatch). |
service_method_guard/3 | service, method, guarded_field | Analyse statique des gardes | service_method_guard(anchor_batch, finalizeBatch, confirmation_count). |
table_trigger/3 | table, name, type | Instructions SQL dans les migrations | table_trigger(anchor_batch, 'prevent_update_delete_finalized', immutable). |
table_truncate_revoked/1 | table | REVOKE TRUNCATE dans migrations | table_truncate_revoked(audit_log). |
s3_bucket/3 | name, property, value | Resource Terraform aws_s3_bucket | s3_bucket('pv-worm', compliance_mode, 'COMPLIANCE'). |
hsm_cluster/3 | name, region, mode | Resource Terraform aws_cloudhsm | hsm_cluster('probatiovault-hsm', 'eu-west-3', 'FIPS'). |
crr/3 | bucket, property, value | Regle de replication S3 | crr('pv-worm', enabled, true). |
service_method_guard_cmp/5 | service, method, field, operator, comparand | Analyse statique des comparaisons | service_method_guard_cmp(anchor_batch, finalizeBatch, confirmation_count, ge, required_confirmations). |
cron_job/3 | service, method, schedule | Decorateur @Cron() NestJS | cron_job(document, 'autoPurge', 'EVERY_DAY_AT_3AM'). |
schema_exists/1 | schema_name | Instructions SQL CREATE SCHEMA | schema_exists(vault_secure). |
test_covers/2 | test_file, invariant_id | Assertions describe/it dans specs | test_covers('anchor-batch.service.spec.ts', 'INV-55-02'). |
geo_copy_count/2 | bucket, count | Regles CRR Terraform | geo_copy_count(archives_frankfurt, 2). |
Le predicat service_method_guard/3 est le plus critique pour la verification semantique. Il est extrait par analyse statique du code des methodes de service : si la methode finalizeBatch contient une condition sur confirmation_count (comparaison, assertion, ou validation), le fait service_method_guard(anchor_batch, finalizeBatch, confirmation_count) est genere.
3.4.4 Verification a deux niveaux¶
Le pattern de verification a deux niveaux est illustre par ANCHOR-16 (finality depth) :
Niveau 1 — Structurel : la colonne confirmation_count existe dans l'entite anchor_batch.
Niveau 2 — Semantique : la methode finalizeBatch() contient une garde sur confirmation_count.
service_method_guard(anchor_batch, finalizeBatch, confirmation_count).
→ check_finalize_confirmations_guard : [OK]
Sans le niveau 2, un systeme pourrait declarer la colonne sans jamais la verifier. Le service_method_guard/3 ferme cette faille.
Le meme pattern s'applique a ANCHOR-17 (signer revocation) :
| Check | Niveau | Predicat verifie | Exigence |
|---|---|---|---|
| CHECK 30 | Structurel | entity_column(anchor_batch, signer_status, _) | Colonne signer_status existe |
| CHECK 31 | Structurel | service_method(anchor_batch, revokeSigner) | Methode revokeSigner() existe |
| CHECK 32 | Semantique | service_method_guard(anchor_batch, submitBatch, signer_active) | submitBatch() verifie que le signer est ACTIVE |
3.4.5 Comment l'inclusion est prouvee¶
Le predicat run_audit/0 orchestre l'execution sequentielle de tous les checks :
run_audit :-
reset_counters,
%% ... invocation de chaque check_*/0 ...
count_results(OK, KO),
Total is OK + KO,
format(" Resultat : ~d/~d OK", [OK, Total]).
Pour chaque check :
- SWI-Prolog tente de resoudre le predicat contre la base de faits chargee.
- Si un fait correspondant est trouve →
[OK], le compteurcheck_okest incremente. - Si aucun fait ne correspond →
[KO], le compteurcheck_koest incremente.
Le verdict final est CONFORME si KO = 0, sinon les echecs sont listes.
Execution :
# Avec les faits generes automatiquement (production)
swipl -l _generated-facts.pl -l pv_anchor_compliance.pl -g "run_audit." -t "halt."
# Avec les faits statiques (test local)
swipl -l test_data.pl -l pv_anchor_compliance.pl -g "run_audit." -t "halt."
3.4.6 Fallback statique¶
Le fichier test_data.pl fournit un ensemble de faits statiques equivalents aux faits generes. Il permet :
- L'execution des checks en local sans le pipeline complet.
- La validation des regles de compliance independamment de l'etat du code.
Le script verify-norm.sh utilise _generated-facts.pl en priorite. Si ce fichier n'existe pas, il se rabat sur test_data.pl.
3.4.7 Limites¶
- Analyse statique : l'extraction parse les decorateurs et noms de methodes, pas le comportement a l'execution. Un decorateur
@Column()incorrectement configure pourrait etre extrait sans erreur. - Extraction AST ts-morph (depuis v1.3.0) : l'extracteur principal utilise le parsing AST TypeScript (
extract-ts-facts.ts). Les anciens extracteurs regex sont conserves comme fallback automatique si ts-morph echoue (Node.js absent, timeout). Les extracteurs non-TypeScript (SQL, Terraform, tests, docs) restent en regex. Cf. Section 9.5.3 pour les metriques de migration. - Granularite semantique des gardes : le predicat
service_method_guard/3detecte la presence d'une garde, mais pas la semantique. Le predicat enrichiservice_method_guard_cmp/5(operateur + constante comparee) est desormais implemente (cf. Section 9.5.1) et les checks Prolog tentent d'abord/5puis fallback sur/3. L'extracteurextract-guards.ts(ts-morph) detecte les gardes directes et les gardes indirectes cross-composant viaguard_mappingsdansnorm.yaml. - Tolerance aux variantes : la tolerance de nommage (
createBatch/create) peut produire des faux positifs si un service expose une methodecreatenon liee au batch.
4. Pipeline d'extraction¶
4.1 Architecture de extract-facts.py¶
Le script extract-facts.py est le composant central du pipeline d'extraction. Il analyse le code source reel de ProbatioVault et genere les fichiers de faits dans les 4 formats formels. Le principe architectural est un AST, N sorties : un seul pass d'extraction alimente tous les formalismes.
4.1.1 Extracteur principal — ts-morph AST (depuis v1.3.0)¶
Le script extract-ts-facts.ts (~730 lignes) effectue un chargement unique du Project ts-morph (~3-5s) puis extrait 6 categories de faits en un seul pass AST :
| Categorie | Predicats extraits | Methode AST |
|---|---|---|
| A. Entites | entity/3, entity_column/3, entity_enum_values/3, entity_index/2 | getDecorators() sur @Entity, @Column, @Index, resolution cross-fichier des enums |
| B. Services | service/2, service_method/2 | getMethods() + getModifiers() pour filtrer public/private |
| C. Guards NestJS | service/2 (classes *Guard) | getClasses() sur *.guard.ts |
| D. Schedulers | cron_job/3 | getDecorator('Cron') avec arguments types |
| E. Gardes semantiques | service_method_guard/3, service_method_guard_cmp/5 | getDescendantsOfKind() + guard_mappings de norm.yaml |
La classe TsMorphExtractor dans extract-facts.py appelle extract-ts-facts.ts en subprocess. Si ts-morph echoue (Node.js absent, timeout), le fallback automatique vers les extracteurs regex se declenche (cf. Section 9.5.3).
4.1.2 Extracteurs complementaires (regex — sources non-TypeScript)¶
| Extracteur | Source | Artefacts extraits |
|---|---|---|
| MigrationExtractor | *Migration*.ts | table_trigger/3, table_truncate_revoked/1, schema_exists/1 |
| TerraformExtractor | *.tf | s3_bucket/3, hsm_cluster/3, crr/3 |
| TestExtractor | *.spec.ts | References INV-\d+-\d+ (251 faits) |
| DerivedFactsGenerator | Faits extraits | Faits derives (inferences, transitivite) |
4.1.3 Generateurs de sortie¶
Les faits intermediaires unifies alimentent 4 generateurs de sortie, un par formalisme :
| Sortie | Fonction | Profondeur d'ancrage |
|---|---|---|
_generated-facts.pl (Prolog) | write_prolog_facts() | Semantique — exploite les 9 predicats dont guard_cmp/5 |
_AnchoredFacts_*.tla (TLA+) | write_tla_constants() | Presence — booleens et ensembles (RealStates, RealHasHSM) |
_AnchoredFacts_*.als (Alloy) | write_alloy_facts() | Presence — singletons one sig Real_* |
_generated-z-lint.yaml (Z) | write_zlint_config() | Structurel — colonnes requises, enums, filtrage par norme |
4.2 Sources de donnees¶
Le script accepte deux sources principales :
python3 extract-facts.py \
--backend /path/to/ProbatioVault-backend/src \
--infra /path/to/ProbatioVault-infra/terraform \
--output-nf /path/to/normes/nf-z42-013/formal/_generated-facts.pl \
--output-anchor /path/to/normes/pv-anchor/formal/_generated-facts.pl
--backend: code source TypeScript (entites TypeORM, services NestJS, migrations, guards).--infra: code Terraform HCL (buckets S3, clusters CloudHSM, regles CRR).
4.3 Quatre sorties par norme¶
Pour chaque norme, le script genere :
| Sortie | Format | Contenu |
|---|---|---|
_generated-facts.pl | Prolog | Ensemble complet de faits (entites, colonnes, services, methodes, gardes, triggers, infrastructure) |
_AnchoredFacts_{MODULE}.tla | TLA+ | Constantes reelles + statements ASSUME |
_AnchoredFacts_{MODULE}.als | Alloy | Singletons one sig Real_* + facts + assertion combinee |
_generated-z-lint.yaml | YAML | Mapping entites → schemas Z, colonnes requises, enums, section anchored: |
Chaque fichier genere porte le commentaire :
4.4 Faits derives¶
Le DerivedFactsGenerator calcule des faits secondaires a partir des faits bruts. Exemple : si un bucket S3 a l'Object Lock en mode COMPLIANCE et une regle CRR active, le fait derive geo_copy_count(bucket, 2) est genere, indiquant que les donnees sont repliquees geographiquement.
4.5 Invariants du pipeline¶
Le pipeline d'extraction respecte trois invariants :
- Determinisme : pour une meme base de code en entree, la sortie est toujours identique. Pas de dependance a l'horloge, a l'ordre de parcours du systeme de fichiers, ou a un etat externe.
- Pas de curation manuelle : les fichiers generes ne sont jamais edites manuellement. Toute modification du code source est automatiquement refletee dans les faits generes au prochain build CI.
- Regeneration systematique : le pipeline est execute a chaque build CI. Les fichiers generes sont des artefacts derives, pas des sources.
5. Pipeline de verification¶
5.1 Architecture de verify-norm.sh¶
Le script verify-norm.sh orchestre 6 etapes de verification sequentielles :
| Etape | Outil | Entree | Sortie | Bloquant |
|---|---|---|---|---|
| 1 | TLC (Java) | {Spec}.tla + _AnchoredFacts_*.tla + .cfg | JUnit XML | Oui |
| 2 | SWI-Prolog | _generated-facts.pl + *_compliance.pl | JUnit XML | Oui |
| 3 | Alloy (Java) | {Model}.als + _AnchoredFacts_*.als | JUnit XML | Oui |
| 4 | z-notation-lint.py | {Spec}.zed + _generated-z-lint.yaml | JUnit XML | Oui |
| 5 | SWI-Prolog | _generated-facts.pl + *_completeness.pl | JUnit XML | Non |
| 6 | SWI-Prolog | _compiled-verdicts.pl + compliance_scoring.pl | Log | Non |
L'ordre est significatif : TLC (etape 1) verifie le design avant Prolog (etape 2) qui verifie l'implementation. Alloy (etape 3) et Z (etape 4) apportent des perspectives complementaires.
5.2 Manifest norm.yaml¶
Chaque norme possede un fichier norm.yaml qui declare ses fichiers formels :
name: "PV Blockchain Anchoring Protocol"
slug: pv-anchor
tla:
spec: PV_Anchor.tla
config: PV_Anchor.cfg
anchored_facts: _AnchoredFacts_ANCHOR.tla
state_set_name: StatusValues
state_mapping:
PENDING: PENDING
BUILDING: BUILDING
SUBMITTED: SUBMITTED
PENDING_FINALITY: PENDING_FINALITY
FINALIZED: FINALIZED
FAILED: FAILED
prolog:
rules: pv_anchor_compliance.pl
completeness: pv_anchor_completeness.pl
facts: _generated-facts.pl
test_data: test_data.pl
alloy:
model: PV_Anchor.als
z:
spec: PV_Anchor.zed
lint_config: _generated-z-lint.yaml
lint_config_static: z-lint.yaml
Le champ state_mapping est critique : il mappe les valeurs d'enum du code aux etats du modele TLA+. Si le code utilise PENDING_FINALITY mais le modele attend WAITING_CONFIRMATION, le mapping detecte l'incoherence.
5.3 Integration CI/CD GitLab¶
Le pipeline CI/CD GitLab execute la verification formelle automatiquement :
extract-facts ──► verify:pv-anchor ──► compile-verdicts
verify:pv-envelope
verify:pv-audit
verify:pv-proof
verify:pv-pre
verify:nf-z42-013
verify:iso-14641
verify:rfc-3161
verify:rfc-5054
- Job
extract-facts: clone les reposbackendetinfra, executeextract-facts.py, genere les fichiers de faits pour toutes les normes. - Jobs
verify:{slug}: un job par norme, executeverify-norm.sh. Les jobs s'executent en parallele. Chaque job produit un artefact JUnit XML pour le reporting GitLab. - Job
compile-verdicts: agregation des baselines et overlays en verdicts Prolog compilees.
Les jobs sont declenches sur modification des fichiers docs/normes/*/formal/* ou scripts/formal/*.
5.4 Interpretation des resultats¶
Chaque formalisme a ses criteres d'echec :
| Formalisme | Critere d'echec | Message type |
|---|---|---|
| TLC | Error: ou is violated dans le log | Invariant FinalityDepthEnforced is violated. |
| Prolog | [KO] present dans la sortie | [KO] final : colonne confirmation_count ABSENTE |
| Alloy | counterexample dans le log | Counterexample found for FinalityDepthCheck |
| Z-lint | Code de sortie non-zero | FAIL: required column 'confirmation_count' not found |
5.5 Codes de sortie¶
Le script verify-norm.sh distingue les echecs de verification (violations formelles detectees) des erreurs d'outil (crash, timeout, fichier manquant) :
| Code | Signification | Action CI |
|---|---|---|
0 | Toutes les verifications passent | Pipeline vert |
1 | Au moins 1 violation formelle detectee | Pipeline rouge — corriger le code ou le modele |
2 | Au moins 1 erreur outil (verification incomplete) | Pipeline rouge — corriger l'environnement (Java, swipl, jars) |
3 | Violations formelles ET erreurs outil | Pipeline rouge — corriger les deux |
Les etapes non-bloquantes (completeness, compliance scoring) ne modifient pas le code de sortie. Les outils manquants (java, swipl) generent un [SKIP], pas un [ERROR] — seuls les crashs d'outils presents declenchent le code 2.
6. Mecanisme d'ancrage¶
Le mecanisme d'ancrage est le differenciateur fondamental de la verification formelle ProbatioVault par rapport aux approches traditionnelles. Il garantit que les modeles formels ne sont pas des abstractions deconnectees du code reel.
6.1 Principe¶
Dans la verification formelle traditionnelle, un modele est ecrit manuellement et peut diverger silencieusement du code au fil des evolutions. ProbatioVault elimine ce risque en generant automatiquement des fichiers d'ancrage qui lient les constantes et structures du modele aux artefacts reels du code.
Si le code evolue et qu'un artefact requis par le modele disparait ou change, la verification echoue immediatement.
6.2 Ancrage TLA+¶
Le module _AnchoredFacts_{MODULE}.tla utilise le mecanisme EXTENDS de TLA+ pour heriter du modele principal et ajouter des contraintes ASSUME :
---- MODULE _AnchoredFacts_ANCHOR ----
EXTENDS PV_Anchor
RealStates == {"PENDING", "BUILDING", "SUBMITTED",
"PENDING_FINALITY", "FINALIZED", "FAILED"}
ASSUME AnchorAssume_States ==
/\ RealStates \subseteq StatusValues
/\ StatusValues \subseteq RealStates \* egalite stricte
RealSignerStatuses == {"ACTIVE", "REVOKED"}
ASSUME AnchorAssume_SignerStatuses ==
/\ RealSignerStatuses \subseteq SignerStatusValues
/\ SignerStatusValues \subseteq RealSignerStatuses
RealHasHSM == TRUE
RealHasWORM == TRUE
RealHasCRR == TRUE
RealHasJournalTrigger == TRUE
RealHasTruncateRevoked == TRUE
ASSUME AnchorAssume_Infrastructure ==
/\ RealHasHSM /\ RealHasWORM /\ RealHasCRR
/\ RealHasJournalTrigger /\ RealHasTruncateRevoked
RealHasConfirmationCount == TRUE
ASSUME AnchorAssume_FinalitySupport ==
RealHasConfirmationCount
====
Controle de compatibilite stricte pour les types : le pipeline genere RealStates directement depuis les enums du code. L'ASSUME RealStates ⊆ StatusValues echoue si le code declare un etat que le modele ne connait pas (ex: ARCHIVED). Un second ASSUME StatusValues ⊆ RealStates echoue si un etat du modele n'est pas implemente dans le code. Les deux inclusions combinees imposent l'egalite RealStates = StatusValues.
6.3 Ancrage Alloy¶
Le module _AnchoredFacts_{MODULE}.als utilise des singletons one sig :
module _AnchoredFacts_ANCHOR
open PV_Anchor
one sig Real_anchor_batch_tx_id {}
one sig Real_anchor_batch_merkle_root {}
one sig Real_anchor_batch_confirmation_count {}
one sig Real_anchor_batch_signer_status {}
one sig Real_WORM_bucket {}
one sig Real_HSM_FIPS {}
one sig Real_journal_trigger {}
fact AnchoredTSAAndAnchoring {
some Real_anchor_batch_tx_id
some Real_anchor_batch_merkle_root
some Real_anchor_batch_confirmation_count
some Real_anchor_batch_signer_status
}
assert AnchoredArtifactsConsistent {
some Real_anchor_batch_tx_id and
some Real_anchor_batch_merkle_root and
some Real_anchor_batch_confirmation_count and
some Real_anchor_batch_signer_status and
some Real_WORM_bucket and
some Real_HSM_FIPS and
some Real_journal_trigger
}
check AnchoredArtifactsConsistent for 4
Les one sig ont un cout de scope nul : ils representent un atome unique qui existe toujours, sans augmenter le nombre de configurations a explorer.
6.4 Ancrage Z¶
Le fichier _generated-z-lint.yaml contient une section anchored: qui mappe les entites reelles aux schemas Z attendus :
anchored:
entities:
- name: anchor_batch
expected_in_schemas: [Fingerprint]
columns:
- block_number
- chain_id
- merkle_root
- signer_address
- status
- tx_id
- window_end
- window_start
Le linter Z verifie que les required_columns du modele sont un sous-ensemble des columns reelles.
6.5 Ancrage Prolog¶
Le fichier _generated-facts.pl est l'ancrage le plus direct : chaque fait represente un artefact reel du code. L'absence d'un fait entraine directement l'echec du check correspondant.
Exemple : si l'entite anchor_batch est supprimee du code, le fait entity(anchor_batch, _, _) disparait du fichier genere, et le CHECK 1 echoue immediatement.
6.6 Cycle de vie¶
Code modifie ──► git push ──► CI/CD Pipeline
│
extract-facts.py
│
extract-ts-facts.ts
(ts-morph AST — 1 seul pass, ~3-5s)
│
Faits intermediaires unifies
(entity/3, service_method/2,
guard_cmp/5, cron_job/3, ...)
│
┌───────┬───────┼───────┬───────┐
▼ ▼ ▼ ▼ ▼
.pl .tla .als .yaml (futur)
Prolog TLA+ Alloy Z-lint .thy/.spthy
(=100%) (~20%) (~20%) (struct) Isabelle/
Tamarin
│ │ │ │
└───────┴───┬───┴───────┘
▼
verify-norm.sh
│
▼
AUDIT-REPORT.md
(verdict mis a jour)
Profondeur d'exploitation de l'AST par formalisme :
| Formalisme | Profondeur actuelle | Gain possible |
|---|---|---|
| Prolog | Semantique (100% — 9 predicats dont guard_cmp/5) | — |
| TLA+ | Presence (~20% — booleens, ensembles) | Injecter guard_cmp/5 en ASSUME pour ancrage semantique |
| Alloy | Presence (~20% — singletons) | Injecter types depuis entity_column/3 |
| Z-lint | Structurel (colonnes, enums) | Filtrage par norme (corrige en v1.4.1) |
Ce cycle garantit que toute modification du code est immediatement refletee dans les modeles formels et verifiee.
7. Piste d'audit¶
7.1 Generation de AUDIT-REPORT.md¶
Le rapport d'audit est genere automatiquement par le script generate-audit-report.py v2. Il consolide les resultats des 4 systemes formels en un document unique, trace et datable.
7.2 Structure du rapport¶
Chaque AUDIT-REPORT.md suit une structure normalisee :
| Section | Contenu |
|---|---|
| 1. Synthese executive | Verdict global, tableau recapitulatif par systeme formel |
| 2. Perimetre et methodologie | 4 systemes formels, strategie d'ancrage, sources des donnees |
| 3. TLA+ — Model Checking | Invariants verifies, actions, espace d'etats explore |
| 4. Alloy — Analyse structurelle | Facts, assertions, commandes check, resultats SAT |
| 5. Z Notation — Specification | Invariants, theoremes, resultats lint |
| 6. Prolog — Conformite | Checks 1 a N, resultats detailles [OK]/[KO] |
| 7. Prolog — Completude | Checks informatifs [OK]/[--] |
| 8. Score de conformite | Score dynamique baselines + overlays |
| 9. Analyse des ecarts | Liste des non-conformites, criticite, artefacts concernes |
| 10. Matrice de couverture | Mapping exigences → modeles → resultats |
| 11. Conclusion | Verdict final, recommandations |
7.3 Verdicts¶
Trois verdicts sont possibles :
| Verdict | Critere |
|---|---|
| CONFORME | Tous les checks de compliance passent, tous les model checks passent, toutes les assertions SAT tiennent |
| PARTIELLEMENT CONFORME | La majorite des checks passent mais des echecs sont identifies (avec gap analysis) |
| NON CONFORME | Echecs critiques sur des exigences fondamentales |
7.4 Score de conformite dynamique¶
Le score de conformite est calcule par le module compliance_scoring.pl :
Le score evolue dans le temps grace au systeme de baselines et overlays :
- Baseline (
baseline.yaml) : verdicts initiaux issus d'un audit multi-agents (ex: PD-244). - Overlays (
overlays/) : verdicts mis a jour suite a des stories completees (Gate 8 GO/RESERVE). - Compilation :
compile-verdicts.pyfusionne baselines + overlays en_compiled-verdicts.pl, un fichier Prolog chargeable directement.
8. Tracabilite¶
8.1 Matrice de tracabilite¶
Le fichier _traceability-matrix.yaml fournit une matrice de couverture globale. Chaque article normatif est mappe a :
- Article : reference normative (ex:
art_5_6_fingerprint) - Description : texte de l'exigence
- Prolog Rule : predicat de verification (ex:
check_5_6_fingerprint) - Status :
COVERED,PARTIEL,GAP,HORS_PERIMETRE - Artifacts : fichiers source et cles d'implementation
Exemple :
pv_anchor:
anchor_16_finality:
article: '3.16'
description: 'Politique de finalite configurable (FINALITY_DEPTH)'
prolog_rule: 'check_confirmation_count_col, check_finalize_confirmations_guard'
status: COVERED
artifacts:
- path: 'src/modules/tsa/entities/anchor-batch.entity.ts'
key: 'column:anchor_batch:confirmation_count'
- path: 'src/modules/tsa/services/anchor-batch.service.ts'
key: 'method:anchor_batch:finalizeBatch:guard:confirmation_count'
8.2 Tracabilite croisee¶
Chaque exigence RFC est tracee a travers les 4 formalismes. La matrice de tracabilite du RFC (Section 7.2 du RFC) formalise ce mapping :
| Exigence | TLA+ | Alloy | Z | Prolog |
|---|---|---|---|---|
| ANCHOR-01 (batch entity) | CreateBatch | AnchorBatch sig | Schema AnchorBatch | check_batch_entity |
| ANCHOR-16 (finality depth) | INV-12 FinalityDepthEnforced | FinalityDepthCheck | INV-12 | check_confirmation_count_col + check_finalize_confirmations_guard |
| ANCHOR-17 (signer revocation) | INV-13 SignerRevocationValid | SignerRevocationEnforced | INV-13 | check_signer_status_mechanism + check_signer_revoke_method + check_submit_active_signer_guard |
8.3 Manifest norm.yaml¶
Le fichier norm.yaml constitue le point d'entree technique de la tracabilite. Il lie :
- Les fichiers de modeles formels (TLA+, Alloy, Z) aux specifications RFC.
- Les fichiers d'ancrage aux faits generes.
- Les fichiers Prolog aux regles de compliance.
- Le mapping d'etats entre le code et les modeles.
9. Limites et perimetre¶
9.1 Limites inherentes aux methodes formelles¶
| Formalisme | Limite | Impact |
|---|---|---|
| TLA+ (TLC) | Model checking borne (MAX_BATCHES=1) | Preuve valide dans le scope, extrapolation via hypothese du petit scope |
| Alloy (SAT) | Scope borne (for 5) | Meme limitation, attenuee par la petite taille typique des contre-exemples |
| Z Notation | Pas de prouveur automatique | Verification structurelle uniquement, pas de preuve de theoreme |
| Prolog | Extraction AST ts-morph (fallback regex), analyse statique | Patterns non conventionnels pourraient echapper a l'extraction. L'AST couvre 96% des faits (cf. 9.5.3) |
9.2 Elements non verifies¶
- Performance et scalabilite du systeme.
- Correction des primitives cryptographiques (OpenSSL, CloudHSM, node:crypto).
- Securite reseau (TLS, pare-feu, routage).
- Interface utilisateur et experience utilisateur.
- Correction des dependances tierces (npm, drivers PostgreSQL).
- Sur-implementation (
SOURCE ⊆ RFC).
9.3 Risques residuels¶
- Gap d'abstraction : les modeles operent sur des valeurs abstraites (entiers pour les hashes, ensembles finis pour les identifiants). Un defaut specifique a une valeur reelle (ex: collision de hash) ne serait pas detecte.
- Espace d'etats non totalement explore : les constantes bornent l'exploration. Un defaut qui ne se manifeste qu'avec 3+ batches simultanees ne serait pas detecte avec
MAX_BATCHES=1. - Faux positifs de tolerance : la tolerance aux variantes de nommage (
createBatch/create) pourrait accepter une methode sans rapport avec le batch. - Modele sequentiel : TLA+ modelise la concurrence par entrelacement non-deterministe, pas par parallelisme reel. Les conditions de course specifiques au threading ne sont pas couvertes.
9.4 Mesures d'attenuation¶
- Defense en profondeur : les 4 formalismes couvrent mutuellement leurs angles morts. Un defaut qui echappe a TLA+ sera probablement detecte par Prolog ou Alloy.
- Extraction automatique : l'absence de curation manuelle elimine les risques de drift modele/code.
- Integration CI/CD : la verification est executee a chaque commit, detectant les regressions immediatement.
- Verification a deux niveaux : le pattern
service_method_guard/3ajoute une couche semantique au-dela de la simple presence structurelle. - Audits periodiques : les rapports
AUDIT-REPORT.mdsont regeneres et revises regulierement.
9.5 Feuille de route d'amelioration¶
Les limites identifiees ci-dessus font l'objet d'un plan d'amelioration progressif. Chaque amelioration est classee par priorite et impact.
9.5.1 Enrichissement semantique des gardes Prolog — FAIT¶
Statut : implemente (v1.2.0, 2026-02-28).
Realisation :
- Nouveau predicat
service_method_guard_cmp/5(Service, Method, Field, Operator, ComparedValue). - Extracteur
extract-guards.ts(ts-morph) scanne*.service.ts,*.processor.ts,*.tracker.tset detecte 4 patterns : comparaisons directes, status transitions, throw-on-missing, append-only checks. - Gardes indirectes cross-composant declarees dans
norm.yaml(guard_mappings) et verifiees contre l'AST. - Checks Prolog 29 et 32 unifies avec fallback
/5→/3(retrocompatible). - Integration dans
extract-facts.pyviaTsMorphGuardExtractor.
Resultats : 14 faits extraits automatiquement (6 /3 + 8 /5), dont 2 via guard_mappings (ANCHOR-16, ANCHOR-17). 32/32 checks OK.
9.5.2 Tests negatifs en CI — FAIT¶
Statut : implemente (v1.2.0, 2026-02-28).
Realisation :
- 6 scenarios negatifs dans
test_negative_data/(pv-anchor), chacun retirant un fait critique. - Script
verify-negative-tests.sh: pour chaqueneg_*.pl, execute l'audit Prolog et verifie que le[KO]attendu apparait. - Header machine-parseable
%% EXPECTED_KO:dans chaque fichier negatif. - Job CI
test:negative-verification:pv-anchordans.gitlab-ci.yml.
Scenarios couverts :
| Fichier | Fait retire | Check KO attendu |
|---|---|---|
neg_no_confirmation_count.pl | entity_column(anchor_batch, confirmation_count, _) | CHECK 28 |
neg_no_finalize_guard.pl | service_method_guard(anchor_batch, finalizeBatch, confirmation_count) | CHECK 29 |
neg_no_signer_status.pl | entity_column(anchor_batch, signer_status, _) | CHECK 30 |
neg_no_revoke_method.pl | service_method(anchor_batch, revokeSigner) | CHECK 31 |
neg_no_submit_guard.pl | service_method_guard(anchor_batch, submitBatch, signer_active) | CHECK 32 |
neg_no_merkle_root.pl | entity_column(anchor_batch, merkle_root, _) | CHECK 2 |
Resultats : 6/6 PASS — chaque non-conformite est correctement detectee.
9.5.3 Extraction AST TypeScript (Priorite : moyenne) — FAIT¶
Statut : implemente (v1.3.0). Le script extract-ts-facts.ts consolide l'extraction de TOUS les faits TypeScript via ts-morph AST. Les anciens extracteurs regex (EntityExtractor, ServiceExtractor, GuardExtractor, SchedulerExtractor) sont conserves comme fallback automatique dans extract-facts.py.
Script : scripts/formal/extract-ts-facts.ts (~730 lignes)
Architecture realisee : chargement unique du Project ts-morph (~3-5s), puis extraction en un seul pass AST de 6 categories de faits (9 predicats Prolog distincts).
| Categorie | Predicats extraits | Methode AST |
|---|---|---|
| A. Entities | entity/3, entity_column/3, entity_enum_values/3, entity_index/2 | getDecorators() sur @Entity, @Column, @Index, resolution cross-fichier des enums |
| B. Services | service/2, service_method/2 | getMethods() + getModifiers() pour filtrer public/private |
| C. Guards NestJS | service/2 (classes *Guard) | getClasses() sur *.guard.ts |
| D. Schedulers | cron_job/3 | getDecorator('Cron') avec arguments types |
| E. Gardes semantiques | service_method_guard/3, service_method_guard_cmp/5 | getDescendantsOfKind() + guard_mappings de norm.yaml |
Resultats de la migration (mesures reelles, backend ProbatioVault) :
| Metrique | Regex | ts-morph | Delta |
|---|---|---|---|
| Faits totaux | 1581 | 1724 | +143 (+9%) |
| Faits communs | — | 1523 | 96% de recouvrement |
| Regex-only | 58 | — | 55 faux positifs service (clearTimeout, on, COALESCE...) + 3 enums avec nom de colonne hardcode |
| TsMorph-only | — | 200 | Entites supplementaires (audit_event, key_record...) + enums Pattern B (varchar + TS type) + gardes semantiques |
| Enums detectes | 5 (avec nom hardcode status) | 38 (avec vrai nom de colonne) | +660% |
| Faux positifs service | ~55 (if, for, setTimeout...) | 0 | -100% |
Integration dans extract-facts.py : la classe TsMorphExtractor appelle extract-ts-facts.ts en subprocess. Si ts-morph echoue (Node.js absent, timeout), le fallback automatique vers les extracteurs regex se declenche. Les deux extracteurs doivent produire des sorties equivalentes — le taux de recouvrement mesure (96%, 1523 faits communs) sert de test de non-regression. Les anciens extracteurs regex sont conserves dans le code source pour permettre la comparaison a tout moment.
Metriques courantes (mesures reelles, 2026-03-01) :
| Source d'extraction | Predicats | Faits | % |
|---|---|---|---|
| ts-morph AST | entity, entity_column, entity_enum_values, entity_index, service, service_method, service_method_guard, service_method_guard_cmp, cron_job | 1951 | 85.8% |
| Regex non-TS | table_trigger, table_truncate_revoked, schema_exists, s3_bucket, crr, hsm_cluster, geo_copy_count, test_covers | 324 | 14.2% |
| Total | 17 predicats | 2275 | 100% |
Evolution : +551 faits depuis la migration v1.3.0 (1724 → 2275, +32%), principalement due a l'ajout d'entites (audit_event, clock_attestation, etc.), de gardes semantiques (service_method_guard_cmp/5) et d'index supplementaires.
Extracteurs restes en regex (non-TypeScript ou textuel) : - MigrationExtractor : patterns SQL dans les fichiers de migration - TerraformExtractor : patterns HCL dans les fichiers .tf - TestExtractor : pattern INV-\d+-\d+ dans les fichiers .spec.ts - DocumentExtractor : patterns de noms de fichiers dans docs/
9.5.4 Propriete de vivacite bornee TLA+ (Priorite : moyenne)¶
Probleme : le modele TLA+ verifie uniquement des proprietes de surete (safety). Aucune propriete de vivacite (liveness) n'est verifiee — par exemple, « un batch SUBMITTED finit par etre FINALIZED ou FAILED ».
Evolution cible : ajouter au moins une propriete de vivacite bornee (weak fairness) au modele TLA+ :
\* Liveness : chaque batch SUBMITTED atteint eventuellement FINALIZED ou FAILED
EventualFinality == \A id \in batches :
batch_status[id] = "SUBMITTED" ~> batch_status[id] \in {"FINALIZED", "FAILED"}
Prerequis : activation de la weak fairness dans la specification (WF_vars(Next)) et augmentation de MAX_ITERATIONS pour permettre l'exploration des traces de vivacite.
Impact : verifie l'absence de deadlocks dans le protocole d'ancrage (dans le scope explore).
9.5.5 Constantes normatives unifiees (Priorite : basse)¶
Probleme : la constante FINALITY_DEPTH apparait dans 4 fichiers (.tla, .als, .zed, .pl) avec des valeurs potentiellement differentes. Toute modification manuelle risque une incoherence.
Evolution cible : definir les constantes normatives dans norm.yaml et les injecter automatiquement dans les 4 modeles via extract-facts.py :
Le pipeline genererait alors les fichiers d'ancrage avec les constantes normatives comme source de verite unique.
9.5.6 Vocabulaire de bornage explicite (Priorite : basse)¶
Probleme : les rapports d'audit utilisent des termes comme « preuve » ou « garanti » pour des resultats qui sont strictement bornes (scope fini).
Evolution cible : standardiser le vocabulaire dans les rapports generes :
| Terme a eviter | Terme correct |
|---|---|
| « prouve que... » | « verifie dans le scope explore que... » |
| « garanti » | « satisfait pour les bornes configurees » |
| « aucune violation possible » | « aucune violation trouvee dans le scope for N » |
Cette normalisation sera integree dans le template de generate-audit-report.py.
10. Annexe A — Demonstration complete ANCHOR-16¶
Cette annexe demontre le parcours complet d'une seule exigence normative — ANCHOR-16 : Politique de finalite configurable — a travers les 4 formalismes, depuis le RFC jusqu'a la verification du code source.
10.1 Exigence normative (RFC)¶
ANCHOR-16 : Chaque chaine cible definit un seuil de finalite (
FINALITY_DEPTH) representant le nombre minimum de confirmations blockchain requises.FinalizeBatchexige que le nombre de confirmations du bloc contenant la transaction soit superieur ou egal aFINALITY_DEPTH.
Source : RFC PV-ANCHOR-001, Section 3.16.
10.2 TLA+ — Modelisation temporelle¶
Constante :
Variable :
Precondition de FinalizeBatch :
FinalizeBatch(id) ==
/\ id \in batches
/\ batch_status[id] = "PENDING_FINALITY"
/\ batch_confirmations[id] >= FINALITY_DEPTH
/\ batch_status' = [batch_status EXCEPT ![id] = "FINALIZED"]
/\ ...
Invariant INV-12 :
FinalityDepthEnforced ==
\A id \in batches :
batch_status[id] = "FINALIZED"
=> batch_confirmations[id] >= FINALITY_DEPTH
Theoreme :
TLC verifie que cet invariant est satisfait dans tous les etats atteignables. Avec FINALITY_DEPTH = 1 et ConfirmValues == 0..10, TLC explore toutes les combinaisons possibles et ne trouve aucune violation.
10.3 TLA+ — Ancrage code¶
\* _AnchoredFacts_ANCHOR.tla
RealHasConfirmationCount == TRUE \* entity_column(anchor_batch, confirmation_count, *)
ASSUME AnchorAssume_FinalitySupport ==
RealHasConfirmationCount
Ce fait est genere car extract-facts.py a detecte la colonne confirmation_count dans l'entite TypeORM anchor_batch. Si la colonne est supprimee du code, RealHasConfirmationCount passerait a FALSE et l'ASSUME echouerait.
10.4 Alloy — Analyse structurelle¶
Fait :
fact FinalityDepthEnforced {
all b : AnchorBatch |
b.status = Finalized implies
(one b.confirmations and b.confirmations >= 1)
}
Assertion :
assert FinalityDepthCheck {
all b : AnchorBatch |
b.status = Finalized implies
(one b.confirmations and b.confirmations >= 1)
}
check FinalityDepthCheck for 5
Le solveur SAT cherche un AnchorBatch avec status = Finalized et confirmations < 1 dans toutes les configurations possibles contenant jusqu'a 5 atomes par type. Aucun contre-exemple n'est trouve dans ce scope — l'assertion est satisfaite.
10.5 Alloy — Ancrage code¶
\* _AnchoredFacts_ANCHOR.als
one sig Real_anchor_batch_confirmation_count {}
\* entity_column(anchor_batch, confirmation_count, *)
Ce singleton est present dans fact AnchoredTSAAndAnchoring et assert AnchoredArtifactsConsistent, garantissant que la colonne existe dans le code reel.
10.6 Z — Specification mathematique¶
Invariant INV-12 dans ANCHOR_State :
│ ── INV-12 : FinalityDepthEnforced
│ ∀ bid : dom batches •
│ batches(bid).status = FINALIZED ⇒
│ batches(bid).confirmations ≥ finalityDepth
Precondition de FinalizeBatch :
┌─ FinalizeBatch ──────────────────────────────────────
│ ΔANCHOR_State
│ batchId? : BatchId
├───────────────────────────────────────────────────────────
│ batches(batchId?).status = PENDING_FINALITY
│ ── ANCHOR-16 : confirmations >= finalityDepth
│ batches(batchId?).confirmations ≥ finalityDepth
└───────────────────────────────────────────────────────────
Theoreme THM-09 :
── THM-09 : FinalizeBatch preserve INV-12 (finality depth enforced)
── ∀ batchId? •
── pre(FinalizeBatch) ⇒ confirmations ≥ finalityDepth
10.7 Z — Ancrage code¶
Le linter Z verifie que confirmation_count est presente dans les colonnes reelles extraites du code.
10.8 Prolog — Verification structurelle¶
%% CHECK 28 : Colonne confirmation_count (ANCHOR-16)
check_confirmation_count_col :-
entity_column(anchor_batch, confirmation_count, _), !,
format(" [OK] final : colonne anchor_batch.confirmation_count (ANCHOR-16)~n"),
record_ok.
Fait genere :
Resolution : le fait entity_column(anchor_batch, confirmation_count, 'integer') unifie avec la regle → [OK].
10.9 Prolog — Verification semantique¶
%% CHECK 29 : Garde confirmations dans finalizeBatch (ANCHOR-16 — semantique)
check_finalize_confirmations_guard :-
service_method_guard(anchor_batch, finalizeBatch, confirmation_count), !,
format(" [OK] final : finalizeBatch() verifie confirmation_count (ANCHOR-16)~n"),
record_ok.
Fait genere :
Ce fait est genere par extract-facts.py car l'analyse statique de la methode finalizeBatch() dans AnchorBatchService a detecte une verification sur confirmation_count (condition if, assertion, ou validation).
10.10 Synthese¶
| Couche | Artefact | Propriete verifiee | Resultat |
|---|---|---|---|
| TLA+ modele | PV_Anchor.tla : INV-12 | Invariant temporel : FINALIZED ⇒ confirmations >= FINALITY_DEPTH | Aucune violation trouvee |
| TLA+ ancrage | _AnchoredFacts_ANCHOR.tla : ASSUME | Colonne confirmation_count presente dans le code | ASSUME verifie |
| Alloy modele | PV_Anchor.als : FinalityDepthCheck | Coherence structurelle : FINALIZED ⇒ confirmations >= 1 | Aucun contre-exemple (scope 5) |
| Alloy ancrage | _AnchoredFacts_ANCHOR.als : Real_* | Singleton artefact present | Coherent |
| Z modele | PV_Anchor.zed : INV-12, THM-09 | Pre-condition mathematique : confirmations >= finalityDepth | Lint OK |
| Z ancrage | _generated-z-lint.yaml | Colonne requise presente | Present |
| Prolog structurel | CHECK 28 : check_confirmation_count_col | Colonne existe dans l'entite | [OK] |
| Prolog semantique | CHECK 29 : check_finalize_confirmations_guard | Garde existe dans la methode | [OK] |
Conclusion : l'exigence ANCHOR-16 est formellement verifiee par les 4 systemes, tant au niveau du design (TLA+, Alloy, Z) qu'au niveau de l'implementation (Prolog structurel + semantique), avec un ancrage automatique au code source reel.
11. Annexe B — Arborescence des fichiers¶
Structure type d'une norme (exemple : pv-anchor) :
normes/pv-anchor/
│
├── RFC-PV-ANCHOR.md # Source normative (verite)
│
└── formal/
│
├── norm.yaml # Manifest (mapping fichiers ↔ formalismes)
│
│── PV_Anchor.tla # Modele TLA+ (13 invariants, 11 actions)
│── PV_Anchor.cfg # Config TLC (constantes, invariants)
│── _AnchoredFacts_ANCHOR.tla # Code → TLA+ (ASSUME genere)
│
│── PV_Anchor.als # Modele Alloy (12 facts, 12 assertions)
│── _AnchoredFacts_ANCHOR.als # Code → Alloy (one-sig genere)
│
│── PV_Anchor.zed # Specification Z (17 invariants, 9 theoremes)
│── z-lint.yaml # Config Z lint statique (source de verite)
│── _generated-z-lint.yaml # Code → Z (colonnes/enums generes)
│
│── pv_anchor_compliance.pl # RFC → Prolog (32 checks bloquants)
│── pv_anchor_completeness.pl # Recommandations (4 checks informatifs)
│── test_data.pl # Fallback statique (tests locaux)
│── _generated-facts.pl # Code → Prolog (faits generes)
│
│── AUDIT-REPORT.md # Rapport d'audit genere
│
└── compliance/
├── _compiled-verdicts.pl # Verdicts compiles (baselines + overlays)
├── baseline.yaml # Verdicts initiaux
└── overlays/ # Verdicts mis a jour par story
Convention de nommage :
| Prefixe | Signification |
|---|---|
_generated-* | Fichier genere automatiquement par extract-facts.py. Ne pas editer. |
_AnchoredFacts_* | Module d'ancrage code → modele formel. Genere automatiquement. |
_compiled-* | Fichier compile par compile-verdicts.py. Ne pas editer. |
| Sans prefixe | Fichier source, edite manuellement (modele, regles, config). |
12. Annexe C — Glossaire¶
| Terme francais | Terme anglais | Definition |
|---|---|---|
| Analyse structurelle | Structural analysis | Verification de coherence des relations entre entites (Alloy) |
| Ancrage | Anchoring | Liaison automatique entre un modele formel et le code source reel |
| Check bloquant | Blocking check | Verification dont l'echec constitue une non-conformite |
| Check informatif | Informational check | Verification dont l'echec est une recommandation, pas une non-conformite |
| Contre-exemple | Counterexample | Instance concrete violant une assertion (Alloy) ou un invariant (TLA+) |
| Defense en profondeur | Defense in depth | Combinaison de plusieurs formalismes pour couvrir differentes classes de defauts |
| Espace d'etats | State space | Ensemble de tous les etats atteignables du systeme (TLA+) |
| Fait (Prolog) | Fact | Enonce vrai dans la base de connaissances, representant un artefact du code |
| Garde | Guard | Precondition metier verifiee par une methode avant d'executer une operation |
| Hypothese du petit scope | Small scope hypothesis | Les defauts de conception se manifestent generalement dans de petites instances |
| Invariant | Invariant | Propriete qui doit etre vraie dans chaque etat atteignable du systeme |
| Model checking | Model checking | Exploration exhaustive de l'espace d'etats pour verifier des proprietes (TLC) |
| Preuve bornee | Bounded proof | Preuve valide pour un scope fini (nombre d'atomes ou de batches) |
| Propriete de surete | Safety property | Propriete affirmant qu'un « mauvais » etat n'est jamais atteint |
| Propriete de vivacite | Liveness property | Propriete affirmant qu'un « bon » etat est eventuellement atteint |
| Schema Delta (Δ) | Delta schema | Schema Z decrivant une operation qui modifie l'etat |
| Schema Xi (Ξ) | Xi schema | Schema Z decrivant une operation qui n'observe l'etat sans le modifier |
| Solveur SAT | SAT solver | Algorithme determinant la satisfiabilite d'une formule booleenne |
| Theoreme de preservation | Preservation theorem | Preuve qu'une operation maintient un invariant |
| UNSAT | Unsatisfiable | Absence de contre-exemple — l'assertion tient dans le scope explore |
| Verification formelle | Formal verification | Utilisation de methodes mathematiques pour prouver des proprietes d'un systeme |