PD-52 — Plan d'implémentation¶
Story : PD-52 — Setup connexion Ethereum L2 (Polygon/Arbitrum) Epic : PD-187 — BLOCKCHAIN Date : 2026-02-12 Auteur : Claude (orchestrateur IA) Étape : 4 — Plan d'implémentation
1. Synthèse du périmètre¶
Objectif¶
Livrer une capacité de connexion Ethereum L2 (Polygon/Arbitrum) avec : - Connectivité RPC multi-provider avec failover automatique - Gestion de wallet avec signature secp256k1 (custody modes S1/S2/S3) - Émission et confirmation de transactions sur testnet - Endpoint d'observabilité /health/blockchain
Métriques de succès¶
- 13 invariants à respecter
- 13 critères d'acceptation à satisfaire
- 14 codes d'erreur à implémenter
- Couverture tests ≥ 80%
2. Architecture technique¶
2.1 Vue d'ensemble des modules¶
┌─────────────────────────────────────────────────────────────────────┐
│ BlockchainModule │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ RpcProviderSvc │ │ CustodyService │ │ TransactionSvc │ │
│ │ │ │ │ │ │ │
│ │ - Polygon RPC │ │ - S1 (HSM) │ │ - Build TX │ │
│ │ - Arbitrum RPC │ │ - S2 (KMS) │ │ - Sign TX │ │
│ │ - Failover │ │ - S3 (Hybrid) │ │ - Send TX │ │
│ │ - Health check │ │ - Mode select │ │ - Confirm TX │ │
│ └────────┬────────┘ └────────┬────────┘ └────────┬────────┘ │
│ │ │ │ │
│ └────────────────────┼────────────────────┘ │
│ │ │
│ ┌─────────────────┐ ┌───────┴───────┐ ┌─────────────────┐ │
│ │ WalletService │ │ EthersAdapter │ │ HealthService │ │
│ │ │ │ │ │ │ │
│ │ - Address │ │ - JsonRpc │ │ - /health/bc │ │
│ │ - Sign EIP-191 │ │ - Contract │ │ - Metrics │ │
│ │ - Verify sig │ │ - Utils │ │ - Latency │ │
│ └─────────────────┘ └───────────────┘ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
2.2 Dépendances externes¶
| Dépendance | Version | Usage |
|---|---|---|
ethers | ^6.9.0 | Librairie Web3 TypeScript |
@nestjs/common | ^10.x | Framework applicatif |
@nestjs/config | ^3.x | Configuration |
@aws-sdk/client-kms | ^3.x | Custody mode S2 |
@hashicorp/vault-client | ^1.x | Custody mode S3 |
2.3 Structure des fichiers¶
src/modules/blockchain/
├── blockchain.module.ts # Module NestJS
├── blockchain.config.ts # Configuration (registerAs)
├── constants/
│ ├── chain-ids.ts # Chain IDs testnet/mainnet
│ └── error-codes.ts # 14 codes d'erreur
├── providers/
│ ├── rpc-provider.service.ts # Gestion RPC + failover
│ ├── rpc-provider.interface.ts # Types provider
│ └── rpc-health.service.ts # Health check RPC
├── custody/
│ ├── custody.service.ts # Orchestration custody modes
│ ├── custody.interface.ts # Types custody
│ ├── strategies/
│ │ ├── s1-hsm.strategy.ts # CloudHSM/PKCS#11
│ │ ├── s2-kms.strategy.ts # AWS KMS
│ │ └── s3-hybrid.strategy.ts # Hybrid HSM+Vault
│ └── custody.guard.ts # Validation mode unique
├── wallet/
│ ├── wallet.service.ts # Signature EIP-191
│ ├── wallet.interface.ts # Types wallet
│ └── wallet.validator.ts # Validation adresse
├── transaction/
│ ├── transaction.service.ts # Build/Sign/Send/Confirm
│ ├── transaction.interface.ts # Types TX
│ ├── gas-estimator.service.ts # Estimation gas
│ └── confirmation.tracker.ts # Suivi confirmations
├── health/
│ ├── blockchain-health.controller.ts # GET /health/blockchain
│ └── blockchain-health.service.ts # Métriques
├── validators/
│ ├── payload.validator.ts # INV-52-08 : validation payload
│ └── chain-id.guard.ts # INV-52-02 : testnet only
└── dto/
├── transaction.dto.ts # DTO transaction
└── health.dto.ts # DTO health response
3. Décisions architecturales¶
DA-01 : Custody mode initial = S2 (AWS KMS)¶
Contexte : CloudHSM ne supporte pas nativement secp256k1 (courbe Ethereum).
Décision : Implémenter S2 (AWS KMS avec ECC_SECG_P256K1) pour la v1.
Justification : - AWS KMS supporte secp256k1 nativement - Simplification de l'intégration (SDK AWS) - Sécurité équivalente pour l'usage testnet - S1 (HSM) et S3 (hybrid) seront implémentés en parallèle mais S2 sera le mode par défaut
Alternative rejetée : S1 (HSM) nécessiterait un développement complexe pour la dérivation de clé.
Comportement des stubs S1/S3 (ECT-PLAN-01) : - Les stubs S1 et S3 DOIVENT retourner CUSTODY_MODE_INVALID lors de initialize() et validateCapability() - Cela garantit que le système refuse explicitement ces modes non implémentés - Les stubs respectent l'interface CustodyStrategy pour permettre les tests unitaires
DA-02 : Failover RPC synchrone avec retry¶
Contexte : SLA de 5 secondes pour bascule primaire → secondaire.
Décision : Implémentation synchrone avec timeout 2s + 1 retry avant failover.
Justification : - Timeout 2s conforme à spec (FN-52-06) - 1 retry tolère les erreurs transitoires - Switch secondaire immédiat après 2 échecs - Total < 5s garanti (2s timeout × 2 tentatives + switch ~100ms)
DA-03 : Pattern Strategy pour custody modes¶
Contexte : 3 modes de custody avec comportements distincts (S1/S2/S3).
Décision : Utiliser le pattern Strategy avec interface commune CustodyStrategy.
Justification : - Extensibilité : ajout de modes futurs simple - Testabilité : chaque stratégie testable indépendamment - Cohérence : INV-52-04 (un seul mode) garanti par le service orchestrateur
DA-04 : Validation payload défensive¶
Contexte : INV-52-08 interdit toute donnée non-hash dans les transactions.
Décision : Validation stricte du payload avec whitelist de formats acceptés.
Justification : - Seuls les payloads commençant par le préfixe keccak256("ProbatioVault:anchor:...) sont acceptés - Rejet immédiat avec PAYLOAD_VALIDATION_FAILED sinon - Défense en profondeur avant émission
DA-05 : Endpoint /health/blockchain JSON¶
Contexte : INV-52-10 requiert des métriques exposables.
Décision : Endpoint REST GET /health/blockchain retournant JSON.
DA-06 : Propagation uniforme des erreurs (ECT-PLAN-03)¶
Contexte : INV-52-11 requiert des erreurs déterministes et journalisables sans fuite de secret.
Décision : Tous les services DOIVENT utiliser BlockchainError avec les règles suivantes : - Chaque erreur utilise un code de BlockchainErrorCode (enum T-03) - Le champ context ne contient JAMAIS de secret (clé, token, seed, mnemonic) - Les erreurs sont loggées via le logger NestJS avec niveau error ou warn - Le format de log est : [BlockchainModule] {code}: {message} | context: {sanitized_context}
Implémentation : Chaque service (T-05, T-12, T-13, T-16, T-17) inclut un handler d'erreur uniforme.
DA-07 : Mesure de l'écart gas (ECT-PLAN-04)¶
Contexte : CA-52-05 requiert que l'écart entre gas estimé et consommé soit < 25%.
Décision : Le ConfirmationTracker (T-17) capture le gasUsed réel après confirmation et calcule l'écart.
Observable : TransactionResult.gasMetrics: { estimated: bigint, actual: bigint, deviationPercent: number }
DA-08 : Point d'observabilité SLA failover (ECT-PLAN-07)¶
Contexte : CA-52-07 requiert de démontrer que le failover < 5s.
Décision : Le RpcProviderService (T-05) horodate chaque événement de failover : - failoverStartedAt: Date — début de la séquence (premier timeout) - failoverCompletedAt: Date — fin de la séquence (switch effectué) - failoverDurationMs: number — durée totale calculée
Observable : Log structuré [RpcProvider] FAILOVER | network={polygon|arbitrum} | duration={ms}ms | from=primary | to=secondary
Format :
{
"timestamp": "2026-02-12T19:30:00Z",
"networks": {
"polygon": {
"status": "connected",
"provider": "primary",
"latencyMs": 45,
"gasPriceGwei": 35.2,
"walletBalance": "0.5",
"blockNumber": 12345678
},
"arbitrum": {
"status": "connected",
"provider": "secondary",
"latencyMs": 32,
"gasPriceGwei": 0.1,
"walletBalance": "0.3",
"blockNumber": 87654321
}
},
"custodyMode": "S2",
"custodyStatus": "valid"
}
3bis. Préconditions d'environnement (ECT-PLAN-08/09)¶
Infrastructure requise avant implémentation¶
| Composant | Précondition | Vérification |
|---|---|---|
| AWS KMS | Clé ECC_SECG_P256K1 créée dans eu-west-3 | aws kms describe-key --key-id $KMS_KEY_ID |
| IAM | Permissions kms:Sign, kms:GetPublicKey | aws iam simulate-principal-policy |
| RPC Alchemy | API key valide avec accès Polygon Amoy + Arbitrum Sepolia | Test eth_blockNumber |
| RPC Public | Endpoints publics accessibles (fallback) | Test eth_blockNumber |
| Vault (si S3) | Moteur Transit activé, clé secp256k1 | vault read transit/keys/ethereum |
| HSM (si S1) | Slot PKCS#11 configuré, clé secp256k1 | pkcs11-tool --list-objects |
Wallet funding (testnet)¶
| Réseau | Wallet | Solde minimum | Faucet |
|---|---|---|---|
| Polygon Amoy | $WALLET_ADDRESS | 0.1 MATIC | https://faucet.polygon.technology/ |
| Arbitrum Sepolia | $WALLET_ADDRESS | 0.01 ETH | https://www.alchemy.com/faucets/arbitrum-sepolia |
Procédure de funding : 1. Récupérer l'adresse wallet via CustodyService.getStrategy().getAddress() 2. Demander des fonds sur les faucets listés 3. Vérifier le solde via eth_getBalance 4. Documenter les TX IDs de funding pour traçabilité
Checklist de readiness¶
# 1. Vérifier KMS
aws kms get-public-key --key-id $BLOCKCHAIN_KMS_KEY_ID --output text
# 2. Vérifier RPC Polygon
curl -X POST $BLOCKCHAIN_POLYGON_RPC_PRIMARY \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# 3. Vérifier RPC Arbitrum
curl -X POST $BLOCKCHAIN_ARBITRUM_RPC_PRIMARY \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}'
# 4. Vérifier solde wallet (après funding)
curl -X POST $BLOCKCHAIN_POLYGON_RPC_PRIMARY \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"eth_getBalance","params":["'$WALLET_ADDRESS'","latest"],"id":1}'
Provisioning KMS et IAM (Terraform)¶
La clé KMS et le user IAM sont provisionnés automatiquement via Terraform dans ProbatioVault-infra.
Fichier : terraform/iam-blockchain-signer.tf
Ressources créées : - aws_kms_key.ethereum_signing — Clé ECC_SECG_P256K1 pour signature secp256k1 - aws_kms_alias.ethereum_signing — Alias probatiovault-eth-signing-{env} - aws_iam_user.blockchain_signer — User probatiovault-blockchain-signer - aws_iam_policy.blockchain_signer — Permissions minimales (kms:Sign, kms:GetPublicKey, kms:DescribeKey)
Déploiement :
# Via CI/CD (recommandé)
git push origin dev # Pipeline terraform:apply:dev
# Manuel (debug)
cd ProbatioVault-infra/terraform
./scripts/terraform-with-vault.sh apply
Outputs à stocker dans Vault :
terraform output blockchain_kms_key_id # ARN de la clé KMS
terraform output -raw blockchain_signer_access_key_id # AWS Access Key ID
terraform output -raw blockchain_signer_secret_access_key # AWS Secret Access Key
Mise à jour Vault :
curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
--request POST \
--data '{
"data": {
"BLOCKCHAIN_KMS_KEY_ID": "<ARN_FROM_OUTPUT>",
"AWS_ACCESS_KEY_ID": "<ACCESS_KEY_FROM_OUTPUT>",
"AWS_SECRET_ACCESS_KEY": "<SECRET_FROM_OUTPUT>",
"BLOCKCHAIN_POLYGON_RPC_PRIMARY": "https://rpc-amoy.polygon.technology",
"BLOCKCHAIN_POLYGON_RPC_SECONDARY": "https://polygon-amoy-bor-rpc.publicnode.com",
"BLOCKCHAIN_ARBITRUM_RPC_PRIMARY": "https://sepolia-rollup.arbitrum.io/rpc",
"BLOCKCHAIN_ARBITRUM_RPC_SECONDARY": "https://arbitrum-sepolia-rpc.publicnode.com",
"BLOCKCHAIN_CUSTODY_MODE": "S2"
}
}' \
"https://vault.dev.probatiovault.com/v1/kv/data/app/blockchain"
4. Séquençage des tâches¶
Phase 1 : Fondations (Tâches 1-4)¶
| # | Tâche | Agent | Dépendances | Fichiers |
|---|---|---|---|---|
| T-01 | Configuration module + constants | developer | - | blockchain.module.ts, blockchain.config.ts, constants/*.ts |
| T-02 | Interface et types | developer | T-01 | */interfaces.ts, dto/.ts |
| T-03 | Codes d'erreur | developer | T-01 | constants/error-codes.ts |
| T-04 | Chaîne IDs + guard testnet | developer | T-01 | constants/chain-ids.ts, validators/chain-id.guard.ts |
Phase 2 : RPC Provider (Tâches 5-7)¶
| # | Tâche | Agent | Dépendances | Fichiers |
|---|---|---|---|---|
| T-05 | RpcProviderService + failover | developer | T-01, T-03 | providers/rpc-provider.service.ts |
| T-06 | Health check RPC | developer | T-05 | providers/rpc-health.service.ts |
| T-07 | Tests unitaires RPC | qa-unit | T-05, T-06 | tests/providers/*.spec.ts |
Phase 3 : Custody (Tâches 8-12)¶
| # | Tâche | Agent | Dépendances | Fichiers |
|---|---|---|---|---|
| T-08 | Interface CustodyStrategy | developer | T-02 | custody/custody.interface.ts |
| T-09 | S2 KMS Strategy | developer | T-08 | custody/strategies/s2-kms.strategy.ts |
| T-10 | S1 HSM Strategy (stub → CUSTODY_MODE_INVALID) | developer | T-08 | custody/strategies/s1-hsm.strategy.ts |
| T-11 | S3 Hybrid Strategy (stub → CUSTODY_MODE_INVALID) | developer | T-08 | custody/strategies/s3-hybrid.strategy.ts |
| T-12 | CustodyService orchestrateur | developer | T-09, T-10, T-11 | custody/custody.service.ts |
Phase 4 : Wallet & Transaction (Tâches 13-17)¶
| # | Tâche | Agent | Dépendances | Fichiers |
|---|---|---|---|---|
| T-13 | WalletService (sign EIP-191) | developer | T-12 | wallet/wallet.service.ts |
| T-14 | PayloadValidator | developer | T-03 | validators/payload.validator.ts |
| T-15 | GasEstimatorService | developer | T-05 | transaction/gas-estimator.service.ts |
| T-16 | TransactionService | developer | T-13, T-14, T-15 | transaction/transaction.service.ts |
| T-17 | ConfirmationTracker | developer | T-05, T-16 | transaction/confirmation.tracker.ts |
Phase 5 : Health & Observability (Tâches 18-19)¶
| # | Tâche | Agent | Dépendances | Fichiers |
|---|---|---|---|---|
| T-18 | BlockchainHealthService | developer | T-05, T-12, T-13 | health/blockchain-health.service.ts |
| T-19 | BlockchainHealthController | developer | T-18 | health/blockchain-health.controller.ts |
Phase 6 : Tests & Intégration (Tâches 20-25)¶
| # | Tâche | Agent | Dépendances | Fichiers |
|---|---|---|---|---|
| T-20 | Tests unitaires custody | qa-unit | T-12 | tests/custody/*.spec.ts |
| T-21 | Tests unitaires wallet/tx | qa-unit | T-13, T-16, T-17 | tests/wallet/.spec.ts, tests/transaction/.spec.ts |
| T-22 | Tests intégration testnet (10/10 + SLA) | qa-integration | T-16, T-17 | tests/integration/blockchain.e2e.spec.ts |
| T-23 | Tests health endpoint | qa-integration | T-19 | tests/health/*.spec.ts |
| T-24 | Audit de secrets (gitleaks + logs) | qa-security | T-20, T-21 | scripts/audit-secrets.sh, .gitleaks.toml |
| T-25 | Rapport de couverture Jest/Istanbul | qa-unit | T-20, T-21 | coverage/lcov-report/index.html |
Détail T-22 : Tests intégration testnet (ECT-PLAN-06/07)¶
Test TC-NOM-01/02 : Connectivité 10/10 (CA-52-01, CA-52-02)
describe('Connectivity 10/10', () => {
it.each(['polygon', 'arbitrum'])('should succeed 10 consecutive calls on %s', async (network) => {
const results: number[] = [];
for (let i = 0; i < 10; i++) {
const blockNumber = await rpcService.call(network, 'eth_blockNumber');
results.push(blockNumber);
}
expect(results).toHaveLength(10);
expect(results.every(n => n > 0)).toBe(true);
// Observable: logs contiennent 10 entrées successives
});
});
Test TC-NOM-09 : Failover SLA < 5s (CA-52-07)
describe('Failover SLA', () => {
it('should failover in less than 5 seconds', async () => {
// Simuler timeout primaire
mockPrimaryTimeout();
const startTime = Date.now();
const result = await rpcService.call('polygon', 'eth_blockNumber');
const endTime = Date.now();
const failoverDuration = endTime - startTime;
expect(failoverDuration).toBeLessThan(5000);
expect(rpcService.getActiveProvider('polygon')).toBe('secondary');
// Observable: log FAILOVER avec duration
});
});
Détail T-24 : Audit de secrets (ECT-PLAN-05)¶
Script scripts/audit-secrets.sh :
#!/bin/bash
set -e
echo "=== Audit de secrets PD-52 ==="
# 1. Scan gitleaks sur le code source
gitleaks detect --source . --config .gitleaks.toml --report-path gitleaks-report.json
# 2. Scan des logs applicatifs (patterns de clé privée)
grep -rn "0x[a-fA-F0-9]{64}" logs/ && echo "FAIL: Potential private key in logs" && exit 1
# 3. Scan des variables d'environnement
env | grep -E "PRIVATE_KEY|MNEMONIC|SEED" && echo "FAIL: Secret in env" && exit 1
# 4. Scan de l'historique git
git log --all --full-history -p | grep -E "0x[a-fA-F0-9]{64}" && echo "FAIL: Secret in git history" && exit 1
echo "=== Audit OK : aucun secret détecté ==="
Observable : Rapport gitleaks-report.json avec 0 finding.
Détail T-25 : Rapport de couverture (ECT-PLAN-15)¶
Commande : npm run test:cov
Artefact : coverage/lcov-report/index.html
Critère CA-52-12 : Couverture unitaire ≥ 80%
Vérification CI :
# .gitlab-ci.yml
test:coverage:
script:
- npm run test:cov
- |
COVERAGE=$(cat coverage/coverage-summary.json | jq '.total.lines.pct')
if (( $(echo "$COVERAGE < 80" | bc -l) )); then
echo "FAIL: Coverage $COVERAGE% < 80%"
exit 1
fi
artifacts:
paths:
- coverage/
4bis. Diagrammes Mermaid¶
Graphe de dépendances des tâches¶
graph TD
subgraph "Phase 1 — Fondations"
T01["T-01<br/>blockchain.module.ts<br/>blockchain.config.ts"]
T02["T-02<br/>Interfaces & DTOs"]
T03["T-03<br/>Error codes"]
T04["T-04<br/>Chain IDs + guard testnet"]
end
subgraph "Phase 2 — RPC Provider"
T05["T-05<br/>RpcProviderService<br/>+ failover"]
T06["T-06<br/>RPC Health check"]
T07["T-07<br/>Tests unitaires RPC"]
end
subgraph "Phase 3 — Custody"
T08["T-08<br/>CustodyStrategy<br/>interface"]
T09["T-09<br/>S2 KMS Strategy"]
T10["T-10<br/>S1 HSM Strategy<br/>(stub)"]
T11["T-11<br/>S3 Hybrid Strategy<br/>(stub)"]
T12["T-12<br/>CustodyService<br/>orchestrateur"]
end
subgraph "Phase 4 — Wallet & Transaction"
T13["T-13<br/>WalletService<br/>(sign EIP-191)"]
T14["T-14<br/>PayloadValidator"]
T15["T-15<br/>GasEstimatorService"]
T16["T-16<br/>TransactionService"]
T17["T-17<br/>ConfirmationTracker"]
end
subgraph "Phase 5 — Health & Observability"
T18["T-18<br/>BlockchainHealthService"]
T19["T-19<br/>BlockchainHealthController"]
end
subgraph "Phase 6 — Tests & Intégration"
T20["T-20<br/>Tests custody"]
T21["T-21<br/>Tests wallet/tx"]
T22["T-22<br/>Tests intégration<br/>testnet"]
T23["T-23<br/>Tests health"]
T24["T-24<br/>Audit secrets"]
T25["T-25<br/>Rapport couverture"]
end
%% Phase 1 deps
T01 --> T02
T01 --> T03
T01 --> T04
%% Phase 2 deps
T01 --> T05
T03 --> T05
T05 --> T06
T05 --> T07
T06 --> T07
%% Phase 3 deps
T02 --> T08
T08 --> T09
T08 --> T10
T08 --> T11
T09 --> T12
T10 --> T12
T11 --> T12
%% Phase 4 deps
T12 --> T13
T03 --> T14
T05 --> T15
T13 --> T16
T14 --> T16
T15 --> T16
T05 --> T17
T16 --> T17
%% Phase 5 deps
T05 --> T18
T12 --> T18
T13 --> T18
T18 --> T19
%% Phase 6 deps
T12 --> T20
T13 --> T21
T16 --> T21
T17 --> T21
T16 --> T22
T17 --> T22
T19 --> T23
T20 --> T24
T21 --> T24
T20 --> T25
T21 --> T25 Diagramme de séquence — Transaction avec failover RPC et custody S2¶
sequenceDiagram
participant Client
participant TxSvc as TransactionService
participant PayloadVal as PayloadValidator
participant GasEst as GasEstimatorService
participant RpcSvc as RpcProviderService
participant Custody as CustodyService
participant KMS as S2 KMS Strategy
participant Wallet as WalletService
participant Tracker as ConfirmationTracker
participant Health as BlockchainHealthService
Client->>TxSvc: buildAndSend(payload, network)
TxSvc->>PayloadVal: validate(payload)
PayloadVal-->>TxSvc: OK (hash only, testnet)
TxSvc->>GasEst: estimate(network)
GasEst->>RpcSvc: call(eth_gasPrice)
alt Primary RPC timeout (>2s)
RpcSvc->>RpcSvc: retry (1x)
RpcSvc->>RpcSvc: failover to secondary
Note over RpcSvc: Log FAILOVER<br/>duration < 5s (DA-08)
end
RpcSvc-->>GasEst: gasPrice
GasEst-->>TxSvc: estimatedGas
TxSvc->>Custody: getStrategy()
Custody-->>TxSvc: S2 (KMS)
TxSvc->>KMS: sign(txData)
KMS-->>TxSvc: signedTx
TxSvc->>RpcSvc: sendTransaction(signedTx)
RpcSvc-->>TxSvc: txHash
TxSvc->>Tracker: track(txHash, network)
loop Until confirmations >= threshold
Tracker->>RpcSvc: getTransactionReceipt(txHash)
RpcSvc-->>Tracker: receipt (confirmations)
end
Tracker-->>TxSvc: TransactionResult (gasMetrics)
TxSvc-->>Client: TransactionResult
Note over Health: Expose /health/blockchain<br/>RPC status + custody mode<br/>+ wallet balance + latency 5. Matrice de couverture¶
Invariants → Tâches¶
| Invariant | Tâches responsables |
|---|---|
| INV-52-01 (Polygon + Arbitrum) | T-04, T-05 |
| INV-52-02 (testnet only) | T-04, T-14 |
| INV-52-03 (pas de secret en clair) | T-09, T-10, T-11, T-13 |
| INV-52-04 (un seul custody mode) | T-12 |
| INV-52-05 (refus si mode indispo) | T-12 |
| INV-52-06 (primaire + secondaire) | T-05 |
| INV-52-07 (failover < 5s) | T-05, T-06 |
| INV-52-08 (hash only) | T-14 |
| INV-52-09 (seuil confirmations) | T-17 |
| INV-52-10 (/health/blockchain) | T-18, T-19 |
| INV-52-11 (erreurs déterministes) | T-03, T-05, T-12, T-13, T-16, T-17 (propagation uniforme DA-06) |
| INV-52-12 (tests CI) | T-20, T-21, T-22, T-23, T-24, T-25 |
| INV-52-13 (seuil configurable) | T-01, T-17 |
Cas d'erreur → Tâches¶
| Code erreur | Tâche responsable |
|---|---|
| RPC_PRIMARY_FAILED | T-05 |
| RPC_UNAVAILABLE | T-05 |
| CUSTODY_MODE_MISSING | T-12 |
| CUSTODY_MODE_INVALID | T-12 |
| SIGNATURE_VERIFICATION_FAILED | T-13 |
| INSUFFICIENT_FUNDS | T-16 |
| GAS_PRICE_CEILING_EXCEEDED | T-15, T-16 |
| TRANSACTION_REORG | T-17 |
| SECRET_LEAK_DETECTED | T-20 |
| CUSTODY_MODE_AMBIGUOUS | T-12 |
| CUSTODY_MODE_UNKNOWN | T-12 |
| RPC_INVALID_ENDPOINT | T-05 |
| MAINNET_FORBIDDEN | T-04, T-14 |
| PAYLOAD_VALIDATION_FAILED | T-14 |
6. Risques techniques et mitigations¶
| Risque | Impact | Mitigation |
|---|---|---|
| AWS KMS latence élevée pour signature | Performance dégradée | Cache de session KMS, parallélisation |
| Testnet RPC instable | Tests flaky | Mock pour tests unitaires, retry pour intégration |
| Complexity S1/S3 strategies | Retard livraison | S2 prioritaire, S1/S3 en stub |
| Gas estimation imprécise | Transactions bloquées | Marge +20%, fallback sur gasLimit fixe |
7. Environnement de développement¶
Variables d'environnement requises¶
# RPC Providers
BLOCKCHAIN_POLYGON_RPC_PRIMARY=https://polygon-amoy.g.alchemy.com/v2/xxx
BLOCKCHAIN_POLYGON_RPC_SECONDARY=https://rpc-amoy.polygon.technology
BLOCKCHAIN_ARBITRUM_RPC_PRIMARY=https://arb-sepolia.g.alchemy.com/v2/xxx
BLOCKCHAIN_ARBITRUM_RPC_SECONDARY=https://sepolia-rollup.arbitrum.io/rpc
# Custody
BLOCKCHAIN_CUSTODY_MODE=S2
BLOCKCHAIN_KMS_KEY_ID=arn:aws:kms:eu-west-3:xxx:key/xxx
# Confirmations
BLOCKCHAIN_POLYGON_CONFIRMATIONS=5
BLOCKCHAIN_ARBITRUM_CONFIRMATIONS=1
# Timeouts
BLOCKCHAIN_RPC_TIMEOUT_MS=2000
BLOCKCHAIN_RPC_RETRY_COUNT=1
# Gas
BLOCKCHAIN_GAS_PRICE_CEILING_GWEI=100
Testnet Faucets¶
- Polygon Amoy : https://faucet.polygon.technology/
- Arbitrum Sepolia : https://www.alchemy.com/faucets/arbitrum-sepolia
8. Estimation effort¶
| Phase | Tâches | Effort estimé |
|---|---|---|
| Phase 1 : Fondations | T-01 à T-04 | 2h |
| Phase 2 : RPC Provider | T-05 à T-07 | 3h |
| Phase 3 : Custody | T-08 à T-12 | 4h |
| Phase 4 : Wallet & TX | T-13 à T-17 | 4h |
| Phase 5 : Health | T-18 à T-19 | 1h |
| Phase 6 : Tests & Audit | T-20 à T-25 | 4h |
| Total | 25 tâches | ~18h |
8bis. Notes de conception (ECT-PLAN-10/14)¶
MAINNET_FORBIDDEN : double vérification (défense en profondeur)¶
Le code d'erreur MAINNET_FORBIDDEN peut être émis depuis deux points : 1. chain-id.guard.ts (T-04) : Vérification au niveau configuration/module 2. payload.validator.ts (T-14) : Vérification avant émission de transaction
Ce chevauchement est intentionnel (défense en profondeur). La première barrière bloque au démarrage, la seconde bloque à l'exécution.
Métadonnées on-chain : risque de corrélation (ECT-PLAN-10)¶
Le payload de transaction type contient : keccak256("ProbatioVault:anchor:test:" || timestamp_unix)
Analyse : - Le préfixe ProbatioVault:anchor:test: est public et non identifiant - Le timestamp unix seul ne permet pas d'identifier un individu - La corrélation nécessiterait l'accès aux systèmes internes de ProbatioVault
Mitigation : Le timestamp est arrondi à la minute (pas à la milliseconde) pour réduire la précision de corrélation.
9. Prochaines étapes¶
- Gate 5 : Review plan d'implémentation (ChatGPT + confrontation)
- Étape 6 : Implémentation multi-agents
- Étape 7 : Acceptabilité (reviews + tests)
- Gate 8 : Closure
Références¶
- Specification : PD-52-specification.md
- Tests : PD-52-tests.md
- Verdict Gate 3 : PD-52-verdict-step3-v2.yaml