def get_digest_result(self):
     """Retrieving the digest of a particular ledger."""
     try:
         data = get_requested_data()
         digest = get_digest_result(data["ledger_name"])
         return digest
     except Exception as e:
         logger.exception('Unable to get a ledger digest!')
         return fail_response('Unable to delete the ledger. Please try again.', HTTPStatus.UNPROCESSABLE_ENTITY)
 def up(self):
     try:
         self.create_ledger()
         if self.table_exist():
             logger.info('Table already exist!')
             return True
         self.create_table()
         self.create_table_indexes(['service_id', 'document_id', 'document_hash', 'field_list', 'op_mode'])
     except Exception as e:
         logger.exception('Migration UP failed! {}.'.format(e))
 def ledger_exist(self):
     """
     Returns information about a ledger, including its state and when it was created.
     """
     try:
         logger.info("Let's describe ledger...{}".format(self.ledger_name))
         ledger = qldb.client().describe_ledger(Name=self.ledger_name)
         logger.info('Success. describe ledger...{}.'.format(ledger))
         return ledger
     except Exception as e:
         logger.exception('Unable to list ledgers!')
         return None
 def describe_ledger(ledger_name):
     """
     Returns information about a ledger, including its state and when it was created.
     """
     try:
         logger.info("Let's describe ledger...{}".format(ledger_name))
         ledger = qldb.client().describe_ledger(Name=ledger_name)
         logger.info('Success. describe ledger...{}.'.format(ledger))
         return ledger
     except Exception as e:
         logger.exception('Unable to list ledgers!')
         return fail_response('Unable to list ledgers!. Please try again.',
                              HTTPStatus.UNPROCESSABLE_ENTITY)
 def create_ledger(ledger_name):
     """Create a ledger and wait for it to be active."""
     try:
         DdlServices.do_create_ledger(ledger_name)
         wait_for_active(ledger_name)
         DdlServices.reset_ledgers()
         return success_response('Ledger is active and ready to use.',
                                 HTTPStatus.CREATED)
     except Exception as e:
         logger.exception('Unable to create the ledger!')
         return fail_response(
             'Unable to create the ledger. error: {}. Please try again.'.
             format(e), HTTPStatus.UNPROCESSABLE_ENTITY)
 def delete_ledger(ledger_name):
     """Delete a ledger."""
     try:
         set_deletion_protection(ledger_name, False)
         DdlServices.do_delete_ledger(ledger_name)
         wait_for_deleted(ledger_name)
         DdlServices.reset_ledgers()
         return success_response('The ledger is successfully deleted.',
                                 HTTPStatus.ACCEPTED)
     except Exception as e:
         logger.exception('Unable to delete the ledger.')
         return fail_response(
             'Unable to delete the ledger. Please try again.',
             HTTPStatus.UNPROCESSABLE_ENTITY)
Exemplo n.º 7
0
 def get_table_data(ledger_name, table_name, where='1=1', limit=10, offset=0):
     """ Scan for all the documents in a table."""
     try:
         with qldb.session(ledger_name) as session:
             # Scan all the tables and print their documents.
             tables = session.list_tables()
             for table in tables:
                 cursor = session.execute_lambda(
                     lambda executor: DmlServices.scan_table(executor, table_name, where, limit, offset),
                     retry_indicator=lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
                 logger.info('Scan successful!')
                 return parse_result(cursor)
     except Exception as e:
         logger.exception('Unable to scan tables. {}'.format(e))
 def table_exist(self):
     """
         Connect to a session for a given ledger using default settings.
         """
     try:
         qldb_session = qldb.session(self.ledger_name)
         logger.info('Listing table names ')
         tables = qldb_session.list_tables()
         _tables = []
         for table in tables:
             _tables.append(table)
         return self.hash_table_name in _tables
     except Exception as e:
         logger.exception('Unable to create session.')
         return False
    def get_ledger_list():
        """
        List all ledgers.

        :rtype: list
        :return: List of ledgers.
        """
        try:
            logger.info("Let's list all the ledgers...")
            ledgers = DdlServices.ledger_list()
            logger.info('Success. List of ledgers: {}.'.format(ledgers))
            return ledgers
        except Exception as e:
            logger.exception('Unable to list ledgers!')
            return fail_response('Unable to list ledgers!. Please try again.',
                                 HTTPStatus.UNPROCESSABLE_ENTITY)
Exemplo n.º 10
0
 def drop_table(ledger_name=None, table_name=None):
     """Create a Table"""
     try:
         with qldb.session(ledger_name) as session:
             session.execute_lambda(
                 lambda x: DdlServices.do_drop_table(x, table_name),
                 lambda retry_attempt: logger.info(
                     'Retrying due to OCC conflict...'))
             logger.info('Table dropped successfully.')
             return success_response('Table dropped successfully.',
                                     HTTPStatus.CREATED)
     except Exception:
         logger.exception('Error creating table.')
         return fail_response(
             'Unable to create the table. Please try again.',
             HTTPStatus.UNPROCESSABLE_ENTITY)
Exemplo n.º 11
0
 def list_tables(ledger_name):
     """
         Connect to a session for a given ledger using default settings.
         """
     try:
         qldb_session = qldb.session(ledger_name)
         logger.info('Listing table names ')
         tables = qldb_session.list_tables()
         _tables = []
         for table in tables:
             _tables.append(table)
         return _tables
     except Exception as e:
         logger.exception('Unable to create session.')
         return fail_response(
             'Unable to connect ledgers!. Please try again.',
             HTTPStatus.UNPROCESSABLE_ENTITY)
Exemplo n.º 12
0
 def insert_documents(ledger_name, table_name, documents):
     """
         Insert documents into a table in a QLDB ledger.
         """
     try:
         with qldb.session(ledger_name) as session:
             # An INSERT statement creates the initial revision of a document with a version number of zero.
             # QLDB also assigns a unique document identifier in GUID format as part of the metadata.
             session.execute_lambda(
                 lambda executor: DmlServices.do_insert_documents(executor, table_name, documents),
                 lambda retry_attempt: logger.info('Retrying due to OCC conflict...'))
             logger.info('Documents inserted successfully!')
             return success_response('Ledger is active and ready to use.', HTTPStatus.CREATED)
     except Exception as e:
         logger.exception('Error inserting or updating documents.')
         return fail_response('Error inserting or updating documents. error: {}. Please try again.'.format(e),
                              HTTPStatus.UNPROCESSABLE_ENTITY)
def create_export_and_wait_for_completion(name,
                                          bucket,
                                          prefix,
                                          encryption_config,
                                          role_arn=None):
    """
    Request QLDB to export the contents of the journal for the given time period and S3 configuration. Before calling
    this function the S3 bucket should be created, see
    :py:class:`hash_chain.ledger.export_journal.create_s3_bucket_if_not_exists`

    :type name: str
    :param name: Name of the ledger to create a journal export for.

    :type bucket: str
    :param bucket: S3 bucket to write the data to.

    :type prefix: str
    :param prefix: S3 prefix to be prefixed to the files being written.

    :type encryption_config: dict
    :param encryption_config: Encryption configuration for S3.

    :type role_arn: str
    :param role_arn: The IAM role ARN to be used when exporting the journal.

    :rtype: dict
    :return: The result of the request.
    """
    if role_arn is None:
        role_arn = create_export_role(EXPORT_ROLE_NAME,
                                      encryption_config.get('KmsKeyArn'),
                                      ROLE_POLICY_NAME, bucket)
    try:
        start_time = datetime.utcnow() - timedelta(
            minutes=JOURNAL_EXPORT_TIME_WINDOW_MINUTES)
        end_time = datetime.utcnow()

        result = create_export(name, start_time, end_time, bucket, prefix,
                               encryption_config, role_arn)
        wait_for_export_to_complete(config.LEDGER_NAME, result.get('ExportId'))
        logger.info('JournalS3Export for exportId {} is completed.'.format(
            result.get('ExportId')))
        return result
    except Exception as e:
        logger.exception('Unable to create an export!')
        raise e
Exemplo n.º 14
0
 def create_table_index(ledger_name=None,
                        table_name=None,
                        index_attribute=None):
     """Create index on tables in a particular ledger."""
     logger.info('Creating index on all tables in a single transaction...')
     try:
         with qldb.session(ledger_name) as session:
             session.execute_lambda(
                 lambda x: DdlServices.do_create_table_index(
                     x, table_name, index_attribute), lambda retry_attempt:
                 logger.info('Retrying due to OCC conflict...'))
             logger.info('Index created successfully.')
             return success_response('Index created successfully.',
                                     HTTPStatus.CREATED)
     except Exception:
         logger.exception('Unable to create index.')
         return fail_response(
             'Unable to create the index. Please try again.',
             HTTPStatus.UNPROCESSABLE_ENTITY)
Exemplo n.º 15
0
    def verify_block(self):
        """
        Get a journal block from a QLDB ledger.

        After getting the block, we get the digest of the ledger and validate the
        proof returned in the getBlock response.
        """
        try:
            data = get_requested_data()
            with qldb.session(data["ledger_name"]) as session:
                cursor = session.execute_lambda(lambda executor: query_revision_history(executor,
                                                                                        data["table_name"],
                                                                                        data["condition_str"],
                                                                                        data["condition_value"]),
                                                lambda retry_indicator: logger.info('Retrying due to OCC conflict...'))
                row = next(cursor)
                block_address = row.get('blockAddress')
                verify_block(data["ledger_name"], block_address)
        except Exception:
            logger.exception('Unable to query vehicle registration by Vin.')
 def down(self):
     try:
         self.drop_table()
         self.drop_ledger()
     except Exception as e:
         logger.exception('Migration DOWN failed! {}.'.format(e))
Exemplo n.º 17
0
def verify_block(ledger_name, block_address):
    """
    Verify block by validating the proof returned in the getBlock response.

    :type ledger_name: str
    :param ledger_name: The ledger to get digest from.

    :type block_address: str/:py:class:`amazon.ion.simple_types.IonPyDict`
    :param block_address: The address of the block to verify.

    :raises AssertionError: When verification failed.
    """
    logger.info(
        "Let's verify blocks for ledger with name={}.".format(ledger_name))

    try:
        logger.info("First, let's get a digest.")
        digest_result = get_digest_result(ledger_name)

        digest_tip_address = digest_result.get('DigestTipAddress')
        digest_bytes = digest_result.get('Digest')

        logger.info(
            'Got a ledger digest. Digest end address={}, digest={}'.format(
                value_holder_to_string(digest_tip_address.get('IonText')),
                to_base_64(digest_bytes)))
        get_block_result = get_block_with_proof(
            ledger_name, block_address_to_dictionary(block_address),
            digest_tip_address)
        block = get_block_result.get('Block')
        block_hash = parse_block(block)

        verified = verify_document(block_hash, digest_bytes,
                                   get_block_result.get('Proof'))

        if not verified:
            raise AssertionError('Block is not verified!')
        else:
            logger.info('Success! The block is verified.')

        altered_digest = flip_random_bit(digest_bytes)
        logger.info(
            "Let's try flipping one bit in the digest and assert that the block is NOT verified. "
            "The altered digest is: {}".format(to_base_64(altered_digest)))

        verified = verify_document(block_hash, altered_digest,
                                   get_block_result.get('Proof'))

        if verified:
            raise AssertionError(
                'Expected block to not be verified against altered digest.')
        else:
            logger.info(
                'Success! As expected flipping a bit in the digest causes verification to fail.'
            )

        altered_block_hash = flip_random_bit(block_hash)
        logger.info(
            "Let's try flipping one bit in the block's hash and assert that the block is NOT verified. "
            "The altered block hash is: {}.".format(
                to_base_64(altered_block_hash)))

        verified = verify_document(altered_block_hash, digest_bytes,
                                   get_block_result.get('Proof'))

        if verified:
            raise AssertionError(
                'Expected altered block hash to not be verified against digest.'
            )
        else:
            logger.info(
                'Success! As expected flipping a bit in the block hash causes verification to fail.'
            )
    except Exception as e:
        logger.exception(
            'Failed to verify blocks in the ledger with name={}.'.format(
                ledger_name))
        raise e
    if len(journal_blocks) == 0:
        return
    reduce(compare_journal_blocks, journal_blocks)


if __name__ == '__main__':
    """
    Validate the hash chain of a QLDB ledger by stepping through its S3 export.
    
    This code accepts an exportID as an argument, if exportID is passed the code 
    will use that or request QLDB to generate a new export to perform QLDB hash 
    chain validation.
    """
    s3_client = client('s3')
    try:
        if len(argv) == 2:
            export_id = argv[1]
            logger.info('Validating qldb hash chain for ExportId: {}.'.format(
                export_id))

        else:
            logger.info('Requesting qldb to create an export.')
            export_id = create_journal_export()

        journal_export = describe_journal_export(
            config.LEDGER_NAME, export_id).get('ExportDescription')
        journal_blocks = read_export(journal_export, s3_client)
        verify(journal_blocks)
    except Exception:
        logger.exception('Unable to perform hash chain verification.')