Aller au contenu
Blockchain Association of Cameroon
BAC-L0-FOUND-1063 h

Arbres de Merkle et preuves d'inclusion

Comment prouver qu'une donnée appartient à un ensemble sans transmettre l'ensemble. La structure qui rend possibles clients légers et échantillonnage de disponibilité.

  • Construire un arbre de Merkle à partir d'un ensemble de transactions
  • Produire et vérifier une preuve d'inclusion
  • Calculer la taille d'une preuve en fonction du nombre de feuilles

Un bloc peut contenir des milliers de transactions. Comment prouver à un téléphone bon marché, sur une connexion limitée, que votre paiement y figure, sans lui envoyer tout le bloc ? La réponse est l'arbre de Merkle : une seule empreinte de 32 octets résume tout l'ensemble, et une courte liste d'empreintes suffit à prouver qu'une donnée en fait partie. Dans cet atelier, vous construisez un arbre à la main, vous vérifiez une preuve, vous calculez la taille d'une preuve pour un million de feuilles, et vous exécutez une attaque réelle contre un arbre mal conçu.

Leçons 1/4 · 20 min

Construire un arbre de Merkle

La question de départ

Un commerçant reçoit un lot de quatre paiements. Il veut une seule valeur courte qui change si n'importe lequel des quatre est modifié, et qui permette ensuite de prouver qu'un paiement précis en fait partie. Hacher la concaténation de tout le lot donne la première propriété, mais pas la seconde : pour prouver un paiement, il faudrait rendre les trois autres. L'arbre de Merkle donne les deux.

Le concept (convention jouet de cet atelier)

Règles, que tous les outils du cours appliquent :

  1. Feuille = SHA-256 du texte de la donnée (par exemple une transaction).
  2. Nœud parent = SHA-256 de la concaténation des deux empreintes enfants, en octets bruts, gauche puis droite.
  3. Si un niveau compte un nombre impair de nœuds, le dernier est dupliqué.
  4. On répète jusqu'à un seul nœud : la racine de Merkle.

Exemple détaillé : quatre transactions

Textes : tx0:Amina->Bello:20, tx1:Bello->Chantal:5, tx2:Chantal->Didier:12, tx3:Didier->Amina:7. Empreintes calculées avec SHA-256, tronquées à 8 caractères hexadécimaux pour la lecture :

NœudCalculDébut de l'empreinte
L0SHA-256(tx0)801c17a0…
L1SHA-256(tx1)251f1575…
L2SHA-256(tx2)b25a5829…
L3SHA-256(tx3)f62a1b13…
N1SHA-256(L0 ‖ L1)82bcaf46…
N2SHA-256(L2 ‖ L3)024b1837…
RacineSHA-256(N1 ‖ N2)803bff37…

Le symbole ‖ désigne la concaténation. Vous pouvez recalculer chaque ligne avec les outils : sha256 pour les feuilles, hexhash pour les nœuds (en entrant les deux empreintes complètes l'une après l'autre).

Ce que la racine garantit

  • Si une seule feuille change d'un bit, sa feuille change, puis chaque nœud sur le chemin jusqu'à la racine : la racine change (effet d'avalanche, atelier 104).
  • La racine dépend aussi de l'ordre des feuilles : échanger tx0 et tx1 donne une autre racine, car L0 ‖ L1 ≠ L1 ‖ L0.
  • Cela suppose que SHA-256 est résistant aux collisions : on ne sait pas, aujourd'hui, produire deux entrées différentes de même empreinte.

Un cas à nombre impair de feuilles

Avec trois feuilles a, b, c : on calcule N1 = H(La ‖ Lb), puis N2 = H(Lc ‖ Lc) (c est dupliquée), puis la racine H(N1 ‖ N2). Pour cinq feuilles, la duplication intervient à deux niveaux. L'outil merkle-explorer le montre : saisissez 5 feuilles et regardez les nœuds copiés.

Ce que cela ne garantit pas

La racine ne dit rien de la validité des données (une transaction peut être frauduleuse et figurer joliment dans l'arbre), ni de leur origine : c'est une empreinte, pas une signature. Et la règle de duplication a un revers, vu en leçon 3.

En bref

  • Racine = résumé de 32 octets de tout l'ensemble, sensible à chaque bit et à l'ordre.
  • Feuille = H(donnée) ; nœud = H(gauche ‖ droite) ; niveau impair : dupliquer le dernier.
  • La racine prouve la cohérence d'un ensemble, pas sa validité.

Outil interactif

Empreinte SHA-256

Tapez un texte : l'empreinte se met à jour à chaque caractère. Comparez avec un second texte pour mesurer l'effet d'avalanche.

Empreinte (64 caractères hexadécimaux)

…

Empreinte du second texte

…

Les caractères différents sont soulignés et en gras. Le texte est encodé en UTF-8 sans ajout d'espace ni de retour à la ligne.

Outil interactif

SHA-256 d'octets hexadécimaux

Collez des octets en hexadécimal : le hachage porte sur les octets, pas sur le texte des chiffres. Un second champ est concaténé après le premier.

Empreinte

…

Espaces, retours à la ligne et préfixes 0x sont ignorés.

Outil interactif

Explorateur d'arbre de Merkle (jouet)

Une feuille par ligne (2 à 8). Choisissez une feuille pour voir sa preuve d'inclusion ; modifiez un texte pour voir quels nœuds changent.

5 feuilles

Les nœuds modifiés depuis la dernière saisie valide sont soulignés et marqués « modifié ». Les nœuds sur le chemin de la feuille choisie sont entourés. Dernier nœud d'un niveau impair : il est dupliqué comme son propre frère.

Convention : feuille = SHA-256(texte UTF-8) ; parent = SHA-256(gauche ‖ droite, octets bruts) ; nœud impair dupliqué. Arbre jouet : Bitcoin double le hachage et sérialise autrement.

Leçons 2/4 · 20 min

Une preuve d'inclusion : le chemin vers la racine

La question de départ

Didier affirme : « mon paiement tx2 est dans ce lot dont la racine est 803bff37… ». Vous ne voulez pas télécharger les quatre transactions. De quoi avez-vous strictement besoin pour vous en convaincre ?

Le concept

Une preuve d'inclusion pour une feuille liste, de la feuille vers la racine, le frère de chaque nœud du chemin : l'empreinte avec laquelle il est combiné à ce niveau. Le vérificateur reçoit la donnée et cette liste, et il recalcule la racine.

Règle de côté, utilisée par l'outil et par les épreuves : à chaque niveau, si l'indice courant est pair, le frère est à droite ; s'il est impair, il est à gauche. Puis l'indice est divisé par deux (arrondi vers le bas).

Exemple détaillé : prouver tx2 (indice 2)

Arbre de la leçon 1. La feuille tx2 a l'indice 2, donc pair.

  1. Niveau 0 : indice 2 pair → le frère est à droite : L3 (f62a1b13…). On calcule N2 = SHA-256(L2 ‖ L3). Nouvel indice : 1.
  2. Niveau 1 : indice 1 impair → le frère est à gauche : N1 (82bcaf46…). On calcule SHA-256(N1 ‖ N2). C'est la racine candidate.

La preuve ne contient donc que deux empreintes : L3 et N1, soit 64 octets, et non les trois autres transactions. Si le résultat vaut la racine annoncée (803bff37…), la preuve est acceptée.

Pourquoi la preuve « lie » la feuille

Pour fabriquer une fausse preuve pour une autre donnée X', il faudrait trouver des frères tels que le recalcul donne la même racine. Si la structure de l'arbre est fixée sans ambiguïté, cela revient à trouver une collision ou une préimage de SHA-256, ce que l'on ne sait pas faire. La preuve est alors liante pour cette racine, à cette position.

À vous

Dans merkle-explorer, entrez quatre feuilles, cliquez sur la troisième : l'outil affiche sa preuve et la vérifie. Modifiez ensuite un caractère de la feuille dans la preuve (sans toucher à la racine annoncée) : la vérification échoue. Puis modifiez un des frères : elle échoue aussi.

Ce que la preuve ne garantit pas

  • La racine elle-même. Si un attaquant vous fournit une fausse racine avec de fausses preuves cohérentes, tout paraît correct. La racine doit venir d'une source de confiance. Dans Bitcoin, elle est dans l'en-tête de bloc de 80 octets, et un client léger suit la chaîne d'en-têtes dont le travail cumulé est le plus grand (voir la section 8 du papier de Bitcoin).
  • La validité de la transaction. La preuve dit « incluse », pas « conforme aux règles » : un client léger se fie au fait qu'un bloc accepté par le réseau a été validé.
  • L'absence. L'absence de preuve n'est pas une preuve d'absence (voir leçon 4 pour les arbres triés).

En bref

  • Une preuve = les frères le long du chemin ; le vérificateur recalcule la racine.
  • Indice pair → frère à droite ; impair → à gauche ; indice ÷ 2 à chaque niveau.
  • La preuve lie la donnée à une racine, mais la racine doit déjà être digne de confiance.

Outil interactif

Explorateur d'arbre de Merkle (jouet)

Une feuille par ligne (2 à 8). Choisissez une feuille pour voir sa preuve d'inclusion ; modifiez un texte pour voir quels nœuds changent.

5 feuilles

Les nœuds modifiés depuis la dernière saisie valide sont soulignés et marqués « modifié ». Les nœuds sur le chemin de la feuille choisie sont entourés. Dernier nœud d'un niveau impair : il est dupliqué comme son propre frère.

Convention : feuille = SHA-256(texte UTF-8) ; parent = SHA-256(gauche ‖ droite, octets bruts) ; nœud impair dupliqué. Arbre jouet : Bitcoin double le hachage et sérialise autrement.

Outil interactif

SHA-256 d'octets hexadécimaux

Collez des octets en hexadécimal : le hachage porte sur les octets, pas sur le texte des chiffres. Un second champ est concaténé après le premier.

Empreinte

…

Espaces, retours à la ligne et préfixes 0x sont ignorés.

Leçons 3/4 · 20 min

Taille d'une preuve : le logarithme

La question de départ

Un bloc contient 1 000 000 de transactions (hypothèse de cours). Combien d'octets faut-il pour prouver l'inclusion d'une transaction ? Un million ? Mille ? Moins ?

Le concept

À chaque niveau de l'arbre, le nombre de nœuds est à peu près divisé par deux. Le chemin d'une feuille à la racine traverse donc autant de niveaux qu'il faut de divisions par deux pour passer de n à 1. Pour n feuilles (n ≥ 2), la preuve contient

⌈log₂ n⌉ empreintes

où ⌈ ⌉ est l'arrondi vers le haut. Chaque empreinte SHA-256 fait 32 octets.

Exemples détaillés

Feuilles n⌈log₂ n⌉ (empreintes dans la preuve)Taille de la preuveToutes les empreintes de feuilles
4264 octets128 octets
8396 octets256 octets
1 00010 (car 2^10 = 1 024 ≥ 1 000)320 octets32 000 octets
1 000 00020 (car 2^20 = 1 048 576)640 octets32 000 000 octets (32 Mo)

Doubler le nombre de feuilles n'ajoute qu'une empreinte à la preuve. Le million de feuilles se prouve avec 640 octets ; pour comparer, tout le million d'empreintes pèse 32 Mo. Cette propriété est ce qui rend les clients légers possibles sur un téléphone à forfait limité.

Vérifiez avec 5 feuilles : les niveaux ont 5, 3, 2, puis 1 nœuds : trois étapes, et ⌈log₂ 5⌉ = 3. ✔ La duplication ne change pas la formule : elle fournit un frère (une copie) là où il en manque un.

À vous

Dans l'outil, saisissez 8 feuilles : vous verrez des preuves de 3 empreintes. Avec 5 feuilles (niveaux de 5, 3, 2, puis 1 nœuds), les preuves en ont 3 aussi ; pour la dernière feuille, l'un des frères est une copie d'elle-même. L'outil s'arrête à 8 feuilles : pour 9 feuilles (niveaux de 9, 5, 3, 2, puis 1 nœuds), comptez à la main : chaque preuve a 4 empreintes, ce que le laboratoire autonome vérifiera en code.

Un revers de la règle de duplication

Prenons trois feuilles [a, b, c] et quatre feuilles [a, b, c, c]. Dans le premier cas, c est dupliquée pour former N2 = H(Lc ‖ Lc) ; dans le second, c et sa copie forment la même paire. Les deux listes donnent la même racine (dans la convention jouet, d31a37ef… avec des feuilles de texte « a », « b », « c »). Deux ensembles différents, une racine : la racine seule ne distingue pas « 3 transactions » de « 3 transactions dont la dernière est répétée ». Bitcoin applique cette même règle de duplication du dernier hachage, et ce défaut y a donné lieu à une vulnérabilité connue (de malléabilité des blocs, non un vol de fonds) que les implémentations ont dû traiter ; il faut pour cela que le doublon passe les autres règles de validation. Les protocoles qui s'en soucient détectent ces doublons ou changent de convention.

Ce que cela ne garantit pas

« Petite » ne veut pas dire « gratuite » : le vérificateur doit aussi connaître la position (ou les côtés) et la donnée de la feuille. Et prouver une seule transaction ne dit rien des autres. Des idées voisines (arbres de Merkle sur des données redondantes) servent aussi à l'échantillonnage de disponibilité des données dans certaines conceptions de chaînes ; ce sujet est hors programme ici.

En bref

  • Une preuve compte ⌈log₂ n⌉ empreintes de 32 octets : 20 pour un million de feuilles, soit 640 octets.
  • Doubler l'ensemble ajoute une empreinte, pas le double.
  • Avec la duplication du dernier nœud, deux listes différentes peuvent avoir la même racine.

Outil interactif

Explorateur d'arbre de Merkle (jouet)

Une feuille par ligne (2 à 8). Choisissez une feuille pour voir sa preuve d'inclusion ; modifiez un texte pour voir quels nœuds changent.

5 feuilles

Les nœuds modifiés depuis la dernière saisie valide sont soulignés et marqués « modifié ». Les nœuds sur le chemin de la feuille choisie sont entourés. Dernier nœud d'un niveau impair : il est dupliqué comme son propre frère.

Convention : feuille = SHA-256(texte UTF-8) ; parent = SHA-256(gauche ‖ droite, octets bruts) ; nœud impair dupliqué. Arbre jouet : Bitcoin double le hachage et sérialise autrement.

Leçons 4/4 · 25 min

Quand l'arbre ment : séparation des domaines

La question de départ

Dans notre convention, une feuille est SHA-256(donnée) et un nœud est SHA-256(gauche ‖ droite). Les deux sont donc « SHA-256 de quelque chose ». Un vérificateur peut-il confondre une feuille et un nœud interne ? Si oui, que peut en faire un attaquant ?

L'attaque, pas à pas

Reprenons l'arbre de quatre transactions (leçon 1) et sa racine R = 803bff37…

Un nœud interne N1 vaut SHA-256(L0 ‖ L1). Les octets L0 ‖ L1 font 64 octets. Un attaquant dit : « voici une donnée de 64 octets, égale à L0 ‖ L1, c'est ma feuille ; voici ma preuve : un seul frère, N2, à droite ». Le vérificateur applique la règle :

  1. feuille = SHA-256(donnée) = SHA-256(L0 ‖ L1) = N1 ;
  2. racine candidate = SHA-256(N1 ‖ N2) = R ✔.

La preuve est acceptée parce que le vérificateur ne contrôle pas la longueur de la preuve (un vérificateur qui fixerait la profondeur attendue, deux niveaux pour quatre feuilles, refuserait une preuve à un seul niveau), et pourtant la « feuille » n'est pas une transaction : c'est un nœud interne déguisé, et la preuve n'a qu'un niveau au lieu de deux. C'est une attaque par seconde préimage sur l'arbre (plus exactement, une confusion entre niveaux) : l'attaquant n'a cassé aucune fonction de hachage ; il a profité d'une ambiguïté de structure.

La parade : séparation des domaines

On préfixe les deux types de hachage par un octet différent :

  • feuille = SHA-256(0x00 ‖ donnée)
  • nœud = SHA-256(0x01 ‖ gauche ‖ droite)

Ainsi une valeur calculée comme feuille ne peut pas être prise pour un nœud, ni inversement, car les entrées diffèrent dès le premier octet. C'est la solution retenue par le protocole de transparence des certificats (RFC 6962). Autres parades possibles : fixer la profondeur attendue de l'arbre, ou exiger des feuilles d'une longueur différente de 64 octets. L'arbre des transactions de Bitcoin n'emploie pas ces préfixes ; le sujet est connu des chercheurs et dépasse cet atelier.

Ce que la preuve ne garantit toujours pas

  • Même avec séparation des domaines, une preuve établit l'inclusion, pas l'absence : pour prouver qu'une donnée n'est pas dans un ensemble, on peut utiliser un arbre dont les feuilles sont triées : on prouve alors l'inclusion de deux voisins consécutifs a < x < b, entre lesquels x devrait se trouver. C'est le sujet du laboratoire autonome.
  • Une preuve ne vaut que pour la racine donnée. Une racine qui change (un nouveau bloc) demande de nouvelles preuves.

À vous

Faites l'exercice de sécurité : vous allez exécuter l'attaque avec hexhash sur de vraies empreintes.

En bref

  • Sans séparation, une feuille et un nœud interne sont indiscernables : un nœud peut se faire passer pour une feuille.
  • Un préfixe distinct (0x00 / 0x01) supprime cette ambiguïté.
  • Une preuve d'inclusion ne prouve jamais l'absence.

Outil interactif

SHA-256 d'octets hexadécimaux

Collez des octets en hexadécimal : le hachage porte sur les octets, pas sur le texte des chiffres. Un second champ est concaténé après le premier.

Empreinte

…

Espaces, retours à la ligne et préfixes 0x sont ignorés.

Laboratoire guidé

Dans ce laboratoire, vous calculez une racine de Merkle, vérifiez une preuve et estimez sa taille. Les données sont générées pour vous à chaque tentative : inutile de copier la réponse d'un autre. Le navigateur n'exécute pas votre propre code Python (c'est le laboratoire autonome) : vous utilisez les outils sha256 et hexhash, qui font le même calcul que le programme à écrire ensuite.

Le navigateur ne remplace pas un vrai terminal : ce laboratoire entraîne la lecture et le raisonnement ; la pratique réelle est dans le laboratoire autonome.

Un compte certifié est nécessaire

Les activités notées enregistrent des résultats à votre nom et servent de base à un titre. Elles exigent donc un dossier certifiant complet et un consentement en vigueur. Remplir le dossier ne vérifie pas votre identité ; le Secrétariat peut ensuite la vérifier (badge « Vérifié »).

  1. 1.Calculer une racine à cinq feuilles

    Lisez les cinq textes de feuilles de l'énoncé. Avec sha256, calculez l'empreinte de chacun. Avec hexhash, combinez les empreintes deux à deux (gauche puis droite) pour obtenir le niveau suivant ; comme il y a cinq feuilles, la cinquième est dupliquée. Continuez jusqu'à la racine et saisissez-la en hexadécimal.

    Outil interactif

    Empreinte SHA-256

    Tapez un texte : l'empreinte se met à jour à chaque caractère. Comparez avec un second texte pour mesurer l'effet d'avalanche.

    Empreinte (64 caractères hexadécimaux)

    …

    Empreinte du second texte

    …

    Les caractères différents sont soulignés et en gras. Le texte est encodé en UTF-8 sans ajout d'espace ni de retour à la ligne.

    Outil interactif

    SHA-256 d'octets hexadécimaux

    Collez des octets en hexadécimal : le hachage porte sur les octets, pas sur le texte des chiffres. Un second champ est concaténé après le premier.

    Empreinte

    …

    Espaces, retours à la ligne et préfixes 0x sont ignorés.

  2. 2.Explorer l'arbre et les preuves

    Dans merkle-explorer, saisissez 8 feuilles. Cliquez sur la 6e feuille (indice 5) et lisez sa preuve : combien d'empreintes, et de quel côté se trouve chaque frère ? Modifiez ensuite une feuille et observez la racine. Il n'y a pas de saisie à valider ici ; ce que vous observez sert aux deux étapes suivantes.

    Outil interactif

    Explorateur d'arbre de Merkle (jouet)

    Une feuille par ligne (2 à 8). Choisissez une feuille pour voir sa preuve d'inclusion ; modifiez un texte pour voir quels nœuds changent.

    5 feuilles

    Les nœuds modifiés depuis la dernière saisie valide sont soulignés et marqués « modifié ». Les nœuds sur le chemin de la feuille choisie sont entourés. Dernier nœud d'un niveau impair : il est dupliqué comme son propre frère.

    Convention : feuille = SHA-256(texte UTF-8) ; parent = SHA-256(gauche ‖ droite, octets bruts) ; nœud impair dupliqué. Arbre jouet : Bitcoin double le hachage et sérialise autrement.

  3. 3.Vérifier une preuve d'inclusion

    L'énoncé donne une feuille, son indice, ses frères dans l'ordre et la racine annoncée, pour un arbre de 8 feuilles. Appliquez la règle des côtés (indice pair : frère à droite, impair : à gauche ; indice ÷ 2 à chaque niveau) avec hexhash. Saisissez la racine recalculée, puis dites si la preuve établit l'inclusion. Attention : la preuve peut avoir été falsifiée, soit dans la feuille, soit dans un frère.

    Outil interactif

    Empreinte SHA-256

    Tapez un texte : l'empreinte se met à jour à chaque caractère. Comparez avec un second texte pour mesurer l'effet d'avalanche.

    Empreinte (64 caractères hexadécimaux)

    …

    Empreinte du second texte

    …

    Les caractères différents sont soulignés et en gras. Le texte est encodé en UTF-8 sans ajout d'espace ni de retour à la ligne.

    Outil interactif

    SHA-256 d'octets hexadécimaux

    Collez des octets en hexadécimal : le hachage porte sur les octets, pas sur le texte des chiffres. Un second champ est concaténé après le premier.

    Empreinte

    …

    Espaces, retours à la ligne et préfixes 0x sont ignorés.

  4. 4.Estimer la taille d'une preuve

    Un bloc fictif contient 3 000 transactions. Calculez le nombre d'empreintes SHA-256 de 32 octets que contient une preuve d'inclusion de l'une d'elles dans l'arbre de la leçon 3, puis répondez à la question de contrôle. Aide : cherchez la plus petite puissance de 2 qui atteint ou dépasse 3 000.

Exercice de sécurité — Exécuter l'attaque : un nœud interne déguisé en feuille

Un vérificateur jouet applique la convention de l'atelier sans séparation des domaines : feuille = SHA-256(octets de la donnée), nœud = SHA-256(gauche ‖ droite). Il connaît cette racine publiée pour un lot de quatre transactions (fictives) :

racine R = 803bff37ed78fb52ccf54851b3ca635e05a4c004fa6dbbe4863ff21c4e8f589c

Un attaquant présente :

  • une « donnée » de 64 octets : L0 ‖ L1, c'est-à-dire la concaténation des deux empreintes de feuilles suivantes ;
  • une preuve d'un seul niveau : le frère N2, placé à droite.
L0 = 801c17a0d398ef843a0a781c773fe759a8a8542e4850e58224a344140642da2e
L1 = 251f15750c47f9694536abb76897fec69513cea2e42cf05d872874604c5bfcac
N2 = 024b1837a1edc8fe1fbb1ed744fa946674e22d46791ad22228678e8183a80bf8

Procédure avec l'outil hexhash :

  1. Entrez L0 puis L1 (deux éléments concaténés) : relevez l'empreinte. Ce sera la « feuille » vue par le vérificateur.
  2. Entrez cette empreinte puis N2 : relevez l'empreinte obtenue.

Saisissez les 8 premiers caractères hexadécimaux de l'empreinte obtenue à l'étape 2. Comparez-les au début de R. Puis demandez-vous : quel préfixe d'octet ajouteriez-vous à la feuille et au nœud pour que cette preuve soit refusée ?

Questionnaire de l'atelier

Huit questions tirées d'une réserve plus large, seuil de réussite 70 %. Il s'ouvre quand tous les points de contrôle du laboratoire sont réussis. Vous pouvez le repasser : le tirage change.

Un compte certifié est nécessaire

Les activités notées enregistrent des résultats à votre nom et servent de base à un titre. Elles exigent donc un dossier certifiant complet et un consentement en vigueur. Remplir le dossier ne vérifie pas votre identité ; le Secrétariat peut ensuite la vérifier (badge « Vérifié »).

Laboratoire autonome (hors du site)

Non noté en ligne : à réaliser sur votre machine et à publier sur votre dépôt Git.

À faire sur votre machine, hors du site, dans un dépôt Git, en Python (bibliothèque standard, module hashlib).

  1. Écrivez merkle.py avec trois fonctions : build(leaves) qui renvoie tous les niveaux de l'arbre (convention de l'atelier : feuille = SHA-256(texte), nœud = SHA-256(gauche ‖ droite), dernier nœud d'un niveau impair dupliqué), prove(levels, index) qui renvoie la liste des (frère, côté), et verify(leaf, proof, root).
  2. Testez-les avec les quatre transactions de la leçon 1 : la racine doit commencer par 803bff37. Puis avec 1 à 20 feuilles : vérifiez la preuve de chaque feuille.
  3. Mesurez la croissance : pour n = 2, 4, 8, ..., 2^16, affichez la longueur de la preuve et comparez à ⌈log₂ n⌉.
  4. Reproduisez l'attaque de la leçon 4 en code (donnée de 64 octets présentée comme feuille). Ajoutez ensuite les préfixes 0x00 et 0x01 et montrez que l'attaque échoue.
  5. Pour aller plus loin : triez les feuilles, et écrivez une preuve de non-inclusion pour une valeur absente en fournissant les preuves d'inclusion de ses deux voisins consécutifs (en traitant les bords : avant la première feuille et après la dernière) et en vérifiant que leurs indices sont consécutifs, i et i + 1. Notez dans un fichier NOTES.md ce que cette preuve suppose (arbre trié et racine de confiance).

Livrable : merkle.py, vos tests, NOTES.md. Les données restent fictives.

À retenir

  • La racine de Merkle résume tout un ensemble en 32 octets ; changer un bit d'une feuille ou l'ordre change la racine.
  • Une preuve d'inclusion liste les frères du chemin feuille-racine ; le vérificateur recalcule la racine et la compare à celle qu'il connaît déjà.
  • La taille d'une preuve croît comme ⌈log₂ n⌉ : 20 empreintes (640 octets) pour un million de feuilles.
  • La preuve lie une donnée à une racine, mais ne garantit ni l'authenticité de la racine, ni la validité de la donnée, ni l'absence d'autres données.
  • Sans séparation des domaines (préfixes 0x00 / 0x01), un nœud interne peut se faire passer pour une feuille.

Idées fausses courantes

  • Idée reçue : « Une preuve de Merkle valide prouve que la transaction est valide. »

    En réalité : Elle prouve seulement que la donnée figure à cette position sous cette racine. La validité (solde, signature) est vérifiée ailleurs.

  • Idée reçue : « La preuve pour un million de feuilles contient un million d'empreintes, ou au moins mille. »

    En réalité : Elle en contient ⌈log₂ n⌉ : 20 pour un million, soit 640 octets.

  • Idée reçue : « Si je ne reçois aucune preuve pour une donnée, c'est qu'elle n'est pas dans l'arbre. »

    En réalité : L'absence de preuve ne prouve rien. Prouver l'absence demande une structure particulière, comme un arbre trié avec deux voisins.

  • Idée reçue : « Une preuve valide suffit, même si la racine vient d'un inconnu. »

    En réalité : Un attaquant peut fabriquer racine et preuves cohérentes. La racine doit venir d'une source de confiance, par exemple un en-tête de bloc suivi par le client.

Glossaire

Racine de Merkle
L'empreinte du sommet de l'arbre ; elle résume toutes les feuilles, dans leur ordre, en 32 octets pour SHA-256.
Feuille
Le nœud du bas de l'arbre : l'empreinte d'une donnée (par exemple une transaction).
Preuve d'inclusion
Liste des empreintes frères le long du chemin de la feuille à la racine, permettant à un vérificateur de recalculer la racine.
Séparation des domaines
Procédé qui préfixe différemment les hachages de feuilles et de nœuds (par exemple 0x00 et 0x01) pour qu'une valeur d'un type ne soit jamais prise pour l'autre.
Client léger
Programme qui ne télécharge que les en-têtes de bloc et vérifie des preuves d'inclusion, au lieu de tous les blocs.