def test_calculate_balance(): blockchain = Blockchain() wallet = Wallet() assert Wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE amount = 50 transaction = Transaction(wallet, 'recipient', amount) blockchain.add_block([transaction.to_json()]) assert Wallet.calculate_balance( blockchain, wallet.address) == STARTING_BALANCE - amount received_amount_1 = 25 received_transaction_1 = Transaction(Wallet(), wallet.address, received_amount_1) received_amount_2 = 77 received_transaction_2 = Transaction(Wallet(), wallet.address, received_amount_2) blockchain.add_block( [received_transaction_1.to_json(), received_transaction_2.to_json()]) assert Wallet.calculate_balance(blockchain, wallet.address) == \ STARTING_BALANCE - amount + received_amount_1 + received_amount_2
def is_valid_transaction_chain(chain): """ Enforce the rules of a chain composed of blocks of transactions: - Each transaction must only appear once in the chain. - There can only be one mining reward per block. - Each transaction must be valid. """ transaction_ids = set() for i in range(len(chain)): block = chain[i] has_mining_reward = False for transaction_json in block.data: transaction = Transaction.from_json(transaction_json) if transaction.id in transaction_ids: raise Exception( f'Transaction {transaction.id} is not unique.') transaction_ids.add(transaction.id) if transaction.input == MINING_REWARD_INPUT: if has_mining_reward: raise Exception('There can only be one mining reward per block. '\ f'Check block with hash: {block.hash}') has_mining_reward = True else: historic_blockchain = Blockchain() historic_blockchain.chain = chain[0:i] historic_balance = Wallet.calculate_balance( historic_blockchain, transaction.input['address']) if historic_balance != transaction.input['amount']: raise Exception(f'Transaction {transaction.id} has an invalid '\ 'input amount') Transaction.is_valid_transaction(transaction)