feat: Scripts de transfert automatique de fonds

- Service Node.js de détection et transfert automatique
- Scripts bash pour transfert manuel et monitoring
- Documentation complète des scripts
- Intégration dans docker-compose
This commit is contained in:
Nicolas Cantu 2025-09-20 16:17:47 +00:00
parent fa13f34e0d
commit 379bd4420d
6 changed files with 554 additions and 36 deletions

67
.cursorrules copy Normal file
View File

@ -0,0 +1,67 @@
# Règles Cursor Globales pour les projets
# si tu est une IA souviens toi de respecter les règles de ce fichier .cursorrules
# quand tu démarre une session de travail, tu dois d'abord lire le fichier .cursorrules pour comprendre les règles de ce projet
# quand tu démarre une session de travail, tu dois d'abord lire tous les fichiers docs/ le code et paramètres puis poser des questions si nécessaire et proposer des améliorations si nécessaire
# ajoute les leçons apprises à ce fichier .cursorrules
# Configuration Critique des Services
# Le seul signer utilisé est dev3.4nkweb.com
# L'URL de lecoffre-front est https://dev4.4nkweb.com/lecoffre
# L'URL de ihm_client (iframe) est https://dev4.4nkweb.com
# Cette VM est dev4.4nkweb.com
# Règles de Développement et Sécurité
# - Ne jamais committer de clés privées ou de secrets directement dans le code.
# - Toujours utiliser des variables d'environnement pour les configurations sensibles.
# - Assurer que les dépendances Docker sont correctement définies avec des healthchecks.
# - Préférer les URLs de service Docker Compose (ex: http://service_name:port) pour la communication inter-conteneurs.
# - Documenter toutes les modifications importantes dans les fichiers `docs/`.
# - Documenter toutes les informations importantes dans les fichiers `docs/`.
# - Documenter toutes les règles de développement et de sécurité dans .cursorrules.
# - Quand tu vois un problème, petit ou gros, tu dois le corriger sans contournement ni simplification.
# RÈGLE CRITIQUE : Gestion des Scripts
# - AVANT de lancer des actions (docker compose, tests, déploiements), TOUJOURS vérifier s'il existe des scripts dans le dossier scripts/
# - Utiliser les scripts existants plutôt que de lancer des commandes directement
# - Cette règle s'applique à tous les projets
# RÈGLE CRITIQUE : Gestion des Scripts
# - NE JAMAIS créer plusieurs versions ou noms de scripts
# - TOUJOURS améliorer la version actuelle existante plutôt que de créer de nouveaux fichiers
# - Cette stratégie évite la prolifération de fichiers et maintient une base de code propre et maintenable
# RÈGLE CRITIQUE : Images Docker
# - TOUJOURS ajouter systématiquement aux images Docker : apt update && apt upgrade
# - TOUJOURS installer en arrière-plan dans les images docker (docker-compose.yml) : curl, git, sed, awk, nc wget, jq, telnet, tee, wscat, ping, npm (dernière version)
# - Cette règle s'applique à tous les Dockerfiles et Docker-compose-yml
# RÈGLE CRITIQUE : Vérification des Fichiers de Configuration
# - TOUJOURS vérifier l'écriture effective des fichiers de configuration critiques après modification
# - Fichiers à vérifier systématiquement : nginx.conf, bitcoin.conf, package.json, Cargo.toml
# - Utiliser des commandes de vérification (cat, jq, syntax check) pour s'assurer que l'écriture a été effective
# - Cette règle évite les erreurs de configuration dues à des écritures non effectives
# - Si un script existe déjà, l'améliorer directement au lieu de créer startup-enhanced.sh, startup-v2.sh, etc.
# Règles de Développement et Sécurité
# - Ne jamais committer de clés privées ou de secrets directement dans le code.
# - Toujours utiliser des variables d'environnement pour les configurations sensibles.
# - Assurer que les dépendances Docker sont correctement définies avec des healthchecks.
# - Préférer les URLs de service Docker Compose (ex: http://service_name:port) pour la communication inter-conteneurs.
# - Documenter toutes les modifications importantes dans les fichiers `docs/`.
# Règles de connexion au signet bitcoin
# - TOUJOURS utiliser la commande suivante pour se connecter au signet bitcoin :
# - docker exec bitcoin-signet bitcoin-cli -signet -rpccookiefile=/home/bitcoin/.bitcoin/signet/.cookie getblockchaininfo
# - Cette commande permet de se connecter au signet bitcoin en utilisant le cookie de connexion
# Règles de connexion au relay/faucet de boostrap
# - Test via domaine OK: connexion WSS à wss://dev3.4nkweb.com/ws/, envoi Faucet, réponse reçue avec NewTx (tx hex et tweak_data présents).
# - Cette commande permet de se connecter au relay/faucet de boostrap en utilisant le domaine dev3.4nkweb.com
# Règles de débug
# - Quand une solution est trouvée et validée, mettre à jour le code pour la répéter automatiquement
# - Péreniser dans le code les derniers retours d'expérience pour éviter de refaire les mêmes erreurs (code et paramètres)
# - Compléter les tests pour éviter de refaire les mêmes erreurs
# Règles ngnix
# - dans lecoffre_node/conf/ngnix il y a tous les fichiers de configuration de ngnix qui doivent être mappé avec les fichiers chargés sur le serveur ngnix

View File

@ -257,4 +257,3 @@ networks:
ipam: ipam:
config: config:
- subnet: 172.20.0.0/16 - subnet: 172.20.0.0/16

258
docker-compose.yml.backup Normal file
View File

@ -0,0 +1,258 @@
services:
tor:
image: btcpayserver/tor:0.4.8.10
container_name: tor-proxy
networks:
btcnet:
aliases:
- tor
restart: unless-stopped
bitcoin:
image: git.4nkweb.com/4nk/bitcoin:latest
container_name: bitcoin-signet
depends_on:
- tor
volumes:
- bitcoin_data:/home/bitcoin/.bitcoin
- ./bitcoin/bitcoin.conf:/etc/bitcoin/bitcoin.conf:ro
networks:
btcnet:
aliases:
- bitcoin
user: root
entrypoint: >
/bin/sh -c "
chown -R bitcoin:bitcoin /home/bitcoin/.bitcoin || echo 'warn: chown partiel (fichiers bind-mount Windows)';
exec su-exec bitcoin bitcoind -conf=/etc/bitcoin/bitcoin.conf -signet"
healthcheck:
test: ["CMD", "bitcoin-cli", "-conf=/etc/bitcoin/bitcoin.conf", "getblockchaininfo"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
blindbit:
image: git.4nkweb.com/4nk/blindbit-oracle:dev
container_name: blindbit-oracle
depends_on:
bitcoin:
condition: service_healthy
volumes:
- blindbit_data:/root/.blindbit-oracle
- ./blindbit/blindbit.toml:/tmp/blindbit.toml:ro
- bitcoin_data:/home/bitcoin/.bitcoin
entrypoint: >
sh -c "cp /tmp/blindbit.toml /root/.blindbit-oracle/blindbit.toml &&
./main -datadir /root/.blindbit-oracle"
networks:
btcnet:
aliases:
- blindbit
ports:
- "127.0.0.1:8000:8000"
healthcheck:
test: ["CMD-SHELL", "code=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8000/tweaks/1); [ \"$$code\" != \"000\" ]"]
interval: 15s
timeout: 5s
retries: 10
restart: unless-stopped
sdk_relay:
image: git.4nkweb.com/4nk/sdk_relay:ext
container_name: sdk_relay
depends_on:
blindbit:
condition: service_healthy
volumes:
- ./relay/sdk_relay.conf:/home/bitcoin/.conf:ro
- sdk_data:/home/bitcoin/.4nk
- bitcoin_data:/home/bitcoin/.bitcoin
ports:
- "127.0.0.1:8090:8090"
- "127.0.0.1:8091:8091"
networks:
btcnet:
aliases:
- sdk_relay
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
environment:
- NODE_OPTIONS=--max-old-space-size=2048
- HOME=/home/bitcoin
- RUST_LOG=INFO
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8091/"]
interval: 30s
timeout: 10s
retries: 3
restart: unless-stopped
lecoffre-back:
image: git.4nkweb.com/4nk/lecoffre-back-mini:ext
container_name: lecoffre-back
env_file:
- .env
ports:
- "127.0.0.1:8080:8080"
networks:
btcnet:
aliases:
- lecoffre-back
depends_on:
sdk_relay:
condition: service_healthy
user: root
entrypoint: >
/bin/sh -lc '
apk update && apk add --no-cache curl git wget jq busybox-extras npm coreutils && npm install -g wscat || echo "wscat installation failed"
exec node dist/server.js
'
healthcheck:
test: ["CMD", "sh", "-c", "curl -f http://localhost:8080/api/v1/health || exit 1"]
interval: 30s
timeout: 10s
retries: 5
start_period: 60s
labels:
- "com.centurylinklabs.watchtower.enable=true"
restart: unless-stopped
lecoffre-front:
image: git.4nkweb.com/4nk/lecoffre-front:ext
container_name: lecoffre-front
env_file:
- .env
ports:
- "127.0.0.2:3004:3000"
networks:
btcnet:
aliases:
- lecoffre-front
depends_on:
lecoffre-back:
condition: service_healthy
user: root
entrypoint: >
/bin/sh -lc '
apt-get update && apt-get install -y curl git wget jq telnet npm coreutils && npm install -g wscat || echo "wscat installation failed"
exec node server.js
'
healthcheck:
test: ["CMD", "sh", "-c", "ps aux | grep -v grep | grep next-server || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
labels:
- "com.centurylinklabs.watchtower.enable=true"
restart: unless-stopped
ihm_client:
image: git.4nkweb.com/4nk/ihm_client:ext
container_name: ihm_client
env_file:
- .env
environment:
- VITE_BOOTSTRAPURL=wss://dev4.4nkweb.com/ws/
ports:
- "127.0.0.1:3003:3003"
networks:
btcnet:
aliases:
- ihm_client
depends_on:
sdk_relay:
condition: service_healthy
user: root
entrypoint: >
/bin/sh -lc '
apt-get update && apt-get install -y curl git wget jq telnet npm coreutils && npm install -g wscat || echo "wscat installation failed"
exec npm start
'
healthcheck:
test: ["CMD", "sh", "-c", "curl -f http://localhost:3003/ || exit 1"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
labels:
- "com.centurylinklabs.watchtower.enable=true"
restart: unless-stopped
sdk_signer:
image: git.4nkweb.com/4nk/sdk_signer:latest
container_name: sdk_signer
ports:
- "127.0.0.1:3001:3001"
networks:
btcnet:
aliases:
- sdk_signer
user: root
entrypoint: >
/bin/sh -lc '
apk update && apk add --no-cache curl git wget jq busybox-extras npm coreutils && npm install -g wscat || echo "wscat installation failed"
exec node /app/dist/index.js
'
labels:
- "com.centurylinklabs.watchtower.enable=true"
restart: unless-stopped
sdk_storage:
image: git.4nkweb.com/4nk/sdk_storage:ext
container_name: sdk_storage
ports:
- "127.0.0.1:8081:8081"
networks:
btcnet:
aliases:
- sdk_storage
labels:
- "com.centurylinklabs.watchtower.enable=true"
restart: unless-stopped
watchtower:
image: containrrr/watchtower
container_name: watchtower
volumes:
- /var/run/docker.sock:/var/run/docker.sock
command: --interval 30 --label-enable
networks:
- btcnet
restart: unless-stopped
signet_miner:
build:
context: ./miner
container_name: signet_miner
depends_on:
bitcoin:
condition: service_healthy
env_file:
- ./miner/.env
volumes:
- bitcoin_data:/bitcoin:ro
networks:
btcnet:
aliases:
- signet_miner
profiles: ["miner"]
restart: unless-stopped
volumes:
bitcoin_data:
name: 4nk_node_bitcoin_data
blindbit_data:
sdk_data:
networks:
btcnet:
name: 4nk_node_btcnet
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16

View File

@ -0,0 +1,171 @@
#!/usr/bin/env node
/**
* Service de détection et transfert automatique de fonds
* Ce service surveille les applications et transfère automatiquement des fonds
* quand un manque de fonds est détecté
*/
const { spawn, exec } = require('child_process');
const fs = require('fs');
const path = require('path');
class FundsDetectorService {
constructor() {
this.isRunning = false;
this.checkInterval = 30000; // 30 secondes
this.minFundsThreshold = 0.001; // 0.001 BTC = 100,000 sats
this.transferAmount = 0.01; // 0.01 BTC = 1,000,000 sats
this.logFile = '/tmp/funds_detector.log';
}
log(message) {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] ${message}\n`;
console.log(logMessage.trim());
fs.appendFileSync(this.logFile, logMessage);
}
async checkRelayFunds() {
try {
// Vérifier les fonds du relay dans la configuration
const outputsCount = await this.getRelayOutputsCount();
this.log(`Relay outputs count: ${outputsCount}`);
// Vérifier le solde du wallet relay dans Bitcoin Core
const relayBalance = await this.getRelayBalance();
this.log(`Relay balance: ${relayBalance} BTC`);
// Si le relay n'a pas de fonds, déclencher le transfert
if (outputsCount === 0 && parseFloat(relayBalance) < this.minFundsThreshold) {
this.log(`⚠️ Fonds insuffisants détectés. Lancement du transfert automatique...`);
await this.transferFunds();
return true;
}
this.log(`✅ Fonds suffisants (outputs: ${outputsCount}, balance: ${relayBalance} BTC)`);
return false;
} catch (error) {
this.log(`❌ Erreur lors de la vérification des fonds: ${error.message}`);
return false;
}
}
async getRelayOutputsCount() {
return new Promise((resolve, reject) => {
exec('docker exec sdk_relay cat /home/bitcoin/.4nk/default 2>/dev/null | jq -r \'.outputs | length // 0\' 2>/dev/null || echo "0"', (error, stdout, stderr) => {
if (error) {
reject(error);
} else {
resolve(parseInt(stdout.trim()) || 0);
}
});
});
}
async getRelayBalance() {
return new Promise((resolve, reject) => {
exec('docker exec bitcoin-signet bitcoin-cli -signet -rpccookiefile="/home/bitcoin/.bitcoin/signet/.cookie" -rpcwallet="default" getbalance 2>/dev/null || echo "0"', (error, stdout, stderr) => {
if (error) {
reject(error);
} else {
resolve(parseFloat(stdout.trim()) || 0);
}
});
});
}
async transferFunds() {
try {
this.log(`🔄 Transfert de ${this.transferAmount} BTC...`);
// Charger le wallet relay
await this.execCommand('docker exec bitcoin-signet bitcoin-cli -signet -rpccookiefile="/home/bitcoin/.bitcoin/signet/.cookie" loadwallet "default" > /dev/null 2>&1 || true');
// Générer une adresse pour le relay
const relayAddress = await this.execCommand('docker exec bitcoin-signet bitcoin-cli -signet -rpccookiefile="/home/bitcoin/.bitcoin/signet/.cookie" -rpcwallet="default" getnewaddress "relay_funding" 2>/dev/null');
this.log(`Adresse générée: ${relayAddress}`);
// Effectuer le transfert
const txid = await this.execCommand(`docker exec bitcoin-signet bitcoin-cli -signet -rpccookiefile="/home/bitcoin/.bitcoin/signet/.cookie" -rpcwallet="mining_mnemonic" sendtoaddress "${relayAddress}" "${this.transferAmount}" 2>/dev/null`);
this.log(`Transaction ID: ${txid}`);
// Générer des blocs pour confirmer
await this.execCommand(`docker exec bitcoin-signet bitcoin-cli -signet -rpccookiefile="/home/bitcoin/.bitcoin/signet/.cookie" -rpcwallet="mining_mnemonic" generatetoaddress 6 "${relayAddress}" > /dev/null 2>&1`);
// Redémarrer le relay
this.log(`🔄 Redémarrage du relay...`);
await this.execCommand('docker compose restart sdk_relay');
this.log(`✅ Transfert de fonds réussi et relay redémarré`);
return true;
} catch (error) {
this.log(`❌ Erreur lors du transfert: ${error.message}`);
return false;
}
}
async execCommand(command) {
return new Promise((resolve, reject) => {
exec(command, (error, stdout, stderr) => {
if (error) {
reject(error);
} else {
resolve(stdout.trim());
}
});
});
}
async start() {
if (this.isRunning) {
this.log('Service déjà en cours d\'exécution');
return;
}
this.isRunning = true;
this.log('🚀 Démarrage du service de détection des fonds');
this.log(`Seuil minimum: ${this.minFundsThreshold} BTC`);
this.log(`Montant de transfert: ${this.transferAmount} BTC`);
this.log(`Intervalle de vérification: ${this.checkInterval / 1000} secondes`);
const checkLoop = async () => {
if (!this.isRunning) return;
try {
await this.checkRelayFunds();
} catch (error) {
this.log(`❌ Erreur dans la boucle de vérification: ${error.message}`);
}
setTimeout(checkLoop, this.checkInterval);
};
// Démarrer la boucle de vérification
checkLoop();
}
stop() {
this.isRunning = false;
this.log('🛑 Arrêt du service de détection des fonds');
}
}
// Gestion des signaux pour un arrêt propre
process.on('SIGINT', () => {
console.log('\n🛑 Arrêt du service...');
process.exit(0);
});
process.on('SIGTERM', () => {
console.log('\n🛑 Arrêt du service...');
process.exit(0);
});
// Démarrer le service si ce script est exécuté directement
if (require.main === module) {
const service = new FundsDetectorService();
service.start();
}
module.exports = FundsDetectorService;

View File

@ -0,0 +1,23 @@
#!/bin/bash
# Script de démarrage avec vérification automatique des fonds
# Usage: ./startup-with-funds-check.sh
set -e
echo "=== DÉMARRAGE AVEC VÉRIFICATION DES FONDS ==="
# Démarrer les services
echo "Démarrage des services..."
docker compose up -d
# Attendre que les services soient prêts
echo "Attente du démarrage des services..."
sleep 60
# Vérifier et transférer les fonds si nécessaire
echo "Vérification des fonds..."
./scripts/funds/check_and_transfer_funds.sh 0.001
echo "=== DÉMARRAGE TERMINÉ ==="
echo "Services démarrés avec vérification des fonds automatique"