# Correction : Erreur lors de la génération du hash pour un document **Auteur** : Équipe 4NK **Date** : 2026-01-24 **Version** : 1.0 ## Problème Identifié Erreur lors de la génération d'un hash pour un document dans le dashboard signet. ### Symptômes - Erreur lors de la génération du hash pour les fichiers binaires (PDF, images, etc.) - Le hash généré était incorrect pour les fichiers binaires - Pas de message d'erreur clair lorsque la lecture du fichier échouait - Les fichiers texte fonctionnaient correctement ## Cause Racine **Root cause** : La fonction `generateHashFromFile()` utilisait `FileReader.readAsText()` qui convertit le fichier en texte UTF-8. Cette méthode échoue pour les fichiers binaires car : 1. **Fichiers binaires non valides en UTF-8** : Les fichiers binaires (PDF, images, etc.) contiennent des bytes qui ne sont pas valides en UTF-8, ce qui cause des erreurs lors de la conversion 2. **Hash incorrect** : Même si la conversion réussissait partiellement, le hash calculé était basé sur la représentation texte corrompue du fichier, pas sur les bytes bruts, ce qui produisait un hash incorrect 3. **Backend traite comme UTF-8** : Le backend utilisait `crypto.createHash('sha256').update(content, 'utf8')` qui suppose que le contenu est en UTF-8, ce qui est incorrect pour les fichiers binaires 4. **Pas de gestion d'erreur** : Il n'y avait pas de gestion d'erreur pour `reader.onerror`, donc les erreurs de lecture n'étaient pas capturées et affichées à l'utilisateur **Problème technique** : Le code ne distinguait pas les fichiers texte des fichiers binaires et utilisait une méthode de lecture inadaptée pour les fichiers binaires. ## Correctifs Appliqués ### 1. Modification de `generateHashFromFile()` pour utiliser `readAsArrayBuffer()` **Fichier** : `signet-dashboard/public/app.js` **Avant** : ```javascript const reader = new FileReader(); reader.onload = async (e) => { const fileContent = e.target.result; // ... body: JSON.stringify({ fileContent }), }; reader.readAsText(selectedFile); ``` **Après** : ```javascript const reader = new FileReader(); await new Promise((resolve, reject) => { reader.onload = async (e) => { try { const arrayBuffer = e.target.result; // Convertir l'ArrayBuffer en base64 pour l'envoi au backend // Utiliser une boucle pour éviter les limites de taille des arguments de fonction const uint8Array = new Uint8Array(arrayBuffer); let binaryString = ''; for (let i = 0; i < uint8Array.length; i++) { binaryString += String.fromCharCode(uint8Array[i]); } const base64 = btoa(binaryString); // ... body: JSON.stringify({ fileContent: base64, isBase64: true }), } catch (error) { showResult('anchor-result', 'error', `Erreur : ${error.message}`); reject(error); } }; reader.onerror = (error) => { showResult('anchor-result', 'error', `Erreur lors de la lecture du fichier : ${error.message || 'Erreur inconnue'}`); reject(error); }; reader.readAsArrayBuffer(selectedFile); }); ``` **Impact** : - Les fichiers binaires sont maintenant lus correctement comme ArrayBuffer - Le contenu est converti en base64 pour l'envoi au backend - Gestion d'erreur complète avec `reader.onerror` - Utilisation d'une boucle pour la conversion base64 pour éviter les limites de taille des arguments de fonction pour les gros fichiers - Meilleure gestion des erreurs HTTP avec affichage des messages d'erreur du backend ### 2. Modification du backend pour accepter les fichiers binaires en base64 **Fichier** : `signet-dashboard/src/server.js` **Avant** : ```javascript app.post('/api/hash/generate', (req, res) => { try { const { text, fileContent } = req.body; if (!text && !fileContent) { return res.status(400).json({ error: 'text or fileContent is required' }); } const content = text || fileContent; const hash = crypto.createHash('sha256').update(content, 'utf8').digest('hex'); res.json({ hash }); } catch (error) { logger.error('Error generating hash', { error: error.message }); res.status(500).json({ error: error.message }); } }); ``` **Après** : ```javascript app.post('/api/hash/generate', (req, res) => { try { const { text, fileContent, isBase64 } = req.body; if (!text && !fileContent) { return res.status(400).json({ error: 'text or fileContent is required' }); } let hash; if (text) { // Pour le texte, utiliser UTF-8 hash = crypto.createHash('sha256').update(text, 'utf8').digest('hex'); } else if (fileContent) { if (isBase64) { // Pour les fichiers binaires, décoder le base64 et calculer le hash sur les bytes bruts const buffer = Buffer.from(fileContent, 'base64'); hash = crypto.createHash('sha256').update(buffer).digest('hex'); } else { // Fallback pour compatibilité : traiter comme UTF-8 (pour fichiers texte) hash = crypto.createHash('sha256').update(fileContent, 'utf8').digest('hex'); } } else { return res.status(400).json({ error: 'text or fileContent is required' }); } res.json({ hash }); } catch (error) { logger.error('Error generating hash', { error: error.message, stack: error.stack }); res.status(500).json({ error: error.message }); } }); ``` **Impact** : - Le backend distingue maintenant les fichiers texte (UTF-8) des fichiers binaires (base64) - Le hash est calculé sur les bytes bruts pour les fichiers binaires, garantissant un hash correct - Compatibilité maintenue avec l'ancien format (fichiers texte sans `isBase64`) - Meilleure journalisation des erreurs avec la stack trace - Gestion d'erreur robuste pour le décodage base64 avec messages d'erreur clairs - Validation des paramètres pour éviter les erreurs silencieuses ### 3. Augmentation de la limite de taille du body JSON **Fichier** : `signet-dashboard/src/server.js` **Avant** : ```javascript app.use(express.json()); app.use(express.urlencoded({ extended: true })); ``` **Après** : ```javascript // Augmenter la limite de taille pour le body JSON (100MB) pour supporter les gros fichiers en base64 app.use(express.json({ limit: '100mb' })); app.use(express.urlencoded({ extended: true, limit: '100mb' })); ``` **Impact** : - Support des gros fichiers (jusqu'à ~75MB en binaire, ~100MB en base64) - Évite les erreurs "payload too large" pour les fichiers volumineux ### 4. Ajout d'un middleware pour gérer les erreurs de parsing JSON **Fichier** : `signet-dashboard/src/server.js` **Ajout** : ```javascript // Middleware pour gérer les erreurs de parsing JSON app.use((err, req, res, next) => { if (err instanceof SyntaxError && err.status === 400 && 'body' in err) { logger.error('JSON parsing error', { error: err.message, path: req.path, method: req.method }); return res.status(400).json({ error: 'Invalid JSON', message: err.message }); } next(err); }); ``` **Impact** : - Capture les erreurs de parsing JSON avant qu'elles n'atteignent les routes - Retourne des messages d'erreur clairs pour les requêtes JSON mal formées - Améliore la journalisation des erreurs de parsing ## Modifications ### Fichiers Modifiés - `signet-dashboard/public/app.js` : - Modification de `generateHashFromFile()` pour utiliser `readAsArrayBuffer()` au lieu de `readAsText()` - Ajout de la gestion d'erreur `reader.onerror` - Conversion en base64 avec une boucle pour supporter les gros fichiers - Amélioration de la gestion des erreurs HTTP avec affichage des messages d'erreur du backend - `signet-dashboard/src/server.js` : - Modification de la route `/api/hash/generate` pour accepter `isBase64` (lignes 472-530) - Calcul du hash sur les bytes bruts pour les fichiers binaires - Distinction entre fichiers texte (UTF-8) et fichiers binaires (base64) - Amélioration de la journalisation des erreurs - Gestion d'erreur robuste pour le décodage base64 - Validation des paramètres d'entrée - Augmentation de la limite de taille du body JSON à 100MB (lignes 132-134) - Ajout d'un middleware pour gérer les erreurs de parsing JSON (lignes 144-156) ### Fichiers Créés - `fixKnowledge/dashboard-hash-generation-error.md` : Cette documentation ## Modalités de Déploiement ### Déploiement des Modifications 1. **Vérifier que le dashboard est en cours d'exécution** : ```bash curl http://localhost:3020/api/blockchain/info ``` 2. **Redémarrer le dashboard** : ```bash # Si démarré avec npm start # Arrêter avec Ctrl+C puis redémarrer cd /home/ncantu/Bureau/code/bitcoin/signet-dashboard npm start # Si démarré avec systemd sudo systemctl restart signet-dashboard ``` 3. **Vérifier que le dashboard fonctionne** : ```bash curl http://localhost:3020/ ``` ### Tests de Validation 1. **Test avec un fichier texte** : - Créer un fichier texte simple - Générer le hash via l'interface - Vérifier que le hash est généré correctement 2. **Test avec un fichier binaire (PDF)** : - Sélectionner un fichier PDF - Générer le hash via l'interface - Vérifier que le hash est généré correctement (pas d'erreur) 3. **Test avec un fichier binaire (image)** : - Sélectionner une image (PNG, JPG, etc.) - Générer le hash via l'interface - Vérifier que le hash est généré correctement 4. **Test avec un gros fichier** : - Sélectionner un fichier de plusieurs Mo - Générer le hash via l'interface - Vérifier que le hash est généré correctement (pas d'erreur de mémoire) ## Modalités d'Analyse ### Vérification que la génération de hash fonctionne 1. **Test de l'API directement** : ```bash # Test avec du texte curl -X POST http://localhost:3020/api/hash/generate \ -H "Content-Type: application/json" \ -d '{"text": "Hello World"}' # Test avec un fichier binaire (base64) # Générer le base64 d'un fichier base64 -i test.pdf > test.pdf.base64 # Envoyer au backend curl -X POST http://localhost:3020/api/hash/generate \ -H "Content-Type: application/json" \ -d "{\"fileContent\": \"$(cat test.pdf.base64)\", \"isBase64\": true}" ``` 2. **Vérifier les logs** : ```bash # Si démarré avec npm start tail -f /tmp/dashboard.log # Si démarré avec systemd sudo journalctl -u signet-dashboard -f ``` 3. **Test dans le navigateur** : - Ouvrir `https://dashboard.certificator.4nkweb.com/` - Aller dans l'onglet "Ancrage" - Sélectionner un fichier PDF - Cliquer sur "Générer le Hash" - Vérifier que le hash est généré sans erreur ### Diagnostic des Erreurs 1. **Erreur "Erreur lors de la lecture du fichier"** : - Vérifier que le fichier est valide - Vérifier les logs du navigateur (Console JavaScript) - Vérifier les logs du backend 2. **Erreur "Erreur lors de la génération du hash"** : - Vérifier les logs du backend pour voir l'erreur exacte - Vérifier que le backend reçoit bien `isBase64: true` pour les fichiers binaires - Vérifier que le base64 est valide 3. **Hash incorrect** : - Vérifier que le hash est calculé sur les bytes bruts (pas sur UTF-8) - Comparer avec un hash calculé localement : `sha256sum fichier.pdf` ## Résultat ✅ **Problème résolu** - Les fichiers binaires (PDF, images, etc.) peuvent maintenant être hashés correctement - Le hash est calculé sur les bytes bruts, garantissant un hash correct et reproductible - Les erreurs sont maintenant capturées et affichées clairement à l'utilisateur - Les gros fichiers sont supportés grâce à la conversion base64 par boucle et l'augmentation de la limite de taille du body - Compatibilité maintenue avec les fichiers texte - L'erreur 500 (Internal Server Error) est maintenant corrigée avec une gestion d'erreur robuste - Les erreurs de parsing JSON sont maintenant gérées correctement avec des messages d'erreur clairs - Validation des paramètres pour éviter les erreurs silencieuses ## Prévention Pour éviter ce problème à l'avenir : 1. **Toujours utiliser `readAsArrayBuffer()` pour les fichiers binaires** : Ne jamais utiliser `readAsText()` pour les fichiers binaires 2. **Distinguer les fichiers texte des fichiers binaires** : Utiliser un flag (`isBase64`) pour indiquer le type de contenu 3. **Calculer le hash sur les bytes bruts** : Ne jamais calculer le hash sur une représentation texte d'un fichier binaire 4. **Gérer les erreurs de FileReader** : Toujours implémenter `reader.onerror` pour capturer les erreurs de lecture 5. **Utiliser des boucles pour les conversions base64** : Éviter `String.fromCharCode(...array)` qui peut échouer pour les gros fichiers ## Pages Affectées - `signet-dashboard/public/app.js` : Modification de `generateHashFromFile()` (lignes 534-590) - `signet-dashboard/src/server.js` : Modification de la route `/api/hash/generate` (lignes 472-502) - `fixKnowledge/dashboard-hash-generation-error.md` : Documentation (nouveau)