def test_transaction_create_invalid_schema(self, mock_is_valid_schema): err_message = 'Validation error' mock_is_valid_schema.side_effect = Mock( side_effect=TransactionError(err_message)) with self.assertRaises(TransactionError) as err: Transaction.create(**self.transaction_info) self.assertTrue(mock_is_valid_schema.called) self.assertIsInstance(err, TransactionError) self.assertIn(err_message, err.message)
def test_transaction_create_valid_schema(self, mock_is_valid_schema): mock_is_valid_schema.return_value = True transaction = Transaction.create(**self.transaction_info) self.assertTrue(mock_is_valid_schema.called) self.assertIsInstance(transaction, Transaction) self.assertTrue( all([ attr in self.transaction.info.keys() for attr in self.transaction_info.keys() ])) self.assertTrue( all([ value in self.transaction.info.values() for value in self.transaction_info.values() ]))
def is_valid_transaction_data(chain: list): """ Perform checks to enforce the consistnecy of transactions data in the chain blocks: Each transaction mush only appear once in the chain, there can only be one mining reward per block and each transaction must be valid. :param list chain: blockchain chain of blocks. :raise BlockchainError: on invalid transaction data. """ from src.client.models.transaction import Transaction from src.client.models.wallet import Wallet transaction_uuids = set() for index, block in enumerate(chain, start=0): has_reward = False for transaction_info in block.data: try: transaction = Transaction.create(**transaction_info) Transaction.is_valid(transaction) except TransactionError as err: message = f'Invalid transaction. {err.message}.' logger.error(f'[Blockchain] Validation error. {message}') raise BlockchainError(message) if transaction.uuid in transaction_uuids: message = f'Repetead transaction uuid found: {transaction.uuid}.' logger.error(f'[Blockchain] Validation error. {message}') raise BlockchainError(message) transaction_uuids.add(transaction.uuid) address = transaction.input.get('address') if address == MINING_REWARD_INPUT.get('address'): if has_reward: message = f'Multiple mining rewards in the same block: {block}.' logger.error( f'[Blockchain] Validation error. {message}') raise BlockchainError(message) has_reward = True else: historic_blockchain = Blockchain() historic_blockchain.chain = chain[:index] historic_balance = Wallet.get_balance( historic_blockchain, address) amount = transaction.input.get('amount') if historic_balance != amount: message = f'Address {address} historic balance inconsistency: {historic_balance} ({amount}).' logger.error( f'[Blockchain] Validation error. {message}') raise BlockchainError(message)
async def transact(data: dict): logger.info('[API] POST transact. New transaction.') recipient = data.get('recipient') amount = data.get('amount') transaction = router.transactions_pool.get_transaction( router.wallet.address) if transaction: transaction.update(router.wallet, recipient, amount) logger.info( f'[API] POST transact. Transaction updated: {transaction}.') else: transaction = Transaction.create(sender=router.wallet, recipient=recipient, amount=amount) logger.info(f'[API] POST transact. Transaction made: {transaction}.') router.transactions_pool.add_transaction(transaction) await router.p2p_server.broadcast_transaction(transaction) return {'transaction': transaction}
def _generate_transaction(self): output = {} output[self.wallet.address] = MINING_REWARD input = MINING_REWARD_INPUT id = uuid.uuid4().int return Transaction.create(uuid=id, input=input, output=output)