La blockchain Cours 7

Le protocole Bitcoin

Connaître les règles de consensus du protocole Bitcoin est essentiel, mais les règles utilisées par le client pour traiter les messages sont tout aussi importantes. Il est crucial que les clients suivent certaines règles afin de maintenir la cohérence à travers le réseau et de protéger les garanties de sécurité Bitcoin.

Ici, l’accent est mis sur la gestion des messages des transactions (tx) et de bloc, car c’est là que la logique est délicate. Nous allons donc ignorer la méthode de demande et de transfert de ces messages pour l’instant, et décrire ce qu’il faut faire lorsqu’ils sont reçus. En outre, nous décrirons les structures de données minimales en termes plutôt abstraits, en ignorant les divers index, cartes et tables de hachage du client utilisés pour l’efficacité. Ce sera une description conceptuelle. Tout cela est basé sur une lecture assez littérale du code source.

Les règles de minage (génération de blocs) ne sont pas encore présentées.

Structures de données

Les principales structures de données sont les transactions et les blocs. Les blocs sont composés de l’en-tête de bloc suivi des transactions dans le bloc. Les transactions sont identifiées par leur hachage; les blocs sont identifiés par le hash de leur en-tête. Les blocs ont des pointeurs ‘prev’ vers le bloc précédent, ce qui les lient entre-eux dans un graphique.
Conceptuellement, le client a les structures de données suivantes:

  1. Transactions
    Il existe deux collections de transactions:

    1. pool de transactions : une collection non ordonnée de transactions qui ne sont pas en blocs dans la chaîne principale, mais pour lesquelles nous avons des transactions d’entrée
    2. transactions orphelines : transactions qui ne peuvent pas entrer dans le pool en raison d’une ou plusieurs transactions d’entrée manquantes
  2. Blocs
    Il existe 3 catégories de blocs:

    1. blocs dans la chaîne principale : les transactions dans ces blocs sont considérées comme confirmées au moins provisoirement
    2. blocs sur les chaînes latérales de la chaîne principale :ces blocs ont au moins provisoirement perdu la course pour être dans la branche principale
    3. blocs orphelins : ce sont des blocs qui ne se lient pas à la chaîne principale, généralement à cause d’un prédécesseur manquant (ou d’un prédécesseur de nième niveau manquant)

    Les blocs des deux premières catégories forment un arbre enraciné dans le bloc de genèse, lié par le pointeur prev, qui pointe vers la racine. (Il s’agit d’un arbre très linéaire avec quelques branches courtes et éloignées de la branche principale.) La chaîne (branche) principale est définie comme la chaîne ayant la difficulté totale la plus élevée, en additionnant les difficultés pour chaque bloc de la branche.

Changement de difficulté

La difficulté change tous les 2016 blocs. Ce choix est conçu pour se produire environ toutes les deux semaines.

2 semaines / 10 minutes = 14 * 24 * 60/10 = 2016

Une fois que 2016 blocs ont été atteints, on remonte la blockchaine jusqu’à ce que nous atteignions le 2016e bloc avant le bloc actuel. On retrouve la différence de temps entre le bloc courant et celui-là. Cette différence (appelée la durée réelle) est limitée dans les limites entre 2 semaines / 4 et 2 semaines * 4.

Ensuite, nous obtenons la dernière cible pour cette ancienne fenêtre de 2 semaines et la multiplions par le rapport entre la durée réelle et la durée cible (2 semaines en secondes).

nouvel objectif = ancien objectif * durée pour 2016 blocs / 2 semaines.

Si l’ancien ensemble de blocs est terminé trop rapidement, la cible est abaissée (la difficulté augmente), et donc la résolution des nouveaux blocs sera plus longue … et vice versa. De cette façon, la difficulté oscille autour de l’idéal de 2 semaines (et de 10 minutes par bloc).

Récompense de création de bloc

Les frais de création de blocs changent tous les 210000 blocs. Les frais de création de bloc sont fonction de la hauteur du bloc sur la chaîne (le bloc de genèse est le bloc numéroté 0) et sont calculés à l’aide d’opérations entières 64 bits (en satoshis) comme:

(50 * 100000000) >> (hauteur / 210000)

Les frais de création de bloc initiallement étaient de 50 BTC, sont tombés à 25 BTC au bloc 210000, puis à 12,5 BTC au bloc 420000, et finalement à 0 satoshi avec le bloc 6930000. Le nombre de bitcoin créés lors de chaque bloc s’élèveront à 209999999769690000 satoshis, pratiquement 21 millions de BTC.

Messages “tx”

Ces messages contiennent une seule transaction.

  1. Vérifier l’exactitude syntaxique
  2. Assurez-vous qu’aucune liste d’entrée ou de sortie n’est vide
  3. Taille en octets <= MAX_BLOCK_SIZE
  4. Chaque valeur de sortie, ainsi que le total, doit être dans la plage monétaire légale
  5. Assurez-vous qu’aucune des entrées n’a de hachage = 0, n = -1 (transactions coinbase)
  6. Vérifiez que nLockTime <= INT_MAX [1], la taille en octets> = 100 [2] et sig opcount <= 2 [3]
  7. Rejeter les transactions “non standard”: scriptSig faisant autre chose que pousser des nombres sur la pile, ou scriptPubkey ne correspondant pas aux deux formes habituelles [4]
  8. Rejeter si nous avons déjà des tx correspondants dans le pool ou dans un bloc de la branche principale
  9. Pour chaque entrée, si la sortie référencée existe dans tout autre tx du pool, rejetez cette transaction. [5]
  10. Pour chaque entrée, regarder dans la branche principale et le pool de transactions pour trouver la transaction de sortie référencée. Si la transaction de sortie est manquante pour une entrée, ce sera une transaction orpheline. Ajoutez aux transactions orphelines, si une transaction correspondante n’y figure pas déjà.
    Pour chaque entrée, si la transaction de sortie référencée est coinbase (c’est-à-dire seulement 1 entrée, avec hachage = 0, n = -1), elle doit avoir au moins des confirmations COINBASE_MATURITY (100); sinon rejeter cette transaction
  11. Pour chaque entrée, si la sortie référencée n’existe pas (par exemple n’a jamais existé ou a déjà été dépensée), rejetez cette transaction [6]
  12. En utilisant les transactions de sortie référencées pour obtenir les valeurs d’entrée, vérifiez que chaque valeur d’entrée, ainsi que la somme, sont dans la plage monétaire légale
  13. Rejeter si la somme des valeurs d’entrée
  14. Rejeter si les frais de transaction (définis comme la somme des valeurs d’entrée moins la somme des valeurs de sortie) seraient trop bas pour entrer dans un bloc vide
  15. Vérifiez que scriptPubKey accepte pour chaque entrée; rejeter si certains sont mauvais
  16. Ajouter au pool de transactions [7]
  17. “Ajouter au portefeuille si le mien”
  18. Relayer la transaction aux autres nœuds du réseau
  19. Pour chaque transaction orpheline qui utilise celle-ci comme l’une de ses entrées, exécutez toutes ces étapes (y compris celle-ci) récursivement sur cet orphelin

Messages Blocs

Ces messages contiennent un seul bloc.

    1. Vérifier l’exactitude syntaxique
    2. Rejeter si le double du bloc que nous avons dans l’une des trois catégories
    3. La liste des transactions doit être non vide
    4. Le hachage de bloc doit satisfaire la preuve de travail nBits revendiquée
    5. L’horodatage du bloc ne doit pas dépasser deux heures à l’avenir
    6. La première transaction doit être coinbase (c.-à-d. Seulement 1 entrée, avec hachage = 0, n = -1), le reste ne doit pas être
    7. Pour chaque transaction, appliquez les contrôles “tx” 2-4
    8. Pour la (première) transaction coinbase, la longueur de scriptSig doit être comprise entre 2 et 100
    9. Rejeter si la somme des comptes d’opérations de signature> MAX_BLOCK_SIGOPS
    10. Vérifier le hachage Merkle
    11. Vérifiez si le bloc prev (hachage prev correspondant) se trouve dans la branche principale ou les branches latérales. Si ce n’est pas le cas, ajoutez ceci aux blocs orphelins, puis interrogez l’homologue dont nous avons obtenu cela pour le premier bloc orphelin manquant dans la chaîne précédente ; fait avec bloc
    12. Vérifiez que la valeur de nBits correspond aux règles de difficulté
    13. Rejeter si l’horodatage est le temps médian des 11 derniers blocs ou avant
    14. Pour certains anciens blocs (c’est-à-dire lors du téléchargement initial des blocs), vérifiez que le hachage correspond aux valeurs connues
    15. Ajoutez un bloc dans l’arborescence. Il existe trois cas:
      • 1. le bloc étend encore la branche principale;
      • 2. le bloc prolonge une branche latérale mais n’ajoute pas assez de difficulté pour qu’elle devienne la nouvelle branche principale;
      • 3. Le bloc prolonge une branche latérale et en fait la nouvelle branche principale.
    16. Pour le cas 1, ajout à la branche principale:
      1. Pour toutes les transactions, sauf la transaction Coinbase, appliquez ce qui suit:
        1. Pour chaque entrée, regardez dans la branche principale pour trouver la transaction de sortie référencée. Rejeter si la transaction de sortie est manquante pour une entrée.
        2. Pour chaque entrée, si nous utilisons la n ème sortie de la transaction précédente, mais qu’elle a moins de n + 1 sorties, rejetez.
        3. Pour chaque entrée, si la transaction de sortie référencée est coinbase (c’est-à-dire seulement 1 entrée, avec hachage = 0, n = -1), elle doit avoir au moins des confirmations COINBASE_MATURITY (100); sinon rejeter.
        4. Vérifiez les signatures cryptographiques pour chaque entrée; rejeter si certains sont mauvais
        5. Pour chaque entrée, si la sortie référencée a déjà été dépensée par une transaction dans la branche principale, rejetez
        6. En utilisant les transactions de sortie référencées pour obtenir les valeurs d’entrée, vérifiez que chaque valeur d’entrée, ainsi que la somme, sont dans la plage monétaire légale
        7. Rejeter si la somme des valeurs d’entrée <somme des valeurs de sortie
      2. Rejeter si valeur de la base> somme des frais de création de bloc et des frais de transaction
      3. (Si nous n’avons pas rejeté):
      4. Pour chaque transaction, “Ajouter au portefeuille si le mien”
      5. Pour chaque transaction du bloc, supprimez toute transaction correspondante du pool de transactions
      6. Bloc de relais vers nos pairs
      7. Si nous refusons, le bloc n’est pas compté comme faisant partie de la branche principale
    17. Pour le cas 2, en ajoutant à une branche latérale, nous ne faisons rien.
    18. Pour le cas 3, une branche latérale devenant la branche principale:
      1. Trouvez le bloc de fourche sur la branche principale dont cette branche latérale bifurque
      2. Redéfinir la branche principale pour ne monter que sur ce bloc de fourche
      3. Pour chaque bloc de la branche latérale, de l’enfant du bloc de fourche à la feuille, ajoutez à la branche principale:
        1. Effectuer des vérifications de “succursale” 3-11
        2. Pour toutes les transactions, sauf la transaction Coinbase, appliquez ce qui suit:
          1. Pour chaque entrée, regardez dans la branche principale pour trouver la transaction de sortie référencée. Rejeter si la transaction de sortie est manquante pour une entrée.
          2. Pour chaque entrée, si nous utilisons la n ème sortie de la transaction précédente, mais qu’elle a moins de n + 1 sorties, rejetez.
          3. Pour chaque entrée, si la transaction de sortie référencée est coinbase (c’est-à-dire seulement 1 entrée, avec hachage = 0, n = -1), elle doit avoir au moins des confirmations COINBASE_MATURITY (100); sinon rejeter.
          4. Vérifiez les signatures cryptographiques pour chaque entrée; rejeter si certains sont mauvais
          5. Pour chaque entrée, si la sortie référencée a déjà été dépensée par une transaction dans la branche principale, rejetez
          6. En utilisant les transactions de sortie référencées pour obtenir les valeurs d’entrée, vérifiez que chaque valeur d’entrée, ainsi que la somme, sont dans la plage monétaire légale
          7. Rejeter si la somme des valeurs d’entrée <somme des valeurs de sortie
        3. Rejeter si valeur de la base> somme des frais de création de bloc et des frais de transaction
        4. (Si nous n’avons pas rejeté):
        5. Pour chaque transaction, “Ajouter au portefeuille si le mien”
      4. Si nous refusons à tout moment, laissez la branche principale telle qu’elle était à l’origine, faite avec le bloc
      5. Pour chaque bloc de l’ancienne branche principale, de la feuille jusqu’à l’enfant du bloc de fourche :
        1. Pour chaque transaction hors coinbase du bloc:
          1. Appliquer les vérifications “tx” 2-9, sauf à l’étape 8, rechercher uniquement dans le pool de transactions les doublons, pas la branche principale
          2. Ajouter au pool de transactions s’il est accepté, sinon passer à la transaction suivante
      6. Pour chaque bloc de la nouvelle branche principale, de l’enfant du nœud fork à la feuille:
        1. Pour chaque transaction du bloc, supprimez toute transaction correspondante du pool de transactions
      7. Relayer les blocs vers nos pairs
    19. Pour chaque bloc orphelin pour lequel ce bloc est son précédent , exécutez toutes ces étapes (y compris celle-ci) de manière récursive sur cet orphelin

Notes

  1. nLockTime ne doit pas dépasser 31 bits, car certains clients l’interpréteront incorrectement
  2. Une transaction valide nécessite au moins 100 octets. Si c’est moins, la transaction n’est pas valide
  3. Le nombre d’opérandes de signature dans la signature (non, qui n’est pas redondant) pour les transactions standard ne dépassera jamais deux
  4. Notez que ce n’est pas une exigence difficile pour les clients.
  5. Notez que ce n’est pas une exigence difficile pour les clients. La règle imposée par le réseau est qu’une seule transaction dépensant une sortie particulière peut être dans la blockchain, empêchant ainsi les doubles dépenses. Techniquement, les mineurs peuvent choisir celui qu’ils veulent mettre dans le bloc sur lequel ils travaillent tant qu’aucune autre transaction n’a dépensé cette sortie auparavant dans la blockchain ou dans le même bloc. Le pool de transactions en mémoire peut techniquement être géré de la manière que le mineur souhaite mettre en œuvre.
  6. Ceci est la protection contre les doubles dépenses
  7. Notez que lorsque la transaction est acceptée dans le pool de mémoire, une vérification supplémentaire est effectuée pour s’assurer que la valeur de la base de pièces ne dépasse pas les frais de transaction plus la valeur BTC attendue (25BTC au moment de la rédaction de cet article).