Paso 7: verificar un documento en un libro mayor - Amazon Quantum Ledger Database (Amazon QLDB)

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Paso 7: verificar un documento en un libro mayor

Con Amazon QLDB, puede verificar de manera eficiente la integridad de un documento del diario de su libro mayor mediante el uso de hash criptográfico con SHA-256. Para obtener más información sobre cómo funcionan la verificación y el hash criptográfico en QLDB, consulte Verificación de datos en Amazon QLDB.

En este paso, verificará la revisión de un documento en la tabla VehicleRegistration del libro mayor vehicle-registration. En primer lugar, solicita un resumen, que se devuelve como un archivo de salida y actúa como firma de todo el historial de cambios del libro mayor. A continuación, solicita una prueba de la revisión relativa a ese resumen. Con esta prueba, se verifica la integridad de la revisión si se aprueban todas las comprobaciones de validación.

Para verificar la revisión de un documento
  1. Revise los siguientes archivos .py, que representan los objetos de QLDB que se requieren para la verificación y un módulo de utilidad con funciones auxiliares para convertir los tipos de respuesta de QLDB en cadenas.

    1. block_address.py

      # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 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
    2. verifier.py

      # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # This code expects that you have AWS credentials setup per: # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html from array import array from base64 import b64encode from functools import reduce from hashlib import sha256 from random import randrange from amazon.ion.simpleion import loads HASH_LENGTH = 32 UPPER_BOUND = 8 def parse_proof(value_holder): """ Parse the Proof object returned by QLDB into an iterator. The Proof object returned by QLDB is a dictionary like the following: {'IonText': '[{{<hash>}},{{<hash>}}]'} :type value_holder: dict :param value_holder: A structure containing an Ion string value. :rtype: :py:class:`amazon.ion.simple_types.IonPyList` :return: A list of hash values. """ value_holder = value_holder.get('IonText') proof_list = loads(value_holder) return proof_list def parse_block(value_holder): """ Parse the Block object returned by QLDB and retrieve block hash. :type value_holder: dict :param value_holder: A structure containing an Ion string value. :rtype: :py:class:`amazon.ion.simple_types.IonPyBytes` :return: The block hash. """ value_holder = value_holder.get('IonText') block = loads(value_holder) block_hash = block.get('blockHash') return block_hash def flip_random_bit(original): """ Flip a single random bit in the given hash value. This method is used to demonstrate QLDB's verification features. :type original: bytes :param original: The hash value to alter. :rtype: bytes :return: The altered hash with a single random bit changed. """ assert len(original) != 0, 'Invalid bytes.' altered_position = randrange(len(original)) bit_shift = randrange(UPPER_BOUND) altered_hash = bytearray(original).copy() altered_hash[altered_position] = altered_hash[altered_position] ^ (1 << bit_shift) return bytes(altered_hash) def compare_hash_values(hash1, hash2): """ Compare two hash values by converting them into byte arrays, assuming they are little endian. :type hash1: bytes :param hash1: The hash value to compare. :type hash2: bytes :param hash2: The hash value to compare. :rtype: int :return: Zero if the hash values are equal, otherwise return the difference of the first pair of non-matching bytes. """ assert len(hash1) == HASH_LENGTH assert len(hash2) == HASH_LENGTH hash_array1 = array('b', hash1) hash_array2 = array('b', hash2) for i in range(len(hash_array1) - 1, -1, -1): difference = hash_array1[i] - hash_array2[i] if difference != 0: return difference return 0 def join_hash_pairwise(hash1, hash2): """ Take two hash values, sort them, concatenate them, and generate a new hash value from the concatenated values. :type hash1: bytes :param hash1: Hash value to concatenate. :type hash2: bytes :param hash2: Hash value to concatenate. :rtype: bytes :return: The new hash value generated from concatenated hash values. """ if len(hash1) == 0: return hash2 if len(hash2) == 0: return hash1 concatenated = hash1 + hash2 if compare_hash_values(hash1, hash2) < 0 else hash2 + hash1 new_hash_lib = sha256() new_hash_lib.update(concatenated) new_digest = new_hash_lib.digest() return new_digest def calculate_root_hash_from_internal_hashes(internal_hashes, leaf_hash): """ Combine the internal hashes and the leaf hash until only one root hash remains. :type internal_hashes: map :param internal_hashes: An iterable over a list of hash values. :type leaf_hash: bytes :param leaf_hash: The revision hash to pair with the first hash in the Proof hashes list. :rtype: bytes :return: The root hash constructed by combining internal hashes. """ root_hash = reduce(join_hash_pairwise, internal_hashes, leaf_hash) return root_hash def build_candidate_digest(proof, leaf_hash): """ Build the candidate digest representing the entire ledger from the Proof hashes. :type proof: dict :param proof: The Proof object. :type leaf_hash: bytes :param leaf_hash: The revision hash to pair with the first hash in the Proof hashes list. :rtype: bytes :return: The calculated root hash. """ parsed_proof = parse_proof(proof) root_hash = calculate_root_hash_from_internal_hashes(parsed_proof, leaf_hash) return root_hash def verify_document(document_hash, digest, proof): """ Verify document revision against the provided digest. :type document_hash: bytes :param document_hash: The SHA-256 value representing the document revision to be verified. :type digest: bytes :param digest: The SHA-256 hash value representing the ledger digest. :type proof: dict :param proof: The Proof object retrieved from :func:`pyqldbsamples.get_revision.get_revision`. :rtype: bool :return: If the document revision verify against the ledger digest. """ candidate_digest = build_candidate_digest(proof, document_hash) return digest == candidate_digest def to_base_64(input): """ Encode input in base64. :type input: bytes :param input: Input to be encoded. :rtype: string :return: Return input that has been encoded in base64. """ encoded_value = b64encode(input) return str(encoded_value, 'UTF-8')
    3. qldb_string_utils.py

      # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. from amazon.ion.simpleion import dumps, loads def value_holder_to_string(value_holder): """ Returns the string representation of a given `value_holder`. :type value_holder: dict :param value_holder: The `value_holder` to convert to string. :rtype: str :return: The string representation of the supplied `value_holder`. """ ret_val = dumps(loads(value_holder), binary=False, indent=' ', omit_version_marker=True) val = '{{ IonText: {}}}'.format(ret_val) return val def block_response_to_string(block_response): """ Returns the string representation of a given `block_response`. :type block_response: dict :param block_response: The `block_response` to convert to string. :rtype: str :return: The string representation of the supplied `block_response`. """ string = '' if block_response.get('Block', {}).get('IonText') is not None: string += 'Block: ' + value_holder_to_string(block_response['Block']['IonText']) + ', ' if block_response.get('Proof', {}).get('IonText') is not None: string += 'Proof: ' + value_holder_to_string(block_response['Proof']['IonText']) return '{' + string + '}' def digest_response_to_string(digest_response): """ Returns the string representation of a given `digest_response`. :type digest_response: dict :param digest_response: The `digest_response` to convert to string. :rtype: str :return: The string representation of the supplied `digest_response`. """ string = '' if digest_response.get('Digest') is not None: string += 'Digest: ' + str(digest_response['Digest']) + ', ' if digest_response.get('DigestTipAddress', {}).get('IonText') is not None: string += 'DigestTipAddress: ' + value_holder_to_string(digest_response['DigestTipAddress']['IonText']) return '{' + string + '}'
  2. Utilice dos programas .py (get_digest.py y get_revision.py) para realizar los siguientes pasos:

    • Solicite un nuevo resumen del libro mayor vehicle-registration.

    • Solicite una prueba de cada revisión del documento con el VIN 1N4AL11D75C109151 de la tabla VehicleRegistration.

    • Verifique las revisiones utilizando el resumen devuelto y compruébelo recalculando el resumen.

    El programa get_digest.py contiene el siguiente código.

    # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # This code expects that you have AWS credentials setup per: # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html from logging import basicConfig, getLogger, INFO from boto3 import client from pyqldbsamples.constants import Constants from pyqldbsamples.qldb.qldb_string_utils import digest_response_to_string logger = getLogger(__name__) basicConfig(level=INFO) qldb_client = client('qldb') def get_digest_result(name): """ Get the digest of a ledger's journal. :type name: str :param name: Name of the ledger to operate on. :rtype: dict :return: The digest in a 256-bit hash value and a block address. """ logger.info("Let's get the current digest of the ledger named {}".format(name)) result = qldb_client.get_digest(Name=name) logger.info('Success. LedgerDigest: {}.'.format(digest_response_to_string(result))) return result def main(ledger_name=Constants.LEDGER_NAME): """ This is an example for retrieving the digest of a particular ledger. """ try: get_digest_result(ledger_name) except Exception as e: logger.exception('Unable to get a ledger digest!') raise e if __name__ == '__main__': main()
    nota

    Utilice la función get_digest_result para solicitar un resumen que incluya el tip actual del diario del libro mayor. La sugerencia del diario hace referencia al último bloqueo comprometido en el momento en que QLDB recibe su solicitud.

    El programa get_revision.py contiene el siguiente código.

    3.x
    # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # This code expects that you have AWS credentials setup per: # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html from logging import basicConfig, getLogger, INFO from amazon.ion.simpleion import loads from boto3 import client from pyqldbsamples.constants import Constants from pyqldbsamples.get_digest import get_digest_result from pyqldbsamples.model.sample_data import SampleData, convert_object_to_ion from pyqldbsamples.qldb.block_address import block_address_to_dictionary from pyqldbsamples.verifier import verify_document, flip_random_bit, to_base_64 from pyqldbsamples.connect_to_ledger import create_qldb_driver from pyqldbsamples.qldb.qldb_string_utils import value_holder_to_string logger = getLogger(__name__) basicConfig(level=INFO) qldb_client = client('qldb') def get_revision(ledger_name, document_id, block_address, digest_tip_address): """ Get the revision data object for a specified document ID and block address. Also returns a proof of the specified revision for verification. :type ledger_name: str :param ledger_name: Name of the ledger containing the document to query. :type document_id: str :param document_id: Unique ID for the document to be verified, contained in the committed view of the document. :type block_address: dict :param block_address: The location of the block to request. :type digest_tip_address: dict :param digest_tip_address: The latest block location covered by the digest. :rtype: dict :return: The response of the request. """ result = qldb_client.get_revision(Name=ledger_name, BlockAddress=block_address, DocumentId=document_id, DigestTipAddress=digest_tip_address) return result def lookup_registration_for_vin(driver, vin): """ Query revision history for a particular vehicle for verification. :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver` :param driver: An instance of the QldbDriver class. :type vin: str :param vin: VIN to query the revision history of a specific registration with. :rtype: :py:class:`pyqldb.cursor.buffered_cursor.BufferedCursor` :return: Cursor on the result set of the statement query. """ logger.info("Querying the 'VehicleRegistration' table for VIN: {}...".format(vin)) query = 'SELECT * FROM _ql_committed_VehicleRegistration WHERE data.VIN = ?' return driver.execute_lambda(lambda txn: txn.execute_statement(query, convert_object_to_ion(vin))) def verify_registration(driver, ledger_name, vin): """ Verify each version of the registration for the given VIN. :type driver: :py:class:`pyqldb.driver.qldb_driver.QldbDriver` :param driver: An instance of the QldbDriver class. :type ledger_name: str :param ledger_name: The ledger to get digest from. :type vin: str :param vin: VIN to query the revision history of a specific registration with. :raises AssertionError: When verification failed. """ logger.info("Let's verify the registration with VIN = {}, in ledger = {}.".format(vin, ledger_name)) digest = get_digest_result(ledger_name) digest_bytes = digest.get('Digest') digest_tip_address = digest.get('DigestTipAddress') logger.info('Got a ledger digest: digest tip address = {}, digest = {}.'.format( value_holder_to_string(digest_tip_address.get('IonText')), to_base_64(digest_bytes))) logger.info('Querying the registration with VIN = {} to verify each version of the registration...'.format(vin)) cursor = lookup_registration_for_vin(driver, vin) logger.info('Getting a proof for the document.') for row in cursor: block_address = row.get('blockAddress') document_id = row.get('metadata').get('id') result = get_revision(ledger_name, document_id, block_address_to_dictionary(block_address), digest_tip_address) revision = result.get('Revision').get('IonText') document_hash = loads(revision).get('hash') proof = result.get('Proof') logger.info('Got back a proof: {}.'.format(proof)) verified = verify_document(document_hash, digest_bytes, proof) if not verified: raise AssertionError('Document revision is not verified.') else: logger.info('Success! The document is verified.') altered_document_hash = flip_random_bit(document_hash) logger.info("Flipping one bit in the document's hash and assert that the document is NOT verified. " "The altered document hash is: {}.".format(to_base_64(altered_document_hash))) verified = verify_document(altered_document_hash, digest_bytes, proof) if verified: raise AssertionError('Expected altered document hash to not be verified against digest.') else: logger.info('Success! As expected flipping a bit in the document hash causes verification to fail.') logger.info('Finished verifying the registration with VIN = {} in ledger = {}.'.format(vin, ledger_name)) def main(ledger_name=Constants.LEDGER_NAME): """ Verify the integrity of a document revision in a QLDB ledger. """ registration = SampleData.VEHICLE_REGISTRATION[0] vin = registration['VIN'] try: with create_qldb_driver(ledger_name) as driver: verify_registration(driver, ledger_name, vin) except Exception as e: logger.exception('Unable to verify revision.') raise e if __name__ == '__main__': main()
    2.x
    # Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. # SPDX-License-Identifier: MIT-0 # # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # This code expects that you have AWS credentials setup per: # https://boto3.amazonaws.com/v1/documentation/api/latest/guide/quickstart.html from logging import basicConfig, getLogger, INFO from amazon.ion.simpleion import loads from boto3 import client from pyqldbsamples.constants import Constants from pyqldbsamples.get_digest import get_digest_result from pyqldbsamples.model.sample_data import SampleData, convert_object_to_ion from pyqldbsamples.qldb.block_address import block_address_to_dictionary from pyqldbsamples.verifier import verify_document, flip_random_bit, to_base_64 from pyqldbsamples.connect_to_ledger import create_qldb_session from pyqldbsamples.qldb.qldb_string_utils import value_holder_to_string logger = getLogger(__name__) basicConfig(level=INFO) qldb_client = client('qldb') def get_revision(ledger_name, document_id, block_address, digest_tip_address): """ Get the revision data object for a specified document ID and block address. Also returns a proof of the specified revision for verification. :type ledger_name: str :param ledger_name: Name of the ledger containing the document to query. :type document_id: str :param document_id: Unique ID for the document to be verified, contained in the committed view of the document. :type block_address: dict :param block_address: The location of the block to request. :type digest_tip_address: dict :param digest_tip_address: The latest block location covered by the digest. :rtype: dict :return: The response of the request. """ result = qldb_client.get_revision(Name=ledger_name, BlockAddress=block_address, DocumentId=document_id, DigestTipAddress=digest_tip_address) return result def lookup_registration_for_vin(qldb_session, vin): """ Query revision history for a particular vehicle for verification. :type qldb_session: :py:class:`pyqldb.session.qldb_session.QldbSession` :param qldb_session: An instance of the QldbSession class. :type vin: str :param vin: VIN to query the revision history of a specific registration with. :rtype: :py:class:`pyqldb.cursor.buffered_cursor.BufferedCursor` :return: Cursor on the result set of the statement query. """ logger.info("Querying the 'VehicleRegistration' table for VIN: {}...".format(vin)) query = 'SELECT * FROM _ql_committed_VehicleRegistration WHERE data.VIN = ?' parameters = [convert_object_to_ion(vin)] cursor = qldb_session.execute_statement(query, parameters) return cursor def verify_registration(qldb_session, ledger_name, vin): """ Verify each version of the registration for the given VIN. :type qldb_session: :py:class:`pyqldb.session.qldb_session.QldbSession` :param qldb_session: An instance of the QldbSession class. :type ledger_name: str :param ledger_name: The ledger to get digest from. :type vin: str :param vin: VIN to query the revision history of a specific registration with. :raises AssertionError: When verification failed. """ logger.info("Let's verify the registration with VIN = {}, in ledger = {}.".format(vin, ledger_name)) digest = get_digest_result(ledger_name) digest_bytes = digest.get('Digest') digest_tip_address = digest.get('DigestTipAddress') logger.info('Got a ledger digest: digest tip address = {}, digest = {}.'.format( value_holder_to_string(digest_tip_address.get('IonText')), to_base_64(digest_bytes))) logger.info('Querying the registration with VIN = {} to verify each version of the registration...'.format(vin)) cursor = lookup_registration_for_vin(qldb_session, vin) logger.info('Getting a proof for the document.') for row in cursor: block_address = row.get('blockAddress') document_id = row.get('metadata').get('id') result = get_revision(ledger_name, document_id, block_address_to_dictionary(block_address), digest_tip_address) revision = result.get('Revision').get('IonText') document_hash = loads(revision).get('hash') proof = result.get('Proof') logger.info('Got back a proof: {}.'.format(proof)) verified = verify_document(document_hash, digest_bytes, proof) if not verified: raise AssertionError('Document revision is not verified.') else: logger.info('Success! The document is verified.') altered_document_hash = flip_random_bit(document_hash) logger.info("Flipping one bit in the document's hash and assert that the document is NOT verified. " "The altered document hash is: {}.".format(to_base_64(altered_document_hash))) verified = verify_document(altered_document_hash, digest_bytes, proof) if verified: raise AssertionError('Expected altered document hash to not be verified against digest.') else: logger.info('Success! As expected flipping a bit in the document hash causes verification to fail.') logger.info('Finished verifying the registration with VIN = {} in ledger = {}.'.format(vin, ledger_name)) if __name__ == '__main__': """ Verify the integrity of a document revision in a QLDB ledger. """ registration = SampleData.VEHICLE_REGISTRATION[0] vin = registration['VIN'] try: with create_qldb_session() as session: verify_registration(session, Constants.LEDGER_NAME, vin) except Exception: logger.exception('Unable to verify revision.')
    nota

    Una vez que la función get_revision devuelve una prueba de la revisión del documento especificado, este programa utiliza una API del lado del cliente para verificar esa revisión.

  3. Para ejecutar el programa, introduzca el siguiente comando.

    python get_revision.py

Si ya no necesita usar el libro mayor vehicle-registration, continúe con Paso 8 (opcional): limpiar recursos.