From e0ce7a9d830d069815347dcefeb805f26e8505c9 Mon Sep 17 00:00:00 2001 From: ncantu Date: Sun, 8 Feb 2026 08:18:26 +0100 Subject: [PATCH] Optimize sync-utxos RPC calls and document bitcoind crash issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Motivations:** - Prevent bitcoind crashes caused by heavy RPC calls without timeout - Document bitcoind crash and wallet loading stuck issues - Clean up obsolete files (configure-nginx-proxy.sh, userwallet components, website-skeleton, old fixKnowledge docs) **Root causes:** - RPC calls without timeout causing bitcoind crashes - No pre-check of bitcoind health before heavy operations - Large wallet (315MB) causing long loading times and potential hangs - Missing retry mechanism for transient errors **Correctifs:** - Add timeouts on RPC calls (5 minutes for listunspent, 10 seconds for healthcheck) - Add bitcoind health check before synchronization - Implement retry with exponential backoff - Reduce maximumCount limit from 9999999 to 500000 UTXOs - Improve cron script with pre-checks and better error handling - Add container status verification before script execution **Evolutions:** - New check-services-status.sh script for service diagnostics - Documentation of crash issues in fixKnowledge - Improved logging with timestamps - Better error messages and handling **Pages affectées:** - data/sync-utxos-spent-status.mjs - data/sync-utxos-cron.sh - data/restart-services-cron.sh - data/check-services-status.sh (new) - fixKnowledge/sync-utxos-rpc-optimization.md (new) - fixKnowledge/signet-bitcoind-crash-mining-stopped.md (new) - fixKnowledge/signet-bitcoind-crash-wallet-loading-stuck.md (new) - Removed obsolete files: configure-nginx-proxy.sh, userwallet components, website-skeleton files, old fixKnowledge docs Co-authored-by: Cursor --- configure-nginx-proxy.sh | 391 -------------- data/check-services-status.sh | 81 +++ data/restart-services-cron.sh | 27 +- data/start-docker-services.log | 13 + data/sync-utxos-cron.sh | 35 ++ data/sync-utxos-spent-status.mjs | 85 ++- data/sync-utxos.log | 152 +++--- ...anchor-wrong-chain-insufficient-balance.md | 124 ----- fixKnowledge/mempool-api-healthcheck-fix.md | 88 ---- fixKnowledge/mempool-websocket-offline-fix.md | 106 ---- .../signet-bitcoind-crash-mining-stopped.md | 55 ++ ...net-bitcoind-crash-wallet-loading-stuck.md | 186 +++++++ fixKnowledge/sync-utxos-rpc-optimization.md | 135 +++++ .../src/components/PairingDisplayScreen.tsx | 269 ---------- .../src/components/PairingSetupBlock.tsx | 275 ---------- website-skeleton/contrat.html | 242 --------- website-skeleton/cryptographie.html | 495 ------------------ website-skeleton/index.html | 232 -------- website-skeleton/membre.html | 182 ------- website-skeleton/technique.html | 312 ----------- 20 files changed, 682 insertions(+), 2803 deletions(-) delete mode 100755 configure-nginx-proxy.sh create mode 100755 data/check-services-status.sh create mode 100644 data/start-docker-services.log delete mode 100644 fixKnowledge/dashboard-anchor-wrong-chain-insufficient-balance.md delete mode 100644 fixKnowledge/mempool-api-healthcheck-fix.md delete mode 100644 fixKnowledge/mempool-websocket-offline-fix.md create mode 100644 fixKnowledge/signet-bitcoind-crash-mining-stopped.md create mode 100644 fixKnowledge/signet-bitcoind-crash-wallet-loading-stuck.md create mode 100644 fixKnowledge/sync-utxos-rpc-optimization.md delete mode 100644 userwallet/src/components/PairingDisplayScreen.tsx delete mode 100644 userwallet/src/components/PairingSetupBlock.tsx delete mode 100644 website-skeleton/contrat.html delete mode 100644 website-skeleton/cryptographie.html delete mode 100644 website-skeleton/index.html delete mode 100644 website-skeleton/membre.html delete mode 100644 website-skeleton/technique.html diff --git a/configure-nginx-proxy.sh b/configure-nginx-proxy.sh deleted file mode 100755 index 02b8502..0000000 --- a/configure-nginx-proxy.sh +++ /dev/null @@ -1,391 +0,0 @@ -#!/bin/bash - -# Script de configuration Nginx pour les sous-domaines certificator.4nkweb.com -# Usage: ./configure-nginx-proxy.sh - -set -e - -PROXY_HOST="192.168.1.100" -PROXY_USER="ncantu" -NGINX_SITES_AVAILABLE="/etc/nginx/sites-available" -NGINX_SITES_ENABLED="/etc/nginx/sites-enabled" -CERTBOT_BIN="/usr/bin/certbot" - -echo "=== Configuration Nginx pour certificator.4nkweb.com ===" -echo "" - -# Vérifier que nous sommes sur le proxy ou que nous pouvons y accéder -# Note: Le script peut être exécuté localement ou via SSH -CURRENT_IP=$(hostname -I 2>/dev/null | awk '{print $1}' || echo "") -if [ "$CURRENT_IP" != "192.168.1.100" ] && [ -z "$SSH_CONNECTION" ]; then - echo "ℹ️ Ce script peut être exécuté sur le proxy (192.168.1.100)" - echo " Ou via SSH: ssh ${PROXY_USER}@${PROXY_HOST} 'sudo bash -s' < $0" - echo "" -fi - -# Vérifier les permissions (sudo disponible pour ncantu) -if [ "$EUID" -ne 0 ]; then - if command -v sudo &> /dev/null && sudo -n true 2>/dev/null; then - echo "✅ Utilisation de sudo (droits non interactifs)" - # Le script continuera avec sudo pour les commandes nécessitant root - else - echo "⚠️ Ce script nécessite les permissions root pour configurer Nginx" - echo " Utilisez: sudo $0" - exit 1 - fi -fi - -# Fonction pour exécuter les commandes nécessitant root -SUDO_CMD="" -if [ "$EUID" -ne 0 ]; then - SUDO_CMD="sudo" -fi - -echo "✅ Vérification de Nginx..." -# Vérifier Nginx (peut être dans /usr/sbin/nginx) -NGINX_BIN="" -if command -v nginx &> /dev/null; then - NGINX_BIN="nginx" -elif [ -f /usr/sbin/nginx ]; then - NGINX_BIN="/usr/sbin/nginx" -elif [ -f /usr/bin/nginx ]; then - NGINX_BIN="/usr/bin/nginx" -else - echo "❌ Nginx n'est pas installé" - exit 1 -fi -echo " Nginx trouvé: ${NGINX_BIN}" - -echo "✅ Vérification de Certbot..." -# Vérifier Certbot (peut être dans /usr/bin/certbot) -CERTBOT_BIN="" -if command -v certbot &> /dev/null; then - CERTBOT_BIN="certbot" -elif [ -f /usr/bin/certbot ]; then - CERTBOT_BIN="/usr/bin/certbot" -else - echo "⚠️ Certbot n'est pas installé. Installation..." - ${SUDO_CMD} apt-get update - ${SUDO_CMD} apt-get install -y certbot python3-certbot-nginx - CERTBOT_BIN="certbot" -fi -echo " Certbot trouvé: ${CERTBOT_BIN}" - -# Créer les configurations Nginx pour chaque sous-domaine - -# 1. Dashboard (port 3020) -echo "" -echo "📝 Configuration de dashboard.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/dashboard.certificator.4nkweb.com" > /dev/null << 'EOF' -# Dashboard Bitcoin Signet -server { - listen 80; - server_name dashboard.certificator.4nkweb.com; - - # Logs - access_log /var/log/nginx/dashboard.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/dashboard.certificator.4nkweb.com.error.log; - - # Proxy vers le service Node.js (port 3020) - # Note: Les services tournent sur 192.168.1.105 - location / { - proxy_pass http://192.168.1.105:3020; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# 2. Faucet (port 3021) -echo "📝 Configuration de faucet.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/faucet.certificator.4nkweb.com" > /dev/null << 'EOF' -# API Faucet Bitcoin Signet -server { - listen 80; - server_name faucet.certificator.4nkweb.com; - - # Logs - access_log /var/log/nginx/faucet.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/faucet.certificator.4nkweb.com.error.log; - - # Proxy vers le service Node.js (port 3021) - # Note: Les services tournent sur 192.168.1.105 - location / { - proxy_pass http://192.168.1.105:3021; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# 3. Anchorage (port 3010) -echo "📝 Configuration de anchorage.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/anchorage.certificator.4nkweb.com" > /dev/null << 'EOF' -# API Anchorage Bitcoin Signet -server { - listen 80; - server_name anchorage.certificator.4nkweb.com; - - # Logs - access_log /var/log/nginx/anchorage.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/anchorage.certificator.4nkweb.com.error.log; - - # Proxy vers le service Node.js (port 3010) - # Note: Les services tournent sur 192.168.1.105 - location / { - proxy_pass http://192.168.1.105:3010; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# 4. Watermark (port 3022) -echo "📝 Configuration de watermark.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/watermark.certificator.4nkweb.com" > /dev/null << 'EOF' -# API Watermark Bitcoin Signet -server { - listen 80; - server_name watermark.certificator.4nkweb.com; - - # Logs - access_log /var/log/nginx/watermark.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/watermark.certificator.4nkweb.com.error.log; - - # Proxy vers le service Node.js (port 3022) - # Note: Les services tournent sur 192.168.1.105 - location / { - proxy_pass http://192.168.1.105:3022; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# 5. UserWallet (port 3018) -echo "📝 Configuration de userwallet.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/userwallet.certificator.4nkweb.com" > /dev/null << 'EOF' -# UserWallet frontend (Vite) -server { - listen 80; - server_name userwallet.certificator.4nkweb.com; - - # Logs - access_log /var/log/nginx/userwallet.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/userwallet.certificator.4nkweb.com.error.log; - - # Proxy vers le frontend UserWallet (port 3018) sur 192.168.1.105 - location / { - proxy_pass http://192.168.1.105:3018; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# 6. Website skeleton (port 3024) -echo "📝 Configuration de skeleton.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/skeleton.certificator.4nkweb.com" > /dev/null << 'EOF' -# Website skeleton (UserWallet iframe) -server { - listen 80; - server_name skeleton.certificator.4nkweb.com; - - access_log /var/log/nginx/skeleton.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/skeleton.certificator.4nkweb.com.error.log; - - location / { - proxy_pass http://192.168.1.105:3024; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# 7. Website data (port 3025) -echo "📝 Configuration de data.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/data.certificator.4nkweb.com" > /dev/null << 'EOF' -# Website data (iframe data, non clés) -server { - listen 80; - server_name data.certificator.4nkweb.com; - - access_log /var/log/nginx/data.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/data.certificator.4nkweb.com.error.log; - - location / { - proxy_pass http://192.168.1.105:3025; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# 8. Relay / api-relay (port 3019) -echo "📝 Configuration de relay.certificator.4nkweb.com..." -${SUDO_CMD} tee "${NGINX_SITES_AVAILABLE}/relay.certificator.4nkweb.com" > /dev/null << 'EOF' -# API Relay (UserWallet) -server { - listen 80; - server_name relay.certificator.4nkweb.com; - - # Logs - access_log /var/log/nginx/relay.certificator.4nkweb.com.access.log; - error_log /var/log/nginx/relay.certificator.4nkweb.com.error.log; - - # Proxy vers api-relay (port 3019) sur 192.168.1.105 - location / { - proxy_pass http://192.168.1.105:3019; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - proxy_cache_bypass $http_upgrade; - proxy_read_timeout 300s; - proxy_connect_timeout 75s; - } -} -EOF - -# Activer les sites -echo "" -echo "🔗 Activation des sites..." -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/dashboard.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/dashboard.certificator.4nkweb.com" -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/faucet.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/faucet.certificator.4nkweb.com" -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/anchorage.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/anchorage.certificator.4nkweb.com" -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/watermark.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/watermark.certificator.4nkweb.com" -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/userwallet.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/userwallet.certificator.4nkweb.com" -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/skeleton.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/skeleton.certificator.4nkweb.com" -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/data.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/data.certificator.4nkweb.com" -${SUDO_CMD} ln -sf "${NGINX_SITES_AVAILABLE}/relay.certificator.4nkweb.com" "${NGINX_SITES_ENABLED}/relay.certificator.4nkweb.com" - -# Tester la configuration Nginx -echo "" -echo "🔍 Test de la configuration Nginx..." -if ${SUDO_CMD} ${NGINX_BIN} -t; then - echo "✅ Configuration Nginx valide" -else - echo "❌ Erreur dans la configuration Nginx" - exit 1 -fi - -# Recharger Nginx (configuration HTTP uniquement pour l'instant) -echo "" -echo "🔄 Rechargement de Nginx (configuration HTTP)..." -${SUDO_CMD} systemctl reload nginx || ${SUDO_CMD} service nginx reload - -# Générer les certificats SSL avec Certbot -echo "" -echo "🔐 Génération des certificats SSL avec Certbot..." -echo " Note: Certbot va automatiquement créer les configurations HTTPS" -echo "" - -# Générer les certificats (un par un pour éviter les erreurs) -DOMAINS=( - "dashboard.certificator.4nkweb.com" - "faucet.certificator.4nkweb.com" - "anchorage.certificator.4nkweb.com" - "watermark.certificator.4nkweb.com" - "userwallet.certificator.4nkweb.com" - "skeleton.certificator.4nkweb.com" - "data.certificator.4nkweb.com" - "relay.certificator.4nkweb.com" -) - -for domain in "${DOMAINS[@]}"; do - echo "📜 Génération du certificat pour ${domain}..." - # Certbot va automatiquement modifier la config pour ajouter HTTPS et redirection - if ${SUDO_CMD} ${CERTBOT_BIN} --nginx -d "${domain}" --non-interactive --agree-tos --email admin@4nkweb.com --redirect; then - echo "✅ Certificat généré et configuration HTTPS créée pour ${domain}" - else - echo "⚠️ Erreur lors de la génération du certificat pour ${domain}" - echo " Vous pouvez le générer manuellement avec:" - echo " sudo ${CERTBOT_BIN} --nginx -d ${domain}" - fi -done - -# Recharger Nginx final -echo "" -echo "🔄 Rechargement final de Nginx..." -${SUDO_CMD} systemctl reload nginx || ${SUDO_CMD} service nginx reload - -echo "" -echo "✅ Configuration terminée !" -echo "" -echo "📋 Résumé:" -echo " - dashboard.certificator.4nkweb.com -> http://192.168.1.105:3020" -echo " - faucet.certificator.4nkweb.com -> http://192.168.1.105:3021" -echo " - anchorage.certificator.4nkweb.com -> http://192.168.1.105:3010" -echo " - watermark.certificator.4nkweb.com -> http://192.168.1.105:3022" -echo " - userwallet.certificator.4nkweb.com -> http://192.168.1.105:3018" -echo " - skeleton.certificator.4nkweb.com -> http://192.168.1.105:3024" -echo " - data.certificator.4nkweb.com -> http://192.168.1.105:3025" -echo " - relay.certificator.4nkweb.com -> http://192.168.1.105:3019" -echo "" -echo "⚠️ Note: Si les services tournent sur une autre machine," -echo " modifiez les IP dans les fichiers de configuration Nginx" -echo "" -echo "🔍 Vérification:" -echo " - Test Nginx: nginx -t" -echo " - Status: systemctl status nginx" -echo " - Logs: tail -f /var/log/nginx/*.error.log" -echo "" diff --git a/data/check-services-status.sh b/data/check-services-status.sh new file mode 100755 index 0000000..694f29f --- /dev/null +++ b/data/check-services-status.sh @@ -0,0 +1,81 @@ +#!/bin/bash +# Check status of Docker services (bitcoind signet, mempool) and mining. +# Local only: no SSH. Run on the machine where Docker runs. +# Usage: ./data/check-services-status.sh + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_DIR="$(dirname "$SCRIPT_DIR")" +BITCOIND_CONTAINER="bitcoin-signet-instance" +DATADIR="/root/.bitcoin" + +echo "=== État des services (${BITCOIND_CONTAINER}, mempool) ===" +echo "" + +# Docker containers +echo "--- Conteneurs Docker ---" +docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}" 2>/dev/null || echo "Docker non disponible ou erreur" +echo "" + +# Bitcoind +if docker ps -q -f "name=^${BITCOIND_CONTAINER}$" 2>/dev/null | grep -q .; then + echo "--- Bitcoind (${BITCOIND_CONTAINER}) ---" + BITCOIN_DIR=$(docker exec "$BITCOIND_CONTAINER" printenv BITCOIN_DIR 2>/dev/null || echo "$DATADIR") + if docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir="$BITCOIN_DIR" getblockchaininfo &>/dev/null; then + BCI=$(docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir="$BITCOIN_DIR" getblockchaininfo 2>/dev/null) + BLOCKS=$(echo "$BCI" | jq -r '.blocks // "?"') + CHAIN=$(echo "$BCI" | jq -r '.chain // "?"') + HEADERS=$(echo "$BCI" | jq -r '.headers // "?"') + echo " RPC: OK" + echo " Chaîne: $CHAIN | Blocs: $BLOCKS | Headers: $HEADERS" + if [ "$BLOCKS" != "?" ]; then + TIP_TIME=$(docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir="$BITCOIN_DIR" getblock "$(docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir="$BITCOIN_DIR" getblockhash "$BLOCKS" 2>/dev/null)" 2>/dev/null | jq -r '.time // 0') + if [ -n "$TIP_TIME" ] && [ "$TIP_TIME" != "0" ]; then + echo " Dernier bloc (time): $TIP_TIME ($(date -d "@${TIP_TIME}" 2>/dev/null || echo "N/A"))" + fi + fi + else + echo " RPC: HORS SERVICE (bitcoind ne répond pas)" + RPC_ERROR=$(docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir="$BITCOIN_DIR" getblockchaininfo 2>&1 || true) + if echo "$RPC_ERROR" | grep -q "Loading wallet"; then + echo " ⚠️ Bitcoind est bloqué en chargement de wallet" + echo " Cause probable: wallet volumineux ou corrompu" + echo " Solution: Attendre quelques minutes ou redémarrer: docker restart ${BITCOIND_CONTAINER}" + elif echo "$RPC_ERROR" | grep -q "Could not connect"; then + echo " ⚠️ Bitcoind ne répond pas du tout" + echo " Cause probable: bitcoind a planté dans le conteneur" + echo " Solution: Redémarrer le conteneur: docker restart ${BITCOIND_CONTAINER}" + else + echo " Erreur RPC: $RPC_ERROR" + echo " Solution: Redémarrer le conteneur: docker restart ${BITCOIND_CONTAINER}" + fi + fi + echo "" + echo " Processus dans le conteneur:" + docker exec "$BITCOIND_CONTAINER" ps aux 2>/dev/null | head -20 || true + # Vérifier si bitcoind est présent dans les processus + if ! docker exec "$BITCOIND_CONTAINER" ps aux 2>/dev/null | grep -q "[b]itcoind"; then + echo " ⚠️ ATTENTION: Le processus bitcoind n'est pas présent dans le conteneur" + echo " Le conteneur est actif mais bitcoind a planté" + echo " Solution immédiate: docker restart ${BITCOIND_CONTAINER}" + fi + echo "" + echo " Dernières lignes debug.log:" + docker exec "$BITCOIND_CONTAINER" tail -3 "${BITCOIN_DIR}/signet/debug.log" 2>/dev/null || true +else + echo "--- Bitcoind: conteneur ${BITCOIND_CONTAINER} non trouvé ou arrêté ---" +fi +echo "" + +# Mempool stack +echo "--- Mempool (docker-compose.signet.yml) ---" +if [ -f "${PROJECT_DIR}/mempool/docker-compose.signet.yml" ]; then + (cd "${PROJECT_DIR}/mempool" && docker compose -f docker-compose.signet.yml ps 2>/dev/null) || \ + (cd "${PROJECT_DIR}/mempool" && docker-compose -f docker-compose.signet.yml ps 2>/dev/null) || true +else + echo " Fichier compose non trouvé" +fi +echo "" + +echo "=== Fin du rapport ===" diff --git a/data/restart-services-cron.sh b/data/restart-services-cron.sh index 8d469fa..f246a78 100755 --- a/data/restart-services-cron.sh +++ b/data/restart-services-cron.sh @@ -81,20 +81,33 @@ if docker ps -a -q -f "name=^${BITCOIND_CONTAINER}$" 2>/dev/null | grep -q .; th if [ "$bitcoind_ok" -eq 1 ]; then log " $BITCOIND_CONTAINER started, waiting for RPC to be ready..." - # Wait for bitcoind RPC to be ready (max 60 seconds) - max_wait=60 + # Wait for bitcoind RPC to be ready (max 300 seconds = 5 minutes) + # Increased timeout to handle large wallet loading (e.g., 315MB wallet can take several minutes) + max_wait=300 wait_count=0 while [ $wait_count -lt $max_wait ]; do BITCOIN_DATADIR=$(docker exec "$BITCOIND_CONTAINER" printenv BITCOIN_DIR 2>/dev/null || echo "/root/.bitcoin") - if docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir="$BITCOIN_DATADIR" getblockchaininfo &>/dev/null; then - log " $BITCOIND_CONTAINER RPC ready" + RPC_RESPONSE=$(docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir="$BITCOIN_DATADIR" getblockchaininfo 2>&1) + if echo "$RPC_RESPONSE" | grep -q '"chain"'; then + log " $BITCOIND_CONTAINER RPC ready after ${wait_count}s" break + elif echo "$RPC_RESPONSE" | grep -q "Loading wallet"; then + # Wallet is still loading, continue waiting + if [ $((wait_count % 30)) -eq 0 ]; then + log " Wallet still loading... (${wait_count}s elapsed)" + fi + elif echo "$RPC_RESPONSE" | grep -q "Could not connect"; then + # Bitcoind not started yet, continue waiting + if [ $((wait_count % 30)) -eq 0 ]; then + log " Bitcoind starting... (${wait_count}s elapsed)" + fi fi - sleep 1 - wait_count=$((wait_count + 1)) + sleep 2 + wait_count=$((wait_count + 2)) done if [ $wait_count -ge $max_wait ]; then - log " WARN: $BITCOIND_CONTAINER RPC not ready after ${max_wait}s" + log " WARN: $BITCOIND_CONTAINER RPC not ready after ${max_wait}s (wallet may still be loading)" + log " Check with: docker exec $BITCOIND_CONTAINER bitcoin-cli -datadir=$BITCOIN_DATADIR getblockchaininfo" fi else log " $BITCOIND_CONTAINER FAILED" diff --git a/data/start-docker-services.log b/data/start-docker-services.log new file mode 100644 index 0000000..b04f089 --- /dev/null +++ b/data/start-docker-services.log @@ -0,0 +1,13 @@ +2026-02-07T02:07:15+01:00 === Start Docker services at boot === +2026-02-07T02:07:15+01:00 Starting bitcoin-signet-instance... +2026-02-07T02:07:15+01:00 bitcoin-signet-instance started +2026-02-07T02:32:36+01:00 === Start Docker services at boot === +2026-02-07T02:32:37+01:00 bitcoin-signet-instance already running +2026-02-07T02:32:37+01:00 Starting mempool stack... +2026-02-07T02:32:37+01:00 mempool stack started +2026-02-07T02:32:37+01:00 === Done === +2026-02-08T08:08:57+01:00 === Start Docker services at boot === +2026-02-08T08:08:58+01:00 bitcoin-signet-instance already running +2026-02-08T08:08:58+01:00 Starting mempool stack... +2026-02-08T08:08:58+01:00 mempool stack started +2026-02-08T08:08:58+01:00 === Done === diff --git a/data/sync-utxos-cron.sh b/data/sync-utxos-cron.sh index d307e61..b7e124d 100755 --- a/data/sync-utxos-cron.sh +++ b/data/sync-utxos-cron.sh @@ -2,15 +2,50 @@ # Script de synchronisation des UTXOs dépensés # À exécuter via cron pour maintenir la synchronisation +# Vérifie que bitcoind est disponible avant d'exécuter la synchronisation SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(dirname "$SCRIPT_DIR")" LOG_FILE="$SCRIPT_DIR/sync-utxos.log" +BITCOIND_CONTAINER="bitcoin-signet-instance" + +log() { + echo "$(date -Iseconds) $*" | tee -a "$LOG_FILE" +} cd "$PROJECT_DIR" || exit 1 +log "=== Synchronisation des UTXOs dépensés ===" + +# Vérifier que bitcoind est disponible avant de commencer +if docker ps -q -f "name=^${BITCOIND_CONTAINER}$" 2>/dev/null | grep -q .; then + log "Vérification de la disponibilité de bitcoind..." + if docker exec "$BITCOIND_CONTAINER" bitcoin-cli -datadir=/root/.bitcoin getblockchaininfo &>/dev/null; then + log " ✅ Bitcoind disponible, démarrage de la synchronisation..." + else + log " ❌ Bitcoind ne répond pas (RPC non disponible)" + log " ⚠️ Arrêt de la synchronisation pour éviter de surcharger bitcoind" + log " ℹ️ Le script sera réexécuté à la prochaine heure" + exit 1 + fi +else + log " ❌ Conteneur bitcoind non trouvé ou arrêté" + log " ⚠️ Arrêt de la synchronisation" + exit 1 +fi + # Exécuter le script de synchronisation +log "Exécution du script de synchronisation..." node "$SCRIPT_DIR/sync-utxos-spent-status.mjs" >> "$LOG_FILE" 2>&1 +EXIT_CODE=$? + +if [ $EXIT_CODE -eq 0 ]; then + log "✅ Synchronisation terminée avec succès" +else + log "❌ Synchronisation échouée (code: $EXIT_CODE)" +fi # Garder seulement les 100 dernières lignes du log tail -n 100 "$LOG_FILE" > "$LOG_FILE.tmp" && mv "$LOG_FILE.tmp" "$LOG_FILE" + +exit $EXIT_CODE diff --git a/data/sync-utxos-spent-status.mjs b/data/sync-utxos-spent-status.mjs index ff20b97..d127684 100755 --- a/data/sync-utxos-spent-status.mjs +++ b/data/sync-utxos-spent-status.mjs @@ -33,9 +33,43 @@ const RPC_WALLET = process.env.BITCOIN_RPC_WALLET || 'custom_signet'; const DB_PATH = join(__dirname, 'signet.db'); /** - * Effectue un appel RPC Bitcoin + * Effectue un appel RPC Bitcoin avec timeout et retry */ -function rpcCall(method, params = []) { +function rpcCall(method, params = [], timeoutMs = 300000, maxRetries = 3) { + return new Promise(async (resolve, reject) => { + let lastError; + + for (let attempt = 0; attempt < maxRetries; attempt++) { + try { + const result = await rpcCallOnce(method, params, timeoutMs); + resolve(result); + return; + } catch (error) { + lastError = error; + const isTimeoutError = error.message.includes('timeout') || + error.message.includes('ETIMEDOUT') || + error.message.includes('ECONNRESET') || + error.message.includes('socket hang up'); + + if (attempt < maxRetries - 1 && isTimeoutError) { + const delay = Math.min(1000 * Math.pow(2, attempt), 10000); // Backoff exponentiel, max 10s + console.log(` ⚠️ Tentative ${attempt + 1}/${maxRetries} échouée, nouvelle tentative dans ${delay}ms...`); + await new Promise(resolve => setTimeout(resolve, delay)); + } else { + reject(error); + return; + } + } + } + + reject(lastError); + }); +} + +/** + * Effectue un appel RPC Bitcoin unique avec timeout + */ +function rpcCallOnce(method, params = [], timeoutMs = 300000) { return new Promise((resolve, reject) => { const url = new URL(RPC_URL); const isHttps = url.protocol === 'https:'; @@ -60,6 +94,7 @@ function rpcCall(method, params = []) { 'Authorization': `Basic ${auth}`, 'Content-Length': Buffer.byteLength(postData), }, + timeout: timeoutMs, }; const req = httpModule.request(options, (res) => { @@ -87,17 +122,44 @@ function rpcCall(method, params = []) { reject(error); }); + req.on('timeout', () => { + req.destroy(); + reject(new Error(`RPC timeout after ${timeoutMs}ms for method ${method}`)); + }); + req.write(postData); req.end(); }); } +/** + * Vérifie que bitcoind est disponible et répond + */ +async function checkBitcoindHealth() { + try { + await rpcCall('getblockchaininfo', [], 10000, 1); // Timeout court pour le healthcheck + return true; + } catch (error) { + console.error(` ❌ Bitcoind non disponible: ${error.message}`); + return false; + } +} + /** * Synchronise les UTXOs dépensés */ async function syncSpentUtxos() { console.log('🔍 Démarrage de la synchronisation des UTXOs dépensés...\n'); + // Vérifier que bitcoind est disponible avant de commencer + console.log('🔍 Vérification de la disponibilité de bitcoind...'); + const bitcoindAvailable = await checkBitcoindHealth(); + if (!bitcoindAvailable) { + console.error('❌ Bitcoind n\'est pas disponible. Arrêt de la synchronisation.'); + process.exit(1); + } + console.log(' ✅ Bitcoind disponible\n'); + // Ouvrir la base de données const db = new Database(DB_PATH); @@ -117,11 +179,17 @@ async function syncSpentUtxos() { } // Récupérer tous les UTXOs disponibles depuis Bitcoin + // Note: listunspent ne supporte pas la pagination avec skip + // Limiter maximumCount pour éviter les problèmes de mémoire avec de gros wallets + // Timeout augmenté à 5 minutes pour les gros wallets console.log('📡 Récupération des UTXOs depuis Bitcoin...'); + console.log(' ⏳ Cela peut prendre plusieurs minutes avec un wallet volumineux...'); + // Limite à 500000 UTXOs pour éviter les problèmes de mémoire + // Timeout de 5 minutes (300000ms) pour permettre le traitement des gros wallets const unspent = await rpcCall('listunspent', [0, 9999999, [], false, { minimumAmount: 0, - maximumCount: 9999999, - }]); + maximumCount: 500000, // Limite réduite de 9999999 à 500000 pour éviter les problèmes de mémoire + }], 300000, 2); // Timeout 5 minutes, max 2 retries console.log(`📊 UTXOs disponibles dans Bitcoin: ${unspent.length}`); @@ -195,6 +263,15 @@ async function syncSpentUtxos() { } catch (error) { console.error('❌ Erreur lors de la synchronisation:', error.message); + // Ne pas faire échouer le cron si c'est un problème temporaire de bitcoind + // Le script sera réexécuté à l'heure suivante + if (error.message.includes('timeout') || + error.message.includes('ECONNRESET') || + error.message.includes('socket hang up') || + error.message.includes('Could not connect')) { + console.error(' ⚠️ Problème de connexion avec bitcoind (peut être temporaire)'); + console.error(' ℹ️ Le script sera réexécuté à la prochaine heure'); + } process.exit(1); } finally { db.close(); diff --git a/data/sync-utxos.log b/data/sync-utxos.log index bf6add7..d9a16a9 100644 --- a/data/sync-utxos.log +++ b/data/sync-utxos.log @@ -1,100 +1,100 @@ +📊 UTXOs à vérifier: 80378 +📡 Récupération des UTXOs depuis Bitcoin... +📊 UTXOs disponibles dans Bitcoin: 268376 +💾 Création de la table temporaire... +💾 Insertion des UTXOs disponibles par batch... + ⏳ Traitement: 10000/268376 UTXOs insérés... + ⏳ Traitement: 20000/268376 UTXOs insérés... + ⏳ Traitement: 30000/268376 UTXOs insérés... + ⏳ Traitement: 40000/268376 UTXOs insérés... + ⏳ Traitement: 50000/268376 UTXOs insérés... + ⏳ Traitement: 60000/268376 UTXOs insérés... + ⏳ Traitement: 70000/268376 UTXOs insérés... + ⏳ Traitement: 80000/268376 UTXOs insérés... + ⏳ Traitement: 90000/268376 UTXOs insérés... + ⏳ Traitement: 100000/268376 UTXOs insérés... + ⏳ Traitement: 110000/268376 UTXOs insérés... + ⏳ Traitement: 120000/268376 UTXOs insérés... + ⏳ Traitement: 130000/268376 UTXOs insérés... + ⏳ Traitement: 140000/268376 UTXOs insérés... + ⏳ Traitement: 150000/268376 UTXOs insérés... + ⏳ Traitement: 160000/268376 UTXOs insérés... + ⏳ Traitement: 170000/268376 UTXOs insérés... + ⏳ Traitement: 180000/268376 UTXOs insérés... + ⏳ Traitement: 190000/268376 UTXOs insérés... + ⏳ Traitement: 200000/268376 UTXOs insérés... + ⏳ Traitement: 210000/268376 UTXOs insérés... + ⏳ Traitement: 220000/268376 UTXOs insérés... + ⏳ Traitement: 230000/268376 UTXOs insérés... + ⏳ Traitement: 240000/268376 UTXOs insérés... + ⏳ Traitement: 250000/268376 UTXOs insérés... + ⏳ Traitement: 260000/268376 UTXOs insérés... +💾 Mise à jour des UTXOs dépensés... + +📊 Résumé: + - UTXOs vérifiés: 80378 + - UTXOs toujours disponibles: 80378 - UTXOs dépensés détectés: 0 📈 Statistiques finales: - Total UTXOs: 283165 - - Dépensés: 202127 - - Non dépensés: 81038 + - Dépensés: 202787 + - Non dépensés: 80378 ✅ Synchronisation terminée 🔍 Démarrage de la synchronisation des UTXOs dépensés... -📊 UTXOs à vérifier: 80978 +📊 UTXOs à vérifier: 80378 📡 Récupération des UTXOs depuis Bitcoin... -📊 UTXOs disponibles dans Bitcoin: 267500 +📊 UTXOs disponibles dans Bitcoin: 268385 💾 Création de la table temporaire... 💾 Insertion des UTXOs disponibles par batch... - ⏳ Traitement: 10000/267500 UTXOs insérés... - ⏳ Traitement: 20000/267500 UTXOs insérés... - ⏳ Traitement: 30000/267500 UTXOs insérés... - ⏳ Traitement: 40000/267500 UTXOs insérés... - ⏳ Traitement: 50000/267500 UTXOs insérés... - ⏳ Traitement: 60000/267500 UTXOs insérés... - ⏳ Traitement: 70000/267500 UTXOs insérés... - ⏳ Traitement: 80000/267500 UTXOs insérés... - ⏳ Traitement: 90000/267500 UTXOs insérés... - ⏳ Traitement: 100000/267500 UTXOs insérés... - ⏳ Traitement: 110000/267500 UTXOs insérés... - ⏳ Traitement: 120000/267500 UTXOs insérés... - ⏳ Traitement: 130000/267500 UTXOs insérés... - ⏳ Traitement: 140000/267500 UTXOs insérés... - ⏳ Traitement: 150000/267500 UTXOs insérés... - ⏳ Traitement: 160000/267500 UTXOs insérés... - ⏳ Traitement: 170000/267500 UTXOs insérés... - ⏳ Traitement: 180000/267500 UTXOs insérés... - ⏳ Traitement: 190000/267500 UTXOs insérés... - ⏳ Traitement: 200000/267500 UTXOs insérés... - ⏳ Traitement: 210000/267500 UTXOs insérés... - ⏳ Traitement: 220000/267500 UTXOs insérés... - ⏳ Traitement: 230000/267500 UTXOs insérés... - ⏳ Traitement: 240000/267500 UTXOs insérés... - ⏳ Traitement: 250000/267500 UTXOs insérés... - ⏳ Traitement: 260000/267500 UTXOs insérés... + ⏳ Traitement: 10000/268385 UTXOs insérés... + ⏳ Traitement: 20000/268385 UTXOs insérés... + ⏳ Traitement: 30000/268385 UTXOs insérés... + ⏳ Traitement: 40000/268385 UTXOs insérés... + ⏳ Traitement: 50000/268385 UTXOs insérés... + ⏳ Traitement: 60000/268385 UTXOs insérés... + ⏳ Traitement: 70000/268385 UTXOs insérés... + ⏳ Traitement: 80000/268385 UTXOs insérés... + ⏳ Traitement: 90000/268385 UTXOs insérés... + ⏳ Traitement: 100000/268385 UTXOs insérés... + ⏳ Traitement: 110000/268385 UTXOs insérés... + ⏳ Traitement: 120000/268385 UTXOs insérés... + ⏳ Traitement: 130000/268385 UTXOs insérés... + ⏳ Traitement: 140000/268385 UTXOs insérés... + ⏳ Traitement: 150000/268385 UTXOs insérés... + ⏳ Traitement: 160000/268385 UTXOs insérés... + ⏳ Traitement: 170000/268385 UTXOs insérés... + ⏳ Traitement: 180000/268385 UTXOs insérés... + ⏳ Traitement: 190000/268385 UTXOs insérés... + ⏳ Traitement: 200000/268385 UTXOs insérés... + ⏳ Traitement: 210000/268385 UTXOs insérés... + ⏳ Traitement: 220000/268385 UTXOs insérés... + ⏳ Traitement: 230000/268385 UTXOs insérés... + ⏳ Traitement: 240000/268385 UTXOs insérés... + ⏳ Traitement: 250000/268385 UTXOs insérés... + ⏳ Traitement: 260000/268385 UTXOs insérés... 💾 Mise à jour des UTXOs dépensés... 📊 Résumé: - - UTXOs vérifiés: 80978 - - UTXOs toujours disponibles: 80978 + - UTXOs vérifiés: 80378 + - UTXOs toujours disponibles: 80378 - UTXOs dépensés détectés: 0 📈 Statistiques finales: - Total UTXOs: 283165 - - Dépensés: 202187 - - Non dépensés: 80978 + - Dépensés: 202787 + - Non dépensés: 80378 ✅ Synchronisation terminée 🔍 Démarrage de la synchronisation des UTXOs dépensés... -📊 UTXOs à vérifier: 80978 +📊 UTXOs à vérifier: 80378 📡 Récupération des UTXOs depuis Bitcoin... -📊 UTXOs disponibles dans Bitcoin: 267516 -💾 Création de la table temporaire... -💾 Insertion des UTXOs disponibles par batch... - ⏳ Traitement: 10000/267516 UTXOs insérés... - ⏳ Traitement: 20000/267516 UTXOs insérés... - ⏳ Traitement: 30000/267516 UTXOs insérés... - ⏳ Traitement: 40000/267516 UTXOs insérés... - ⏳ Traitement: 50000/267516 UTXOs insérés... - ⏳ Traitement: 60000/267516 UTXOs insérés... - ⏳ Traitement: 70000/267516 UTXOs insérés... - ⏳ Traitement: 80000/267516 UTXOs insérés... - ⏳ Traitement: 90000/267516 UTXOs insérés... - ⏳ Traitement: 100000/267516 UTXOs insérés... - ⏳ Traitement: 110000/267516 UTXOs insérés... - ⏳ Traitement: 120000/267516 UTXOs insérés... - ⏳ Traitement: 130000/267516 UTXOs insérés... - ⏳ Traitement: 140000/267516 UTXOs insérés... - ⏳ Traitement: 150000/267516 UTXOs insérés... - ⏳ Traitement: 160000/267516 UTXOs insérés... - ⏳ Traitement: 170000/267516 UTXOs insérés... - ⏳ Traitement: 180000/267516 UTXOs insérés... - ⏳ Traitement: 190000/267516 UTXOs insérés... - ⏳ Traitement: 200000/267516 UTXOs insérés... - ⏳ Traitement: 210000/267516 UTXOs insérés... - ⏳ Traitement: 220000/267516 UTXOs insérés... - ⏳ Traitement: 230000/267516 UTXOs insérés... - ⏳ Traitement: 240000/267516 UTXOs insérés... - ⏳ Traitement: 250000/267516 UTXOs insérés... - ⏳ Traitement: 260000/267516 UTXOs insérés... -💾 Mise à jour des UTXOs dépensés... +❌ Erreur lors de la synchronisation: socket hang up +🔍 Démarrage de la synchronisation des UTXOs dépensés... -📊 Résumé: - - UTXOs vérifiés: 80978 - - UTXOs toujours disponibles: 80978 - - UTXOs dépensés détectés: 0 - -📈 Statistiques finales: - - Total UTXOs: 283165 - - Dépensés: 202187 - - Non dépensés: 80978 - -✅ Synchronisation terminée +📊 UTXOs à vérifier: 80378 +📡 Récupération des UTXOs depuis Bitcoin... +❌ Erreur lors de la synchronisation: read ECONNRESET diff --git a/fixKnowledge/dashboard-anchor-wrong-chain-insufficient-balance.md b/fixKnowledge/dashboard-anchor-wrong-chain-insufficient-balance.md deleted file mode 100644 index 171c3d9..0000000 --- a/fixKnowledge/dashboard-anchor-wrong-chain-insufficient-balance.md +++ /dev/null @@ -1,124 +0,0 @@ -# Dashboard mauvaise chaîne / API d'ancrage "Insufficient Balance" - -**Auteur** : Équipe 4NK -**Date** : 2026-02-02 -**Version** : 1.0 - -## Symptômes - -- **Dashboard** (https://dashboard.certificator.4nkweb.com/) : affiche une mauvaise chaîne (hauteur 0, "-", ou valeurs incohérentes) au lieu d’environ 11535 blocs. -- **Clients de l’API d’ancrage** : reçoivent `{"error":"Insufficient Balance","message":"Insufficient balance. Required: 0.00001 BTC, Available: 0 BTC"}` et l’API d’ancrage ne fonctionne pas correctement. - -## Impacts - -- Les utilisateurs ne voient pas l’état réel de la blockchain. -- L’ancrage de documents échoue (solde 0 côté nœud utilisé par l’API). - -## Cause - -Une seule cause racine couvre les deux symptômes : **le nœud Bitcoin Signet auquel se connectent le Dashboard et l’API d’ancrage n’a pas la bonne chaîne ou n’a pas de solde**. - -Causes possibles : - -1. **Chaîne perdue** : le conteneur `bitcoin-signet-instance` a été recréé **sans volume persistant** (`-v signet-bitcoin-data:/root/.bitcoin`). Le nœud repart sur une nouvelle chaîne (hauteur 0 ou très basse), sans historique de mining → solde 0. Voir [signet-chain-lost-volume-persistent.md](./signet-chain-lost-volume-persistent.md). -2. **Mauvais déploiement** : le Dashboard et/ou l’API d’ancrage tournent sur une **autre machine** (ex. prod 192.168.1.103). Avec `BITCOIN_RPC_HOST=127.0.0.1`, ils appellent alors le RPC de cette machine, où il n’y a pas de nœud Signet (ou un nœud vide) → hauteur 0 ou erreur, solde 0. -3. **Wallet par défaut** : le nœud a la bonne chaîne mais le **wallet par défaut** utilisé par l’API n’est pas celui qui reçoit les récompenses de minage (`custom_signet`) → `getBalance()` retourne 0. - -## Correctifs - -### 0. Script de correction (machine bitcoin) - -Sur la machine bitcoin (192.168.1.105), à la racine du projet : - -```bash -cd /home/ncantu/Bureau/code/bitcoin - -# Vérifier, redémarrer Dashboard et API d’ancrage, lancer la vérification d’alignement -./fix-dashboard-anchor-chain.sh - -# Si la chaîne a été perdue, restaurer depuis une sauvegarde puis redémarrer -./fix-dashboard-anchor-chain.sh backups/signet-datadir-YYYYMMDD-HHMMSS.tar.gz -``` - -Le script teste d'abord la config RPC (même nœud que Mempool) avec `./test-mempool-rpc-config.sh 127.0.0.1 38332`, puis redémarre `signet-dashboard` et `anchorage-api`, lance `verify-chain-alignment.sh`, et affiche le wallet du nœud (solde). - -**Configuration unique (une seule chaîne pour Mempool, dashboard, APIs, miner) :** Un seul nœud : **bitcoin-signet-instance** sur **38332** (Mempool = host.docker.internal:38332, dashboard/APIs/miner = 127.0.0.1:38332). **Volume par défaut (chaîne complète) :** `update-signet.sh` utilise par défaut le volume contenant la chaîne Signet complète (~11530 blocs) s'il existe : volume Docker d'ID **4b5dca4d940b9f6e5db67b460f40f230a5ef1195a3769e5f91fa02be6edde649** (`SIGNET_VOLUME_FULL_CHAIN` dans le script). Sinon, volume nommé **signet-bitcoin-data**. **Sauvegarde prête à télécharger :** `backups/signet-datadir-latest.tar.gz` (symlink vers la dernière archive créée par `./save-signet-datadir-backup.sh`). **Alignement :** Même machine : `BITCOIN_RPC_HOST=127.0.0.1`, `BITCOIN_RPC_PORT=38332`. Le seul processus sur 38332 doit être le conteneur **bitcoin-signet-instance** (Mempool utilise `host.docker.internal:38332` = ce même conteneur). Vérifier qu’il ne s’agit pas d’un autre Docker : `ss -tlnp | grep 38332` et `docker ps --format '{{.Names}}' | grep bitcoin-signet-instance`. Le miner utilise `BITCOIN_RPC_HOST` / `BITCOIN_RPC_PORT` en env (défaut 127.0.0.1:38332). Tester : `./test-mempool-rpc-config.sh 127.0.0.1 38332`. Vérifier le dashboard : `./verify-dashboard-signet.sh`. - -### 1. Vérifier où tournent le Dashboard et l’API d’ancrage - -- **Dashboard** : doit être sur la **machine bitcoin (192.168.1.105)**. Vérifier le service `signet-dashboard` sur cette machine. -- **API d’ancrage** (`anchorage.certificator.4nkweb.com`) : doit être sur la **machine bitcoin (192.168.1.105)**. Vérifier le service `anchorage-api` sur cette machine. - -Les deux doivent utiliser `BITCOIN_RPC_HOST=127.0.0.1` et `BITCOIN_RPC_PORT=38332` pour parler au nœud local (conteneur sur la même machine). - -### 2. Source de vérité : Mempool et utilisateur ncantu - -**Mempool** (machine bitcoin 192.168.1.105, `/srv/4NK/mempool.4nkweb.com`) se connecte au nœud Signet du même hôte (`host.docker.internal:38332`). Si Mempool affiche la bonne chaîne (~11535 blocs), le nœud utilisé par Mempool sur cette machine a encore la chaîne complète. - -**Où chercher la chaîne / les sauvegardes (utilisateur ncantu, machine bitcoin 105) :** - -- **Sauvegarde prête à télécharger** : `backups/signet-datadir-latest.tar.gz` (dernière archive datadir, ~11530 blocs). Créée par `./save-signet-datadir-backup.sh` ; le script met à jour le symlink à chaque sauvegarde. -- **Sauvegardes horodatées** : `backups/signet-datadir-YYYYMMDD-HHMMSS.tar.gz`. -- **Volume Docker « chaîne complète »** : volume d'ID `4b5dca4d940b9f6e5db67b460f40f230a5ef1195a3769e5f91fa02be6edde649` ; `update-signet.sh` l'utilise par défaut s'il existe (voir docs/MAINTENANCE.md). -- **Volume nommé** : après restauration via `restore-signet-from-backup.sh`, le conteneur utilise `signet-bitcoin-data`. Vérifier avec `docker inspect bitcoin-signet-instance` (Mounts). -- **Datadir dans le conteneur** : si le conteneur sur 105 n’a jamais été recréé sans volume, les blocs sont dans le conteneur ; faire une sauvegarde avec `save-signet-datadir-backup.sh` ou `docker exec bitcoin-signet-instance tar czf /tmp/bitcoin-backup.tar.gz /root/.bitcoin/` puis `docker cp` vers l’hôte. - -Pour corriger la machine dont le nœud n’a que quelques blocs (ex. 6) : soit **restaurer** depuis une archive issue de 105 ou de `backups/` sous ncantu, soit **pointer** le Dashboard / l’API d’ancrage vers le RPC du nœud sur 105 (ex. `BITCOIN_RPC_HOST=192.168.1.105`) si l’architecture le permet. - -### 3. Restaurer la chaîne si elle a été perdue - -Si le nœud a une hauteur très basse (ex. 0, 6) ou pas de volume persistant : - -1. Sur la machine qui a encore la chaîne ~11535 (ex. machine bitcoin 105, ou là où Mempool affiche la bonne chaîne) : exécuter `./save-signet-datadir-backup.sh`, ou récupérer une archive depuis `/home/ncantu/Bureau/code/bitcoin/backups/` (utilisateur ncantu). -2. Copier l’archive sur la machine à corriger si besoin. -3. Sur la machine à corriger : exécuter `./restore-signet-from-backup.sh backups/signet-datadir-YYYYMMDD-HHMMSS.tar.gz`. -4. Redémarrer le conteneur si nécessaire et vérifier : `sudo docker exec bitcoin-signet-instance bitcoin-cli -datadir=$(docker exec bitcoin-signet-instance printenv BITCOIN_DIR 2>/dev/null || echo /root/.bitcoin) getblockchaininfo`. - -Voir [signet-chain-lost-volume-persistent.md](./signet-chain-lost-volume-persistent.md) et [MAINTENANCE.md](../docs/MAINTENANCE.md). - -### 4. Vérifier l’alignement chaîne / solde - -Sur la machine bitcoin : - -```bash -./verify-chain-alignment.sh -sudo docker exec bitcoin-signet-instance bitcoin-cli -datadir=/root/.bitcoin getblockchaininfo | grep -E '"chain"|"blocks"' -sudo docker exec bitcoin-signet-instance bitcoin-cli -datadir=/root/.bitcoin getwalletinfo -``` - -- `chain` doit être `"signet"`, `blocks` proche de 11535. -- Le wallet utilisé par défaut (ex. `custom_signet`) doit avoir un solde > 0 après mining. - -### 5. Si la chaîne est bonne mais le solde reste 0 - -Vérifier que le wallet contenant les récompenses de minage est bien celui utilisé par l’API : - -- Le miner utilise en général le wallet `custom_signet`. -- L’API d’ancrage appelle `getBalance()` sans nom de wallet → utilise le **wallet par défaut** du nœud. -- Si le nœud a plusieurs wallets, s’assurer que le wallet par défaut est celui qui a du solde (ou charger `custom_signet` au démarrage du nœud comme wallet par défaut). - -## Modalités de déploiement - -- Sur la machine bitcoin : exécuter `./fix-dashboard-anchor-chain.sh` (avec chemin de backup si la chaîne a été perdue). Le script redémarre `signet-dashboard` et `anchorage-api`. -- Recréer le conteneur Bitcoin via `./update-signet.sh` : le script utilise par défaut le volume chaîne complète (ID `4b5dca4d940b9f6e5db67b460f40f230a5ef1195a3769e5f91fa02be6edde649`) s'il existe, sinon `signet-bitcoin-data`. Ne pas recréer manuellement sans volume persistant. -- Sauvegarde prête à télécharger : `backups/signet-datadir-latest.tar.gz` (créée par `./save-signet-datadir-backup.sh`). - -## Modalités d’analyse - -- Consulter les logs du Dashboard : `sudo journalctl -u signet-dashboard -f` (erreurs RPC). -- Consulter les logs de l’API d’ancrage : `sudo journalctl -u anchorage-api -f` (erreurs "Insufficient balance", connexion RPC). -- Vérifier la hauteur et le wallet sur le nœud : commandes ci-dessus. - -## Pages affectées - -- fixKnowledge/dashboard-anchor-wrong-chain-insufficient-balance.md (ce fichier) -- fixKnowledge/signet-chain-lost-volume-persistent.md (volume chaîne complète, sauvegarde latest) -- update-signet.sh (SIGNET_VOLUME_FULL_CHAIN, utilisation par défaut du volume chaîne complète) -- save-signet-datadir-backup.sh (symlink signet-datadir-latest.tar.gz, tolérance tar exit 1) -- backups/README.md (sauvegarde prête à télécharger, volume par défaut) -- docs/MAINTENANCE.md (volume chaîne complète, sauvegarde latest) -- test-mempool-rpc-config.sh (test de la config RPC utilisée par Mempool) -- verify-dashboard-signet.sh (vérification que le dashboard affiche le signet custom) -- fix-dashboard-anchor-chain.sh (script de correction sur la machine bitcoin) -- signet-dashboard.service, anchorage-api.service, faucet-api.service (alignement RPC sur le même nœud que Mempool) -- signet-dashboard/public/app.js (vérification response.ok et alerte chaîne anormale) diff --git a/fixKnowledge/mempool-api-healthcheck-fix.md b/fixKnowledge/mempool-api-healthcheck-fix.md deleted file mode 100644 index 86249c4..0000000 --- a/fixKnowledge/mempool-api-healthcheck-fix.md +++ /dev/null @@ -1,88 +0,0 @@ -# Fix: Mempool API Healthcheck - curl not found - -**Date:** 2026-01-27 -**Auteur:** Équipe 4NK - -## Problème - -Le conteneur Docker `mempool_api_1` était marqué comme "unhealthy" avec un FailingStreak de 2963 échecs consécutifs. - -### Symptômes - -- Statut Docker: `unhealthy` -- Erreur répétée: `/bin/sh: 1: curl: not found` -- Le healthcheck ne pouvait pas s'exécuter car `curl` n'est pas installé dans l'image `mempool/backend:latest` - -### Impact - -- Le conteneur fonctionnait normalement (les logs montraient une synchronisation correcte des index Bitcoin) -- Le statut "unhealthy" générait des alertes et masquait l'état réel du service -- Pas d'impact fonctionnel direct, mais confusion sur l'état réel du service - -## Root cause - -Le healthcheck dans `docker-compose.signet.yml` utilisait la commande `curl` qui n'est pas disponible dans l'image Docker `mempool/backend:latest`. L'image ne contient que les dépendances minimales nécessaires au backend Node.js. - -## Correctifs - -### Modification du healthcheck - -**Fichier modifié:** `mempool/docker-compose.signet.yml` - -**Avant:** -```yaml -healthcheck: - test: ["CMD-SHELL", "curl -f http://localhost:8999/api/v1/backend-info | grep -q . || exit 1"] -``` - -**Après:** -```yaml -healthcheck: - test: ["CMD-SHELL", "node -e \"require('http').get('http://localhost:8999/api/v1/backend-info', (r) => { process.exit(r.statusCode === 200 ? 0 : 1); }).on('error', () => process.exit(1));\""] -``` - -### Justification - -- `node` est disponible dans l'image (backend Node.js) -- Utilisation de l'API HTTP native de Node.js au lieu de `curl` -- Même logique de vérification: requête HTTP vers `/api/v1/backend-info` avec vérification du code de statut 200 - -## Modifications - -- `mempool/docker-compose.signet.yml`: Modification du healthcheck du service `api` - -## Modalités de déploiement - -1. Modifier le fichier `docker-compose.signet.yml` -2. Recréer le conteneur pour appliquer la nouvelle configuration: - ```bash - cd /srv/4NK/mempool.4nkweb.com - docker-compose -f docker-compose.signet.yml up -d --force-recreate api - ``` -3. Vérifier que le healthcheck passe à "healthy" après le délai de démarrage (40s) - -## Modalités d'analyse - -### Vérification du statut - -```bash -docker inspect mempool_api_1 --format='{{.State.Health.Status}}' -``` - -### Vérification des logs du healthcheck - -```bash -docker inspect mempool_api_1 --format='{{json .State.Health}}' | python3 -m json.tool -``` - -### Test manuel du healthcheck - -```bash -docker exec mempool_api_1 node -e "require('http').get('http://localhost:8999/api/v1/backend-info', (r) => { console.log('Status:', r.statusCode); process.exit(r.statusCode === 200 ? 0 : 1); }).on('error', (e) => { console.error('Error:', e.message); process.exit(1); });" -``` - -## Résultat - -- Le conteneur `mempool_api_1` est maintenant marqué comme "healthy" -- Le healthcheck fonctionne correctement avec Node.js -- Aucun impact sur le fonctionnement du service diff --git a/fixKnowledge/mempool-websocket-offline-fix.md b/fixKnowledge/mempool-websocket-offline-fix.md deleted file mode 100644 index 4d327aa..0000000 --- a/fixKnowledge/mempool-websocket-offline-fix.md +++ /dev/null @@ -1,106 +0,0 @@ -# Correction: Mempool affiche "hors connexion" - -**Date**: 2026-01-26 -**Auteur**: Équipe 4NK - -## Motivations - -- Le mempool affichait le site mais indiquait "hors connexion" -- Le WebSocket ne pouvait pas se connecter au backend -- L'utilisateur ne pouvait pas utiliser l'explorateur blockchain - -## Root causes - -- Le backend mempool était en état "unhealthy" mais fonctionnait partiellement -- Le backend avait des problèmes de connexion avec Bitcoin RPC (erreurs `ECONNRESET`) -- Le processus de mise à jour des blocs était bloqué (`$updateBlocks stalled`) -- Le backend nécessitait un redémarrage pour se resynchroniser correctement - -## Correctifs - -- Redémarrage du backend mempool pour résoudre les problèmes de connexion -- Vérification de la configuration nginx du frontend (proxy WebSocket vers `http://api:8999/`) -- Vérification de la connectivité entre le frontend et le backend via le réseau Docker -- Restauration de la configuration nginx après des modifications incorrectes - -## Evolutions - -- Création d'un script de diagnostic (`mempool/diagnose-mempool.sh`) pour vérifier l'état des services localement -- Création d'un script de diagnostic et correction (`mempool/fix-mempool-websocket.sh`) pour diagnostiquer et corriger les problèmes sur la machine bitcoin -- Amélioration de la configuration nginx pour le WebSocket (ajout des headers nécessaires dans `mempool/nginx-mempool.conf`) - -## Pages affectées - -- `mempool/diagnose-mempool.sh` : Script de diagnostic local -- `mempool/fix-mempool-websocket.sh` : Script de diagnostic et correction distant -- `mempool/nginx-mempool.conf` : Configuration nginx pour le WebSocket (amélioration des headers) - -## Modalités de déploiement - -1. **Diagnostic local** (depuis la racine du projet `/home/ncantu/Bureau/code/bitcoin/`) : - ```bash - cd mempool - ./diagnose-mempool.sh - ``` - -2. **Redémarrage du backend si nécessaire** : - ```bash - docker-compose -f docker-compose.signet.yml restart api - ``` - -3. **Vérification de la connectivité** : - ```bash - docker-compose -f docker-compose.signet.yml exec -T web curl -f -s http://api:8999/api/v1/backend-info - ``` - -4. **Redémarrage du frontend si la configuration nginx a été modifiée** : - ```bash - docker-compose -f docker-compose.signet.yml restart web - ``` - -## Modalités d'analyse - -### Vérifier l'état des services - -```bash -docker-compose -f docker-compose.signet.yml ps -``` - -### Vérifier les logs du backend - -```bash -docker-compose -f docker-compose.signet.yml logs --tail=50 api | grep -E "(ERROR|ERR|WARN|failed|error)" -``` - -### Tester la connectivité backend - -```bash -# Depuis le conteneur frontend -docker-compose -f docker-compose.signet.yml exec -T web curl -f -s http://api:8999/api/v1/backend-info - -# Depuis l'extérieur -curl -s http://localhost:3015/api/v1/backend-info -``` - -### Vérifier la configuration nginx WebSocket - -```bash -docker-compose -f docker-compose.signet.yml exec -T web cat /etc/nginx/conf.d/nginx-mempool.conf | grep -A 5 "api/v1/ws" -``` - -La configuration doit être : -```nginx -location /api/v1/ws { - proxy_pass http://api:8999/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "Upgrade"; -} -``` - -## Notes - -- Le backend mempool écoute sur toutes les routes pour les WebSockets (WebSocket.Server créé directement sur le serveur HTTP) -- Le nginx du frontend proxyfie `/api/v1/ws` vers `http://api:8999/` (racine du backend) -- Le backend peut être en état "unhealthy" mais fonctionner quand même si le healthcheck échoue pour une raison technique (par exemple, `curl` non disponible dans le conteneur) -- Les erreurs `ECONNRESET` avec Bitcoin RPC peuvent être temporaires et se résoudre avec un redémarrage diff --git a/fixKnowledge/signet-bitcoind-crash-mining-stopped.md b/fixKnowledge/signet-bitcoind-crash-mining-stopped.md new file mode 100644 index 0000000..561ee60 --- /dev/null +++ b/fixKnowledge/signet-bitcoind-crash-mining-stopped.md @@ -0,0 +1,55 @@ +# Signet: bitcoind crash, mining stopped + +**Date:** 2026-02-05 +**Auteur:** Équipe 4NK + +## Problème + +La chaîne signet ne mine plus. Les logs du miner affichent en boucle : +`error: timeout on transient error: Could not connect to the server 127.0.0.1:38332`. + +## Cause + +Le processus **bitcoind** a cessé de tourner à l’intérieur du conteneur `bitcoin-signet-instance`. Le conteneur reste « Up », mais seul le script `run.sh` et `mine.sh` sont actifs ; bitcoind n’apparaît plus dans `ps aux`. Le RPC bitcoind (port 38332) ne répond donc pas. + +Dans `signet/debug.log`, la dernière ligne utile est un `CreateNewBlock()` ou `UpdateTip` ; aucune ligne d’erreur ou de shutdown explicite (crash ou kill possible, ex. OOM). + +## Diagnostic + +1. Lancer le script d’état : + ```bash + ./data/check-services-status.sh + ``` +2. Si « RPC: HORS SERVICE » pour bitcoind alors que le conteneur est Up : confirmer avec + ```bash + docker exec bitcoin-signet-instance ps aux + ``` + (absence du processus `bitcoind`). +3. Dernières lignes du log bitcoind : + ```bash + docker exec bitcoin-signet-instance tail -20 /root/.bitcoin/signet/debug.log + ``` + +## Correction + +Redémarrer le conteneur pour relancer bitcoind (et donc le minage) : + +```bash +docker restart bitcoin-signet-instance +``` + +Attendre ~30–60 s que bitcoind et le wallet soient prêts, puis vérifier : + +```bash +docker exec bitcoin-signet-instance bitcoin-cli -datadir=/root/.bitcoin getblockchaininfo +``` + +## Prévention / suivi + +- Utiliser `./data/check-services-status.sh` régulièrement (ou via cron) pour détecter RPC hors service. +- En cas de répétition des crashes, investiguer (RAM, disque, `dmesg` OOM, logs bitcoind complets). + +## Pages affectées + +- `data/check-services-status.sh` (script de vérification) +- `fixKnowledge/signet-bitcoind-crash-mining-stopped.md` (ce document) diff --git a/fixKnowledge/signet-bitcoind-crash-wallet-loading-stuck.md b/fixKnowledge/signet-bitcoind-crash-wallet-loading-stuck.md new file mode 100644 index 0000000..c8a1159 --- /dev/null +++ b/fixKnowledge/signet-bitcoind-crash-wallet-loading-stuck.md @@ -0,0 +1,186 @@ +# Signet: bitcoind crash puis blocage en chargement de wallet + +**Date:** 2026-02-08 +**Auteur:** Équipe 4NK + +## Problème + +Bitcoind a planté silencieusement dans le conteneur `bitcoin-signet-instance` vers 3h08 du matin. Le conteneur reste actif, mais le processus bitcoind n'est plus en cours d'exécution. Au redémarrage du conteneur à 07:12, bitcoind est bloqué en chargement de wallet ("Loading wallet…"). + +### Symptômes + +1. **Conteneur actif mais bitcoind planté** : + - Le conteneur Docker est "Up" mais bitcoind n'est plus en cours d'exécution + - `ps aux` dans le conteneur montre uniquement `entrypoint.sh`, `run.sh`, `mine.sh`, `tail -f /dev/null` + - Pas de processus `bitcoind` + +2. **RPC inaccessible** : + - `bitcoin-cli` retourne : `Could not connect to the server 127.0.0.1:38332` + - `curl` vers le RPC depuis l'hôte : connexion reset (exit 56) + - Le port 38332 est toujours en écoute côté hôte (mapping Docker) mais il n'y a plus de processus RPC derrière + +3. **Blocage en chargement de wallet** : + - Après redémarrage, bitcoind est bloqué en "Loading wallet…" + - `bitcoin-cli getblockchaininfo` retourne : `error code: -28 error message: Loading wallet…` + - CPU à 100% mais aucun progrès visible + - Wallet volumineux : 315MB (`wallet.dat`) + +4. **Erreurs des services dépendants** : + - Dashboard : erreurs "socket hang up" et "read ECONNRESET" + - Script `sync-utxos-cron.sh` : erreurs de connexion RPC + +### Impact + +- **Fonctionnalité** : Tous les services dépendants de bitcoind sont hors service +- **Mining** : Le minage est arrêté +- **API** : L'API d'ancrage ne peut plus fonctionner +- **Dashboard** : Le dashboard ne peut plus afficher les données de la blockchain + +## Root causes + +1. **Crash silencieux de bitcoind** : + - Bitcoind a planté silencieusement vers 3h08 sans message d'erreur dans les logs + - Dernière activité normale : bloc miné à 02:54:29 (height 13139) + - Nouveau bloc reçu à 03:08:48 (height 13140) + - Après cela, plus aucune activité jusqu'au redémarrage à 07:12:16 + +2. **Wallet volumineux** : + - Le wallet `custom_signet` fait 315MB + - Le chargement du wallet peut prendre beaucoup de temps ou bloquer si le wallet est corrompu ou trop volumineux + +3. **Absence de détection automatique** : + - Le conteneur Docker reste actif même si bitcoind plante + - Aucun mécanisme de redémarrage automatique de bitcoind dans le conteneur + - Le script `check-services-status.sh` détecte le problème mais ne le corrige pas automatiquement + +## Diagnostic + +### Vérifier l'état actuel + +1. **Vérifier les processus dans le conteneur** : + ```bash + docker exec bitcoin-signet-instance ps aux + ``` + Si bitcoind n'apparaît pas, il a planté. + +2. **Vérifier l'état du RPC** : + ```bash + docker exec bitcoin-signet-instance bitcoin-cli -datadir=/root/.bitcoin getblockchaininfo + ``` + Si erreur "Could not connect" ou "Loading wallet…", bitcoind est planté ou bloqué. + +3. **Vérifier les logs** : + ```bash + docker logs bitcoin-signet-instance --tail 100 + docker exec bitcoin-signet-instance tail -50 /root/.bitcoin/signet/debug.log + ``` + +4. **Vérifier la taille du wallet** : + ```bash + docker exec bitcoin-signet-instance du -sh /root/.bitcoin/signet/wallets/custom_signet/ + ``` + +### Utiliser le script de diagnostic + +```bash +./data/check-services-status.sh +``` + +Si "RPC: HORS SERVICE", bitcoind a planté. + +## Correctifs + +### Solution immédiate : Redémarrer le conteneur + +```bash +docker restart bitcoin-signet-instance +``` + +Attendre 60-120 secondes que bitcoind démarre et charge le wallet, puis vérifier : + +```bash +docker exec bitcoin-signet-instance bitcoin-cli -datadir=/root/.bitcoin getblockchaininfo +``` + +### Si le wallet reste bloqué en chargement + +Si après redémarrage, bitcoind reste bloqué en "Loading wallet…" pendant plus de 5 minutes : + +1. **Arrêter le conteneur** : + ```bash + docker stop bitcoin-signet-instance + ``` + +2. **Vérifier l'intégrité du wallet** : + ```bash + docker exec bitcoin-signet-instance ls -lah /root/.bitcoin/signet/wallets/custom_signet/ + ``` + Vérifier la présence de `wallet.dat-journal` (fichier de journalisation) + +3. **Redémarrer avec vérification** : + ```bash + docker start bitcoin-signet-instance + docker logs bitcoin-signet-instance -f + ``` + Surveiller les logs pour voir si le wallet se charge correctement. + +4. **Si le wallet est corrompu** : + - Sauvegarder le wallet actuel + - Essayer de réparer avec `bitcoin-wallet` (si disponible) + - Ou restaurer depuis une sauvegarde + +### Solution préventive : Améliorer la détection et le redémarrage automatique + +1. **Améliorer le script `check-services-status.sh`** : + - Détecter si bitcoind est planté même si le conteneur est actif + - Proposer automatiquement un redémarrage si le problème est détecté + +2. **Ajouter un healthcheck Docker** : + - Créer un healthcheck qui vérifie que le RPC bitcoind répond + - Configurer Docker pour redémarrer automatiquement le conteneur si le healthcheck échoue + +3. **Surveillance proactive** : + - Ajouter un cron job qui exécute `check-services-status.sh` toutes les 5 minutes + - Envoyer une alerte si bitcoind est hors service + +## Prévention / suivi + +- **Surveillance régulière** : Exécuter `./data/check-services-status.sh` régulièrement (ou via cron toutes les 5 minutes) +- **Logs** : Surveiller les logs bitcoind pour détecter les problèmes avant qu'ils ne causent un crash +- **Sauvegardes** : Sauvegarder régulièrement le wallet et la chaîne +- **Investigation** : En cas de répétition des crashes, investiguer (RAM, disque, `dmesg` OOM, logs bitcoind complets) + +## Note importante sur les crons et la cause probable + +**Le script `restart-services-cron.sh` n'est PAS actuellement dans les crontabs.** + +**Cause probable du crash récurrent vers 3h** : Le script `sync-utxos-cron.sh` s'exécute à 3h00 et fait un appel RPC très lourd (`listunspent` avec `maximumCount: 9999999`). Avec un wallet volumineux (315MB) et potentiellement des centaines de milliers d'UTXOs, cet appel peut : +- Consommer beaucoup de mémoire +- Prendre beaucoup de temps +- Causer un crash de bitcoind si la mémoire est insuffisante ou si bitcoind est déjà dans un état fragile + +**Recommandation pour ajouter `restart-services-cron.sh` à 4h** : + +✅ **OUI, c'est prudent d'ajouter `restart-services-cron.sh` à 4h**, car : +- Le script a été amélioré pour gérer les wallets volumineux (timeout de 5 minutes, détection du chargement de wallet) +- Cela permettra de redémarrer bitcoind automatiquement après un crash potentiel vers 3h +- Le redémarrage à 4h laisse le temps au script `sync-utxos-cron.sh` de se terminer (ou d'échouer) avant le redémarrage + +⚠️ **Mais il faut aussi** : +- Surveiller les logs pour confirmer que le crash est bien lié au script `sync-utxos-cron.sh` +- Considérer d'optimiser le script `sync-utxos-spent-status.mjs` pour réduire la charge sur bitcoind +- Surveiller la mémoire disponible pour bitcoind + +**Configuration recommandée** : +```bash +# Ajouter dans crontab +0 4 * * * /home/ncantu/Bureau/code/bitcoin/data/restart-services-cron.sh +``` + +Cela redémarrera bitcoind tous les jours à 4h, ce qui permettra de récupérer automatiquement après un crash potentiel vers 3h. + +## Pages affectées + +- `data/check-services-status.sh` (script de vérification) +- `fixKnowledge/signet-bitcoind-crash-wallet-loading-stuck.md` (ce document) +- `fixKnowledge/signet-bitcoind-crash-mining-stopped.md` (documentation similaire) diff --git a/fixKnowledge/sync-utxos-rpc-optimization.md b/fixKnowledge/sync-utxos-rpc-optimization.md new file mode 100644 index 0000000..ca29609 --- /dev/null +++ b/fixKnowledge/sync-utxos-rpc-optimization.md @@ -0,0 +1,135 @@ +# Optimisation du script sync-utxos pour éviter les crashes RPC + +**Date:** 2026-02-08 +**Auteur:** Équipe 4NK + +## Problème + +Le script `sync-utxos-cron.sh` s'exécute toutes les heures (à 3h00 notamment) et fait un appel RPC très lourd (`listunspent` avec `maximumCount: 9999999`). Avec un wallet volumineux (315MB) et potentiellement des centaines de milliers d'UTXOs, cet appel peut : + +- Consommer beaucoup de mémoire +- Prendre beaucoup de temps (plusieurs minutes) +- Causer un crash de bitcoind si la mémoire est insuffisante ou si bitcoind est déjà dans un état fragile +- Bloquer le RPC pendant plusieurs minutes, empêchant les autres services de fonctionner + +### Symptômes observés + +- Crash silencieux de bitcoind vers 3h08 (après l'exécution du script à 3h00) +- Erreurs "socket hang up" et "read ECONNRESET" dans les logs du dashboard +- Bitcoind bloqué en chargement de wallet après redémarrage +- Wallet volumineux (315MB) causant des chargements longs + +## Root causes + +1. **Appel RPC sans timeout** : L'appel `listunspent` n'avait pas de timeout, pouvant bloquer indéfiniment +2. **Pas de vérification préalable** : Le script ne vérifiait pas si bitcoind était disponible avant de faire l'appel lourd +3. **Pas de retry avec backoff** : En cas d'échec temporaire, le script échouait immédiatement +4. **Limite trop élevée** : `maximumCount: 9999999` pouvait charger des centaines de milliers d'UTXOs en mémoire d'un coup +5. **Pas de gestion d'erreurs** : Les erreurs n'étaient pas gérées de manière gracieuse + +## Correctifs appliqués + +### 1. Ajout de timeout sur les appels RPC + +**Fichier** : `data/sync-utxos-spent-status.mjs` + +- **Timeout de 5 minutes** (300000ms) pour l'appel `listunspent` avec gros wallet +- **Timeout de 10 secondes** pour le healthcheck `getblockchaininfo` +- Les timeouts sont configurables via les paramètres de la fonction `rpcCall` + +### 2. Vérification de santé de bitcoind avant synchronisation + +**Fichier** : `data/sync-utxos-spent-status.mjs` + +- Ajout de la fonction `checkBitcoindHealth()` qui vérifie que bitcoind répond avant de commencer +- Si bitcoind n'est pas disponible, le script s'arrête immédiatement sans faire d'appel RPC lourd + +### 3. Retry avec backoff exponentiel + +**Fichier** : `data/sync-utxos-spent-status.mjs` + +- Ajout de retry automatique avec backoff exponentiel (1s, 2s, 4s, max 10s) +- Maximum 3 tentatives pour les erreurs de timeout/connexion +- Maximum 2 tentatives pour l'appel `listunspent` (pour éviter de surcharger bitcoind) + +### 4. Réduction de la limite maximumCount + +**Fichier** : `data/sync-utxos-spent-status.mjs` + +- Réduction de `maximumCount` de 9999999 à 500000 UTXOs +- Limite la consommation mémoire et réduit le temps de traitement +- Si vous avez plus de 500000 UTXOs, il faudra augmenter cette limite ou optimiser l'approche + +### 5. Amélioration du script cron + +**Fichier** : `data/sync-utxos-cron.sh` + +- Vérification que le conteneur bitcoind est actif avant d'exécuter le script Node.js +- Vérification que le RPC bitcoind répond avant de commencer +- Logs améliorés avec timestamps +- Gestion des codes de sortie pour permettre la détection d'échecs + +### 6. Gestion d'erreurs améliorée + +**Fichier** : `data/sync-utxos-spent-status.mjs` + +- Distinction entre erreurs temporaires (timeout, connexion) et erreurs permanentes +- Messages d'erreur plus informatifs +- Le script ne fait pas échouer le cron si c'est un problème temporaire de bitcoind + +## Modifications + +**Fichiers modifiés :** + +- `data/sync-utxos-spent-status.mjs` : + - Ajout de `rpcCall()` avec timeout et retry + - Ajout de `rpcCallOnce()` avec timeout HTTP + - Ajout de `checkBitcoindHealth()` + - Réduction de `maximumCount` à 500000 + - Timeout de 5 minutes pour `listunspent` + - Gestion d'erreurs améliorée + +- `data/sync-utxos-cron.sh` : + - Vérification de disponibilité de bitcoind avant exécution + - Logs améliorés avec timestamps + - Gestion des codes de sortie + +## Modalités de déploiement + +1. **Vérifier que les scripts sont exécutables** : + ```bash + chmod +x data/sync-utxos-cron.sh + ``` + +2. **Tester manuellement** : + ```bash + ./data/sync-utxos-cron.sh + ``` + +3. **Vérifier les logs** : + ```bash + tail -f data/sync-utxos.log + ``` + +4. **Surveiller la prochaine exécution** (à 3h00) pour vérifier que le problème est résolu + +## Modalités d'analyse + +- **Logs du script** : `data/sync-utxos.log` (100 dernières lignes conservées) +- **Vérifier les timeouts** : Chercher "timeout" dans les logs +- **Vérifier les retries** : Chercher "Tentative" dans les logs +- **Vérifier les erreurs** : Chercher "❌" dans les logs + +## Prévention / suivi + +- **Surveillance** : Surveiller les logs après chaque exécution pour détecter les problèmes +- **Ajustement** : Si vous avez plus de 500000 UTXOs, augmenter `maximumCount` ou optimiser l'approche +- **Timeout** : Si le timeout de 5 minutes est insuffisant, l'augmenter progressivement +- **Investigation** : En cas de répétition des problèmes, investiguer la mémoire disponible pour bitcoind + +## Pages affectées + +- `data/sync-utxos-spent-status.mjs` (script de synchronisation amélioré) +- `data/sync-utxos-cron.sh` (script cron amélioré) +- `fixKnowledge/sync-utxos-rpc-optimization.md` (ce document) +- `fixKnowledge/signet-bitcoind-crash-wallet-loading-stuck.md` (documentation du problème) diff --git a/userwallet/src/components/PairingDisplayScreen.tsx b/userwallet/src/components/PairingDisplayScreen.tsx deleted file mode 100644 index f207b6d..0000000 --- a/userwallet/src/components/PairingDisplayScreen.tsx +++ /dev/null @@ -1,269 +0,0 @@ -import { useState, FormEvent, useEffect } from 'react'; -import { Link } from 'react-router-dom'; -import { - addRemotePairFromWords, - ensureLocalPairForSetup, - getStoredPairs, - parseAndValidatePairingWords, - removePair, -} from '../utils/pairing'; -import { usePairingWordsContext } from '../contexts/PairingWordsContext'; -import { useIdentity } from '../hooks/useIdentity'; -import { usePairingConnected } from '../hooks/usePairingConnected'; -import { getStoredRelays } from '../utils/relay'; -import { runDevice2Confirmation } from '../services/pairingConfirm'; -import { WordInputGrid } from './WordInputGrid'; - -export function PairingDisplayScreen(): JSX.Element { - const ctx = usePairingWordsContext(); - const { identity, isLoading, createNewIdentity } = useIdentity(); - const { connected: pairingConnected } = usePairingConnected(); - const [words2nd, setWords2nd] = useState([]); - const [wordInput, setWordInput] = useState([]); - const [error, setError] = useState(null); - const [success, setSuccess] = useState(false); - const [isConfirming, setIsConfirming] = useState(false); - const [justConnected, setJustConnected] = useState(false); - const [visibleWordIndices, setVisibleWordIndices] = useState>(new Set()); - - const toggleWordVisibility = (index: number): void => { - setVisibleWordIndices((prev) => { - const next = new Set(prev); - if (next.has(index)) { - next.delete(index); - } else { - next.add(index); - } - return next; - }); - }; - - const handleGenerateAnotherKey = (): void => { - const pairs = getStoredPairs(); - const local = pairs.find((p) => p.is_local); - if (local !== undefined) { - removePair(local.uuid); - } - createNewIdentity(); - }; - - useEffect(() => { - if (identity !== null && identity.publicKey !== undefined) { - const w = ensureLocalPairForSetup(identity.publicKey); - setWords2nd(w); - } - }, [identity]); // eslint-disable-line react-hooks/exhaustive-deps - - useEffect(() => { - if (isLoading || identity !== null) { - return; - } - createNewIdentity(); - }, [isLoading, identity, createNewIdentity]); - - useEffect(() => { - ctx?.setOfferWords(words2nd); - return () => { - ctx?.setOfferWords(null); - }; - }, [ctx, words2nd]); - - const handleSubmit = (e: FormEvent): void => { - e.preventDefault(); - void (async (): Promise => { - setError(null); - const wordsText = wordInput.join(' '); - const parsed = parseAndValidatePairingWords(wordsText); - if (parsed === null) { - setError('Mots invalides. 17 mots requis.'); - return; - } - const pair = addRemotePairFromWords(parsed, []); - if (pair === null) { - setError('Mots invalides. Vérifiez la saisie.'); - return; - } - const pairs = getStoredPairs(); - const local = pairs.find((p) => p.is_local); - const remote = pairs.find((p) => !p.is_local); - if ( - identity === null || - local === undefined || - remote === undefined || - identity.privateKey === undefined - ) { - setSuccess(true); - return; - } - const relays = getStoredRelays().filter((r) => r.enabled); - if (relays.length === 0) { - setError('Aucun relais activé. Configurez les relais pour finaliser le pairing.'); - return; - } - // Use pair's publicKey if available (will be updated from signatures if not) - const remotePublicKey = remote.publicKey; - setIsConfirming(true); - try { - const ok = await runDevice2Confirmation({ - pairLocal: local.uuid, - pairRemote: remote.uuid, - identity, - relays, - start: identity.t0_anniversaire, - end: Date.now(), - remotePublicKey, - }); - setJustConnected(ok); - } catch (err) { - console.error('Pairing confirmation (device 2):', err); - setError( - err instanceof Error ? err.message : 'Erreur lors de la confirmation du pairing.', - ); - setIsConfirming(false); - return; - } - setIsConfirming(false); - setSuccess(true); - })(); - }; - - if (success) { - const showConnected = pairingConnected || justConnected; - return ( -
- {showConnected && ( -

- Connecté -

- )} -

Pair associé

-

Le pair du 1ᵉʳ appareil a été ajouté.

-
-

- Mots du 2ᵉ appareil — à copier sur le 1ᵉʳ -

-

- -

- {words2nd.length > 0 ? ( -
- {words2nd.map((word, index) => { - const visible = visibleWordIndices.has(index); - return ( -
- {index + 1}. - - {visible ? word : '•••••'} - - -
- ); - })} -
- ) : ( -

- )} -
-

- Accueil — Gérer les pairs -

-
- ); - } - - return ( -
-

Saisir les mots du 1ᵉʳ appareil

-

- Saisissez les 17 mots affichés par le 1ᵉʳ appareil. -

-
void handleSubmit(ev)} - aria-label="Saisir les mots du 1er appareil" - > - - {error !== null && ( - - )} - -
-

- Accueil — Gérer les pairs -

-
- ); -} diff --git a/userwallet/src/components/PairingSetupBlock.tsx b/userwallet/src/components/PairingSetupBlock.tsx deleted file mode 100644 index a647ee2..0000000 --- a/userwallet/src/components/PairingSetupBlock.tsx +++ /dev/null @@ -1,275 +0,0 @@ -import { useEffect, useMemo, useState, FormEvent } from 'react'; -import { useNavigate } from 'react-router-dom'; -import QRCode from 'qrcode'; -import { - ensureLocalPairForSetup, - addRemotePairFromWords, - getStoredPairs, - parseAndValidatePairingWords, - removePair, -} from '../utils/pairing'; -import { useIdentity } from '../hooks/useIdentity'; -import { getStoredRelays } from '../utils/relay'; -import { runDevice1Confirmation } from '../services/pairingConfirm'; -import { WordInputGrid } from './WordInputGrid'; - -const PAIRING_DISPLAY_PATH = '/pairing-display'; -const QR_SIZE = 256; - -function buildPairingDisplayUrl(): string { - return `${window.location.origin}${PAIRING_DISPLAY_PATH}`; -} - -export interface PairingSetupBlockProps { - onDone?: () => void; - isAddingDevice?: boolean; -} - -export function PairingSetupBlock({ - onDone, - isAddingDevice = false, -}: PairingSetupBlockProps): JSX.Element { - const navigate = useNavigate(); - const { identity, createNewIdentity } = useIdentity(); - const [words, setWords] = useState([]); - const [qrDataUrl, setQrDataUrl] = useState(null); - const [remoteWordsInput, setRemoteWordsInput] = useState([]); - const [remoteError, setRemoteError] = useState(null); - const [hasCopiedToSecondDevice, setHasCopiedToSecondDevice] = useState(false); - const [isConfirming, setIsConfirming] = useState(false); - const [visibleWordIndices, setVisibleWordIndices] = useState>(new Set()); - - const toggleWordVisibility = (index: number): void => { - setVisibleWordIndices((prev) => { - const next = new Set(prev); - if (next.has(index)) { - next.delete(index); - } else { - next.add(index); - } - return next; - }); - }; - - const handleGenerateAnotherKey = (): void => { - const pairs = getStoredPairs(); - const local = pairs.find((p) => p.is_local); - if (local !== undefined) { - removePair(local.uuid); - } - createNewIdentity(); - }; - - useEffect(() => { - if (identity !== null && identity.publicKey !== undefined) { - const w = ensureLocalPairForSetup(identity.publicKey); - setWords(w); - } - }, [identity]); - - const url = useMemo(() => buildPairingDisplayUrl(), []); - - useEffect(() => { - if (words.length === 0) { - return; - } - QRCode.toDataURL(url, { width: QR_SIZE }) - .then(setQrDataUrl) - .catch((err: unknown) => { - console.error('QR generation failed:', err); - }); - }, [words, url]); - - const handleSubmitRemote = async (e: FormEvent): Promise => { - e.preventDefault(); - setRemoteError(null); - const wordsText = remoteWordsInput.join(' '); - const parsed = parseAndValidatePairingWords(wordsText); - if (parsed === null) { - setRemoteError('Mots invalides. 17 mots requis.'); - return; - } - const newPair = addRemotePairFromWords(parsed, []); - if (newPair === null) { - setRemoteError('Mots invalides. Vérifiez la saisie.'); - return; - } - const pairs = getStoredPairs(); - const local = pairs.find((p) => p.is_local); - if (identity === null || local === undefined || identity.privateKey === undefined) { - setRemoteWordsInput([]); - if (onDone !== undefined) { - onDone(); - } else { - navigate('/manage-pairs'); - } - return; - } - const relays = getStoredRelays().filter((r) => r.enabled); - if (relays.length === 0) { - setRemoteError('Aucun relais activé. Configurez les relais pour finaliser le pairing.'); - return; - } - const remotePublicKey = newPair.publicKey; - setIsConfirming(true); - try { - await runDevice1Confirmation({ - pairLocal: local.uuid, - pairRemote: newPair.uuid, - identity, - relays, - remotePublicKey, - }); - } catch (err) { - console.error('Pairing confirmation (device 1):', err); - setRemoteError( - err instanceof Error ? err.message : 'Erreur lors de la confirmation du pairing.', - ); - setIsConfirming(false); - return; - } - setIsConfirming(false); - setRemoteWordsInput([]); - if (onDone !== undefined) { - onDone(); - } else { - navigate('/manage-pairs'); - } - }; - - const setupHeading = isAddingDevice - ? 'Ajouter un appareil' - : 'Configurer le pairing avec un 2ᵉ appareil'; - - return ( -
-

{setupHeading}

- {words.length > 0 && ( - <> - {!hasCopiedToSecondDevice ? ( - <> -

- Mots du 1ᵉʳ appareil — à saisir sur le 2ᵉ (QR) : -

-

- -

-
- {words.map((word, index) => { - const visible = visibleWordIndices.has(index); - return ( -
- {index + 1}. - - {visible ? word : '•••••'} - - -
- ); - })} -
- {qrDataUrl !== null && ( -

- QR code : URL pour ouvrir la page de pairing sur le 2e appareil -

- )} -

- URL pour le 2ᵉ appareil :{' '} - - {url} - -

-

- -

- - ) : ( -
-

- {isAddingDevice ? 'Mots du nouvel appareil' : 'Mots du 2ᵉ appareil'} -

-
void handleSubmitRemote(ev)} - aria-label="Saisir les mots du 2e appareil" - > - - {remoteError !== null && ( - - )} - -
-
- )} - - )} -
- ); -} diff --git a/website-skeleton/contrat.html b/website-skeleton/contrat.html deleted file mode 100644 index 667d048..0000000 --- a/website-skeleton/contrat.html +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - Le contrat – 4NK un nouveau web - - - -

Le contrat

-

← Retour à l'accueil · Qui êtes-vous ? · Réseau P2P · Cryptographie

- -
- En résumé : Ce contrat définit les règles de confiance entre vous et le service. - Il précise qui peut faire quoi et comment prouver son identité. - Tout est vérifiable et transparent. -
- -

Qu'est-ce que ce contrat ?

-

- Ce n'est pas un contrat papier, mais un accord numérique qui établit les règles du jeu. - Il définit : -

-
    -
  • Le service : Website Skeleton (ce site de démonstration).
  • -
  • Les actions possibles : se connecter (login).
  • -
  • Les validateurs : qui a le droit de vérifier les connexions.
  • -
- -

Ce que le service s'engage à faire

-
-
    -
  • Ne jamais stocker vos clés privées — elles restent sur votre appareil.
  • -
  • Vérifier votre identité de façon transparente — via une preuve cryptographique que vous fournissez.
  • -
  • Respecter les règles du contrat — publiquement vérifiables.
  • -
  • Utiliser une clé de service déclarée — jamais exposée, mais vérifiable.
  • -
-
- -

Ce que vous vous engagez à faire

-
-
    -
  • Protéger votre appareil — c'est votre coffre-fort numérique.
  • -
  • Fournir une preuve valide — en signant avec vos clés lors de la connexion.
  • -
  • Être responsable de vos clés — si vous les perdez, personne ne peut les récupérer.
  • -
-
- -

Les parties prenantes

- -

Le service (Website Skeleton)

-

- C'est le site que vous utilisez. Il possède sa propre identité (un « validateur ») qui permet - de vérifier que les connexions sont légitimes. -

- -

Vous (le membre connecté)

-

- Vous êtes l'utilisateur qui souhaite accéder au service. Votre identité est prouvée par - la signature de votre appareil (en savoir plus). -

- -

Comment fonctionne la validation ?

-
    -
  1. Vous demandez à vous connecter.
  2. -
  3. Votre appareil crée une preuve (login-proof) signée avec vos clés.
  4. -
  5. Le service vérifie cette preuve grâce aux règles définies dans ce contrat.
  6. -
  7. Si la preuve est valide, vous êtes connecté. Sinon, l'accès est refusé.
  8. -
-

- Ce système est plus sûr qu'un mot de passe classique car il n'y a rien à voler côté serveur. -

- -

Pourquoi c'est plus sûr ?

-
    -
  • Pas de base de mots de passe à pirater.
  • -
  • Vos clés ne transitent jamais sur le réseau.
  • -
  • Chaque connexion est unique (signature à usage unique).
  • -
  • Vérifiable par tous — les règles du contrat sont publiques.
  • -
- -

Infrastructure, gouvernance et preuves

-

- L'infrastructure (relais, stockage des messages et des signatures) est décentralisée et neutre : - elle ne dépend pas d'un acteur unique. -

-

- Les membres ont une liberté de gouvernance. Certains peuvent choisir une organisation - centralisée lorsque c'est préférable (équipe, processus, conformité). D'autres restent décentralisés. -

-

- L'important est d'avoir les preuves de la bonne exécution du contrat entre les parties : - signatures, nonces, règles publiques. Ces preuves restent vérifiables quelle que soit l'organisation - (décentralisée ou centralisée) des membres. -

- -
- Détails techniques du contrat -

Identifiants

-
    -
  • Contrat UUID : f9b9b336-4282-4c1c-b70b-e5197aeae3fa
  • -
  • Service UUID : 32b9095a-562d-4239-ae45-2d7ffb1a40de
  • -
  • Action login UUID : 0ac7de59-9e81-4bdc-bd19-c07750fad48e
  • -
  • Validateur (membre) : 0e865301-362f-4951-bfbc-531b7bddf820
  • -
-

Rôles par défaut (types de Champ)

-

- Les 11 rôles ci‑dessous existent par défaut. Chacun peut être vide (aucun membre) - ou plein (membres et pairs définis). Tous sont connus des participants ; - les conditions d'usage et de validation s'appliquent dès qu'un rôle est rempli. -

- -
-

1. Partage avec les institutions

-

Usage : Partager des données avec des institutions.

-

Validation : Conformité aux validateurs du champ ; signatures requises selon membres_du_role.

-
-
-

2. Messages au RSSI

-

Usage : Messages au RSSI de la société responsable du service.

-

Validation : Signatures des membres du rôle conformes aux signatures_obligatoires.

-
-
-

3. Messages au Correspondant CNIL

-

Usage : Messages au Correspondant CNIL de la société responsable du service.

-

Validation : Signatures des membres du rôle ; attributs CNIL requis si applicables.

-
-
-

4. Messages au Responsable cybersécurité

-

Usage : Messages au Responsable cybersécurité de la société responsable du service.

-

Validation : Signatures conformes aux validateurs du champ.

-
-
-

5. Messages de support infogérant

-

Usage : Support infogérant du service (peut inclure un membre du miner pour la gestion des clés API).

-

Validation : Signatures des membres du rôle ; membre_miner_uuid optionnel dans datajson si applicable.

-
-
-

6. Messages de support administrateur système

-

Usage : Support administrateur système du service.

-

Validation : Signatures conformes aux validateurs du champ.

-
-
-

7. Messages de support niveau 1

-

Usage : Support niveau 1 du service.

-

Validation : Signatures conformes aux validateurs du champ.

-
-
-

8. Messages de support niveau 2

-

Usage : Support niveau 2 du service.

-

Validation : Signatures conformes aux validateurs du champ.

-
-
-

9. Messages de support niveau 3

-

Usage : Support niveau 3 du service.

-

Validation : Signatures conformes aux validateurs du champ.

-
-
-

10. Validation (contrat)

-

Usage : Valider le contrat ; définir qui peut signer pour le contrat (contrat.validateurs.membres_du_role).

-

Validation : Au moins une signature valide par membre du rôle ; clés publiques autorisées dans signatures_obligatoires.

-
-
-

11. Validation du login

-

Usage : Valider le login ; définir qui peut signer pour l'action login (action.validateurs_action.membres_du_role).

-

Validation : Preuve de login (hash, nonce) signée par les pairs des membres du rôle ; nonce unique ; clés autorisées.

-
- -

Membres par rôles (ce contrat)

-

Ce contrat skeleton remplit uniquement les rôles Validation (contrat) et Validation du login. Les autres rôles sont vides mais connus.

- -

Validation (contrat)

-
    -
  • - Membre : 0e865301-362f-4951-bfbc-531b7bddf820 -
    Signatures obligatoires : 1 -
      -
    • - Pair 1 : f2779304-0d9b-4139-9aee-8d3347819d98 -
      Clé publique : 0244f299538f4a091d93561dcee0c77de3e0d8bb917c9378405653c57f7800f174 -
    • -
    -
  • -
- -

Validation du login

-
    -
  • - Membre : 0e865301-362f-4951-bfbc-531b7bddf820 -
    Signatures obligatoires : 1 -
      -
    • - Pair 1 : f2779304-0d9b-4139-9aee-8d3347819d98 -
      Clé publique : 0244f299538f4a091d93561dcee0c77de3e0d8bb917c9378405653c57f7800f174 -
    • -
    -
  • -
-
- -

← Retour à l'accueil · Qui êtes-vous ? · Réseau P2P · Cryptographie

- - diff --git a/website-skeleton/cryptographie.html b/website-skeleton/cryptographie.html deleted file mode 100644 index 029ec42..0000000 --- a/website-skeleton/cryptographie.html +++ /dev/null @@ -1,495 +0,0 @@ - - - - - - Cryptographie expliquée – 4NK un nouveau web - - - -

Cryptographie expliquée

-

← Retour à l'accueil · Le contrat · Qui êtes-vous ? · Réseau P2P

- -
- En résumé : Vos messages sont protégés par plusieurs couches de cryptographie. - Seul le destinataire peut les lire (chiffrement), et il peut vérifier que c'est bien vous qui les avez envoyés (signature). - Tout cela sans que personne ne connaisse vos clés secrètes. -
- -

Comment ça marche en simple ?

-

- Imaginez que vous voulez envoyer une lettre secrète à un ami. Dans le monde réel, vous pourriez : -

-
    -
  1. Mettre la lettre dans une enveloppe fermée (chiffrement) — seul celui qui a la clé peut l'ouvrir
  2. -
  3. Signer l'enveloppe (signature) — pour prouver que c'est bien vous qui l'avez envoyée
  4. -
  5. Donner la clé au destinataire (échange de clé) — mais sans que personne d'autre ne puisse l'intercepter
  6. -
-

- C'est exactement ce que fait la cryptographie numérique, mais de façon mathématiquement impossible à falsifier. -

- -

Les algorithmes utilisés

-

- Voici les « outils » cryptographiques du système. Chacun a un rôle précis : -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
AlgorithmeRôleAnalogie
SHA-256Empreinte unique du messageComme une empreinte digitale : unique et impossible à inverser
ECDH secp256k1Échange de clé sécuriséComme mélanger deux couleurs en public pour créer un secret commun
HKDF-SHA256Dérivation de cléTransformer le secret partagé en une clé utilisable
AES-256-GCMChiffrement du messageUn coffre-fort numérique ultra-sécurisé
Schnorr secp256k1Signature numériqueVotre signature manuscrite, mais impossible à falsifier
- -

Détail de chaque algorithme

- -
-
SHA-256 — L'empreinte digitale
-

- SHA-256 crée une « empreinte » unique de 64 caractères hexadécimaux (256 bits) pour n'importe quel message. - Deux messages différents auront toujours des empreintes différentes. -

-
    -
  • Entrée : n'importe quel texte ou données
  • -
  • Sortie : 64 caractères hexadécimaux (ex: a7f3c9...)
  • -
  • Propriété clé : impossible de retrouver le message à partir de l'empreinte
  • -
-

Usage : identifier chaque message de façon unique sur le relais.

-
- -
-
ECDH secp256k1 — Le secret partagé
-

- ECDH (Elliptic Curve Diffie-Hellman) permet à deux personnes de créer un secret commun - sans jamais l'échanger directement. C'est comme de la magie mathématique ! -

-
- L'analogie des couleurs : Imaginez qu'Alice et Bob veulent créer une couleur secrète. -
    -
  • Alice mélange une couleur publique avec sa couleur secrète → obtient « couleur A »
  • -
  • Bob mélange la même couleur publique avec sa couleur secrète → obtient « couleur B »
  • -
  • Ils échangent « couleur A » et « couleur B » (publiquement, tout le monde peut voir)
  • -
  • Alice mélange « couleur B » + sa couleur secrète → obtient la couleur finale
  • -
  • Bob mélange « couleur A » + sa couleur secrète → obtient la même couleur finale
  • -
- Résultat : Alice et Bob ont la même couleur secrète, sans l'avoir jamais échangée ! - Un espion qui a vu « couleur A » et « couleur B » ne peut pas retrouver la couleur finale. -
-
    -
  • Courbe : secp256k1 (la même que Bitcoin)
  • -
  • Entrée : votre clé privée + la clé publique du destinataire
  • -
  • Sortie : un secret partagé (32 octets)
  • -
-

Usage : créer un secret pour chiffrer les messages entre deux personnes.

-
- -
-
HKDF-SHA256 — Le raffineur de clé
-

- HKDF (HMAC-based Key Derivation Function) transforme le secret partagé ECDH en une clé - de chiffrement de haute qualité. -

-
    -
  • Entrée : le secret partagé ECDH (32 octets)
  • -
  • Sortie : une clé AES-256 (32 octets) de qualité cryptographique
  • -
  • Pourquoi : s'assurer que la clé est uniformément aléatoire
  • -
-
- -
-
AES-256-GCM — Le coffre-fort
-

- AES-256-GCM est l'algorithme de chiffrement qui protège le contenu du message. - Il offre à la fois la confidentialité (personne ne peut lire) et - l'intégrité (personne ne peut modifier sans qu'on le sache). -

-
    -
  • Taille de clé : 256 bits (très sécurisé)
  • -
  • Mode : GCM (Galois/Counter Mode) avec authentification
  • -
  • IV : vecteur d'initialisation de 12 octets (différent à chaque chiffrement)
  • -
  • Tag : code d'authentification de 16 octets (détecte les modifications)
  • -
-

Usage : chiffrer le message pour que seul le destinataire puisse le lire.

-
- -
-
Schnorr secp256k1 — La signature
-

- La signature Schnorr prouve que vous êtes bien l'auteur du message, sans révéler votre clé privée. - Elle est plus efficace et plus simple que les signatures ECDSA traditionnelles. -

-
    -
  • Entrée : le hash du message + votre clé privée
  • -
  • Sortie : une signature de 64 octets (128 caractères hex)
  • -
  • Vérification : n'importe qui peut vérifier avec votre clé publique
  • -
-
- Important : La signature prouve que vous avez signé, mais elle ne révèle - jamais votre clé privée. Même en voyant 1000 de vos signatures, personne ne peut - retrouver votre clé secrète. -
-
- -

Dérivation de clés et clés multiples par pair

-

- Chaque pair (appareil local ou distant) peut être associé à plusieurs clés publiques : - une clé principale et, optionnellement, une liste de clés supplémentaires. Cela permet d’utiliser - plusieurs clés pour un même pair sans multiplier les secrets à gérer. -

-

Dérivation déterministe à partir d’une clé privée

-

- À partir d’une seule clé privée (celle de l’identité), le système peut calculer d’autres paires - clé privée / clé publique de façon déterministe : pour un index entier (0, 1, 2, …), - le calcul produit toujours la même paire. Aucune donnée aléatoire n’est utilisée pour cette dérivation. -

-
    -
  • Entrée : la clé privée parente (64 caractères hexadécimaux) et un index (entier ≥ 0).
  • -
  • Procédé : une fonction de dérivation (HMAC-SHA256 avec un domaine fixe et l’index) - produit 32 octets, puis une réduction modulo l’ordre de la courbe secp256k1 donne un nombre - valide comme clé privée sur la courbe ; la clé publique enfant est obtenue par multiplication - du point de base de la courbe par ce nombre (format compressé, 66 caractères hex).
  • -
  • Sortie : une paire (clé privée enfant, clé publique enfant). La clé « principale » - est celle dérivée directement de la clé privée de l’identité (sans index enfant).
  • -
-

- Ainsi, une même identité peut exposer plusieurs clés publiques (la principale et les dérivées 0, 1, 2, …) - tout en ne stockant qu’une seule clé privée ; les autres sont recalculées à la demande. -

-

Vérification rapide : une clé publique appartient-elle à mon identité ?

-

- Pour savoir si une clé publique donnée correspond à votre clé privée (identité), le système fait : -

-
    -
  1. Calculer la clé publique à partir de votre clé privée (une opération sur la courbe).
  2. -
  3. Comparer le résultat à la clé publique donnée. Si elles sont égales, la clé appartient à votre identité.
  4. -
  5. Si on autorise les clés dérivées : calculer les clés publiques dérivées pour les indices 0, 1, 2, … - jusqu’à une borne fixée, et comparer chacune à la clé donnée. Dès qu’une égalité est trouvée, la réponse est oui.
  6. -
-

- Le coût est donc une seule opération pour la clé principale, puis au plus N opérations si on teste - N clés dérivées. En limitant N (par exemple 100), la vérification reste rapide. -

-

- Pour un pair distant (autre appareil), les clés sont celles enregistrées pour ce pair (clé principale - et liste optionnelle) : la vérification consiste à tester si la clé donnée est dans cet ensemble, - sans dérivation côté local. -

- -

Le workflow complet

-

- Voici ce qui se passe quand vous envoyez un message sécurisé, étape par étape : -

- -

Phase 1 : Préparation

-
- 1 - Création du message
- Votre message est structuré avec : les données, un horodatage, un identifiant unique (UUID), et les règles de validation. -
-
- 2 - Calcul de l'empreinte (hash)
- Message → SHA-256 → Hash (64 caractères)
- Cette empreinte unique identifie le message sur le réseau. -
-
- 3 - Génération du nonce
- Un nombre aléatoire unique (nonce) est créé pour éviter qu'un même message soit rejoué. -
- -

Phase 2 : Chiffrement

-
- 4 - Échange de clé ECDH
- Votre clé privée + Clé publique du destinataire → ECDH → Secret partagé
- Un secret commun est calculé mathématiquement, sans jamais être transmis. -
-
- 5 - Dérivation de la clé AES
- Secret partagé → HKDF-SHA256 → Clé AES-256
- Le secret est transformé en une clé de chiffrement de qualité. -
-
- 6 - Chiffrement du message
- Clé AES + IV aléatoire + Message → AES-256-GCM → Message chiffré + Tag
- Le message est enfermé dans un « coffre-fort » numérique. -
- -

Phase 3 : Signature

-
- 7 - Signature Schnorr
- Hash + Nonce + Votre clé privée → Schnorr → Signature
- Vous signez l'empreinte du message avec votre clé privée. -
- -

Phase 4 : Publication sur le relais

-
- 8 - Envoi au relais
- Trois éléments sont envoyés séparément : -
    -
  • MsgChiffre : le message chiffré + son hash
  • -
  • MsgCle : l'IV + votre clé publique (pour que le destinataire puisse déchiffrer)
  • -
  • MsgSignature : votre signature + clé publique
  • -
-
- -

Phase 5 : Collecte des signatures (multi-signature)

-
- 9 - Attente des co-signataires
- Si le contrat exige plusieurs signatures (ex: 2 appareils sur 3), le système attend que les autres signent : -
    -
  • Interrogation du relais toutes les 2 secondes
  • -
  • Timeout après 5 minutes si signatures manquantes
  • -
  • Progression affichée (ex: "2/3 signatures")
  • -
-
-
- 10 - Validation
- Le message est validé quand : -
    -
  • Toutes les signatures requises sont présentes (cardinalité)
  • -
  • Les dépendances sont respectées (ex: "A doit signer avant B")
  • -
  • Les clés publiques correspondent aux signataires autorisés
  • -
-
- -

Phase 6 : Réception et déchiffrement

-
- 11 - Scan des clés
- Le destinataire interroge le relais pour récupérer les MsgCle récentes. -
-
- 12 - Récupération du message
- Avec le hash trouvé dans la MsgCle, il récupère le message chiffré. -
-
- 13 - Déchiffrement ECDH inverse
- Sa clé privée + Clé publique de l'émetteur (df) → ECDH → Même secret partagé
- Il recalcule le même secret, puis déchiffre avec AES-GCM. -
-
- 14 - Vérification des signatures
- Chaque signature est vérifiée avec la clé publique du signataire. -
- -

Schéma récapitulatif

-
┌─────────────────────────────────────────────────────────────────┐ -│ ÉMETTEUR │ -├─────────────────────────────────────────────────────────────────┤ -│ Message → SHA-256 → Hash │ -│ │ -│ Clé privée émetteur ─┐ │ -│ ├──→ ECDH → Secret partagé │ -│ Clé publique dest. ──┘ │ -│ │ -│ Secret partagé → HKDF-SHA256 → Clé AES-256 │ -│ │ -│ Clé AES + IV + Message → AES-256-GCM → Message chiffré │ -│ │ -│ Hash + Nonce + Clé privée → Schnorr → Signature │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ RELAIS │ -├─────────────────────────────────────────────────────────────────┤ -│ POST /messages ← MsgChiffre (hash, message_chiffre) │ -│ POST /keys ← MsgCle (hash, iv, df_ecdh = clé pub émett.) │ -│ POST /signatures← MsgSignature (hash, signature, clé publique) │ -│ │ -│ ... attente des co-signatures (GET /signatures/:hash) ... │ -└─────────────────────────────────────────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────────────────┐ -│ DESTINATAIRE │ -├─────────────────────────────────────────────────────────────────┤ -│ GET /keys?start=&end= → Liste des MsgCle récentes │ -│ GET /messages/:hash → MsgChiffre │ -│ GET /signatures/:hash → MsgSignature[] │ -│ │ -│ Clé privée dest. ────┐ │ -│ ├──→ ECDH → Même secret partagé │ -│ Clé publique émett. ─┘ (df_ecdh_scannable) │ -│ │ -│ Secret partagé → HKDF-SHA256 → Clé AES-256 │ -│ │ -│ Clé AES + IV + Ciphertext → AES-256-GCM → Message original │ -│ │ -│ Vérification : Signature + Clé publique → Schnorr verify → OK │ -└─────────────────────────────────────────────────────────────────┘
- -

Pourquoi c'est sécurisé ?

- -

Confidentialité

-
    -
  • Seul le destinataire peut lire : le secret partagé ECDH ne peut être calculé que par l'émetteur et le destinataire
  • -
  • AES-256 : considéré incassable avec les technologies actuelles (2²⁵⁶ combinaisons possibles)
  • -
  • IV unique : même si vous envoyez le même message deux fois, le résultat chiffré sera différent
  • -
- -

Intégrité

-
    -
  • GCM : le mode Galois/Counter détecte toute modification du message chiffré
  • -
  • Hash : l'empreinte SHA-256 garantit que le message n'a pas été altéré
  • -
- -

Authenticité

-
    -
  • Signature Schnorr : prouve mathématiquement que c'est bien vous qui avez signé
  • -
  • Multi-signature : plusieurs appareils peuvent être requis pour valider
  • -
  • Anti-rejeu : le nonce empêche de réutiliser une ancienne signature
  • -
- -

Séparation des données

-
    -
  • Message, clé et signature séparés : même si quelqu'un intercepte un élément, il ne peut rien faire sans les autres
  • -
  • Clé privée jamais transmise : seules les clés publiques et les signatures circulent
  • -
- -
- Détails techniques pour les développeurs - -

Paramètres cryptographiques

- - - - - - - - - -
ParamètreValeur
Courbe elliptiquesecp256k1 (256 bits)
Taille clé AES256 bits
Taille IV (AES-GCM)96 bits (12 octets)
Taille Tag (AES-GCM)128 bits (16 octets)
HashSHA-256 (256 bits)
KDFHKDF-SHA256
SignatureSchnorr secp256k1 (64 octets)
- -

Bibliothèques utilisées

-
    -
  • @noble/secp256k1 : ECDH, Schnorr, manipulation de clés
  • -
  • @noble/hashes : SHA-256, HKDF
  • -
  • Web Crypto API : AES-256-GCM (navigateur)
  • -
- -

Format des clés

-
    -
  • Clé privée : 32 octets (64 caractères hex)
  • -
  • Clé publique (compressée) : 33 octets (66 caractères hex, préfixe 02 ou 03)
  • -
  • Signature Schnorr : 64 octets (128 caractères hex)
  • -
- -

Fichiers source

-
    -
  • userwallet/src/utils/encryption.ts : encryptWithECDH, decryptWithECDH
  • -
  • userwallet/src/utils/crypto.ts : signMessage, verifySignature, generateChallenge, deriveChildKeyPair, getDerivedPublicKeys, publicKeyBelongsToIdentity
  • -
  • userwallet/src/utils/pairing.ts : getPairPublicKeys, pairContainsPublicKey, addPairPublicKey
  • -
  • userwallet/src/utils/relay.ts : postMessageChiffre, postSignature, postKey
  • -
  • userwallet/src/utils/collectSignatures.ts : runCollectLoop, fetchSignaturesForHash
  • -
  • userwallet/src/utils/loginValidation.ts : hasEnoughSignatures, checkDependenciesSatisfied
  • -
  • service-login-verify/ : verifyLoginProof (côté parent)
  • -
-

Dérivation de clés et clés multiples par pair : docs/USERWALLET_KEY_DERIVATION.md.

- -

Collecte des signatures

- - - - - -
ConstanteValeurDescription
COLLECT_POLL_MS2 000 msIntervalle entre chaque interrogation
COLLECT_TIMEOUT_MS300 000 msTimeout global (5 minutes)
COLLECT_FETCH_TIMEOUT_MS15 000 msTimeout par requête
-
- -

En résumé

-
- Le système combine plusieurs couches de protection : -
    -
  1. ECDH pour créer un secret sans jamais l'échanger
  2. -
  3. AES-256-GCM pour chiffrer le message
  4. -
  5. Schnorr pour prouver votre identité
  6. -
  7. SHA-256 pour identifier chaque message de façon unique
  8. -
  9. Multi-signature pour exiger plusieurs validations
  10. -
- Résultat : vos messages sont confidentiels, authentiques et infalsifiables. -
- -

← Retour à l'accueil · Le contrat · Qui êtes-vous ? · Réseau P2P

- - diff --git a/website-skeleton/index.html b/website-skeleton/index.html deleted file mode 100644 index 4c0a536..0000000 --- a/website-skeleton/index.html +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - 4NK un nouveau web - site d'exemple - - - -

4NK un nouveau web - site d'exemple

- -
-

Le contrat · Qui êtes-vous ? · Réseau P2P · Cryptographie

- -
- -
- -
- - - - - - diff --git a/website-skeleton/membre.html b/website-skeleton/membre.html deleted file mode 100644 index c252c8d..0000000 --- a/website-skeleton/membre.html +++ /dev/null @@ -1,182 +0,0 @@ - - - - - - Qui êtes-vous ? – 4NK un nouveau web - - - -

Qui êtes-vous ?

-

← Retour à l'accueil · Voir le contrat · Réseau P2P · Cryptographie

- -
- En résumé : Vous êtes un membre qui peut avoir plusieurs appareils (Pairs). - Chaque appareil possède ses propres clés et peut signer selon vos règles. - Vos données sont stockées selon les membres du contrat — vous gardez le contrôle total. -
- -

Vous êtes le « membre connecté »

-

- Quand vous vous connectez à ce service, vous devenez un membre. - Contrairement aux sites classiques où vos identifiants sont stockés sur un serveur distant, - ici votre identité reste chez vous. -

- -

Un membre = plusieurs appareils

-

- Un membre n'est pas limité à un seul appareil. Vous pouvez avoir plusieurs appareils - (ordinateur, téléphone, tablette) qui forment ensemble votre identité : -

-
    -
  • Chaque appareil s'appelle un « Pair » (device).
  • -
  • Chaque Pair possède sa propre paire de clés cryptographiques.
  • -
  • Tous vos Pairs peuvent signer en votre nom, selon les règles que vous définissez.
  • -
-

- Exemple : Vous pouvez configurer votre ordinateur principal et votre téléphone comme deux Pairs. - Si l'un est perdu, vous gardez l'accès via l'autre. -

- -

Vous définissez les règles

-

- Chaque membre a un contrat qui définit les règles de signature et de validation. - C'est vous qui contrôlez ces règles : -

-
    -
  • Quels Pairs peuvent signer — vous décidez quels appareils sont autorisés.
  • -
  • Combien de signatures sont requises — une seule, ou plusieurs pour plus de sécurité.
  • -
  • Pour quelles actions — certaines actions peuvent nécessiter plus de validations.
  • -
-
- Exemple : Vous pouvez exiger qu'une action sensible (comme un paiement) soit signée - par 2 de vos 3 appareils — c'est le principe du « multi-signature ». -
- -

Où sont stockées vos données ?

-

- Les données du service sont stockées selon les membres définis dans le contrat. - Chaque membre a ses propres données, séparées des autres : -

-
    -
  • Vos clés privées — sur vos appareils (Pairs), jamais sur le serveur.
  • -
  • Vos données utilisateur — associées à votre identité de membre.
  • -
  • Les preuves de signature — vérifiables publiquement, liées à vos Pairs.
  • -
- -

Votre appareil = votre coffre-fort

-

- Chaque appareil (Pair) joue le rôle de coffre-fort numérique : -

-
    -
  • Vos clés de sécurité sont créées directement dans votre navigateur (dans la fenêtre de connexion).
  • -
  • Elles ne quittent jamais votre appareil — elles sont stockées localement (IndexedDB).
  • -
  • Personne d'autre n'y a accès, pas même le service.
  • -
- -

Comment ça fonctionne ?

-
    -
  1. Vous cliquez sur « Se connecter ».
  2. -
  3. Une fenêtre s'ouvre (UserWallet) où vous déverrouillez votre identité.
  4. -
  5. Votre appareil signe une preuve que c'est bien vous (comme une signature manuscrite, mais numérique).
  6. -
  7. Le service vérifie cette preuve et vous donne accès.
  8. -
-

- À aucun moment vos clés secrètes ne sont transmises — seule la preuve de votre identité l'est. -

- -
- Important : Si vous perdez l'accès à votre appareil (panne, vol, perte), - vous perdez vos clés. Pensez à configurer un second appareil ou une sauvegarde. -
- -

Quelle différence avec un mot de passe classique ?

-
    -
  • Mot de passe classique : stocké sur le serveur du site → risque de fuite en cas de piratage.
  • -
  • Ici : vos clés restent sur votre appareil → même si le service est piraté, vos clés sont en sécurité.
  • -
- -
- Détails techniques (pour les curieux) -
    -
  • Vos clés utilisent la cryptographie secp256k1 (la même que Bitcoin).
  • -
  • Elles sont stockées dans IndexedDB de votre navigateur.
  • -
  • La connexion utilise l'authentification multi-facteur (MFA).
  • -
  • Le service possède son propre portefeuille (wallet) séparé du vôtre, jamais exposé.
  • -
- -

Vos Pairs et clés publiques

-
-

Non connecté — connectez-vous pour voir vos Pairs.

-
-
- - - -

← Retour à l'accueil · Voir le contrat · Réseau P2P · Cryptographie

- - diff --git a/website-skeleton/technique.html b/website-skeleton/technique.html deleted file mode 100644 index 02227ae..0000000 --- a/website-skeleton/technique.html +++ /dev/null @@ -1,312 +0,0 @@ - - - - - - Réseau P2P – 4NK un nouveau web - - - -

Réseau P2P

-

← Retour à l'accueil · Le contrat · Qui êtes-vous ? · Cryptographie

- -
- En résumé : Au lieu d'utiliser un seul serveur central (comme Gmail ou Facebook), - le système utilise plusieurs relais qui fonctionnent comme des boîtes aux lettres publiques. - Vos messages chiffrés sont stockés dans ces relais, et vous pouvez choisir lesquels utiliser. -
- -

Qu'est-ce qu'un réseau P2P ?

-

- Imaginez que vous voulez envoyer une lettre. Dans un système classique (comme la poste traditionnelle), - tous les courriers passent par un seul bureau central. Si ce bureau tombe en panne, plus rien ne fonctionne. -

-

- Avec un réseau pair-à-pair (P2P), c'est différent : il y a plusieurs relais (comme plusieurs - boîtes aux lettres) répartis sur Internet. Vous pouvez choisir où déposer vos messages, et si un relais - ne fonctionne plus, vous pouvez utiliser un autre. -

-

- Les avantages de cette approche : -

-
    -
  • Plus de robustesse : si un relais tombe en panne, d'autres continuent de fonctionner
  • -
  • Pas de point unique de défaillance : personne ne contrôle tout le système
  • -
  • Vous choisissez : vous décidez quels relais utiliser, comme choisir votre opérateur téléphonique
  • -
  • Copies multiples : vos messages peuvent être copiés sur plusieurs relais pour plus de sécurité
  • -
- -

Comment fonctionnent les relais ?

-

- Un relais, c'est comme une boîte aux lettres publique sur Internet. Quand vous voulez communiquer avec quelqu'un, - vous déposez trois choses séparément dans cette boîte : -

-
    -
  1. Le message chiffré : votre message codé (comme une lettre dans une enveloppe scellée)
  2. -
  3. La signature : une preuve que c'est bien vous qui avez envoyé le message (comme votre signature manuscrite)
  4. -
  5. La clé de déchiffrement : la clé pour décoder le message (comme la clé de votre boîte aux lettres)
  6. -
-
- Pourquoi séparer ces trois éléments ? C'est comme mettre votre lettre, votre signature et votre clé - dans trois enveloppes différentes. Même si quelqu'un trouve une enveloppe, il ne peut pas tout faire sans les autres. - C'est plus sûr ! -
- -

Comment récupérer vos messages ?

-

- Le système fonctionne comme une boîte aux lettres classique : vous allez vérifier régulièrement s'il y a du courrier. - C'est ce qu'on appelle le modèle "pull-only" (vous tirez les informations, elles ne vous sont pas poussées). -

-
    -
  • Votre appareil vérifie périodiquement les relais pour voir s'il y a de nouveaux messages
  • -
  • Pas de notification instantanée (comme une alerte SMS), mais une vérification régulière
  • -
  • Communication simple via Internet, comme consulter une page web
  • -
-

- Cette méthode fonctionne partout, même dans les environnements les plus restrictifs, car elle utilise - simplement le protocole HTTP (comme quand vous visitez un site web). -

- -

Les relais se parlent entre eux

-

- Les relais peuvent être configurés pour se copier mutuellement les messages : -

-
    -
  • Quand vous déposez un message sur un relais, il peut automatiquement le copier vers d'autres relais
  • -
  • Un système intelligent évite de copier plusieurs fois le même message (grâce à une empreinte unique)
  • -
  • Résultat : vos messages sont disponibles sur plusieurs relais, comme avoir plusieurs copies de sauvegarde
  • -
-

- Exemple : Si vous déposez un message sur le relais A, et que A est configuré pour copier vers B et C, - votre message sera disponible sur les trois relais. Si A tombe en panne, vous pouvez toujours récupérer votre message depuis B ou C. -

- -

Quels relais utiliser ?

-

- Par défaut, un relais principal est configuré. Vous pouvez en ajouter d'autres, jusqu'à 8 relais au maximum. - L'application tente de se connecter à tous les relais activés pour déposer et récupérer les messages. -

-
    -
  • - Relais principal (configuré automatiquement) -
    Adresse : https://relay.certificator.4nkweb.com -
    Ce relais est déjà configuré quand vous utilisez le système pour la première fois. -
  • -
-
- Jusqu'à 8 relais : Dans les paramètres de UserWallet, vous pouvez ajouter vos propres relais - (comme plusieurs boîtes aux lettres). Maximum 8 au total. Vous activez ou désactivez chaque relais, - et choisissez l'ordre de priorité. L'application utilise tous les relais activés pour déposer et récupérer. -
- -

Pourquoi dupliquer les relais et les flux ?

-

- Plus vous utilisez de relais, plus vos messages ont de chances d'être disponibles et de circuler. -

-
    -
  • Déposer sur plusieurs relais : quand vous envoyez un message, l'application le dépose sur chaque relais activé. - Si un relais est indisponible, les autres reçoivent quand même le message. Comme poster la même lettre à plusieurs boîtes.
  • -
  • Récupérer depuis plusieurs relais : pour lire vos messages, l'application interroge chaque relais. - Si un relais ne répond pas ou n'a pas le message, un autre peut l'avoir. Comme vérifier plusieurs boîtes aux lettres.
  • -
  • Résultat : plus de robustesse (un relais en panne ne bloque pas tout), plus de chances que vos messages - soient bien livrés, et les relais peuvent se copier entre eux pour multiplier les copies.
  • -
-

- En résumé : configurer jusqu'à 8 relais et tous les activer, c'est maximiser les chemins pour envoyer et recevoir, - sans dépendre d'un seul point. -

- -

Les actions possibles avec un relais

-

- Un relais offre plusieurs "actions" (appelées endpoints) que vous pouvez utiliser. C'est comme une boîte aux lettres - avec plusieurs fonctions : déposer, récupérer, vérifier l'état, etc. -

-

- Note technique : Ces actions utilisent le protocole HTTP (comme les sites web) avec des méthodes GET (lire) - et POST (écrire). -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
MéthodeEndpointDescription
GET/healthVérifier si le relais fonctionne correctement
GET/messages?start=<ts>&end=<ts>&service=<uuid>Récupérer les messages entre deux dates (optionnel : filtrer par service)
POST/messagesDéposer un message chiffré dans le relais
GET/messages/:hashRécupérer un message précis grâce à son identifiant unique (hash)
GET/signatures/:hashRécupérer les signatures associées à un message
POST/signaturesDéposer une signature dans le relais
GET/keys/:hashRécupérer les clés pour déchiffrer un message
POST/keysDéposer une clé de déchiffrement dans le relais
GET/metricsConsulter les statistiques du relais (nombre de messages, signatures, clés stockées)
GET/bloomObtenir une liste des messages déjà vus (pour éviter de demander plusieurs fois le même message)
- -

Comment les relais stockent les données

-

Organisation du stockage

-

- Les relais organisent les données comme une bibliothèque bien rangée : -

-
    -
  • Messages : rangés par identifiant unique (hash) pour les retrouver rapidement
  • -
  • Signatures : classées par message associé
  • -
  • Clés : classées par message associé
  • -
  • Éviter les doublons : le système se souvient des messages déjà vus pour ne pas les stocker plusieurs fois
  • -
  • Indexation : un système de classement permet de retrouver rapidement les messages d'un service particulier
  • -
- -

Protection contre les abus

-

- Les relais sont protégés contre les utilisations abusives : -

-
    -
  • Limitation des requêtes : chaque adresse IP ne peut faire qu'un nombre limité de requêtes par minute
  • -
  • Contrôle d'accès : seuls les sites autorisés peuvent utiliser le relais
  • -
  • Timeout : si une requête prend trop de temps, elle est annulée automatiquement
  • -
  • Compression : les réponses sont compressées pour être plus rapides
  • -
- -

Éviter les doublons

-

- Chaque message a une empreinte unique (comme une empreinte digitale). Le système utilise cette empreinte - pour s'assurer qu'un même message n'est pas stocké plusieurs fois, même s'il arrive depuis plusieurs relais. - Cela évite aussi que les messages tournent en boucle entre les relais. -

- -
- Détails techniques pour les développeurs -

Configuration des relais pairs

-

- Les administrateurs de relais peuvent configurer leurs relais pour se copier mutuellement les messages. - Cela se fait via une variable de configuration : -

-
PEER_RELAYS=http://relay1:3019,http://relay2:3019
-

- Quand un message arrive sur un relais, il est automatiquement copié vers tous - les relais pairs configurés (si le message n'a pas déjà été vu). -

- -

Bloom filter

-

- Le endpoint /bloom retourne une liste compacte des messages déjà vus par le relais. - Les applications peuvent utiliser cette liste pour éviter de demander plusieurs fois le même message, - ce qui réduit la charge sur le réseau et améliore les performances. -

- -

Statistiques du relais

-

- Le endpoint /metrics expose des statistiques au format Prometheus (pour le monitoring) : -

-
    -
  • Nombre de messages stockés
  • -
  • Nombre de signatures stockées
  • -
  • Nombre de clés stockées
  • -
- -

Gestion des erreurs et timeouts

-

- Les applications utilisent un délai d'attente de 15 secondes pour toutes les requêtes vers les relais. - Si une requête échoue ou prend trop de temps, l'application peut essayer un autre relais configuré, - ou réessayer plus tard avec un délai progressif (backoff exponentiel). -

-
- -

Pourquoi cette architecture ?

-

- Cette façon de faire a été choisie pour plusieurs raisons importantes : -

-
    -
  • Simplicité : utilise le protocole HTTP standard (comme les sites web), donc ça fonctionne partout
  • -
  • Fiabilité : pas besoin de maintenir une connexion permanente, comme consulter une page web
  • -
  • Capacité : chaque relais peut gérer de nombreux utilisateurs en même temps
  • -
  • Contrôle : vous choisissez quels relais utiliser, personne ne vous impose un choix
  • -
  • Sécurité : les messages, signatures et clés sont séparés, ce qui rend le système plus sûr
  • -
-

- En résumé, c'est un système simple, fiable et qui vous donne le contrôle, tout en étant sécurisé. -

- -

← Retour à l'accueil · Le contrat · Qui êtes-vous ? · Cryptographie

- -