

Les traductions sont fournies par des outils de traduction automatique. En cas de conflit entre le contenu d'une traduction et celui de la version originale en anglais, la version anglaise prévaudra.

# Vérification des données dans Amazon QLDB
<a name="verification"></a>

**Important**  
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez [Migrer un registre Amazon QLDB vers Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Avec Amazon QLDB, vous pouvez être sûr que l'historique des modifications apportées aux données de votre application est exact. *QLDB utilise un journal transactionnel immuable, appelé journal, pour le stockage des données.* Le journal suit chaque modification apportée à vos données enregistrées et conserve un historique complet et vérifiable des modifications au fil du temps.

*QLDB utilise la fonction de hachage SHA-256 avec un modèle basé sur l'arbre de Merkle pour générer une représentation cryptographique de votre journal, connue sous le nom de résumé.* Le condensé agit comme une signature unique de l'historique complet des modifications de vos données à un moment donné. Vous utilisez le résumé pour vérifier l'intégrité des révisions de votre document par rapport à cette signature.

**Topics**
+ [Quel type de données pouvez-vous vérifier dans QLDB ?](#verification.structure)
+ [Que signifie l'intégrité des données ?](#verification.integrity)
+ [Comment fonctionne la vérification ?](#verification.how-it-works)
+ [Exemple de vérification](#verification.example)
+ [Quel est l'impact de la suppression des données sur la vérification ?](#verification.redaction)
+ [Commencer à utiliser la vérification](#verification.getting-started)
+ [Étape 1 : Demande d'un résumé dans QLDB](verification.digest.md)
+ [Étape 2 : vérification de vos données dans QLDB](verification.verify.md)
+ [Résultats de vérification](verification.results.md)
+ [Tutoriel : vérification des données à l'aide d'un AWS SDK](verification.tutorial-block-hash.md)
+ [Erreurs courantes lors de la vérification](verification.errors.md)

## Quel type de données pouvez-vous vérifier dans QLDB ?
<a name="verification.structure"></a>

Dans QLDB, chaque registre possède exactement un journal. Un journal peut comporter plusieurs *volets*, qui sont des partitions du journal.

**Note**  
QLDB prend actuellement en charge les revues à un seul volet.

Un *bloc* est un objet qui est inscrit dans le volet journal lors d'une transaction. Ce bloc contient des objets *d'entrée*, qui représentent les révisions du document résultant de la transaction. Vous pouvez vérifier une révision individuelle ou un bloc de journal complet dans QLDB.

Le schéma suivant illustre cette structure de journal.

![\[Schéma de structure du journal Amazon QLDB présentant un ensemble de blocs chaînés par hachage constituant un fil, ainsi que le numéro de séquence et le hachage de chaque bloc.\]](http://docs.aws.amazon.com/fr_fr/qldb/latest/developerguide/images/verification/journal-structure.png)


Le diagramme montre que les transactions sont enregistrées dans le journal sous forme de blocs contenant des entrées de révision de documents. Cela montre également que chaque bloc est enchaîné par hachage aux blocs suivants et possède un numéro de séquence pour spécifier son adresse dans le brin.

Pour plus d'informations sur le contenu des données d'un bloc, consultez[Contenu du journal dans Amazon QLDB](journal-contents.md).

## Que signifie l'intégrité des données ?
<a name="verification.integrity"></a>

L'intégrité des données dans QLDB signifie que le journal de votre registre est en fait immuable. En d'autres termes, vos données (en particulier, chaque révision de document) sont dans un état dans lequel les conditions suivantes sont vraies :

1. Il se trouve au même endroit dans votre journal où il a été écrit pour la première fois.

1. Il n'a subi aucune modification depuis qu'il a été écrit.

## Comment fonctionne la vérification ?
<a name="verification.how-it-works"></a>

Pour comprendre le fonctionnement de la vérification dans Amazon QLDB, vous pouvez décomposer le concept en quatre éléments de base.
+ [Hachage](#verification.how-it-works.hashing)
+ [Digest](#verification.how-it-works.digest)
+ [Arbre Merkle](#verification.how-it-works.merkle-tree)
+ [Preuve](#verification.how-it-works.proof)

### Hachage
<a name="verification.how-it-works.hashing"></a>

QLDB utilise la fonction de hachage cryptographique SHA-256 pour créer des valeurs de hachage de 256 bits. Un hachage agit comme une signature unique et de longueur fixe de toute quantité arbitraire de données d'entrée. Si vous modifiez une partie de l'entrée, ne serait-ce qu'un seul caractère ou un seul bit, le hachage de sortie change complètement.

Le schéma suivant montre que la fonction de hachage SHA-256 crée des valeurs de hachage totalement uniques pour deux documents QLDB qui ne diffèrent que d'un chiffre.

![\[Schéma montrant que la fonction de hachage cryptographique SHA-256 crée des valeurs de hachage totalement uniques pour deux documents QLDB qui ne diffèrent que d'un chiffre.\]](http://docs.aws.amazon.com/fr_fr/qldb/latest/developerguide/images/sha256.png)


La fonction de hachage SHA-256 est unidirectionnelle, ce qui signifie qu'il n'est pas mathématiquement possible de calculer l'entrée lorsqu'une sortie est donnée. Le schéma suivant montre qu'il n'est pas possible de calculer le document QLDB en entrée avec une valeur de hachage en sortie.

![\[Schéma montrant qu'il n'est pas possible de calculer le document QLDB d'entrée lorsqu'on lui donne une valeur de hachage en sortie.\]](http://docs.aws.amazon.com/fr_fr/qldb/latest/developerguide/images/sha256-one-way.png)


Les entrées de données suivantes sont hachées dans QLDB à des fins de vérification :
+ Révisions du document
+ Instructions PartiQL
+ Entrées de révision
+ Blocs de journaux

### Digest
<a name="verification.how-it-works.digest"></a>

Un *résumé* est une représentation cryptographique de l'intégralité du journal de votre registre à un moment donné. Un journal est uniquement en ajout, et les blocs de journal sont séquencés et hachés de la même manière que les blockchains.

Vous pouvez demander un résumé pour un registre à tout moment. QLDB génère le condensé et vous le renvoie sous forme de fichier de sortie sécurisé. Vous utilisez ensuite ce résumé pour vérifier l'intégrité des révisions de documents qui ont été validées à un moment antérieur. Si vous recalculez les hachages en commençant par une révision et en terminant par le résumé, vous prouvez que vos données n'ont pas été modifiées entre les deux.

### Arbre Merkle
<a name="verification.how-it-works.merkle-tree"></a>

À mesure que la taille de votre registre augmente, il devient de plus en plus inefficace de recalculer la chaîne de hachage complète du journal à des fins de vérification. QLDB utilise un modèle d'arbre Merkle pour remédier à cette inefficacité.

Un *arbre Merkle* est une structure de données arborescente dans laquelle chaque nœud de feuille représente le hachage d'un bloc de données. Chaque nœud autre qu'une feuille est un hachage de ses nœuds enfants. Couramment utilisé dans les chaînes de blocs, un arbre Merkle vous aide à vérifier efficacement de grands ensembles de données grâce à un mécanisme de preuve d'audit. Pour plus d'informations sur les arbres Merkle, consultez la page [Wikipédia sur les arbres Merkle](https://en.wikipedia.org/wiki/Merkle_tree). Pour en savoir plus sur les preuves d'audit Merkle et pour un exemple de cas d'utilisation, consultez [How Log Proofs Work](https://www.certificate-transparency.org/log-proofs-work) sur le site de transparence des certificats.

L'implémentation QLDB de l'arbre Merkle est construite à partir de la chaîne de hachage complète d'un journal. Dans ce modèle, les nœuds foliaires sont l'ensemble de tous les hachages de révision de documents individuels. Le nœud racine représente le résumé de l'intégralité du journal à un moment donné.

À l'aide d'une preuve d'audit Merkle, vous pouvez vérifier une révision en vérifiant uniquement un petit sous-ensemble de l'historique des révisions de votre registre. Pour ce faire, parcourez l'arbre d'un nœud foliaire donné (révision) à sa racine (résumé). Le long de ce chemin de traversée, vous hachez de manière récursive des paires de nœuds frères pour calculer leur hachage parent jusqu'à la fin du condensé. Cette traversée a une complexité temporelle en ce qui concerne `log(n)` les nœuds de l'arbre.

### Preuve
<a name="verification.how-it-works.proof"></a>

La *preuve* en est la liste ordonnée des hachages de nœuds que QLDB renvoie pour un condensé et une révision de document donnés. Il comprend les hachages requis par un modèle d'arbre Merkle pour enchaîner le hachage du nœud feuille donné (une révision) au hachage racine (le condensé).

La modification de données validées entre une révision et un résumé rompt la chaîne de hachage de votre journal et rend impossible la génération d'une preuve.

## Exemple de vérification
<a name="verification.example"></a>

Le schéma suivant illustre le modèle d'arbre de hachage Amazon QLDB. Il montre un ensemble de hachages de blocs qui s'enroulent jusqu'au nœud racine supérieur, qui représente le condensé d'un fil de journal. Dans un registre comportant un journal à un seul brin, ce nœud racine est également le condensé de l'ensemble du registre.

![\[Schéma d'arbre de hachage Amazon QLDB pour un ensemble de hachages de blocs dans un fil de journal.\]](http://docs.aws.amazon.com/fr_fr/qldb/latest/developerguide/images/verification/hash-tree.png)


Supposons que le nœud **A** soit le bloc contenant la révision du document dont vous souhaitez vérifier le hachage. ****Les nœuds suivants représentent la liste ordonnée des hachages que QLDB fournit dans votre preuve **:** B, E, G.**** **Ces hachages sont nécessaires pour recalculer le condensé à partir du hachage A.**

Pour recalculer le résumé, procédez comme suit :

1. **Commencez par le hachage **A** et concaténez-le avec le hachage B.** Ensuite, hachez le résultat pour calculer **D.**

1. Utilisez **D** et **E** pour calculer **F.**

1. Utilisez **F** et **G** pour calculer le condensé.

La vérification est réussie si votre résumé recalculé correspond à la valeur attendue. À partir d'un hachage de révision et d'un résumé, il n'est pas possible de rétroconcevoir les hachages dans une preuve. Par conséquent, cet exercice prouve que votre révision a bien été écrite à cet emplacement de journal par rapport au condensé.

## Quel est l'impact de la suppression des données sur la vérification ?
<a name="verification.redaction"></a>

Dans Amazon QLDB, `DELETE` une instruction ne supprime logiquement un document qu'en créant une nouvelle révision qui le marque comme supprimé. QLDB prend également en charge *une opération de rédaction de données* qui vous permet de supprimer définitivement les révisions de document inactives dans l'historique d'un tableau.

L'opération de rédaction supprime uniquement les données utilisateur dans la révision spécifiée et laisse inchangées la séquence du journal et les métadonnées du document. Une fois qu'une révision est expurgée, les données utilisateur de la révision (représentées par la `data` structure) sont remplacées par un nouveau `dataHash` champ. La valeur de ce champ est le hachage [Amazon Ion](ion.md) de la `data` structure supprimée. Pour plus d'informations et un exemple d'opération de rédaction, consultez[Rédaction de révisions de documents](working.redaction.md).

Par conséquent, le registre conserve l'intégrité globale de ses données et reste vérifiable cryptographiquement par le biais des opérations d'API de vérification existantes. Vous pouvez toujours utiliser ces opérations d'API comme prévu pour demander un condensé ([GetDigest](https://docs.aws.amazon.com/qldb/latest/developerguide/API_GetDigest.html)), demander une preuve ([GetBlock](https://docs.aws.amazon.com/qldb/latest/developerguide/API_GetBlock.html)ou [GetRevision](https://docs.aws.amazon.com/qldb/latest/developerguide/API_GetRevision.html)), puis exécuter votre algorithme de vérification à l'aide des objets renvoyés.

### Recalculer un hachage de révision
<a name="verification.redaction.recalc-hash"></a>

Si vous envisagez de vérifier une révision individuelle d'un document en recalculant son hachage, vous devez vérifier de manière conditionnelle si la révision a été expurgée. Si la révision a été supprimée, vous pouvez utiliser la valeur de hachage fournie dans le `dataHash` champ. S'il n'a pas été expurgé, vous pouvez recalculer le hachage en utilisant le champ. `data`

En effectuant cette vérification conditionnelle, vous pouvez identifier les révisions expurgées et prendre les mesures appropriées. Par exemple, vous pouvez enregistrer les événements de manipulation de données à des fins de surveillance.

## Commencer à utiliser la vérification
<a name="verification.getting-started"></a>

Avant de pouvoir vérifier les données, vous devez demander un résumé à partir de votre registre et l'enregistrer pour plus tard. Toute révision de document validée avant le dernier bloc couvert par le résumé peut être vérifiée par rapport à ce résumé.

Ensuite, vous demandez une preuve à Amazon QLDB pour une révision éligible que vous souhaitez vérifier. À l'aide de cette preuve, vous appelez une API côté client pour recalculer le résumé, en commençant par le hachage de votre révision. Tant que le résumé précédemment enregistré est *connu et fiable en dehors de QLDB, l'intégrité de* votre document est prouvée si le hachage du résumé recalculé correspond au hachage du résumé enregistré.

**Important**  
Ce que vous prouvez spécifiquement, c'est que la révision du document n'a pas été modifiée entre le moment où vous avez enregistré ce résumé et celui où vous avez effectué la vérification. Vous pouvez demander et enregistrer un résumé dès qu'une révision que vous souhaitez vérifier ultérieurement est validée dans le journal.
À titre de bonne pratique, nous vous recommandons de demander régulièrement des résumés et de les stocker en dehors du registre. Déterminez la fréquence à laquelle vous demandez des résumés en fonction de la fréquence à laquelle vous validez les révisions dans votre registre.  
Pour un article de AWS blog détaillé qui aborde la valeur de la vérification cryptographique dans le contexte d'un cas d'utilisation réaliste, consultez la section Vérification [cryptographique dans le monde réel avec Amazon](https://aws.amazon.com/blogs/database/real-world-cryptographic-verification-with-amazon-qldb/) QLDB.

Pour obtenir step-by-step des guides sur la façon de demander un résumé à partir de votre registre, puis de vérifier vos données, consultez ce qui suit :
+ [Étape 1 : Demande d'un résumé dans QLDB](verification.digest.md)
+ [Étape 2 : vérification de vos données dans QLDB](verification.verify.md)

# Étape 1 : Demande d'un résumé dans QLDB
<a name="verification.digest"></a>

**Important**  
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez [Migrer un registre Amazon QLDB vers Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Amazon QLDB fournit une API permettant de demander un résumé reprenant l'extrait *actuel* du journal figurant dans votre registre. Le conseil du journal fait référence au dernier bloc validé au moment où QLDB reçoit votre demande. Vous pouvez utiliser le AWS Management Console, un AWS SDK ou le AWS Command Line Interface (AWS CLI) pour obtenir un résumé.

**Topics**
+ [AWS Management Console](#verification.digest.con)
+ [API QLDB](#verification.digest.api)

## AWS Management Console
<a name="verification.digest.con"></a>

Procédez comme suit pour demander un résumé à l'aide de la console QLDB.

**Pour demander un résumé (console)**

1. [Connectez-vous à la AWS Management Console console Amazon QLDB et ouvrez-la à l'adresse /qldb. https://console.aws.amazon.com](https://console.aws.amazon.com/qldb)

1. Dans le volet de navigation, choisissez **Ledgers**.

1. Dans la liste des registres, sélectionnez le nom du registre pour lequel vous souhaitez demander un résumé.

1. Choisissez **Get digest**. La boîte de dialogue **Obtenir** le résumé affiche les détails suivants :
   + **Résumé** — La valeur de hachage SHA-256 du résumé que vous avez demandé.
   + **Adresse du résumé** : emplacement du dernier bloc dans le journal concerné par le résumé que vous avez demandé. Une adresse comporte les deux champs suivants :
     + `strandId`— L'identifiant unique du volet du journal qui contient le bloc.
     + `sequenceNo`— Le numéro d'index qui indique l'emplacement du bloc dans le fil.
   + **Grand livre** : nom du registre pour lequel vous avez demandé un résumé.
   + **Date** — Horodatage auquel vous avez demandé le résumé.

1. Passez en revue les informations du résumé. Ensuite, choisissez **Save** (Enregistrer). Vous pouvez conserver le nom de fichier par défaut ou en saisir un nouveau.
**Note**  
Vous remarquerez peut-être que les valeurs de hachage de votre résumé et de votre adresse de pourboire changent même si vous ne modifiez aucune donnée de votre registre. *Cela est dû au fait que la console récupère le catalogue système du registre chaque fois que vous exécutez une requête dans l'éditeur partiQL.* Il s'agit d'une transaction de lecture qui est validée dans le journal et entraîne la modification de la dernière adresse de bloc.

   Cette étape enregistre un fichier texte brut dont le contenu est au format [Amazon Ion](ion.md). Le fichier porte l'extension de nom de fichier `.ion.txt` et contient toutes les informations de synthèse répertoriées dans la boîte de dialogue précédente. Voici un exemple du contenu d'un fichier de synthèse. L'ordre des champs peut varier en fonction de votre navigateur.

   ```
   {
     "digest": "42zaJOfV8iGutVGNaIuzQWhD5Xb/5B9lScHnvxPXm9E=",
     "digestTipAddress": "{strandId:\"BlFTjlSXze9BIh1KOszcE3\",sequenceNo:73}",
     "ledger": "my-ledger",
     "date": "2019-04-17T16:57:26.749Z"
   }
   ```

1. Enregistrez ce fichier pour pouvoir y accéder à l'avenir. Plus tard, vous pourrez utiliser ce fichier pour vérifier une révision de document par rapport à.
**Important**  
La révision du document que vous vérifierez ultérieurement doit être couverte par le résumé que vous avez enregistré. En d'autres termes, le numéro de séquence de l'adresse du document doit être inférieur ou égal au numéro de séquence de l'**adresse du Digest tip**.

## API QLDB
<a name="verification.digest.api"></a>

Vous pouvez également demander un résumé à partir de votre registre en utilisant l'API Amazon QLDB avec AWS un SDK ou le. AWS CLI L'API QLDB fournit les opérations suivantes destinées à être utilisées par les programmes d'application :
+ [GetDigest](https://docs.aws.amazon.com/qldb/latest/developerguide/API_GetDigest.html)— Renvoie le résumé d'un registre au dernier bloc validé du journal. La réponse inclut une valeur de hachage de 256 bits et une adresse de bloc.

Pour plus d'informations sur la demande d'un résumé à l'aide de AWS CLI, consultez la commande [get-digest](https://docs.aws.amazon.com/cli/latest/reference/qldb/get-digest.html) dans la référence des *AWS CLI commandes*.

### Exemple d’application
<a name="verification.digest.api.sample"></a>

Pour des exemples de code Java, consultez le GitHub référentiel [amazon-qldb-dmv-sampleaws-samples/](https://github.com/aws-samples/amazon-qldb-dmv-sample-java) -java. Pour obtenir des instructions sur le téléchargement et l'installation de cet exemple d'application, consultez[Installation de l'exemple d'application Java Amazon QLDB](sample-app.java.md). Avant de demander un résumé, assurez-vous de suivre les étapes 1 à 3 [Tutoriel Java](getting-started.java.tutorial.md) pour créer un registre d'échantillons et le charger avec des exemples de données.

Le code du didacticiel présenté en classe [GetDigest](https://github.com/aws-samples/amazon-qldb-dmv-sample-java/blob/master/src/main/java/software/amazon/qldb/tutorial/GetDigest.java)fournit un exemple de demande de résumé à partir du registre `vehicle-registration` d'échantillons.

Pour vérifier la révision d'un document à l'aide du résumé que vous avez enregistré, passez à[Étape 2 : vérification de vos données dans QLDB](verification.verify.md).

# Étape 2 : vérification de vos données dans QLDB
<a name="verification.verify"></a>

**Important**  
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez [Migrer un registre Amazon QLDB vers Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Amazon QLDB fournit une API permettant de demander une preuve pour un identifiant de document spécifié et le bloc associé. Vous devez également fournir l'adresse du conseil d'un résumé que vous avez précédemment enregistré, comme décrit dans[Étape 1 : Demande d'un résumé dans QLDB](verification.digest.md). Vous pouvez utiliser le AWS Management Console, un AWS SDK ou le AWS CLI pour obtenir une preuve.

Vous pouvez ensuite utiliser la preuve renvoyée par QLDB pour vérifier la révision du document par rapport au résumé enregistré, à l'aide d'une API côté client. Cela vous permet de contrôler l'algorithme que vous utilisez pour vérifier vos données.

**Topics**
+ [AWS Management Console](#verification.verify.con)
+ [API QLDB](#verification.verify.api)

## AWS Management Console
<a name="verification.verify.con"></a>

Cette section décrit les étapes permettant de vérifier la révision d'un document par rapport à un résumé enregistré précédemment à l'aide de la console Amazon QLDB.

Avant de commencer, assurez-vous de suivre les étapes décrites dans[Étape 1 : Demande d'un résumé dans QLDB](verification.digest.md). La vérification nécessite un résumé préalablement enregistré qui couvre la révision que vous souhaitez vérifier.

**Pour vérifier la révision d'un document (console)**

1. [Ouvrez la console Amazon QLDB à l'adresse /qldb. https://console.aws.amazon.com](https://console.aws.amazon.com/qldb)

1. Tout d'abord, recherchez dans votre registre `blockAddress` la `id` et la révision que vous souhaitez vérifier. Ces champs sont inclus dans les métadonnées du document, que vous pouvez interroger dans la *vue validée*.

   Le document `id` est une chaîne d'identification unique attribuée par le système. `blockAddress`Il s'agit d'une structure ionique qui indique l'emplacement du bloc où la révision a été validée.

   Dans le volet de navigation, choisissez l'éditeur **PartiQL**.

1. Choisissez le nom du registre sous lequel vous souhaitez vérifier une révision.

1. Dans la fenêtre de l'éditeur de requêtes, entrez une `SELECT` instruction dans la syntaxe suivante, puis choisissez **Exécuter**.

   ```
   SELECT metadata.id, blockAddress FROM _ql_committed_table_name
   WHERE criteria
   ```

   Par exemple, la requête suivante renvoie un document à partir de la `VehicleRegistration` table du registre d'exemple créé dans[Commencer à utiliser la console Amazon QLDB](getting-started.md).

   ```
   SELECT r.metadata.id, r.blockAddress FROM _ql_committed_VehicleRegistration AS r 
   WHERE r.data.VIN = 'KM8SRDHF6EU074761'
   ```

1. Copiez et enregistrez les `blockAddress` valeurs `id` et renvoyées par votre requête. Veillez à omettre les guillemets pour le `id` champ. Dans [Amazon Ion](ion.md), les types de données de chaîne sont délimités par des guillemets doubles. Par exemple, vous devez uniquement copier le texte alphanumérique dans l'extrait de code suivant.

   `"LtMNJYNjSwzBLgf7sLifrG"`

1. Maintenant qu'une révision de document est sélectionnée, vous pouvez commencer le processus de vérification.

   Dans le volet de navigation, choisissez **Verification**.

1. Dans le formulaire **Vérifier le document**, sous **Spécifiez le document que vous souhaitez vérifier**, entrez les paramètres d'entrée suivants :
   + **Ledger** — Le registre dans lequel vous souhaitez vérifier une révision.
   + **Adresse du bloc** : `blockAddress` valeur renvoyée par votre requête à l'étape 4.
   + **ID du document** : `id` valeur renvoyée par votre requête à l'étape 4.

1. Sous **Spécifier le résumé à utiliser pour la vérification**, sélectionnez le résumé que vous avez enregistré précédemment en choisissant **Choisir le résumé**. Si le fichier est valide, tous les champs du résumé sont automatiquement remplis sur votre console. Vous pouvez également copier et coller manuellement les valeurs suivantes directement à partir de votre fichier condensé :
   + **Digest** — La `digest` valeur de votre fichier de résumé.
   + **Adresse du conseil de synthèse** : `digestTipAddress` valeur de votre fichier de résumé.

1. Passez en revue les paramètres d'entrée de votre document et résumez, puis choisissez **Vérifier**.

   La console automatise deux étapes pour vous :

   1. Demandez une preuve à QLDB pour le document que vous avez spécifié.

   1. Utilisez la preuve renvoyée par QLDB pour appeler une API côté client, qui vérifie la révision de votre document par rapport au résumé fourni. Pour examiner cet algorithme de vérification, consultez la section suivante [API QLDB](#verification.verify.api) pour télécharger l'exemple de code.

   La console affiche les résultats de votre demande dans la fiche de **résultats de vérification**. Pour de plus amples informations, veuillez consulter [Résultats de vérification](verification.results.md).

## API QLDB
<a name="verification.verify.api"></a>

Vous pouvez également vérifier la révision d'un document en utilisant l'API Amazon QLDB avec AWS un SDK ou le. AWS CLI L'API QLDB fournit les opérations suivantes destinées aux programmes d'application :
+ `GetDigest`— Renvoie le résumé d'un registre au dernier bloc validé du journal. La réponse inclut une valeur de hachage de 256 bits et une adresse de bloc.
+ `GetBlock`— Renvoie un objet de type bloc à une adresse spécifiée dans un journal. Renvoie également une preuve du bloc spécifié pour vérification si elle `DigestTipAddress` est fournie.
+ `GetRevision`— Renvoie un objet de données de révision pour un ID de document et une adresse de bloc spécifiés. Renvoie également une preuve de la révision spécifiée pour vérification si elle `DigestTipAddress` est fournie.

Pour une description complète de ces opérations d'API, consultez le[Référence de l'API Amazon QLDB](api-reference.md).

Pour plus d'informations sur la vérification des données à l'aide du AWS CLI, consultez la [référence des AWS CLI commandes](https://docs.aws.amazon.com/cli/latest/reference/qldb/index.html).

### Exemple d’application
<a name="verification.verify.api.sample"></a>

Pour des exemples de code Java, consultez le GitHub référentiel [amazon-qldb-dmv-sampleaws-samples/](https://github.com/aws-samples/amazon-qldb-dmv-sample-java) -java. Pour obtenir des instructions sur le téléchargement et l'installation de cet exemple d'application, consultez[Installation de l'exemple d'application Java Amazon QLDB](sample-app.java.md). Avant de procéder à une vérification, assurez-vous de suivre les étapes 1 à 3 [Tutoriel Java](getting-started.java.tutorial.md) pour créer un registre d'échantillons et le charger avec des exemples de données.

Le code du didacticiel présenté en classe [GetRevision](https://github.com/aws-samples/amazon-qldb-dmv-sample-java/blob/master/src/main/java/software/amazon/qldb/tutorial/GetRevision.java)fournit un exemple de demande de preuve pour une révision de document, puis de vérification de cette révision. Cette classe exécute les étapes suivantes :

1. Demande un nouveau résumé à partir du registre `vehicle-registration` d'échantillons.

1. Demande une preuve pour un exemple de révision de document à partir du `VehicleRegistration` tableau du `vehicle-registration` registre.

1. Vérifie la révision de l'échantillon à l'aide du résumé et de la preuve renvoyés.

# Résultats de vérification
<a name="verification.results"></a>

**Important**  
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez [Migrer un registre Amazon QLDB vers Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Cette section décrit les résultats renvoyés par une demande de vérification des données Amazon QLDB sur le. AWS Management Console Pour obtenir des instructions détaillées sur la procédure à suivre pour soumettre une demande de vérification, consultez[Étape 2 : vérification de vos données dans QLDB](verification.verify.md).

**Sur la page de **vérification** de la console QLDB, les résultats de votre demande sont affichés dans la carte des résultats de vérification.** L'onglet **Preuve** affiche le contenu de la preuve renvoyée par QLDB pour la révision et le résumé du document que vous avez spécifiés. Il inclut les informations suivantes :
+ **Hachage de révision** : valeur SHA-256 qui représente de manière unique la révision du document que vous êtes en train de vérifier.
+ **Hashs de preuve** : liste ordonnée des hachages fournie par QLDB et utilisée pour recalculer le condensé spécifié. La console commence par le **hachage de révision et le combine séquentiellement avec chaque hachage** de preuve jusqu'à ce qu'il se termine par un condensé recalculé.

  La liste est réduite par défaut, vous pouvez donc l'étendre pour révéler les valeurs de hachage. Vous pouvez éventuellement essayer vous-même les calculs de hachage en suivant les étapes décrites dans[Utiliser une preuve pour recalculer votre résumé](#verification.results.recalc).
+ **Résumé calculé** : hachage résultant de la série de **calculs de hachage effectués** sur le hachage de **révision**. Si cette valeur correspond à votre **résumé** précédemment enregistré, la vérification est réussie.

L'onglet **Bloc** affiche le contenu du bloc contenant la révision que vous êtes en train de vérifier. Il inclut les informations suivantes :
+ **ID de transaction** — L'identifiant unique de la transaction qui a validé ce bloc.
+ **Heure de la transaction** : date à laquelle ce bloc a été validé dans le volet.
+ **Hachage du bloc** : valeur SHA-256 qui représente de manière unique ce bloc et l'ensemble de son contenu.
+ **Adresse du bloc** : emplacement dans le journal de votre registre où ce blocage a été validé. Une adresse comporte les deux champs suivants :
  + **ID du volet** — L'identifiant unique du volet du journal qui contient ce bloc.
  + **Numéro de séquence** : numéro d'index qui indique l'emplacement de ce bloc dans le brin.
+ **Instructions : instructions** partiQL exécutées pour valider les entrées de ce bloc.
**Note**  
Si vous exécutez des instructions paramétrées par programmation, elles sont enregistrées dans vos blocs de journal avec des paramètres de liaison au lieu des données littérales. Par exemple, vous pouvez voir l'instruction suivante dans un bloc de journal, où le point d'interrogation (`?`) est un espace réservé variable pour le contenu du document.  

  ```
  INSERT INTO Vehicle ?
  ```
+ **Entrées de documents** : révisions du document validées dans ce bloc.

Si votre demande n'a pas réussi à vérifier la révision du document, reportez-vous à la section [Erreurs courantes lors de la vérification](verification.errors.md) pour obtenir des informations sur les causes possibles.

## Utiliser une preuve pour recalculer votre résumé
<a name="verification.results.recalc"></a>

Une fois que QLDB a renvoyé une preuve pour votre demande de vérification de documents, vous pouvez essayer d'effectuer vous-même les calculs de hachage. Cette section décrit les étapes de haut niveau pour recalculer votre résumé à l'aide de la preuve fournie.

Tout d'abord, associez votre **hachage de révision** au premier hachage de la liste des hachages de **preuve**. Procédez ensuite comme suit.

1. Triez les deux hachages. Comparez les hachages en fonction de leurs valeurs d'octets *signées* dans l'ordre little-endian.

1. Concaténez les deux hachages dans l'ordre trié.

1. Hachez la paire concaténée avec un générateur de hachage SHA-256.

1. Associez votre nouveau hachage au hachage suivant de la preuve et répétez les étapes 1 à 3. Une fois que vous avez traité le dernier hachage de preuve, votre nouveau hachage est votre condensé recalculé.

Si votre résumé recalculé correspond à votre résumé enregistré précédemment, votre document est vérifié avec succès.

Pour un step-by-step didacticiel contenant des exemples de code illustrant ces étapes de vérification, passez à[Tutoriel : vérification des données à l'aide d'un AWS SDK](verification.tutorial-block-hash.md).

# Tutoriel : vérification des données à l'aide d'un AWS SDK
<a name="verification.tutorial-block-hash"></a>

**Important**  
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez [Migrer un registre Amazon QLDB vers Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Dans ce didacticiel, vous allez vérifier un hachage de révision de document et un hachage de bloc de journal dans un registre Amazon QLDB à l'aide de l'API QLDB via un SDK. AWS Vous utilisez également le pilote QLDB pour demander la révision du document.

Prenons l'exemple d'une révision de document contenant des données relatives à un véhicule dont le numéro d'identification du véhicule (VIN) est de`KM8SRDHF6EU074761`. La révision du document se trouve dans une `VehicleRegistration` table figurant dans un registre nommé`vehicle-registration`. Supposons que vous souhaitiez vérifier l'intégrité de la révision du document pour ce véhicule et du bloc de journal qui contient la révision.

**Note**  
Pour un article de AWS blog détaillé qui aborde la valeur de la vérification cryptographique dans le contexte d'un cas d'utilisation réaliste, consultez la section Vérification [cryptographique dans le monde réel avec Amazon](https://aws.amazon.com/blogs/database/real-world-cryptographic-verification-with-amazon-qldb/) QLDB.

**Topics**
+ [Prérequis](#verification.tutorial.prereqs)
+ [Étape 1 : demander un résumé](#verification.tutorial.step-1)
+ [Étape 2 : demander la révision du document](#verification.tutorial.step-2)
+ [Étape 3 : demander une preuve pour la révision](#verification.tutorial.step-3)
+ [Étape 4 : Recalculer le résumé à partir de la révision](#verification.tutorial.step-4)
+ [Étape 5 : Demandez une preuve pour le bloc de journal](#verification.tutorial.step-5)
+ [Étape 6 : Recalculer le condensé à partir du bloc](#verification.tutorial.step-6)
+ [Exécutez l'exemple de code complet](#verification.tutorial.full)

## Prérequis
<a name="verification.tutorial.prereqs"></a>

Avant de commencer, assurez-vous d'effectuer les opérations suivantes :

1. Configurez le pilote QLDB pour la langue de votre choix en remplissant les conditions requises ci-dessous. [Commencer à utiliser le pilote Amazon QLDB](getting-started-driver.md) Cela inclut l'inscription AWS, l'octroi d'un accès programmatique pour le développement et la configuration de votre environnement de développement.

1. Suivez les étapes 1 et 2 ci-dessous [Commencer à utiliser la console Amazon QLDB](getting-started.md) pour créer un registre nommé `vehicle-registration` et le charger avec des exemples de données prédéfinis.

Passez ensuite en revue les étapes suivantes pour savoir comment fonctionne la vérification, puis exécutez l'exemple de code complet du début à la fin.

## Étape 1 : demander un résumé
<a name="verification.tutorial.step-1"></a>

Avant de pouvoir vérifier les données, vous devez d'abord demander un résumé à partir de votre registre `vehicle-registration` pour une utilisation ultérieure.

------
#### [ Java ]

```
// Get a digest
GetDigestRequest digestRequest = new GetDigestRequest().withName(ledgerName);
GetDigestResult digestResult = client.getDigest(digestRequest);

java.nio.ByteBuffer digest = digestResult.getDigest();

// expectedDigest is the buffer we will use later to compare against our calculated digest
byte[] expectedDigest = new byte[digest.remaining()];
digest.get(expectedDigest);
```

------
#### [ .NET ]

```
// Get a digest
GetDigestRequest getDigestRequest = new GetDigestRequest
{
    Name = ledgerName
};
GetDigestResponse getDigestResponse = client.GetDigestAsync(getDigestRequest).Result;

// expectedDigest is the buffer we will use later to compare against our calculated digest
MemoryStream digest = getDigestResponse.Digest;
byte[] expectedDigest = digest.ToArray();
```

------
#### [ Go ]

```
// Get a digest
currentLedgerName := ledgerName
input := qldb.GetDigestInput{Name: &currentLedgerName}
digestOutput, err := client.GetDigest(&input)
if err != nil {
    panic(err)
}

// expectedDigest is the buffer we will later use to compare against our calculated digest
expectedDigest := digestOutput.Digest
```

------
#### [ Node.js ]

```
// Get a digest
const getDigestRequest: GetDigestRequest = {
    Name: ledgerName
};
const getDigestResponse: GetDigestResponse = await qldbClient.getDigest(getDigestRequest).promise();

// expectedDigest is the buffer we will later use to compare against our calculated digest
const expectedDigest: Uint8Array = <Uint8Array>getDigestResponse.Digest;
```

------
#### [ Python ]

```
# Get a digest
get_digest_response = qldb_client.get_digest(Name=ledger_name)

# expected_digest is the buffer we will later use to compare against our calculated digest
expected_digest = get_digest_response.get('Digest')
digest_tip_address = get_digest_response.get('DigestTipAddress')
```

------

## Étape 2 : demander la révision du document
<a name="verification.tutorial.step-2"></a>

Utilisez le pilote QLDB pour interroger les adresses de bloc, les hachages et les IDs documents associés au VIN. `KM8SRDHF6EU074761`

------
#### [ Java ]

```
// Retrieve info for the given vin's document revisions
Result result = driver.execute(txn -> {
    final String query = String.format("SELECT blockAddress, hash, metadata.id FROM _ql_committed_%s WHERE data.VIN = '%s'", tableName, vin);
    return txn.execute(query);
});
```

------
#### [ .NET ]

```
// Retrieve info for the given vin's document revisions
var result = driver.Execute(txn => {
    string query = $"SELECT blockAddress, hash, metadata.id FROM _ql_committed_{tableName} WHERE data.VIN = '{vin}'";
    return txn.Execute(query);
});
```

------
#### [ Go ]

```
// Retrieve info for the given vin's document revisions
result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
    statement := fmt.Sprintf(
            "SELECT blockAddress, hash, metadata.id FROM _ql_committed_%s WHERE data.VIN = '%s'",
            tableName,
            vin)
    result, err := txn.Execute(statement)
    if err != nil {
        return nil, err
    }

    results := make([]map[string]interface{}, 0)

    // Convert the result set into a map
    for result.Next(txn) {
        var doc map[string]interface{}
        err := ion.Unmarshal(result.GetCurrentData(), &doc)
        if err != nil {
            return nil, err
        }
        results = append(results, doc)
    }
    return results, nil
})
if err != nil {
    panic(err)
}
resultSlice := result.([]map[string]interface{})
```

------
#### [ Node.js ]

```
const result: dom.Value[] = await driver.executeLambda(async (txn: TransactionExecutor): Promise<dom.Value[]> => {
    const query: string = `SELECT blockAddress, hash, metadata.id FROM _ql_committed_${tableName} WHERE data.VIN = '${vin}'`;
    const queryResult: Result = await txn.execute(query);
    return queryResult.getResultList();
});
```

------
#### [ Python ]

```
def query_doc_revision(txn):
    query = "SELECT blockAddress, hash, metadata.id FROM _ql_committed_{} WHERE data.VIN = '{}'".format(table_name, vin)
    return txn.execute_statement(query)

# Retrieve info for the given vin's document revisions
result = qldb_driver.execute_lambda(query_doc_revision)
```

------

## Étape 3 : demander une preuve pour la révision
<a name="verification.tutorial.step-3"></a>

Parcourez les résultats de la requête et utilisez chaque adresse de bloc et chaque identifiant de document ainsi que le nom du grand livre pour soumettre une `GetRevision` demande. *Pour obtenir une preuve de la révision, vous devez également fournir l'adresse du pourboire figurant dans le résumé enregistré précédemment.* Cette opération d'API renvoie un objet qui inclut la révision du document et la preuve de la révision.

Pour plus d'informations sur la structure de révision et son contenu, consultez[Interrogation des métadonnées d'un document](working.metadata.md).

------
#### [ Java ]

```
for (IonValue ionValue : result) {
    IonStruct ionStruct = (IonStruct)ionValue;

    // Get the requested fields
    IonValue blockAddress = ionStruct.get("blockAddress");
    IonBlob hash = (IonBlob)ionStruct.get("hash");
    String metadataId = ((IonString)ionStruct.get("id")).stringValue();

    System.out.printf("Verifying document revision for id '%s'%n", metadataId);

    String blockAddressText = blockAddress.toString();

    // Submit a request for the revision
    GetRevisionRequest revisionRequest = new GetRevisionRequest()
            .withName(ledgerName)
            .withBlockAddress(new ValueHolder().withIonText(blockAddressText))
            .withDocumentId(metadataId)
            .withDigestTipAddress(digestResult.getDigestTipAddress());

    // Get a result back
    GetRevisionResult revisionResult = client.getRevision(revisionRequest);

    ...
}
```

------
#### [ .NET ]

```
foreach (IIonValue ionValue in result)
{
    IIonStruct ionStruct = ionValue;

    // Get the requested fields
    IIonValue blockAddress = ionStruct.GetField("blockAddress");
    IIonBlob hash = ionStruct.GetField("hash");
    String metadataId = ionStruct.GetField("id").StringValue;

    Console.WriteLine($"Verifying document revision for id '{metadataId}'");

    // Use an Ion Reader to convert block address to text
    IIonReader reader = IonReaderBuilder.Build(blockAddress);
    StringWriter sw = new StringWriter();
    IIonWriter textWriter = IonTextWriterBuilder.Build(sw);
    textWriter.WriteValues(reader);
    string blockAddressText = sw.ToString();

    // Submit a request for the revision
    GetRevisionRequest revisionRequest = new GetRevisionRequest
    {
        Name = ledgerName,
        BlockAddress = new ValueHolder
        {
            IonText = blockAddressText
        },
        DocumentId = metadataId,
        DigestTipAddress = getDigestResponse.DigestTipAddress
    };
    
    // Get a response back
    GetRevisionResponse revisionResponse = client.GetRevisionAsync(revisionRequest).Result;

    ...
}
```

------
#### [ Go ]

```
for _, value := range resultSlice {
    // Get the requested fields
    ionBlockAddress, err := ion.MarshalText(value["blockAddress"])
    if err != nil {
        panic(err)
    }
    blockAddress := string(ionBlockAddress)
    metadataId := value["id"].(string)
    documentHash := value["hash"].([]byte)

    fmt.Printf("Verifying document revision for id '%s'\n", metadataId)

    // Submit a request for the revision
    revisionInput := qldb.GetRevisionInput{
        BlockAddress:     &qldb.ValueHolder{IonText: &blockAddress},
        DigestTipAddress: digestOutput.DigestTipAddress,
        DocumentId:       &metadataId,
        Name:             &currentLedgerName,
    }

    // Get a result back
    revisionOutput, err := client.GetRevision(&revisionInput)
    if err != nil {
        panic(err)
    }

    ...
}
```

------
#### [ Node.js ]

```
for (let value of result) {
    // Get the requested fields
    const blockAddress: dom.Value = value.get("blockAddress");
    const hash: dom.Value = value.get("hash");
    const metadataId: string = value.get("id").stringValue();

    console.log(`Verifying document revision for id '${metadataId}'`);

    // Submit a request for the revision
    const revisionRequest: GetRevisionRequest = {
        Name: ledgerName,
        BlockAddress: {
            IonText: dumpText(blockAddress)
        },
        DocumentId: metadataId,
        DigestTipAddress: getDigestResponse.DigestTipAddress
    };

    // Get a response back
    const revisionResponse: GetRevisionResponse = await qldbClient.getRevision(revisionRequest).promise();

    ...
}
```

------
#### [ Python ]

```
for value in result:
    # Get the requested fields
    block_address = value['blockAddress']
    document_hash = value['hash']
    metadata_id = value['id']

    print("Verifying document revision for id '{}'".format(metadata_id))

    # Submit a request for the revision and get a result back
    proof_response = qldb_client.get_revision(Name=ledger_name,
                                              BlockAddress=block_address_to_dictionary(block_address),
                                              DocumentId=metadata_id,
                                              DigestTipAddress=digest_tip_address)
```

------

Ensuite, récupérez la preuve de la révision demandée.

L'API QLDB renvoie la preuve sous forme de chaîne de la liste ordonnée des hachages de nœuds. Pour convertir cette chaîne en une liste de la représentation binaire des hachages de nœuds, vous pouvez utiliser un lecteur Ion de la bibliothèque Amazon Ion. Pour plus d'informations sur l'utilisation de la bibliothèque Ion, consultez le [livre de recettes Amazon Ion](http://amzn.github.io/ion-docs/guides/cookbook.html#reading-and-writing-ion-data).

------
#### [ Java ]

Dans cet exemple, vous utilisez `IonReader` pour effectuer la conversion binaire.

```
String proofText = revisionResult.getProof().getIonText();

// Take the proof and convert it to a list of byte arrays
List<byte[]> internalHashes = new ArrayList<>();
IonReader reader = SYSTEM.newReader(proofText);
reader.next();
reader.stepIn();
while (reader.next() != null) {
    internalHashes.add(reader.newBytes());
}
```

------
#### [ .NET ]

Dans cet exemple, vous l'utilisez `IonLoader` pour charger la preuve dans un datagramme ionique.

```
string proofText = revisionResponse.Proof.IonText;
IIonDatagram proofValue = IonLoader.Default.Load(proofText);
```

------
#### [ Go ]

Dans cet exemple, vous utilisez un lecteur Ion pour convertir la preuve en binaire et pour parcourir la liste des hachages de nœuds de la preuve.

```
proofText := revisionOutput.Proof.IonText

// Use ion.Reader to iterate over the proof's node hashes
reader := ion.NewReaderString(*proofText)
// Enter the struct containing node hashes
reader.Next()
if err := reader.StepIn(); err != nil {
    panic(err)
}
```

------
#### [ Node.js ]

Dans cet exemple, vous utilisez la `load` fonction pour effectuer la conversion binaire.

```
let proofValue: dom.Value = load(revisionResponse.Proof.IonText);
```

------
#### [ Python ]

Dans cet exemple, vous utilisez la `loads` fonction pour effectuer la conversion binaire.

```
proof_text = proof_response.get('Proof').get('IonText')
proof_hashes = loads(proof_text)
```

------

## Étape 4 : Recalculer le résumé à partir de la révision
<a name="verification.tutorial.step-4"></a>

Utilisez la liste des hachages de la preuve pour recalculer le résumé, en commençant par le hachage de révision. Tant que le résumé précédemment enregistré est connu et fiable en dehors de QLDB, l'intégrité de la révision du document est prouvée si le hachage du résumé recalculé correspond au hachage du résumé enregistré.

------
#### [ Java ]

```
// Calculate digest
byte[] calculatedDigest = internalHashes.stream().reduce(hash.getBytes(), BlockHashVerification::dot);

boolean verified = Arrays.equals(expectedDigest, calculatedDigest);

if (verified) {
    System.out.printf("Successfully verified document revision for id '%s'!%n", metadataId);
} else {
    System.out.printf("Document revision for id '%s' verification failed!%n", metadataId);
    return;
}
```

------
#### [ .NET ]

```
byte[] documentHash = hash.Bytes().ToArray();
foreach (IIonValue proofHash in proofValue.GetElementAt(0))
{
    // Calculate the digest
    documentHash = Dot(documentHash, proofHash.Bytes().ToArray());
}

bool verified = expectedDigest.SequenceEqual(documentHash);

if (verified)
{
    Console.WriteLine($"Successfully verified document revision for id '{metadataId}'!");
}
else
{
    Console.WriteLine($"Document revision for id '{metadataId}' verification failed!");
    return;
}
```

------
#### [ Go ]

```
// Going through nodes and calculate digest
for reader.Next() {
    val, _ := reader.ByteValue()
    documentHash, err = dot(documentHash, val)
}

// Compare documentHash with the expected digest
verified := reflect.DeepEqual(documentHash, expectedDigest)

if verified {
    fmt.Printf("Successfully verified document revision for id '%s'!\n", metadataId)
} else {
    fmt.Printf("Document revision for id '%s' verification failed!\n", metadataId)
    return
}
```

------
#### [ Node.js ]

```
let documentHash: Uint8Array = hash.uInt8ArrayValue();
proofValue.elements().forEach((proofHash: dom.Value) => {
    // Calculate the digest
    documentHash = dot(documentHash, proofHash.uInt8ArrayValue());
});

let verified: boolean = isEqual(expectedDigest, documentHash);

if (verified) {
    console.log(`Successfully verified document revision for id '${metadataId}'!`);
} else {
    console.log(`Document revision for id '${metadataId}' verification failed!`);
    return;
}
```

------
#### [ Python ]

```
# Calculate digest
calculated_digest = reduce(dot, proof_hashes, document_hash)

verified = calculated_digest == expected_digest
if verified:
    print("Successfully verified document revision for id '{}'!".format(metadata_id))
else:
    print("Document revision for id '{}' verification failed!".format(metadata_id))
```

------

## Étape 5 : Demandez une preuve pour le bloc de journal
<a name="verification.tutorial.step-5"></a>

Ensuite, vous devez vérifier le bloc de journal qui contient la révision du document.

Utilisez l'adresse de blocage et l'adresse du pourboire figurant dans le résumé que vous avez enregistré à [l'étape 1](#verification.tutorial.step-1) pour soumettre une `GetBlock` demande. Comme pour la `GetRevision` demande de l'[étape 2](#verification.tutorial.step-2), *vous devez à nouveau fournir l'adresse du pourboire figurant dans le résumé enregistré pour obtenir une preuve du blocage.* Cette opération d'API renvoie un objet qui inclut le bloc et la preuve du bloc.

Pour plus d'informations sur la structure des blocs de journal et son contenu, consultez[Contenu du journal dans Amazon QLDB](journal-contents.md).

------
#### [ Java ]

```
// Submit a request for the block
GetBlockRequest getBlockRequest = new GetBlockRequest()
        .withName(ledgerName)
        .withBlockAddress(new ValueHolder().withIonText(blockAddressText))
        .withDigestTipAddress(digestResult.getDigestTipAddress());

// Get a result back
GetBlockResult getBlockResult = client.getBlock(getBlockRequest);
```

------
#### [ .NET ]

```
// Submit a request for the block
GetBlockRequest getBlockRequest = new GetBlockRequest
{
    Name = ledgerName,
    BlockAddress = new ValueHolder
    {
        IonText = blockAddressText
    },
    DigestTipAddress = getDigestResponse.DigestTipAddress
};

// Get a response back
GetBlockResponse getBlockResponse = client.GetBlockAsync(getBlockRequest).Result;
```

------
#### [ Go ]

```
// Submit a request for the block
blockInput := qldb.GetBlockInput{
    Name:             &currentLedgerName,
    BlockAddress:     &qldb.ValueHolder{IonText: &blockAddress},
    DigestTipAddress: digestOutput.DigestTipAddress,
}

// Get a result back
blockOutput, err := client.GetBlock(&blockInput)
if err != nil {
    panic(err)
}
```

------
#### [ Node.js ]

```
// Submit a request for the block
const getBlockRequest: GetBlockRequest = {
    Name: ledgerName,
    BlockAddress: {
        IonText: dumpText(blockAddress)
    },
    DigestTipAddress: getDigestResponse.DigestTipAddress
};

// Get a response back
const getBlockResponse: GetBlockResponse = await qldbClient.getBlock(getBlockRequest).promise();
```

------
#### [ Python ]

```
def block_address_to_dictionary(ion_dict):
"""
Convert a block address from IonPyDict into a dictionary.
Shape of the dictionary must be: {'IonText': "{strandId: <"strandId">, sequenceNo: <sequenceNo>}"}

:type ion_dict: :py:class:`amazon.ion.simple_types.IonPyDict`/str
:param ion_dict: The block address value to convert.

:rtype: dict
:return: The converted dict.
"""
block_address = {'IonText': {}}
if not isinstance(ion_dict, str):
    py_dict = '{{strandId: "{}", sequenceNo:{}}}'.format(ion_dict['strandId'], ion_dict['sequenceNo'])
    ion_dict = py_dict
block_address['IonText'] = ion_dict
return block_address

# Submit a request for the block and get a result back
block_response = qldb_client.get_block(Name=ledger_name, BlockAddress=block_address_to_dictionary(block_address),
                                       DigestTipAddress=digest_tip_address)
```

------

Ensuite, récupérez le hachage du bloc et la preuve à partir du résultat.

------
#### [ Java ]

Dans cet exemple, vous l'utilisez `IonLoader` pour charger l'objet du bloc dans un `IonDatagram` conteneur.

```
String blockText = getBlockResult.getBlock().getIonText();

IonDatagram datagram = SYSTEM.getLoader().load(blockText);
ionStruct = (IonStruct)datagram.get(0);

final byte[] blockHash = ((IonBlob)ionStruct.get("blockHash")).getBytes();
```

Vous pouvez également `IonLoader` charger la preuve dans un`IonDatagram`.

```
proofText = getBlockResult.getProof().getIonText();

// Take the proof and create a list of hash binary data
datagram = SYSTEM.getLoader().load(proofText);
ListIterator<IonValue> listIter = ((IonList)datagram.iterator().next()).listIterator();

internalHashes.clear();
while (listIter.hasNext()) {
    internalHashes.add(((IonBlob)listIter.next()).getBytes());
}
```

------
#### [ .NET ]

Dans cet exemple, vous pouvez `IonLoader` charger le bloc et la preuve dans un datagramme ionique pour chacun d'eux.

```
string blockText = getBlockResponse.Block.IonText;
IIonDatagram blockValue = IonLoader.Default.Load(blockText);

// blockValue is a IonDatagram, and the first value is an IonStruct containing the blockHash
byte[] blockHash = blockValue.GetElementAt(0).GetField("blockHash").Bytes().ToArray();

proofText = getBlockResponse.Proof.IonText;
proofValue = IonLoader.Default.Load(proofText);
```

------
#### [ Go ]

Dans cet exemple, vous utilisez un lecteur Ion pour convertir la preuve en binaire et pour parcourir la liste des hachages de nœuds de la preuve.

```
proofText = blockOutput.Proof.IonText

block := new(map[string]interface{})
err = ion.UnmarshalString(*blockOutput.Block.IonText, block)
if err != nil {
    panic(err)
}

blockHash := (*block)["blockHash"].([]byte)

// Use ion.Reader to iterate over the proof's node hashes
reader = ion.NewReaderString(*proofText)
// Enter the struct containing node hashes
reader.Next()
if err := reader.StepIn(); err != nil {
    panic(err)
}
```

------
#### [ Node.js ]

Dans cet exemple, vous utilisez la `load` fonction pour convertir le bloc et la preuve en binaire.

```
const blockValue: dom.Value = load(getBlockResponse.Block.IonText)
let blockHash: Uint8Array = blockValue.get("blockHash").uInt8ArrayValue();

proofValue = load(getBlockResponse.Proof.IonText);
```

------
#### [ Python ]

Dans cet exemple, vous utilisez la `loads` fonction pour convertir le bloc et la preuve en binaire.

```
block_text = block_response.get('Block').get('IonText')
block = loads(block_text)

block_hash = block.get('blockHash')

proof_text = block_response.get('Proof').get('IonText')
proof_hashes = loads(proof_text)
```

------

## Étape 6 : Recalculer le condensé à partir du bloc
<a name="verification.tutorial.step-6"></a>

Utilisez la liste des hachages de la preuve pour recalculer le condensé, en commençant par le hachage du bloc. Tant que le condensé précédemment enregistré est connu et fiable en dehors de QLDB, l'intégrité du bloc est prouvée si le hachage du condensé recalculé correspond au hachage du condensé enregistré.

------
#### [ Java ]

```
// Calculate digest
calculatedDigest = internalHashes.stream().reduce(blockHash, BlockHashVerification::dot);

verified = Arrays.equals(expectedDigest, calculatedDigest);

if (verified) {
    System.out.printf("Block address '%s' successfully verified!%n", blockAddressText);
} else {
    System.out.printf("Block address '%s' verification failed!%n", blockAddressText);
}
```

------
#### [ .NET ]

```
foreach (IIonValue proofHash in proofValue.GetElementAt(0))
{
    // Calculate the digest
    blockHash = Dot(blockHash, proofHash.Bytes().ToArray());
}

verified = expectedDigest.SequenceEqual(blockHash);

if (verified)
{
    Console.WriteLine($"Block address '{blockAddressText}' successfully verified!");
}
else
{
    Console.WriteLine($"Block address '{blockAddressText}' verification failed!");
}
```

------
#### [ Go ]

```
// Going through nodes and calculate digest
for reader.Next() {
    val, err := reader.ByteValue()
    if err != nil {
        panic(err)
    }
    blockHash, err = dot(blockHash, val)
}

// Compare blockHash with the expected digest
verified = reflect.DeepEqual(blockHash, expectedDigest)

if verified {
    fmt.Printf("Block address '%s' successfully verified!\n", blockAddress)
} else {
    fmt.Printf("Block address '%s' verification failed!\n", blockAddress)
    return
}
```

------
#### [ Node.js ]

```
proofValue.elements().forEach((proofHash: dom.Value) => {
    // Calculate the digest
    blockHash = dot(blockHash, proofHash.uInt8ArrayValue());
});

verified = isEqual(expectedDigest, blockHash);

if (verified) {
    console.log(`Block address '${dumpText(blockAddress)}' successfully verified!`);
} else {
    console.log(`Block address '${dumpText(blockAddress)}' verification failed!`);
}
```

------
#### [ Python ]

```
# Calculate digest
calculated_digest = reduce(dot, proof_hashes, block_hash)

verified = calculated_digest == expected_digest
if verified:
    print("Block address '{}' successfully verified!".format(dumps(block_address,
                                                                   binary=False,
                                                                   omit_version_marker=True)))
else:
    print("Block address '{}' verification failed!".format(block_address))
```

------

Les exemples de code précédents utilisent la `dot` fonction suivante lors du recalcul du condensé. Cette fonction prend une entrée de deux hachages, les trie, les concatène, puis renvoie le hachage du tableau concaténé.

------
#### [ Java ]

```
/**
 * Takes two hashes, sorts them, concatenates them, and then returns the
 * hash of the concatenated array.
 *
 * @param h1
 *              Byte array containing one of the hashes to compare.
 * @param h2
 *              Byte array containing one of the hashes to compare.
 * @return the concatenated array of hashes.
 */
public static byte[] dot(final byte[] h1, final byte[] h2) {
    if (h1.length != HASH_LENGTH || h2.length != HASH_LENGTH) {
        throw new IllegalArgumentException("Invalid hash.");
    }

    int byteEqual = 0;
    for (int i = h1.length - 1; i >= 0; i--) {
        byteEqual = Byte.compare(h1[i], h2[i]);
        if (byteEqual != 0) {
            break;
        }
    }

    byte[] concatenated = new byte[h1.length + h2.length];
    if (byteEqual < 0) {
        System.arraycopy(h1, 0, concatenated, 0, h1.length);
        System.arraycopy(h2, 0, concatenated, h1.length, h2.length);
    } else {
        System.arraycopy(h2, 0, concatenated, 0, h2.length);
        System.arraycopy(h1, 0, concatenated, h2.length, h1.length);
    }

    MessageDigest messageDigest;
    try {
        messageDigest = MessageDigest.getInstance("SHA-256");
    } catch (NoSuchAlgorithmException e) {
        throw new IllegalStateException("SHA-256 message digest is unavailable", e);
    }

    messageDigest.update(concatenated);
    return messageDigest.digest();
}
```

------
#### [ .NET ]

```
/// <summary>
/// Takes two hashes, sorts them, concatenates them, and then returns the
/// hash of the concatenated array.
/// </summary>
/// <param name="h1">Byte array containing one of the hashes to compare.</param>
/// <param name="h2">Byte array containing one of the hashes to compare.</param>
/// <returns>The concatenated array of hashes.</returns>
private static byte[] Dot(byte[] h1, byte[] h2)
{
    if (h1.Length == 0)
    {
        return h2;
    }

    if (h2.Length == 0)
    {
        return h1;
    }

    HashAlgorithm hashAlgorithm = HashAlgorithm.Create("SHA256");
    HashComparer comparer = new HashComparer();
    if (comparer.Compare(h1, h2) < 0)
    {
        return hashAlgorithm.ComputeHash(h1.Concat(h2).ToArray());
    }
    else
    {
        return hashAlgorithm.ComputeHash(h2.Concat(h1).ToArray());
    }
}

private class HashComparer : IComparer<byte[]>
{
    private static readonly int HASH_LENGTH = 32;

    public int Compare(byte[] h1, byte[] h2)
    {
        if (h1.Length != HASH_LENGTH || h2.Length != HASH_LENGTH)
        {
            throw new ArgumentException("Invalid hash");
        }

        for (var i = h1.Length - 1; i >= 0; i--)
        {
            var byteEqual = (sbyte)h1[i] - (sbyte)h2[i];
            if (byteEqual != 0)
            {
                return byteEqual;
            }
        }

        return 0;
    }
}
```

------
#### [ Go ]

```
// Takes two hashes, sorts them, concatenates them, and then returns the hash of the concatenated array.
func dot(h1, h2 []byte) ([]byte, error) {
    compare, err := hashComparator(h1, h2)
    if err != nil {
        return nil, err
    }

    var concatenated []byte
    if compare < 0 {
        concatenated = append(h1, h2...)
    } else {
        concatenated = append(h2, h1...)
    }

    newHash := sha256.Sum256(concatenated)
    return newHash[:], nil
}

func hashComparator(h1 []byte, h2 []byte) (int16, error) {
    if len(h1) != hashLength || len(h2) != hashLength {
        return 0, errors.New("invalid hash")
    }
    for i := range h1 {
        // Reverse index for little endianness
        index := hashLength - 1 - i

        // Handle byte being unsigned and overflow
        h1Int := int16(h1[index])
        h2Int := int16(h2[index])
        if h1Int > 127 {
            h1Int = 0 - (256 - h1Int)
        }
        if h2Int > 127 {
            h2Int = 0 - (256 - h2Int)
        }

        difference := h1Int - h2Int
        if difference != 0 {
            return difference, nil
        }
    }
    return 0, nil
}
```

------
#### [ Node.js ]

```
/**
 * Takes two hashes, sorts them, concatenates them, and calculates a digest based on the concatenated hash.
 * @param h1 Byte array containing one of the hashes to compare.
 * @param h2 Byte array containing one of the hashes to compare.
 * @returns The digest calculated from the concatenated hash values.
 */
function dot(h1: Uint8Array, h2: Uint8Array): Uint8Array {
    if (h1.length === 0) {
        return h2;
    }
    if (h2.length === 0) {
        return h1;
    }

    const newHashLib = createHash("sha256");

    let concatenated: Uint8Array;
    if (hashComparator(h1, h2) < 0) {
        concatenated = concatenate(h1, h2);
    } else {
        concatenated = concatenate(h2, h1);
    }
    newHashLib.update(concatenated);
    return newHashLib.digest();
}

/**
 * Compares two hashes by their **signed** byte values in little-endian order.
 * @param hash1 The hash value to compare.
 * @param hash2 The hash value to compare.
 * @returns Zero if the hash values are equal, otherwise return the difference of the first pair of non-matching
 *          bytes.
 * @throws RangeError When the hash is not the correct hash size.
 */
function hashComparator(hash1: Uint8Array, hash2: Uint8Array): number {
    if (hash1.length !== HASH_SIZE || hash2.length !== HASH_SIZE) {
        throw new RangeError("Invalid hash.");
    }
    for (let i = hash1.length-1; i >= 0; i--) {
        const difference: number = (hash1[i]<<24 >>24) - (hash2[i]<<24 >>24);
        if (difference !== 0) {
            return difference;
        }
    }
    return 0;
}

/**
 * Helper method that concatenates two Uint8Array.
 * @param arrays List of arrays to concatenate, in the order provided.
 * @returns The concatenated array.
 */
function concatenate(...arrays: Uint8Array[]): Uint8Array {
    let totalLength = 0;
    for (const arr of arrays) {
        totalLength += arr.length;
    }
    const result = new Uint8Array(totalLength);
    let offset = 0;
    for (const arr of arrays) {
        result.set(arr, offset);
        offset += arr.length;
    }
    return result;
}

/**
 * Helper method that checks for equality between two Uint8Array.
 * @param expected Byte array containing one of the hashes to compare.
 * @param actual Byte array containing one of the hashes to compare.
 * @returns Boolean indicating equality between the two Uint8Array.
 */
function isEqual(expected: Uint8Array, actual: Uint8Array): boolean {
    if (expected === actual) return true;
    if (expected == null || actual == null) return false;
    if (expected.length !== actual.length) return false;

    for (let i = 0; i < expected.length; i++) {
        if (expected[i] !== actual[i]) {
            return false;
        }
    }
    return true;
}
```

------
#### [ Python ]

```
def dot(hash1, hash2):
    """
    Takes two hashes, sorts them, concatenates them, and then returns the
    hash of the concatenated array.

    :type hash1: bytes
    :param hash1: The hash value to compare.

    :type hash2: bytes
    :param hash2: The hash value to compare.

    :rtype: bytes
    :return: The new hash value generated from concatenated hash values.
    """
    if len(hash1) != hash_length or len(hash2) != hash_length:
        raise ValueError('Illegal hash.')

    hash_array1 = array('b', hash1)
    hash_array2 = array('b', hash2)

    difference = 0
    for i in range(len(hash_array1) - 1, -1, -1):
        difference = hash_array1[i] - hash_array2[i]
        if difference != 0:
            break

    if difference < 0:
        concatenated = hash1 + hash2
    else:
        concatenated = hash2 + hash1

    new_hash_lib = sha256()
    new_hash_lib.update(concatenated)
    new_digest = new_hash_lib.digest()
    return new_digest
```

------

## Exécutez l'exemple de code complet
<a name="verification.tutorial.full"></a>

Exécutez l'exemple de code complet comme suit pour effectuer toutes les étapes précédentes du début à la fin.

------
#### [ Java ]

```
import com.amazon.ion.IonBlob;
import com.amazon.ion.IonDatagram;
import com.amazon.ion.IonList;
import com.amazon.ion.IonReader;
import com.amazon.ion.IonString;
import com.amazon.ion.IonStruct;
import com.amazon.ion.IonSystem;
import com.amazon.ion.IonValue;
import com.amazon.ion.system.IonSystemBuilder;
import com.amazonaws.services.qldb.AmazonQLDB;
import com.amazonaws.services.qldb.AmazonQLDBClientBuilder;
import com.amazonaws.services.qldb.model.GetBlockRequest;
import com.amazonaws.services.qldb.model.GetBlockResult;
import com.amazonaws.services.qldb.model.GetDigestRequest;
import com.amazonaws.services.qldb.model.GetDigestResult;
import com.amazonaws.services.qldb.model.GetRevisionRequest;
import com.amazonaws.services.qldb.model.GetRevisionResult;
import com.amazonaws.services.qldb.model.ValueHolder;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ListIterator;

import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.qldbsession.QldbSessionClient;
import software.amazon.awssdk.services.qldbsession.QldbSessionClientBuilder;
import software.amazon.qldb.QldbDriver;
import software.amazon.qldb.Result;

public class BlockHashVerification {
    private static final IonSystem SYSTEM = IonSystemBuilder.standard().build();
    private static final QldbDriver driver = createQldbDriver();
    private static final AmazonQLDB client = AmazonQLDBClientBuilder.standard().build();
    private static final String region = "us-east-1";
    private static final String ledgerName = "vehicle-registration";
    private static final String tableName = "VehicleRegistration";
    private static final String vin = "KM8SRDHF6EU074761";
    private static final int HASH_LENGTH = 32;

    /**
     * Create a pooled driver for creating sessions.
     *
     * @return The pooled driver for creating sessions.
     */
    public static QldbDriver createQldbDriver() {
        QldbSessionClientBuilder sessionClientBuilder = QldbSessionClient.builder();
        sessionClientBuilder.region(Region.of(region));

        return QldbDriver.builder()
                .ledger(ledgerName)
                .sessionClientBuilder(sessionClientBuilder)
                .build();
    }

    /**
     * Takes two hashes, sorts them, concatenates them, and then returns the
     * hash of the concatenated array.
     *
     * @param h1
     *              Byte array containing one of the hashes to compare.
     * @param h2
     *              Byte array containing one of the hashes to compare.
     * @return the concatenated array of hashes.
     */
    public static byte[] dot(final byte[] h1, final byte[] h2) {
        if (h1.length != HASH_LENGTH || h2.length != HASH_LENGTH) {
            throw new IllegalArgumentException("Invalid hash.");
        }

        int byteEqual = 0;
        for (int i = h1.length - 1; i >= 0; i--) {
            byteEqual = Byte.compare(h1[i], h2[i]);
            if (byteEqual != 0) {
                break;
            }
        }

        byte[] concatenated = new byte[h1.length + h2.length];
        if (byteEqual < 0) {
            System.arraycopy(h1, 0, concatenated, 0, h1.length);
            System.arraycopy(h2, 0, concatenated, h1.length, h2.length);
        } else {
            System.arraycopy(h2, 0, concatenated, 0, h2.length);
            System.arraycopy(h1, 0, concatenated, h2.length, h1.length);
        }

        MessageDigest messageDigest;
        try {
            messageDigest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            throw new IllegalStateException("SHA-256 message digest is unavailable", e);
        }

        messageDigest.update(concatenated);
        return messageDigest.digest();
    }

    public static void main(String[] args) {
        // Get a digest
        GetDigestRequest digestRequest = new GetDigestRequest().withName(ledgerName);
        GetDigestResult digestResult = client.getDigest(digestRequest);

        java.nio.ByteBuffer digest = digestResult.getDigest();

        // expectedDigest is the buffer we will use later to compare against our calculated digest
        byte[] expectedDigest = new byte[digest.remaining()];
        digest.get(expectedDigest);

        // Retrieve info for the given vin's document revisions
        Result result = driver.execute(txn -> {
            final String query = String.format("SELECT blockAddress, hash, metadata.id FROM _ql_committed_%s WHERE data.VIN = '%s'", tableName, vin);
            return txn.execute(query);
        });

        System.out.printf("Verifying document revisions for vin '%s' in table '%s' in ledger '%s'%n", vin, tableName, ledgerName);

        for (IonValue ionValue : result) {
            IonStruct ionStruct = (IonStruct)ionValue;

            // Get the requested fields
            IonValue blockAddress = ionStruct.get("blockAddress");
            IonBlob hash = (IonBlob)ionStruct.get("hash");
            String metadataId = ((IonString)ionStruct.get("id")).stringValue();

            System.out.printf("Verifying document revision for id '%s'%n", metadataId);

            String blockAddressText = blockAddress.toString();

            // Submit a request for the revision
            GetRevisionRequest revisionRequest = new GetRevisionRequest()
                    .withName(ledgerName)
                    .withBlockAddress(new ValueHolder().withIonText(blockAddressText))
                    .withDocumentId(metadataId)
                    .withDigestTipAddress(digestResult.getDigestTipAddress());

            // Get a result back
            GetRevisionResult revisionResult = client.getRevision(revisionRequest);

            String proofText = revisionResult.getProof().getIonText();

            // Take the proof and convert it to a list of byte arrays
            List<byte[]> internalHashes = new ArrayList<>();
            IonReader reader = SYSTEM.newReader(proofText);
            reader.next();
            reader.stepIn();
            while (reader.next() != null) {
                internalHashes.add(reader.newBytes());
            }

            // Calculate digest
            byte[] calculatedDigest = internalHashes.stream().reduce(hash.getBytes(), BlockHashVerification::dot);

            boolean verified = Arrays.equals(expectedDigest, calculatedDigest);

            if (verified) {
                System.out.printf("Successfully verified document revision for id '%s'!%n", metadataId);
            } else {
                System.out.printf("Document revision for id '%s' verification failed!%n", metadataId);
                return;
            }

            // Submit a request for the block
            GetBlockRequest getBlockRequest = new GetBlockRequest()
                    .withName(ledgerName)
                    .withBlockAddress(new ValueHolder().withIonText(blockAddressText))
                    .withDigestTipAddress(digestResult.getDigestTipAddress());

            // Get a result back
            GetBlockResult getBlockResult = client.getBlock(getBlockRequest);

            String blockText = getBlockResult.getBlock().getIonText();

            IonDatagram datagram = SYSTEM.getLoader().load(blockText);
            ionStruct = (IonStruct)datagram.get(0);

            final byte[] blockHash = ((IonBlob)ionStruct.get("blockHash")).getBytes();

            proofText = getBlockResult.getProof().getIonText();

            // Take the proof and create a list of hash binary data
            datagram = SYSTEM.getLoader().load(proofText);
            ListIterator<IonValue> listIter = ((IonList)datagram.iterator().next()).listIterator();

            internalHashes.clear();
            while (listIter.hasNext()) {
                internalHashes.add(((IonBlob)listIter.next()).getBytes());
            }

            // Calculate digest
            calculatedDigest = internalHashes.stream().reduce(blockHash, BlockHashVerification::dot);

            verified = Arrays.equals(expectedDigest, calculatedDigest);

            if (verified) {
                System.out.printf("Block address '%s' successfully verified!%n", blockAddressText);
            } else {
                System.out.printf("Block address '%s' verification failed!%n", blockAddressText);
            }
        }
    }
}
```

------
#### [ .NET ]

```
using Amazon.IonDotnet;
using Amazon.IonDotnet.Builders;
using Amazon.IonDotnet.Tree;
using Amazon.QLDB;
using Amazon.QLDB.Driver;
using Amazon.QLDB.Model;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Cryptography;

namespace BlockHashVerification
{
    class BlockHashVerification
    {
        private static readonly string ledgerName = "vehicle-registration";
        private static readonly string tableName = "VehicleRegistration";
        private static readonly string vin = "KM8SRDHF6EU074761";
        private static readonly IQldbDriver driver = QldbDriver.Builder().WithLedger(ledgerName).Build();
        private static readonly IAmazonQLDB client = new AmazonQLDBClient();

        /// <summary>
        /// Takes two hashes, sorts them, concatenates them, and then returns the
        /// hash of the concatenated array.
        /// </summary>
        /// <param name="h1">Byte array containing one of the hashes to compare.</param>
        /// <param name="h2">Byte array containing one of the hashes to compare.</param>
        /// <returns>The concatenated array of hashes.</returns>
        private static byte[] Dot(byte[] h1, byte[] h2)
        {
            if (h1.Length == 0)
            {
                return h2;
            }

            if (h2.Length == 0)
            {
                return h1;
            }

            HashAlgorithm hashAlgorithm = HashAlgorithm.Create("SHA256");
            HashComparer comparer = new HashComparer();
            if (comparer.Compare(h1, h2) < 0)
            {
                return hashAlgorithm.ComputeHash(h1.Concat(h2).ToArray());
            }
            else
            {
                return hashAlgorithm.ComputeHash(h2.Concat(h1).ToArray());
            }
        }

        private class HashComparer : IComparer<byte[]>
        {
            private static readonly int HASH_LENGTH = 32;

            public int Compare(byte[] h1, byte[] h2)
            {
                if (h1.Length != HASH_LENGTH || h2.Length != HASH_LENGTH)
                {
                    throw new ArgumentException("Invalid hash");
                }

                for (var i = h1.Length - 1; i >= 0; i--)
                {
                    var byteEqual = (sbyte)h1[i] - (sbyte)h2[i];
                    if (byteEqual != 0)
                    {
                        return byteEqual;
                    }
                }

                return 0;
            }
        }

        static void Main()
        {
            // Get a digest
            GetDigestRequest getDigestRequest = new GetDigestRequest
            {
                Name = ledgerName
            };
            GetDigestResponse getDigestResponse = client.GetDigestAsync(getDigestRequest).Result;

            // expectedDigest is the buffer we will use later to compare against our calculated digest
            MemoryStream digest = getDigestResponse.Digest;
            byte[] expectedDigest = digest.ToArray();

            // Retrieve info for the given vin's document revisions
            var result = driver.Execute(txn => {
                string query = $"SELECT blockAddress, hash, metadata.id FROM _ql_committed_{tableName} WHERE data.VIN = '{vin}'";
                return txn.Execute(query);
            });

            Console.WriteLine($"Verifying document revisions for vin '{vin}' in table '{tableName}' in ledger '{ledgerName}'");

            foreach (IIonValue ionValue in result)
            {
                IIonStruct ionStruct = ionValue;

                // Get the requested fields
                IIonValue blockAddress = ionStruct.GetField("blockAddress");
                IIonBlob hash = ionStruct.GetField("hash");
                String metadataId = ionStruct.GetField("id").StringValue;

                Console.WriteLine($"Verifying document revision for id '{metadataId}'");

                // Use an Ion Reader to convert block address to text
                IIonReader reader = IonReaderBuilder.Build(blockAddress);
                StringWriter sw = new StringWriter();
                IIonWriter textWriter = IonTextWriterBuilder.Build(sw);
                textWriter.WriteValues(reader);
                string blockAddressText = sw.ToString();

                // Submit a request for the revision
                GetRevisionRequest revisionRequest = new GetRevisionRequest
                {
                    Name = ledgerName,
                    BlockAddress = new ValueHolder
                    {
                        IonText = blockAddressText
                    },
                    DocumentId = metadataId,
                    DigestTipAddress = getDigestResponse.DigestTipAddress
                };
                
                // Get a response back
                GetRevisionResponse revisionResponse = client.GetRevisionAsync(revisionRequest).Result;

                string proofText = revisionResponse.Proof.IonText;
                IIonDatagram proofValue = IonLoader.Default.Load(proofText);

                byte[] documentHash = hash.Bytes().ToArray();
                foreach (IIonValue proofHash in proofValue.GetElementAt(0))
                {
                    // Calculate the digest
                    documentHash = Dot(documentHash, proofHash.Bytes().ToArray());
                }

                bool verified = expectedDigest.SequenceEqual(documentHash);

                if (verified)
                {
                    Console.WriteLine($"Successfully verified document revision for id '{metadataId}'!");
                }
                else
                {
                    Console.WriteLine($"Document revision for id '{metadataId}' verification failed!");
                    return;
                }

                // Submit a request for the block
                GetBlockRequest getBlockRequest = new GetBlockRequest
                {
                    Name = ledgerName,
                    BlockAddress = new ValueHolder
                    {
                        IonText = blockAddressText
                    },
                    DigestTipAddress = getDigestResponse.DigestTipAddress
                };

                // Get a response back
                GetBlockResponse getBlockResponse = client.GetBlockAsync(getBlockRequest).Result;

                string blockText = getBlockResponse.Block.IonText;
                IIonDatagram blockValue = IonLoader.Default.Load(blockText);

                // blockValue is a IonDatagram, and the first value is an IonStruct containing the blockHash
                byte[] blockHash = blockValue.GetElementAt(0).GetField("blockHash").Bytes().ToArray();

                proofText = getBlockResponse.Proof.IonText;
                proofValue = IonLoader.Default.Load(proofText);

                foreach (IIonValue proofHash in proofValue.GetElementAt(0))
                {
                    // Calculate the digest
                    blockHash = Dot(blockHash, proofHash.Bytes().ToArray());
                }

                verified = expectedDigest.SequenceEqual(blockHash);

                if (verified)
                {
                    Console.WriteLine($"Block address '{blockAddressText}' successfully verified!");
                }
                else
                {
                    Console.WriteLine($"Block address '{blockAddressText}' verification failed!");
                }
            }
        }
    }
}
```

------
#### [ Go ]

```
package main

import (
    "context"
    "crypto/sha256"
    "errors"
    "fmt"
    "reflect"

    "github.com/amzn/ion-go/ion"
    "github.com/aws/aws-sdk-go/aws"
    AWSSession "github.com/aws/aws-sdk-go/aws/session"
    "github.com/aws/aws-sdk-go/service/qldb"
    "github.com/aws/aws-sdk-go/service/qldbsession"
    "github.com/awslabs/amazon-qldb-driver-go/qldbdriver"
)

const (
    hashLength = 32
    ledgerName = "vehicle-registration"
    tableName  = "VehicleRegistration"
    vin        = "KM8SRDHF6EU074761"
)

// Takes two hashes, sorts them, concatenates them, and then returns the hash of the concatenated array.
func dot(h1, h2 []byte) ([]byte, error) {
    compare, err := hashComparator(h1, h2)
    if err != nil {
        return nil, err
    }

    var concatenated []byte
    if compare < 0 {
        concatenated = append(h1, h2...)
    } else {
        concatenated = append(h2, h1...)
    }

    newHash := sha256.Sum256(concatenated)
    return newHash[:], nil
}

func hashComparator(h1 []byte, h2 []byte) (int16, error) {
    if len(h1) != hashLength || len(h2) != hashLength {
        return 0, errors.New("invalid hash")
    }
    for i := range h1 {
        // Reverse index for little endianness
        index := hashLength - 1 - i

        // Handle byte being unsigned and overflow
        h1Int := int16(h1[index])
        h2Int := int16(h2[index])
        if h1Int > 127 {
            h1Int = 0 - (256 - h1Int)
        }
        if h2Int > 127 {
            h2Int = 0 - (256 - h2Int)
        }

        difference := h1Int - h2Int
        if difference != 0 {
            return difference, nil
        }
    }
    return 0, nil
}

func main() {
    driverSession := AWSSession.Must(AWSSession.NewSession(aws.NewConfig()))
    qldbSession := qldbsession.New(driverSession)
    driver, err := qldbdriver.New(ledgerName, qldbSession, func(options *qldbdriver.DriverOptions) {})
    if err != nil {
        panic(err)
    }
    client := qldb.New(driverSession)

    // Get a digest
    currentLedgerName := ledgerName
    input := qldb.GetDigestInput{Name: &currentLedgerName}
    digestOutput, err := client.GetDigest(&input)
    if err != nil {
        panic(err)
    }

    // expectedDigest is the buffer we will later use to compare against our calculated digest
    expectedDigest := digestOutput.Digest

    // Retrieve info for the given vin's document revisions
    result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) {
        statement := fmt.Sprintf(
                "SELECT blockAddress, hash, metadata.id FROM _ql_committed_%s WHERE data.VIN = '%s'",
                tableName,
                vin)
        result, err := txn.Execute(statement)
        if err != nil {
            return nil, err
        }

        results := make([]map[string]interface{}, 0)

        // Convert the result set into a map
        for result.Next(txn) {
            var doc map[string]interface{}
            err := ion.Unmarshal(result.GetCurrentData(), &doc)
            if err != nil {
                return nil, err
            }
            results = append(results, doc)
        }
        return results, nil
    })
    if err != nil {
        panic(err)
    }
    resultSlice := result.([]map[string]interface{})

    fmt.Printf("Verifying document revisions for vin '%s' in table '%s' in ledger '%s'\n", vin, tableName, ledgerName)

    for _, value := range resultSlice {
        // Get the requested fields
        ionBlockAddress, err := ion.MarshalText(value["blockAddress"])
        if err != nil {
            panic(err)
        }
        blockAddress := string(ionBlockAddress)
        metadataId := value["id"].(string)
        documentHash := value["hash"].([]byte)

        fmt.Printf("Verifying document revision for id '%s'\n", metadataId)

        // Submit a request for the revision
        revisionInput := qldb.GetRevisionInput{
            BlockAddress:     &qldb.ValueHolder{IonText: &blockAddress},
            DigestTipAddress: digestOutput.DigestTipAddress,
            DocumentId:       &metadataId,
            Name:             &currentLedgerName,
        }

        // Get a result back
        revisionOutput, err := client.GetRevision(&revisionInput)
        if err != nil {
            panic(err)
        }

        proofText := revisionOutput.Proof.IonText

        // Use ion.Reader to iterate over the proof's node hashes
        reader := ion.NewReaderString(*proofText)
        // Enter the struct containing node hashes
        reader.Next()
        if err := reader.StepIn(); err != nil {
            panic(err)
        }

        // Going through nodes and calculate digest
        for reader.Next() {
            val, _ := reader.ByteValue()
            documentHash, err = dot(documentHash, val)
        }

        // Compare documentHash with the expected digest
        verified := reflect.DeepEqual(documentHash, expectedDigest)

        if verified {
            fmt.Printf("Successfully verified document revision for id '%s'!\n", metadataId)
        } else {
            fmt.Printf("Document revision for id '%s' verification failed!\n", metadataId)
            return
        }

        // Submit a request for the block
        blockInput := qldb.GetBlockInput{
            Name:             &currentLedgerName,
            BlockAddress:     &qldb.ValueHolder{IonText: &blockAddress},
            DigestTipAddress: digestOutput.DigestTipAddress,
        }

        // Get a result back
        blockOutput, err := client.GetBlock(&blockInput)
        if err != nil {
            panic(err)
        }

        proofText = blockOutput.Proof.IonText

        block := new(map[string]interface{})
        err = ion.UnmarshalString(*blockOutput.Block.IonText, block)
        if err != nil {
            panic(err)
        }

        blockHash := (*block)["blockHash"].([]byte)

        // Use ion.Reader to iterate over the proof's node hashes
        reader = ion.NewReaderString(*proofText)
        // Enter the struct containing node hashes
        reader.Next()
        if err := reader.StepIn(); err != nil {
            panic(err)
        }

        // Going through nodes and calculate digest
        for reader.Next() {
            val, err := reader.ByteValue()
            if err != nil {
                panic(err)
            }
            blockHash, err = dot(blockHash, val)
        }

        // Compare blockHash with the expected digest
        verified = reflect.DeepEqual(blockHash, expectedDigest)

        if verified {
            fmt.Printf("Block address '%s' successfully verified!\n", blockAddress)
        } else {
            fmt.Printf("Block address '%s' verification failed!\n", blockAddress)
            return
        }
    }
}
```

------
#### [ Node.js ]

```
import { QldbDriver, Result, TransactionExecutor} from "amazon-qldb-driver-nodejs";
import { QLDB } from "aws-sdk"
import { GetBlockRequest, GetBlockResponse, GetDigestRequest, GetDigestResponse, GetRevisionRequest, GetRevisionResponse } from "aws-sdk/clients/qldb";
import { createHash } from "crypto";
import { dom, dumpText, load } from "ion-js"

const ledgerName: string = "vehicle-registration";
const tableName: string = "VehicleRegistration";
const vin: string = "KM8SRDHF6EU074761";
const driver: QldbDriver = new QldbDriver(ledgerName);
const qldbClient: QLDB = new QLDB();
const HASH_SIZE = 32;

/**
 * Takes two hashes, sorts them, concatenates them, and calculates a digest based on the concatenated hash.
 * @param h1 Byte array containing one of the hashes to compare.
 * @param h2 Byte array containing one of the hashes to compare.
 * @returns The digest calculated from the concatenated hash values.
 */
function dot(h1: Uint8Array, h2: Uint8Array): Uint8Array {
    if (h1.length === 0) {
        return h2;
    }
    if (h2.length === 0) {
        return h1;
    }

    const newHashLib = createHash("sha256");

    let concatenated: Uint8Array;
    if (hashComparator(h1, h2) < 0) {
        concatenated = concatenate(h1, h2);
    } else {
        concatenated = concatenate(h2, h1);
    }
    newHashLib.update(concatenated);
    return newHashLib.digest();
}

/**
 * Compares two hashes by their **signed** byte values in little-endian order.
 * @param hash1 The hash value to compare.
 * @param hash2 The hash value to compare.
 * @returns Zero if the hash values are equal, otherwise return the difference of the first pair of non-matching
 *          bytes.
 * @throws RangeError When the hash is not the correct hash size.
 */
function hashComparator(hash1: Uint8Array, hash2: Uint8Array): number {
    if (hash1.length !== HASH_SIZE || hash2.length !== HASH_SIZE) {
        throw new RangeError("Invalid hash.");
    }
    for (let i = hash1.length-1; i >= 0; i--) {
        const difference: number = (hash1[i]<<24 >>24) - (hash2[i]<<24 >>24);
        if (difference !== 0) {
            return difference;
        }
    }
    return 0;
}

/**
 * Helper method that concatenates two Uint8Array.
 * @param arrays List of arrays to concatenate, in the order provided.
 * @returns The concatenated array.
 */
function concatenate(...arrays: Uint8Array[]): Uint8Array {
    let totalLength = 0;
    for (const arr of arrays) {
        totalLength += arr.length;
    }
    const result = new Uint8Array(totalLength);
    let offset = 0;
    for (const arr of arrays) {
        result.set(arr, offset);
        offset += arr.length;
    }
    return result;
}

/**
 * Helper method that checks for equality between two Uint8Array.
 * @param expected Byte array containing one of the hashes to compare.
 * @param actual Byte array containing one of the hashes to compare.
 * @returns Boolean indicating equality between the two Uint8Array.
 */
function isEqual(expected: Uint8Array, actual: Uint8Array): boolean {
    if (expected === actual) return true;
    if (expected == null || actual == null) return false;
    if (expected.length !== actual.length) return false;

    for (let i = 0; i < expected.length; i++) {
        if (expected[i] !== actual[i]) {
            return false;
        }
    }
    return true;
}

const main = async function (): Promise<void> {
    // Get a digest
    const getDigestRequest: GetDigestRequest = {
        Name: ledgerName
    };
    const getDigestResponse: GetDigestResponse = await qldbClient.getDigest(getDigestRequest).promise();

    // expectedDigest is the buffer we will later use to compare against our calculated digest
    const expectedDigest: Uint8Array = <Uint8Array>getDigestResponse.Digest;

    const result: dom.Value[] = await driver.executeLambda(async (txn: TransactionExecutor): Promise<dom.Value[]> => {
        const query: string = `SELECT blockAddress, hash, metadata.id FROM _ql_committed_${tableName} WHERE data.VIN = '${vin}'`;
        const queryResult: Result = await txn.execute(query);
        return queryResult.getResultList();
    });

    console.log(`Verifying document revisions for vin '${vin}' in table '${tableName}' in ledger '${ledgerName}'`);

    for (let value of result) {
        // Get the requested fields
        const blockAddress: dom.Value = value.get("blockAddress");
        const hash: dom.Value = value.get("hash");
        const metadataId: string = value.get("id").stringValue();

        console.log(`Verifying document revision for id '${metadataId}'`);

        // Submit a request for the revision
        const revisionRequest: GetRevisionRequest = {
            Name: ledgerName,
            BlockAddress: {
                IonText: dumpText(blockAddress)
            },
            DocumentId: metadataId,
            DigestTipAddress: getDigestResponse.DigestTipAddress
        };

        // Get a response back
        const revisionResponse: GetRevisionResponse = await qldbClient.getRevision(revisionRequest).promise();

        let proofValue: dom.Value = load(revisionResponse.Proof.IonText);

        let documentHash: Uint8Array = hash.uInt8ArrayValue();
        proofValue.elements().forEach((proofHash: dom.Value) => {
            // Calculate the digest
            documentHash = dot(documentHash, proofHash.uInt8ArrayValue());
        });

        let verified: boolean = isEqual(expectedDigest, documentHash);

        if (verified) {
            console.log(`Successfully verified document revision for id '${metadataId}'!`);
        } else {
            console.log(`Document revision for id '${metadataId}' verification failed!`);
            return;
        }

        // Submit a request for the block
        const getBlockRequest: GetBlockRequest = {
            Name: ledgerName,
            BlockAddress: {
                IonText: dumpText(blockAddress)
            },
            DigestTipAddress: getDigestResponse.DigestTipAddress
        };

        // Get a response back
        const getBlockResponse: GetBlockResponse = await qldbClient.getBlock(getBlockRequest).promise();

        const blockValue: dom.Value = load(getBlockResponse.Block.IonText)
        let blockHash: Uint8Array = blockValue.get("blockHash").uInt8ArrayValue();

        proofValue = load(getBlockResponse.Proof.IonText);

        proofValue.elements().forEach((proofHash: dom.Value) => {
            // Calculate the digest
            blockHash = dot(blockHash, proofHash.uInt8ArrayValue());
        });

        verified = isEqual(expectedDigest, blockHash);

        if (verified) {
            console.log(`Block address '${dumpText(blockAddress)}' successfully verified!`);
        } else {
            console.log(`Block address '${dumpText(blockAddress)}' verification failed!`);
        }
    }
};

if (require.main === module) {
    main();
}
```

------
#### [ Python ]

```
from amazon.ion.simpleion import dumps, loads
from array import array
from boto3 import client
from functools import reduce
from hashlib import sha256
from pyqldb.driver.qldb_driver import QldbDriver

ledger_name = 'vehicle-registration'
table_name = 'VehicleRegistration'
vin = 'KM8SRDHF6EU074761'
qldb_client = client('qldb')
hash_length = 32


def query_doc_revision(txn):
    query = "SELECT blockAddress, hash, metadata.id FROM _ql_committed_{} WHERE data.VIN = '{}'".format(table_name, vin)
    return txn.execute_statement(query)


def block_address_to_dictionary(ion_dict):
    """
    Convert a block address from IonPyDict into a dictionary.
    Shape of the dictionary must be: {'IonText': "{strandId: <"strandId">, sequenceNo: <sequenceNo>}"}

    :type ion_dict: :py:class:`amazon.ion.simple_types.IonPyDict`/str
    :param ion_dict: The block address value to convert.

    :rtype: dict
    :return: The converted dict.
    """
    block_address = {'IonText': {}}
    if not isinstance(ion_dict, str):
        py_dict = '{{strandId: "{}", sequenceNo:{}}}'.format(ion_dict['strandId'], ion_dict['sequenceNo'])
        ion_dict = py_dict
    block_address['IonText'] = ion_dict
    return block_address


def dot(hash1, hash2):
    """
    Takes two hashes, sorts them, concatenates them, and then returns the
    hash of the concatenated array.

    :type hash1: bytes
    :param hash1: The hash value to compare.

    :type hash2: bytes
    :param hash2: The hash value to compare.

    :rtype: bytes
    :return: The new hash value generated from concatenated hash values.
    """
    if len(hash1) != hash_length or len(hash2) != hash_length:
        raise ValueError('Illegal hash.')

    hash_array1 = array('b', hash1)
    hash_array2 = array('b', hash2)

    difference = 0
    for i in range(len(hash_array1) - 1, -1, -1):
        difference = hash_array1[i] - hash_array2[i]
        if difference != 0:
            break

    if difference < 0:
        concatenated = hash1 + hash2
    else:
        concatenated = hash2 + hash1

    new_hash_lib = sha256()
    new_hash_lib.update(concatenated)
    new_digest = new_hash_lib.digest()
    return new_digest


# Get a digest
get_digest_response = qldb_client.get_digest(Name=ledger_name)

# expected_digest is the buffer we will later use to compare against our calculated digest
expected_digest = get_digest_response.get('Digest')
digest_tip_address = get_digest_response.get('DigestTipAddress')

qldb_driver = QldbDriver(ledger_name=ledger_name)

# Retrieve info for the given vin's document revisions
result = qldb_driver.execute_lambda(query_doc_revision)

print("Verifying document revisions for vin '{}' in table '{}' in ledger '{}'".format(vin, table_name, ledger_name))

for value in result:
    # Get the requested fields
    block_address = value['blockAddress']
    document_hash = value['hash']
    metadata_id = value['id']

    print("Verifying document revision for id '{}'".format(metadata_id))

    # Submit a request for the revision and get a result back
    proof_response = qldb_client.get_revision(Name=ledger_name,
                                              BlockAddress=block_address_to_dictionary(block_address),
                                              DocumentId=metadata_id,
                                              DigestTipAddress=digest_tip_address)

    proof_text = proof_response.get('Proof').get('IonText')
    proof_hashes = loads(proof_text)

    # Calculate digest
    calculated_digest = reduce(dot, proof_hashes, document_hash)

    verified = calculated_digest == expected_digest
    if verified:
        print("Successfully verified document revision for id '{}'!".format(metadata_id))
    else:
        print("Document revision for id '{}' verification failed!".format(metadata_id))

    # Submit a request for the block and get a result back
    block_response = qldb_client.get_block(Name=ledger_name, BlockAddress=block_address_to_dictionary(block_address),
                                           DigestTipAddress=digest_tip_address)

    block_text = block_response.get('Block').get('IonText')
    block = loads(block_text)

    block_hash = block.get('blockHash')

    proof_text = block_response.get('Proof').get('IonText')
    proof_hashes = loads(proof_text)

    # Calculate digest
    calculated_digest = reduce(dot, proof_hashes, block_hash)

    verified = calculated_digest == expected_digest
    if verified:
        print("Block address '{}' successfully verified!".format(dumps(block_address,
                                                                       binary=False,
                                                                       omit_version_marker=True)))
    else:
        print("Block address '{}' verification failed!".format(block_address))
```

------

# Erreurs courantes lors de la vérification
<a name="verification.errors"></a>

**Important**  
Avis de fin de support : les clients existants pourront utiliser Amazon QLDB jusqu'à la fin du support le 31 juillet 2025. Pour plus de détails, consultez [Migrer un registre Amazon QLDB vers Amazon Aurora PostgreSQL](https://aws.amazon.com/blogs/database/migrate-an-amazon-qldb-ledger-to-amazon-aurora-postgresql/).

Cette section décrit les erreurs d'exécution émises par Amazon QLDB pour les demandes de vérification.

Voici une liste des exceptions courantes renvoyées par le service. Chaque exception inclut le message d'erreur spécifique, suivi des opérations d'API susceptibles de le générer, une brève description et des suggestions de solutions possibles.<a name="verification.errors.varlist"></a>

**IllegalArgumentException**  
Message : La valeur Ion fournie n'est pas valide et ne peut pas être analysée.  
Opérations d'API : `GetDigest, GetBlock, GetRevision`  
Assurez-vous de fournir une valeur [Amazon Ion](ion.md) valide avant de réessayer votre demande.

**IllegalArgumentException**  
Message : L'adresse de blocage fournie n'est pas valide.  
Opérations d'API : `GetDigest, GetBlock, GetRevision`  
Assurez-vous de fournir une adresse de blocage valide avant de réessayer votre demande. Une adresse de bloc est une structure Amazon Ion qui comporte deux champs : `strandId` et`sequenceNo`.  
Par exemple : `{strandId:"BlFTjlSXze9BIh1KOszcE3",sequenceNo:14}`

**IllegalArgumentException**  
Message : Le numéro de séquence de l'adresse de résumé fournie est supérieur au dernier enregistrement validé du fil.  
Opérations d'API : `GetDigest, GetBlock, GetRevision`  
L'adresse du résumé que vous fournissez doit avoir un numéro de séquence inférieur ou égal au numéro de séquence du dernier enregistrement validé du volet du journal. Avant de réessayer votre demande, assurez-vous de fournir une adresse de résumé avec un numéro de séquence valide.

**IllegalArgumentException**  
Message : L'ID de brin de l'adresse de bloc fournie n'est pas valide.  
Opérations d'API : `GetDigest, GetBlock, GetRevision`  
L'adresse de bloc que vous fournissez doit avoir un identifiant de volet correspondant à l'identifiant de volet du journal. Avant de réessayer votre demande, assurez-vous de fournir une adresse de blocage avec un identifiant de chaîne valide.

**IllegalArgumentException**  
Message : Le numéro de séquence de l'adresse de bloc fournie est supérieur au dernier enregistrement validé du brin.  
Opérations d'API : `GetBlock, GetRevision`  
L'adresse de bloc que vous fournissez doit avoir un numéro de séquence inférieur ou égal au numéro de séquence du dernier enregistrement validé du brin. Avant de réessayer votre demande, assurez-vous de fournir une adresse de blocage avec un numéro de séquence valide.

**IllegalArgumentException**  
Message : L'ID de volet de l'adresse de bloc fournie doit correspondre à l'ID de volet de l'adresse de résumé fournie.  
Opérations d'API : `GetBlock, GetRevision`  
Vous ne pouvez vérifier une révision ou un bloc de document que s'il existe dans le même volet de journal que le résumé que vous fournissez.

**IllegalArgumentException**  
Message : Le numéro de séquence de l'adresse de bloc fournie ne doit pas être supérieur au numéro de séquence de l'adresse de résumé fournie.  
Opérations d'API : `GetBlock, GetRevision`  
Vous ne pouvez vérifier une révision ou un bloc de document que s'il est couvert par le résumé que vous fournissez. Cela signifie qu'il a été enregistré dans le journal avant l'adresse du résumé.

**IllegalArgumentException**  
Message : L'ID de document fourni n'a pas été trouvé dans le bloc à l'adresse de bloc spécifiée.  
Fonctionnement de l'API : `GetRevision`  
L'identifiant du document que vous fournissez doit figurer dans l'adresse de bloc que vous fournissez. Avant de réessayer votre demande, assurez-vous que ces deux paramètres sont cohérents.