Exemplo n.º 1
0
    def validate_policy(policy, transaction):
        if not isinstance(policy, list):
            raise ValidationError('policy must be a list')

        for policy_rule in policy:
            if 'condition' not in policy_rule or 'rule' not in policy_rule:
                raise ValidationError(
                    'policy item must contain a condition and rule')

            parser = PolicyParser(transaction)
            try:
                if parser.parse(policy_rule['condition']) is True:
                    if not parser.parse(policy_rule['rule']) is True:
                        raise ValidationError(
                            'Rule {} evaluated to false'.format(
                                policy_rule['rule']))

            except (AttributeError, KeyError) as e:
                raise ValidationError(
                    'Wrong policy format: {}'.format(policy_rule))
            except TypeError as e:
                pass

        if transaction.operation == Transaction.TRANSFER:
            pass

        return transaction
Exemplo n.º 2
0
    def validate_can_link(bigchain, can_link, public_key):
        logger.info('validating can_link, looking up assets in owner wallet')
        wallet_tx = bigchain.get_owned_ids(public_key)
        wallet_tx_ids = [tx.txid for tx in wallet_tx]
        logger.info('Wallet has %s assets', len(wallet_tx_ids))

        for asset_id in wallet_tx_ids:
            logger.info('Looking up asset: %s', asset_id)
            trans = bigchain.get_transaction(asset_id)
            if trans.operation == Transaction.TRANSFER:
                permission_asset = bigchain.get_transaction(
                    trans.asset['id']).asset
            else:
                permission_asset = trans.asset
            if permission_asset and permission_asset['data'] and\
                    ASSET_RULE_LINK in permission_asset['data']:
                if permission_asset['data']['link'] in can_link:
                    logger.info('Link valid: asset.link found in can_link')
                    break
            else:
                continue
        else:
            raise ValidationError(
                'Linking is not authorized for: {}'.format(public_key))
        return
Exemplo n.º 3
0
    def post_transaction(self, transaction, mode):
        """Submit a valid transaction to the mempool."""
        if not mode or mode not in self.mode_list:
            raise ValidationError('Mode must be one of the following {}.'
                                  .format(', '.join(self.mode_list)))

        payload = {
            'method': mode,
            'jsonrpc': '2.0',
            'params': [encode_transaction(transaction.to_dict())],
            'id': str(uuid4())
        }
        # TODO: handle connection errors!
        return requests.post(self.endpoint, json=payload)
Exemplo n.º 4
0
    def resolve_assets(bigchain, transaction, input_txs):
        if not hasattr(transaction, 'asset'):
            raise ValidationError(
                'Asset not found in transaction {}'.format(transaction))

        if transaction.operation == Transaction.GENESIS:
            return []
        elif transaction.operation == Transaction.CREATE:
            return [transaction.asset]
        elif transaction.operation == Transaction.TRANSFER:
            asset_ids = transaction.get_asset_ids([
                input_tx for (input_, input_tx, status) in input_txs
                if input_tx is not None
            ])
            return [
                bigchain.get_transaction(asset_id).asset
                for asset_id in asset_ids
            ]
Exemplo n.º 5
0
def validate_language(value):
    """Check if `value` is a valid language.
       https://docs.mongodb.com/manual/reference/text-search-languages/

        Args:
            value (str): language to validated

        Returns:
            None: validation successful

        Raises:
            ValidationError: will raise exception in case language is not valid.
    """
    if value not in VALID_LANGUAGES:
        error_str = ('MongoDB does not support text search for the '
                     'language "{}". If you do not understand this error '
                     'message then please rename key/field "language" to '
                     'something else like "lang".').format(value)
        raise ValidationError(error_str)
Exemplo n.º 6
0
def test_validate_transaction_handles_exceptions(b, signed_create_tx):
    """This test makes sure that `BlockPipeline.validate_tx` handles possible
    exceptions from `Transaction.from_dict`.
    """
    from bigchaindb.pipelines.block import BlockPipeline
    block_maker = BlockPipeline()
    from bigchaindb.common.exceptions import ValidationError

    tx_dict = signed_create_tx.to_dict()

    with patch('bigchaindb.models.Transaction.validate') as validate:
        # Assert that validationerror gets caught
        validate.side_effect = ValidationError()
        assert block_maker.validate_tx(tx_dict) is None

        # Assert that another error doesnt
        validate.side_effect = IOError()
        with pytest.raises(IOError):
            block_maker.validate_tx(tx_dict)
def test_vote_validate_transaction(b):
    from bigchaindb.pipelines import vote
    from bigchaindb.common.exceptions import ValidationError

    tx = dummy_tx(b).to_dict()
    vote_obj = vote.Vote()
    validation = vote_obj.validate_tx(tx, 123, 1)
    assert validation == (True, 123, 1)

    with patch('bigchaindb.models.Transaction.validate') as validate:
        # Assert that validationerror gets caught
        validate.side_effect = ValidationError()
        validation = vote_obj.validate_tx(tx, 456, 10)
        assert validation == (False, 456, 10)

        # Assert that another error doesnt
        validate.side_effect = IOError()
        with pytest.raises(IOError):
            validation = vote_obj.validate_tx(tx, 456, 10)
Exemplo n.º 8
0
def validate_key(obj_name, key):
    """Check if `key` contains ".", "$" or null characters
       https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names

        Args:
            obj_name (str): object name to use when raising exception
            key (str): key to validated

        Returns:
            None: indicates validation successfull

        Raises:
            ValidationError: raise execption incase of regex match.
    """
    if re.search(r'^[$]|\.|\x00', key):
        error_str = ('Invalid key name "{}" in {} object. The '
                     'key name cannot contain characters '
                     '".", "$" or null characters').format(key, obj_name)
        raise ValidationError(error_str) from ValueError()
Exemplo n.º 9
0
    def validate(self, bigchain):
        """Validate a transaction.

        Args:
            bigchain (Bigchain): an instantiated bigchaindb.Bigchain object.

        Returns:
            The transaction (Transaction) if the transaction is valid else it
            raises an exception describing the reason why the transaction is
            invalid.

        Raises:
            ValidationError: If the transaction is invalid
        """
        if len(self.inputs) == 0:
            raise ValidationError('Transaction contains no inputs')

        input_conditions = []
        inputs_defined = all([input_.fulfills for input_ in self.inputs])

        # validate amounts
        if any(output.amount < 1 for output in self.outputs):
            raise AmountError('`amount` needs to be greater than zero')

        if self.operation in (Transaction.CREATE, Transaction.GENESIS):
            # validate asset
            if self.asset['data'] is not None and not isinstance(
                    self.asset['data'], dict):
                raise ValidationError(
                    ('`asset.data` must be a dict instance or '
                     'None for `CREATE` transactions'))
            # validate inputs
            if inputs_defined:
                raise ValidationError('A CREATE operation has no inputs')
        elif self.operation == Transaction.TRANSFER:
            # validate asset
            if not isinstance(self.asset['id'], str):
                raise ValidationError('`asset.id` must be a string for '
                                      '`TRANSFER` transations')
            # check inputs
            if not inputs_defined:
                raise ValidationError('Only `CREATE` transactions can have '
                                      'null inputs')

            # store the inputs so that we can check if the asset ids match
            input_txs = []
            for input_ in self.inputs:
                input_txid = input_.fulfills.txid
                input_tx, status = bigchain.\
                    get_transaction(input_txid, include_status=True)

                if input_tx is None:
                    raise InputDoesNotExist(
                        "input `{}` doesn't exist".format(input_txid))

                if status != bigchain.TX_VALID:
                    raise TransactionNotInValidBlock(
                        'input `{}` does not exist in a valid block'.format(
                            input_txid))

                spent = bigchain.get_spent(input_txid, input_.fulfills.output)
                if spent and spent.id != self.id:
                    raise DoubleSpend(
                        'input `{}` was already spent'.format(input_txid))

                output = input_tx.outputs[input_.fulfills.output]
                input_conditions.append(output)
                input_txs.append(input_tx)
                if output.amount < 1:
                    raise AmountError('`amount` needs to be greater than zero')

            # Validate that all inputs are distinct
            links = [i.fulfills.to_uri() for i in self.inputs]
            if len(links) != len(set(links)):
                raise DoubleSpend('tx "{}" spends inputs twice'.format(
                    self.id))

            # validate asset id
            asset_id = Transaction.get_asset_id(input_txs)
            if asset_id != self.asset['id']:
                raise AssetIdMismatch(('The asset id of the input does not'
                                       ' match the asset id of the'
                                       ' transaction'))

            # validate the amounts
            for output in self.outputs:
                if output.amount < 1:
                    raise AmountError('`amount` needs to be greater than zero')

            input_amount = sum([
                input_condition.amount for input_condition in input_conditions
            ])
            output_amount = sum(
                [output_condition.amount for output_condition in self.outputs])

            if output_amount != input_amount:
                raise AmountError(
                    ('The amount used in the inputs `{}`'
                     ' needs to be same as the amount used'
                     ' in the outputs `{}`').format(input_amount,
                                                    output_amount))

        else:
            allowed_operations = ', '.join(Transaction.ALLOWED_OPERATIONS)
            raise ValidationError(
                '`operation`: `{}` must be either {}.'.format(
                    self.operation, allowed_operations))

        if not self.inputs_valid(input_conditions):
            raise InvalidSignature('Transaction signature is invalid.')

        return self
Exemplo n.º 10
0
    def validate_link(transaction, bigchain):
        logger.info('Validating link')
        public_key = transaction.inputs[0].owners_before[0]

        # Dont't do anything when it's GENESIS or TRANSFER transaction
        if transaction.operation == Transaction.GENESIS or\
                transaction.operation == Transaction.TRANSFER:
            return

        if not hasattr(transaction, 'asset'):
            raise ValidationError(
                'Asset not found in transaction {}'.format(transaction))

        # If link is not being used, don't do anything
        if transaction.asset[
                'data'] and ASSET_RULE_LINK not in transaction.asset['data']:
            return

        link = transaction.asset['data']['link']
        logger.info('Link: %s', link)
        tx_to_link = bigchain.get_transaction(link)

        if not tx_to_link:
            raise ValidationError(
                'Transaction not resolved to link: {}'.format(link))

        logger.info('Link Transaction: %s', tx_to_link.id)

        if tx_to_link and not hasattr(tx_to_link, 'metadata'):
            raise ValidationError(
                'Metadata not found in transaction {}'.format(tx_to_link))

        if tx_to_link.metadata is None or METADATA_RULE_CAN_LINK not in tx_to_link.metadata:
            raise ValidationError(
                'can_link not found in metadata of transaction {}'.format(
                    tx_to_link))

        can_link = tx_to_link.metadata[METADATA_RULE_CAN_LINK]
        logger.info('Can link: %s', can_link)

        # can_link validation
        # if can_link is a list
        # check if can_link is a list of transaction ids or public keys
        # check if the public key of the user is a part of it or not
        # OR
        # check if the user has a premission asset linked to the can_link asset
        if isinstance(can_link, list):
            logger.info('can_link is a list')
            if SmartAssetConsensusRules.check_if_transaction_id(
                    bigchain, can_link[0]):
                SmartAssetConsensusRules\
                .validate_can_link(bigchain, can_link, public_key)
            else:
                if public_key in can_link:
                    logger.info('Link valid: public key in can_link')
                    return
                else:
                    raise ValidationError(
                        'Linking is not authorized for: {}'.format(public_key))
        # backward compatibility - if can_link is string then convert it to a list
        elif isinstance(can_link, str):
            logger.info('can_link is a string')
            can_link_list = [can_link]
            SmartAssetConsensusRules\
            .validate_can_link(bigchain, can_link_list, public_key)
        else:
            raise ValidationError('can_link is not valid')
        return