步驟 7:驗證分類帳中的文件 - Amazon Quantum Ledger Database (Amazon QLDB)

本文為英文版的機器翻譯版本,如內容有任何歧義或不一致之處,概以英文版為準。

步驟 7:驗證分類帳中的文件

使用 Amazon QLDB,您可以使用搭配 SHA-256 的加密雜湊,有效地驗證分類帳日誌中文件的完整性。若要深入瞭解驗證和加密雜湊如何在 QLDB 中運作,請參閱Amazon QLDB 中的數據驗證

在此步驟中,您會核對vehicle-registration分類帳中VehicleRegistration表格中的文件修訂。首先,您要求摘要,該摘要會以輸出檔案的形式傳回,並做為分類帳整個變更歷史記錄的簽章。然後,您要求相對於該摘要的修訂版本的證明。使用此證明,如果所有驗證檢查都通過,則會驗證修訂的完整性。

核對文件修訂
  1. 檢閱下列.py檔案,這些檔案代表需要驗證的 QLDB 物件,以及具有協助程式函數的公用程式模組,將 QLDB 回應類型轉換為字串。

    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. 使用兩個.py程式 (get_digest.pyget_revision.py) 來執行下列步驟:

    • 請求分vehicle-registration類帳中的新摘要。

    • 1N4AL11D75C109151從表格中使用 VIN 為文檔的每個修訂版提供證VehicleRegistration明。

    • 透過重新計算摘要,使用傳回的摘要和校樣來驗證修訂。

    get_digest.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 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()
    注意

    使用此get_digest_result函數可要求摘要,其中涵蓋分類帳中分類帳目前提示的摘要。日誌的提示是指 QLDB 收到您的請求時最新的已提交區塊。

    get_revision.py程序包含以下代碼。

    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.')
    注意

    get_revision函數傳回指定文件修訂的證明之後,此程式會使用用戶端 API 來驗證該修訂版本。

  3. 若要執行程式,請輸入下列命令。

    python get_revision.py

如果您不再需要使用vehicle-registration分類帳,請繼續執行步驟 8 (選擇性):清除資源