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
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
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)
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 ]
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)
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)
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()
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
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