0. Résumé exécutif
Le pipeline d’intégrité de l’eMule Community 0.72a étudié est plus solide et plus stratifié que ne le laisse penser la vieille formule « eMule vérifie les chunks en MD4 ».
Il faut distinguer au moins quatre couches :
1. ED2K / MD4
identité historique du fichier et vérification des parties
2. ICH
tentative de sauver une partie corrompue par retéléchargements progressifs
3. AICH
arbre SHA-1 jusqu’à la granularité EMBLOCKSIZE = 180 KiB,
récupération ciblée et authentifiée par le Master Hash
4. CorruptionBlackBox
provenance locale des plages reçues et sanction éventuelle des sources
La règle d’intégrité centrale de la branche étudiée est très conservatrice :
si MD4 et AICH sont tous les deux disponibles pour une partie, les deux doivent l’accepter.
Le mécanisme AICH ne fait pas confiance à un Recovery Packet isolé : il nécessite un Master Hash fiable, vérifie l’arbre reçu, compare les hashes locaux bloc par bloc puis repasse la partie reconstituée par HashSinglePart().
Le BlackBox historique attribue les plages aux IP et, dans son calcul de culpabilité, compte chaque enregistrement corrompu comme au moins :
EMBLOCKSIZE = 180 KiB
même si la source n’a fourni qu’une petite sous-plage.
Speculative Block Racing
Partial Final-Tail Rescue
réaffectations ciblées
qui peuvent créer une provenance plus fine et potentiellement plusieurs contributeurs autour du même bloc AICH.
Ce n’est pas un bug démontré : les doublons déjà entièrement acquis sont rejetés avant enregistrement et l’Endgame nettoie ses races.
Mais l’interaction doit être auditée.
Le corpus historique renforce fortement cette priorité : NetF WARP avait introduit un Dynamic Corruption Threshold « to avoid mass banning when using Dynamic Block Requests » et un CorruptionBlackBoxFix.
instrumenter l’attribution exacte sous E6bis avant de modifier le seuil de 32 %.
1. Racines protocolaires : Hashset eDonkey
Le document historique edonkey-protocol.txt décrit déjà côté client-à-client :
0x51 Hashset request
0x52 Hashset answer
et définit :
Part hash list ::= WORD HASH*
File hash ::= HASH
HASH ::= 16 bytes MD4 digest
Donc la vérification par hashes de parties ne vient pas d’une extension tardive d’eMule.
Elle appartient au cœur historique eDonkey/eD2K.
2. Taille fondamentale : PARTSIZE
Dans la branche étudiée :
PARTSIZE = 9728000ui64;Soit :
9 728 000 octets
≈ 9,27734375 MiB
Cette valeur participe directement :
- au découpage ED2K ;
- au hashset ;
- au FileHash ;
- aux cartes de parties ;
- à ICH ;
- à l’organisation supérieure AICH.
3. Taille fondamentale : EMBLOCKSIZE
Dans opcodes.h :
EMBLOCKSIZE = 184320;Soit :
180 KiB
Cette valeur sert notamment :
- à la granularité classique des demandes ;
- au niveau inférieur de l’arbre AICH ;
- à la récupération AICH ;
- au calcul conservateur de CorruptionBlackBox.
Contrairement à PARTSIZE, il faut distinguer :
taille logique historique utilisée par les mécanismes
et :
obligation absolue que toute requête fasse exactement 180 KiB
Une demande peut couvrir moins.
AICH reste cependant organisé autour de feuilles de 180 KiB, sauf dernière feuille plus courte.
4. Hash ED2K — comportement exact
4.1 Petit fichier
Si le fichier tient dans une seule partie et ne tombe pas sur le cas frontière historique :
FileHash = MD4(file)
4.2 Plusieurs parties
Chaque partie reçoit un MD4 de 16 octets.
Le hash final est calculé sur la concaténation des hashes de parties.
4.3 Cas historique des multiples exacts de PARTSIZE
KnownFile.cpp et FileIdentifier.cpp confirment le comportement ED2K particulier.
Pour :
taille = 1 × PARTSIZE
le hashset comporte :
hash(partie réelle)
+
MD4(partie vide)
Le hash MD4 du vide est le classique :
31D6CFE0D16AE931B73C59D7E0C089C0
GetTheoreticalMD4PartHashCount() reflète cette convention :
uResult = fileSize / PARTSIZE;
if (uResult > 0)
uResult++;Conclusion
Ce n’est pas un bug moderne à corriger.
C’est un cas de compatibilité ED2K figé par l’histoire du réseau.
5. Le Hashset n’est pas accepté aveuglément
La branche 0.72a possède une classe CFileIdentifier qui vérifie la cohérence du hashset reçu.
Pour MD4 :
FileHash attendu
nombre théorique de hashes
hashes de parties reçus
→ recalcul du FileHash
→ comparaison
Si le hashset ne reconstruit pas le FileHash attendu, il est rejeté.
Donc :
un pair envoie une liste de hashes
≠
eMule l’accepte comme vérité
C’est déjà une barrière de confiance importante.
6. Évolution du protocole Hashset
opcodes.h de la branche étudiée contient encore :
OP_HASHSETREQUEST 0x51 // *DEPRECATED*
OP_HASHSETANSWER 0x52 // *DEPRECATED*mais également :
OP_HASHSETREQUEST2 0xB1
OP_HASHSETANSWER2 0xB2La nouvelle forme utilise :
FileIdentifier
+
Options
+
hashsets demandés
CFileIdentifier::WriteHashSetsToPacket() réserve notamment :
bit 0 → MD4 HashSet
bit 1 → AICH HashSet
Intérêt documentaire
Le site doit expliquer deux époques :
eDonkey / premier eMule :
hashset MD4 dédié
eMule moderne :
FileIdentifier
+ MD4
+ AICH
sans donner l’impression que les vieux opcodes ont simplement disparu.
7. HashSinglePart() : le juge local
CPartFile::HashSinglePart() est une fonction essentielle.
Le commentaire source indique explicitement la politique :
si nous disposons de MD4 et AICH,
les deux doivent être d’accord
Objectif du code :
ne jamais livrer comme correcte
une partie que l’un des deux systèmes considère corrompue
MD4
Si le hashset MD4 est disponible :
hash réel de la partie
vs
hash MD4 attendu
AICH
Si le Master Hash et le part-hashset sont disponibles :
arbre SHA local de la partie
vs
AICH Part Hash attendu
Retour
return !bMD4Error && !bAICHError;8. Que se passe-t-il lorsqu’une partie complète échoue ?
Dans FlushBuffer() :
partie devenue complète
→ HashSinglePart()
Si la fonction échoue :
- log « part corrupt » ;
AddGap()rouvre la plage de la partie ;- la partie est ajoutée à
corrupted_list; - si AICH peut être utile,
RequestAICHRecovery()est lancé ; - les statistiques de corruption sont augmentées.
Point important :
les octets restent physiquement dans le .part
mais la gap list dit désormais :
ces positions ne sont plus considérées acquises
Cela permet justement à ICH/AICH de réhabiliter ensuite les zones prouvées bonnes.
9. ICH — comportement réel dans 0.72a
ICH ne possède pas une carte cryptographique fine indiquant immédiatement le bloc faux.
Le mécanisme est opportuniste.
Après corruption :
partie ouverte en gaps
Au fur et à mesure que des données sont retéléchargées, la branche vérifie de nouveau la partie.
Dans FlushBuffer() :
else if (IsCorruptedPart(part)
&& (ICH enabled || forced))
{
if (HashSinglePart(part))
{
...
}
}Si la partie complète redevient valide avant d’avoir remplacé tous les octets :
les gaps restants sont refermés
les anciennes données correspondantes sont sauvées
Caractérisation
ICH = récupération par revalidation globale
Ce n’est pas :
ICH = localisation précise du mauvais bloc
10. AICH — architecture du hash tree
SHAHashSet.h documente directement l’organisation.
Le niveau supérieur représente le fichier.
Puis :
parties de 9,28 MiB
puis, à l’intérieur des parties :
blocs de 180 KiB
Le hash AICH fait :
HASHSIZE = 20;et l’implémentation CSHA utilisée est SHA-1.
Schéma conceptuel :
MasterHash
├── branche(s)
│ ├── PartHash
│ │ ├── 180K hash
│ │ ├── 180K hash
│ │ └── ...
│ └── PartHash
└── ...
Ce n’est pas un hashset plat.
C’est un arbre permettant de fournir seulement les hashes nécessaires pour authentifier une partie jusqu’au Master Hash.
11. AICH V2 et grands fichiers
SHAHashSet.h précise que la version 2 du mécanisme prend en charge des identifiants de hashes 32 bits pour les grands fichiers.
CreatePartRecoveryData() choisit :
identifiants 16 bits
ou
identifiants 32 bits
selon le type de fichier.
Ce détail montre que l’AICH actuel a lui-même évolué pour suivre l’extension des tailles de fichiers eMule.
12. États de confiance AICH
La branche définit :
AICH_ERROR
AICH_EMPTY
AICH_UNTRUSTED
AICH_TRUSTED
AICH_VERIFIED
AICH_HASHSETCOMPLETEIl faut absolument conserver cette distinction dans la documentation.
UNTRUSTED
Le client a vu une valeur, mais n’a pas encore de raison suffisante de la considérer comme référence.
TRUSTED
Un consensus réseau suffisant a été atteint.
VERIFIED
La valeur vient d’un chemin où le client peut la considérer comme vérifiée.
HASHSETCOMPLETE
Pour un fichier local complet, l’arbre entier a été calculé.
13. Consensus AICH : découverte de code
CAICHRecoveryHashSet::UntrustedHashReceived() collecte plusieurs Master Hashes reçus et les origines qui les annoncent.
La branche utilise actuellement :
MINUNIQUEIPS_TOTRUST = 10
MINPERCENTAGE_TOTRUST = 92et accepte le hash dominant lorsque :
au moins 10 groupes/origines
ET
au moins 92 % des déclarations
Une préférence :
AICHTrustEveryHash
permet de court-circuiter ce mécanisme.
Dans Preferences.cpp, sa valeur par défaut est :
false
14. Anomalie documentaire mineure
Près du code de confiance AICH, un commentaire historique parle encore d’un seuil de :
95 %
alors que la constante exécutée vaut :
92
Comme pour le commentaire 2^25 rencontré dans le dossier HighID :
comportement du code = clair
commentaire = légèrement obsolète
15. RequestAICHRecovery()
Une récupération n’est demandée que si :
Master Hash valide
ET
status == TRUSTED ou VERIFIED
Le code refuse également :
- les tailles trop petites pour lesquelles la récupération n’a pas de sens ;
- une seconde demande déjà pending pour la même partie.
Il vérifie d’abord si les données AICH nécessaires sont déjà en mémoire.
16. Sélection du pair AICH
RequestAICHRecovery() parcourt les sources.
Une source n’est candidate que si :
support AICH
+
possède un AICH hash annoncé
+
aucune requête AICH déjà pending
+
Master Hash == notre Master Hash fiable
Les HighID sont préférés.
Le choix final parmi les candidats utilise historiquement :
rand()Analyse
Ce rand() n’a pas la même gravité que le challenge SecureIdent du Dossier 03.
Ici il ne produit pas un secret ni une preuve cryptographique.
Il choisit simplement un pair parmi plusieurs candidats compatibles.
Ce n’est donc pas une priorité de sécurité.
17. Échec d’une source AICH
CAICHRecoveryHashSet::ClientAICHRequestFailed() :
efface l’état pending
récupère le contexte de la requête
si le PartFile existe toujours
→ RequestAICHRecovery(part) de nouveau
Le système sait donc essayer une autre source.
18. Structure du Recovery Packet
CreatePartRecoveryData() détermine le nombre exact de hashes nécessaires :
hashes du chemin dans l’arbre
+
hashes des blocs EMBLOCKSIZE de la partie
ReadRecoveryData() :
- vérifie la quantité attendue ;
- refuse l’identifiant du Master Hash (
identifier == 1) afin qu’un packet de recovery ne puisse pas remplacer la racine ; - vérifie les limites des identifiants ;
- insère les hashes dans l’arbre ;
- appelle
VerifyHashTree(true); - vérifie ensuite que chaque hash de niveau bloc demandé est réellement présent.
Conclusion
Le Recovery Packet ne peut pas simplement décréter :
« tel bloc est bon »
sans rester cohérent avec le Master Hash déjà accepté.
19. AICHRecoveryDataAvailable()
Une fois l’arbre de la partie disponible :
FlushBuffer()force les données locales sur disque ;- eMule calcule son propre arbre SHA de la partie corrompue ;
- boucle sur la partie par tranches
EMBLOCKSIZE; - compare hash local et hash vérifié ;
- pour chaque bloc valide :
FillGap()RemoveBlockFromList()CorruptionBlackBox.VerifiedData()
- pour chaque bloc faux :
CorruptionBlackBox.CorruptedData()
CorruptionBlackBox.EvaluateData(part).
C’est une excellente séparation :
AICH localise
GapList décide ce qu’il manque
BlackBox conserve la provenance
20. Sanity check après récupération
Après réhabilitation des bons blocs, si la partie semble désormais complète :
HashSinglePart(part)
est relancé.
Si MD4/AICH n’acceptent toujours pas la partie :
AddGap(part entière)
et, dans certains cas, l’état AICH peut passer en erreur.
Donc :
la récupération ne contourne jamais le juge final.
21. Rehash final du fichier
PartFileHashFinished() effectue encore une vérification des parties complètes.
Il calcule pour chaque partie :
MD4
AICH si disponible
Si l’un échoue :
la partie est rouverte en gap
Si MD4 et AICH sont tous deux présents mais ne donnent pas le même verdict :
DebugLogError
Le fichier ne passe en finalisation normale que s’il n’y a pas d’erreur.
22. known2_64.met
Dans la branche Community inspectée :
KNOWN2_MET_FILENAME = "known2_64.met"
OLD_KNOWN2_MET_FILENAME = "known2.met"Le fichier stocke les AICH Recovery Hashsets calculés localement.
SaveHashSet() enregistre notamment :
Master Hash
nombre de hashes de niveau inférieur
hashes des blocs
LoadHashSet() :
- retrouve le Master Hash ;
- vérifie le nombre attendu ;
- recharge les feuilles ;
- recalcule l’arbre ;
- compare le Master Hash reconstruit à celui attendu.
23. CorruptionBlackBox : structure
Constante :
CBB_BANTHRESHOLD = 32TransferredData(start,end,client) conserve :
partie
plage relative
IP source
état BBR_NONE
Les enregistrements adjacents compatibles peuvent être fusionnés.
Lorsque des octets sont réécrits par une autre source, le code ajuste/sépare les anciens enregistrements afin que la provenance reflète la dernière donnée conservée sur la plage.
24. Provenance enregistrée avant écriture
Dans CPartFile::ProcessBlockPacket() :
IsComplete(start,end) ?
→ si déjà écrit, retour avant BlackBox
Puis :
m_CorruptionBlackBox.TransferredData(start, end, client);avant ajout au buffer.
Implication pour Speculative Racing
Une copie tardive qui arrive après que toute sa plage a déjà été acquise est normalement rejetée avant de remplacer la provenance.
C’est un garde-fou important.
Il réduit fortement le risque que le « perdant » d’une course soit arbitrairement considéré comme l’auteur des données gagnantes.
25. VerifiedData() / CorruptedData()
Après vérification :
BBR_NONE
→ BBR_VERIFIED
ou :
BBR_NONE / BBR_VERIFIED
→ BBR_CORRUPTED
Le code sait scinder les enregistrements lorsqu’une sous-plage seulement change de statut.
L’architecture peut donc déjà conserver une provenance plus fine qu’une partie entière.
26. EvaluateData() : calcul de culpabilité
Lorsqu’un bloc AICH est déclaré corrompu :
- liste les IP impliquées dans les records corrompus ;
- ignore celles déjà bannies ;
- parcourt les records du fichier ;
- cumule par IP :
- données
VERIFIED - données
CORRUPTED
- données
- calcule :
corrupt %
=
corrupt
/
(corrupt + verified)
Si :
corrupt % > 32
l’adresse est bannie.
Sinon elle est signalée/suivie.
27. Détail crucial : plancher de 180 KiB
Dans EvaluateData() :
aDataCorrupt[k] +=
max(record_length, EMBLOCKSIZE);Donc un record corrompu de :
20 KiB
peut compter comme :
180 KiB
pour l’évaluation de cette source.
Pourquoi ?
AICH sait essentiellement dire :
ce bloc de contrôle de 180 KiB est faux
Il ne sait pas nécessairement prouver quel octet précis à l’intérieur a cassé le hash.
Le code applique donc une pénalité conservatrice à l’échelle du bloc.
28. Problème potentiel avec contributions mixtes
Exemple conceptuel :
Bloc AICH de 180 KiB
Source A : 150 KiB
Source B : 30 KiB
Le bloc final échoue.
La seule preuve cryptographique disponible est :
les 180 KiB assemblés ne correspondent pas au hash attendu
Elle ne permet pas de dire automatiquement :
A est fautive
ou :
B est fautive
Si les deux possèdent des records recouvrant ce bloc, une politique conservatrice peut charger lourdement les deux.
E6bis
Partial Final-Tail Rescue et les réallocations rendent les contributions mixtes sur les zones finales plus plausibles qu’avec le scheduler historique.
D’où la nécessité d’un audit.
29. Speculative Racing : pourquoi le risque est limité mais réel à étudier
Le comportement actuel possède plusieurs protections :
plage déjà complète → paquet ignoré
Racing Cleanup
Partial Final-Tail Rescue ne demande que la gap réelle
Donc :
deux requêtes envoyées
≠
deux écritures conservées
Cependant les situations suivantes méritent mesure :
- arrivée alternée de sous-plages ;
- challenger qui remplit seulement une queue de bloc ;
- source primaire ayant déjà fourni un préfixe ;
- corruption d’un bloc AICH constitué de plusieurs provenances.
Statut
RISQUE ARCHITECTURAL À MESURER
PAS BUG E6bis DÉMONTRÉ
30. Confirmation historique : NetF WARP
Le changelog NetF WARP 0.2a contient exactement :
CorruptionBlackBoxFix
- ensure all data been processed before banning
Dynamic Corruption Threshold
- to avoid mass banning when using Dynamic Block Requests
Only Recover Banned Data
- only redownload data that is "known" to be corrupt
La présence conjointe :
DBR
+
CorruptionBlackBoxFix
+
Dynamic Corruption Threshold
est extrêmement instructive.
Une modification de la granularité des requêtes avait déjà obligé un développeur de Mod à repenser l’évaluation de corruption.
31. Mais NetF fournit aussi un avertissement
Le même document annonce dans ses « Known bugs » :
'Only Recover Banned Data'
might cause looping downloads
or complete stalls
if corruption occurs and recovery fails
Et une version ultérieure mentionne avoir corrigé :
some corrupt parts downloading forever
Conclusion
Ne pas porter Only Recover Banned Data tel quel.
Le statut banned est une conclusion heuristique.
Il ne doit pas devenir la source cryptographique de vérité permettant de décider quelles données sont bonnes.
32. Pourquoi la logique « banni = mauvais octets » est circulaire
Chaîne dangereuse :
CBB soupçonne A
→ A est banni
→ on décide que seules les données de A sont mauvaises
→ on conserve les autres
→ si l’hypothèse CBB était fausse ou incomplète :
la partie ne peut jamais converger correctement
C’est précisément le genre de boucle capable d’expliquer :
redownload loop
complete stall
34. Shadow audit CBB × E6bis
Pour chaque bloc AICH finalement corrompu :
part
aich_block_index
contributors
accepted_bytes_by_contributor
raced?
partial-tail-rescue?
primary/challenger
verified_history_by_contributor
historical_CBB_charge
historical_CBB_ratio
historical_ban_decision
Puis produire un événement :
[CBB_SHADOW]
Part 42
AICHBlock 17
BlockSize 184320
Source A
bytes accepted 151552
role ENDGAME_PRIMARY
CBB charge 184320
Source B
bytes accepted 32768
role ENDGAME_CHALLENGER
CBB charge 184320
Result
block CORRUPT
CBB would ban A no
CBB would ban B yes
mixed provenance yes
C’est exactement le type de cas que nous devons observer avant tout changement.
35. Première amélioration possible : retarder une sanction ambiguë
Si les mesures montrent réellement des faux positifs autour des blocs à provenance multiple, la première évolution ne devrait pas être :
augmenter 32 % à 50 %
pour tout le monde.
Approche plus fine :
bloc corrompu
+
plusieurs contributeurs
+
aucune preuve permettant de distinguer le fautif
→ evidence = AMBIGUOUS
Puis :
ne pas déclencher seul un ban fort
mais conserver l’anomalie
et attendre une répétition indépendante
Cette stratégie colle mieux au Dossier 05 :
preuves graduées
réactions graduées
36. Deuxième amélioration possible : identité sécurisée
Le BlackBox historique indexe la provenance sur :
IP
EvaluateData() finit par :
FindClientByIP()
ou
AddBannedClient(IP)
Limite 2026
Une IP peut être :
dynamique
partagée par NAT
partagée par CGNAT
sortie VPN
Prudence
Ne pas remplacer le ban IP par un ban UserHash non authentifié.
Un UserHash seul est falsifiable.
37. Troisième amélioration possible : séparer attribution et sanction
Architecture historique :
CorruptionBlackBox
→ mesure
→ décide ban
RangeProvenance
↓
IntegrityEvidence
↓
Trust Policy
↓
action graduée
Avantage :
la couche d’intégrité dit :
ce que nous savons
et la Policy dit :
ce que nous faisons
sans mélanger preuve et sanction.
38. Pas de score global « leecher »
Connexion directe avec Dossier 05.
Une source qui a envoyé :
1 bloc corrompu
+
10 GiB valides
n’est pas équivalente à une source qui envoie :
90 % de données corrompues
Et l’intégrité ne doit pas être fusionnée avec :
ModString
QR distant
nickname
Dimension dédiée :
DataIntegrity
avec preuve issue des hashes.
39. AICH root trust : amélioration de diagnostic
AICH Master Hash
state=UNTRUSTED
source_count=...
AICH Master Hash
state=TRUSTED
support=...
consensus=...
AICH Master Hash
state=VERIFIED
origin=...
sans exposer cette complexité dans l’UI normale.
Intérêt
Lorsqu’une récupération AICH ne démarre pas, l’utilisateur saurait si la raison est :
pas de Master Hash
Master Hash non encore trusted
aucun pair compatible avec le même hash
recovery request en cours
recovery data invalide
Aujourd’hui ces états sont surtout visibles dans les logs/debug.
40. AICHTrustEveryHash
Préférence actuelle :
AICHTrustEveryHash=false
Recommandation
Conserver le défaut.
libellé explicite
avertissement
pas de recommandation d’activation
Car cette option désactive précisément le consensus destiné à empêcher qu’un seul pair impose une racine AICH.
41. known2_64.met et résilience locale
La récupération AICH dépend également de métadonnées locales.
Avant d’ajouter un nouveau système :
pour vérifier s’il protège déjà correctement :
.part.met
.part.met.bak
known.met
known2_64.met
preferences.dat
cryptkey.dat
preferencesKad.dat
42. Métadonnées PartFile : idée historique SF/IOM/X-Mod
Le corpus des anciens Mods contient des approches de sauvegarde/reconstruction des métadonnées .part.met.
Conceptuellement elles peuvent être utiles après :
part.met endommagé
crash
migration
.part.met.bak
+
ses propres fonctions de backup
Statut
AUDIT AVANT PORTAGE
Ne pas dupliquer un mécanisme existant.
43. MD4 et SHA-1 en 2026
Du point de vue cryptographique moderne :
MD4 = cassé
SHA-1 = collision-resistant insuffisant pour de nouveaux designs
remplacer MD4 et SHA-1 dans le protocole
ED2K
Le MD4 fait partie de l’identité historique du fichier.
Le changer donne un autre identifiant et casse les liens existants.
AICH
Le SHA-1 et le format du tree font partie de l’interopérabilité AICH.
Un client seul ne peut pas faire comprendre un nouvel arbre aux anciens pairs.
46. Réinitialisation de l’état Endgame après corruption
Lorsqu’une partie complète devient corrompue :
AddGap()
réintroduit brutalement de nouveaux octets manquants.
Cela change :
remaining bytes
remaining blocks
remaining parts
part dispersion
source usefulness
ETA
E6bis calcule justement son risque à partir de ces dimensions.
47. Delayed NNP retrouve ici tout son sens
Dossier 06 :
Delayed NNP à auditer
Dossier 07 explique pourquoi la fonction avait été créée.
Après corruption :
partie auparavant complète
→ devient gap
→ une source qui semblait NNP
peut soudain redevenir utile
Donc la fenêtre de grâce historique de NetF n’est pas seulement une optimisation générale.
Elle est directement liée au cycle d’intégrité.
48. Corruption telemetry minimale à conserver
parts_corrupt
ich_parts_saved
ich_bytes_saved
aich_recovery_requests
aich_recovery_success
aich_recovery_failover
aich_bytes_recovered
cbb_suspects
cbb_bans
cbb_mixed_provenance_blocks
endgame_corrupt_parts
raced_blocks_later_corrupt
partial_tail_blocks_later_corrupt
Sans stocker durablement les IP ou UserHash complets.
49. Indicateur public possible
INTÉGRITÉ
Parties vérifiées 1 248
Corruptions détectées 3
ICH récupérées 1
AICH récupérées 2
Données sauvées AICH 17,6 Mio
Éviter :
3 leechers détectés
car :
corruption détectée
≠
intention malveillante prouvée
50. Integrity Health diagnostic
Vue avancée possible :
FICHIER
ED2K Hash OK
MD4 Hashset complet
AICH Master TRUSTED
AICH Part Hashset complet
Recovery Data disponible
PARTIE 42
MD4 CORRUPT
AICH CORRUPT
Blocs AICH 53
Vérifiés 52
À retélécharger 1
Recovery source nouvelle tentative
Cette vue serait extrêmement utile pour comprendre les cas « bloqué à 99,9 % ».
59. Ordre de travail recommandé
A — Audit CBB × E6bis
Priorité la plus forte.
Aucune modification comportementale initiale.
B — Audit Endgame state après corruption
Très lié à E6bis et facile à instrumenter.
C — Audit Delayed NNP autour d’une réouverture de gap
L’étude intégrité donne enfin le contexte exact.
D — Integrity diagnostics
Statistiques et raisons d’échec AICH.
E — Backup metadata
F — seulement si preuves
Modifier politique de sanction CBB.
60. Ce que nous ne devons pas faire
remplacer MD4 dans ED2K
remplacer SHA-1 AICH unilatéralement
accepter un AICH root unique non fiable
faire confiance à un recovery packet sans arbre valide
considérer toute corruption comme du leeching
porter Only Recover Banned Data tel quel
augmenter arbitrairement le seuil CBB
désactiver le final rehash pour accélérer
63. Relation avec le Dossier 05 Trust
Le Dossier 05 proposait :
REMOTE_CLAIM
LOCAL_MEASURED
HASH_VERIFIED
CRYPTO_VERIFIED
Le présent dossier donne un cas parfait :
pair annonce sa PartStatus
→ REMOTE_CLAIM
octets reçus
→ LOCAL_MEASURED
bloc AICH comparé au tree
→ HASH_VERIFIED / HASH_FAILED
partie MD4 + AICH valide
→ HASH_VERIFIED fort
Le futur Trust Engine ne doit jamais remplacer cette hiérarchie.
Il doit la consommer.
64. Relation avec le Dossier 06 Endgame
Baseline :
Endgame by Akshaan E6bis
Le présent dossier n’a pas trouvé de raison de modifier son comportement immédiatement.
Il révèle cependant deux audits obligatoires avant une évolution E8 :
CBB × provenance fine/racing
corruption → recalcul état Endgame / NNP
Cela devient une dépendance explicite de la roadmap.
65. Conclusion
Le système d’intégrité eMule est l’un des sous-systèmes les mieux construits de l’ancien client :
Hashset
→ validation de partie
ICH
→ récupération opportuniste
AICH
→ récupération fine authentifiée par arbre
CorruptionBlackBox
→ provenance et anti-abus
Elle se trouve à la jonction entre deux époques :
CorruptionBlackBox
pensé pour des blocs historiques de 180 KiB
vs
Endgame E6bis
capable de racing,
de rescue partiel
et de provenance plus fine
NetF WARP nous donne un signal historique très fort : son propre passage aux Dynamic Block Requests l’avait conduit à ajouter un CorruptionBlackBoxFix et un seuil de corruption dynamique.
Mais son expérience montre aussi ce qu’il ne faut pas faire : Only Recover Banned Data pouvait conduire à des boucles et des blocages.
mesurer précisément qui a réellement fourni quels octets, conserver les hashes comme arbitres, puis rendre les sanctions plus explicables si les mesures démontrent que le modèle IP/180K historique est devenu trop grossier.