def test_calculate_balance(): blockchain = Blockchain() wallet = Wallet() assert Wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE amount = 20 transacion = Transaction(wallet, 'recipient', amount) blockchain.add_block([transacion.to_json()]) assert Wallet.calculate_balance( blockchain, wallet.address) == STARTING_BALANCE - amount recieved_amount1 = 10 recieved_transaction1 = Transaction(Wallet(), wallet.address, recieved_amount1) recieved_amount2 = 40 recieved_transaction2 = Transaction(Wallet(), wallet.address, recieved_amount2) blockchain.add_block( [recieved_transaction1.to_json(), recieved_transaction2.to_json()]) assert Wallet.calculate_balance( blockchain, wallet.address ) == STARTING_BALANCE - amount + recieved_amount1 + recieved_amount2
def test_calculate_balance(): blockchain = Blockchain() wallet = Wallet() assert wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE amount = 34 transaction = Transaction(wallet, 'recipient', amount) blockchain.add_block([transaction.to_json()]) assert wallet.calculate_balance(blockchain, wallet.address) ==\ STARTING_BALANCE - amount received_amount_1 = 40 received_transaction_1 = Transaction(Wallet(), wallet.address, received_amount_1) received_amount_2 = 110 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 test_calculate_balance(): blockchain = Blockchain() wallet = Wallet() # NEW: new Wallet() `wallet` should have initial balance assert Wallet.calculate_balance(blockchain, wallet.address) == INITIAL_BALANCE sent_amt = 50 outgoing_transaction = Transaction( wallet, "a13b2bf4", sent_amt) # send to `wallet` from "a13b2bf4" blockchain.add_block([outgoing_transaction.serialize_to_json()]) # SEND: `wallet` should have initial balance sans amt assert Wallet.calculate_balance( blockchain, wallet.address) == INITIAL_BALANCE - sent_amt received_amt_1 = 25 incoming_transaction_1 = Transaction( Wallet(), wallet.address, received_amt_1) # receive by `wallet` from new Wallet() received_amt_2 = 77 incoming_transaction_2 = Transaction(Wallet(), wallet.address, received_amt_2) blockchain.add_block([ incoming_transaction_1.serialize_to_json(), incoming_transaction_2.serialize_to_json() ]) # RECEIVE: `wallet` should have initial balance plus incoming amt(s) assert Wallet.calculate_balance( blockchain, wallet.address ) == INITIAL_BALANCE - sent_amt + received_amt_1 + received_amt_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 a chain - only 1 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 == True: raise Exception('There can only be one mining reward per block!'+f' Check block with hash {block.hash}') has_mining_reward = True else: blockchain_history = Blockchain() blockchain_history.chain = chain[0:i] balance_history = Wallet.calculate_balance(blockchain_history,transaction.input['address']) if balance_history != transaction.input['amount']: raise Exception(f'Transaction {transaction.id} has an invalid input amount!') Transaction.is_valid_transaction(transaction)
def is_valid_transaction_chain(chain): 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( 'Transaction {transaction.id} has an invalid input amount' ) Transaction.is_valid_transaction(transaction)
def is_transaction_chain_valid(chain): """ Validate chain with blocks of transactions -- Only one reward per transaction. -- Each transaction must only apprear once in chain. """ transaction_ids = [] for i in range(len(chain)): block = chain[i] contains_mining_reward = False for transaction_json in block.data: transaction_obj = Transaction.from_json(transaction_json) if transaction_obj.id in transaction_ids: raise Exception( f"Transaction: {transaction_obj.id} is not unique") transaction_ids.append(transaction_obj.id) if transaction_obj.input == MINIING_REWARD_INPUT: if contains_mining_reward: raise Exception( f'Duplicate mining reward in block: {block.hash}') contains_mining_reward = True else: blockchain_tillnow = BlockChain() blockchain_tillnow.chain = chain[0:i] balance_tillnow = Wallet.calculate_balance( blockchain_tillnow, transaction_obj.input["address"]) if balance_tillnow != transaction_obj.input["amount"]: raise Exception( f"Transaction: {transaction_obj.id} has invalid input" ) Transaction.is_transaction_valid(transaction_obj)
def test_wallet_balance_amount(): wallet = Wallet() blockchain = Blockchain() amount = 34 tr1 = Transactions(wallet, 'recp1', amount) blockchain.add_block([tr1.to_json()]) assert Wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE - amount
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 be only 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(f'There can be only one mining reward per block check this block: {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 '\ 'invalid input amount') Transaction.transaction_is_valid(transaction)
def test_calculate_balance(): blockchain = BlockChain() wallet = Wallet() assert wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE sent_amount = 100 transaction = Transaction(wallet, "abcd", sent_amount) blockchain.add_block([transaction.to_json()]) assert wallet.calculate_balance( blockchain, wallet.address) == STARTING_BALANCE - sent_amount received_amount_1 = 100 received_1 = Transaction(Wallet(), wallet.address, received_amount_1) received_amount_2 = 100 received_2 = Transaction(Wallet(), wallet.address, received_amount_2) blockchain.add_block([received_1.to_json(), received_2.to_json()]) assert wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE - \ sent_amount + received_amount_1 + received_amount_2
def test_calculate_balance(): blockchain = Blockchain() wallet = Wallet() assert Wallet.calculate_balance(blockchain, wallet.address) == wallet.balance transaction = Transaction(wallet, 'recipient', 30) blockchain.add_block([transaction.to_json()]) assert Wallet.calculate_balance(blockchain, wallet.address) == wallet.balance - 30 received_amount_1 = 25 recieved_transaction_1 = Transaction(Wallet(), wallet.address, received_amount_1) received_amount_2 = 25 recieved_transaction_2 = Transaction(Wallet(), wallet.address, received_amount_2) blockchain.add_block( [recieved_transaction_1.to_json(), recieved_transaction_2.to_json()]) assert Wallet.calculate_balance( blockchain, wallet.address ) == wallet.balance - 30 + received_amount_1 + received_amount_2
def test_calculate_balance(): ''' Test that a wallet can calculate its balance correctly. ''' blockchain = Blockchain() wallet = Wallet() # Assert that the wallet's balance on a new blockchain is equal to the # global starting balance. assert (Wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE) # Create a transaction of amount 50 and verify that it is subtracted from # the wallet's balance after adding the new transaction to the blockchain. amount = 50 trans = Transaction(wallet, 'recipient', amount) blockchain.add_block([trans.to_json()]) EXPECTED_BALANCE = STARTING_BALANCE - amount # Assert that the new calculated balance is equal to the expected balance. assert (Wallet.calculate_balance(blockchain, wallet.address) == EXPECTED_BALANCE) # Create first transaction to be recieved by the wallet after the calculation. recieved_amount1 = 25 recieved_trans1 = Transaction(Wallet(), wallet.address, recieved_amount1) # Create second transaction to be recieved by the wallet after the calculation. recieved_amount2 = 43 recieved_trans2 = Transaction(Wallet(), wallet.address, recieved_amount2) blockchain.add_block( [recieved_trans1.to_json(), recieved_trans2.to_json()]) EXPECTED_BALANCE = (STARTING_BALANCE - amount + recieved_amount1 + recieved_amount2) # Assert that the new calculated balance is equal to the expected balance. assert (Wallet.calculate_balance(blockchain, wallet.address) == EXPECTED_BALANCE)
def is_tx_chain_valid(blockchain): """ Validate incoming blockchain comprised of blocks with Tx therein qua the following ruleset: - each Tx occurs once in the blockchain (i.e. 'double spend') - there is only one valid reward Tx per block - transaction obj must be intrinsically valid """ # Tx tracked by id, raise if duplicate tx_tracking_pool = set() # unwrap blockchain, unwrap blocks therein, deserialize ea block's Tx and parse them for i in range(len(blockchain)): block = blockchain[i] mining_reward_extant = False for serialized_tx in block.data: deserialized_tx = Transaction.deserialize_from_json( serialized_tx) # if Tx already exists if (deserialized_tx.id in tx_tracking_pool): raise Exception( f"Transaction {deserialized_tx.id} is not unique; this transaction is therefore invalid." ) # add Tx to tracking pool tx_tracking_pool.add(deserialized_tx.id) # if Tx is a block reward, only validate against reward fields if (deserialized_tx.input == MINING_REWARD_INPUT): if (mining_reward_extant): raise Exception(f""" There can only be one mining reward per block. Evaluation of block with hash: {block.hash} recommended.""" ) mining_reward_extant = True else: # recalc balance after every Tx to prevent input tamper blockchain_provenance = Blockchain() blockchain_provenance.chain = blockchain[0:i] balance_provenance = Wallet.calculate_balance( blockchain_provenance, deserialized_tx.input["address"]) if (balance_provenance != deserialized_tx.input["amount"]): raise Exception( f"Transaction {deserialized_tx.id} contains an invalid input amount." ) # last, run validator to check format Transaction.is_tx_valid(deserialized_tx)
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 block in chain: 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.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 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: # Seperate the mining reward blocks from other blocks for amount validation 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)
def is_valid_transaction_chain(chain): """ Enforce the rules of the blockchain - Each transaction must only appear once int ge blockchain - There can only be one mining reward per block - Each transaction must be valid :param chain: :return: """ 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.input == MINING_REWARD_INPUT: if has_mining_reward: raise Exception( f"There can only be one mining reward in a block. Check block {block.hash}" ) has_mining_reward = True else: if transaction.id in transaction_ids: raise Exception( f"Transaction {transaction.id} is not unique") transaction_ids.add(transaction.id) 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)
def is_valid_transaction_chain(chain): """ enforces the rules of a chain composed of blocks of transctions. - Each transaction must only appear once in a 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 duplicated') if transaction.input == MINING_REWARD_INPUT: if has_mining_reward: raise Exception('Invalid mining rewards'\ f'check with the hash: {block.hash}') has_mining_reward = True else: transaction_ids.add(transaction.id) 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)
def is_valid_transaction_chain(chain): """ Enforce the rules of a chain compoesd of blocks of transactions. 1) Each transaction must only appear once in the chain. 2) There can only be one mining reward per block. 3) 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 this has: {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"The transaction {transaction.id} has an invalid input amount." ) Transaction.is_valid_transaction(transaction)
def is_chain_transaction_valid(chain): """ Method to verify if each of the transactions in the chain is valid. Transaction is valid if: 1. Each transation appears only once in the blockchain. 2. There is only one reward transaction. 3. The transaction is valid. """ transaction_ids = set() for index, block in enumerate(chain): if index == 0: continue has_reward_transaction = False for transaction_json in block.data: transaction = Transactions.from_json(transaction_json) if transaction.input == MINING_REWARD_INPUT: if has_reward_transaction is True: raise Exception( "Transaction with id %s has multiple rewards" % (transaction.id)) has_reward_transaction = True else: #calculate historical balance using a historical balance historical_chain = chain[:index] historical_blockchain = Blockchain() historical_blockchain.chain = historical_chain #get the balance amount of the address balance = Wallet.calculate_balance( historical_blockchain, transaction.input['address']) if balance != transaction.input['amount']: raise Exception( "Transaction id %s did not have the correct balance" % (transaction.id)) if transaction.id in transaction_ids: raise Exception("Transaction with id %s is not unique" % (transaction.id)) transaction_ids.add(transaction.id) Transactions.verify_transaction(transaction)
def is_valid_trans_chain(chain): ''' Enforce the rules of a chain composed of blocks of transaction. - Each transaction must only appear ONCE in the chain. - There can only be ONE mining reward per block. - Each transaction MUST be valid. ''' trans_ids = set() for i in range(len(chain)): block = chain[i] has_mining_reward = False for trans_json in block.data: trans = Transaction.from_json(trans_json) if trans.input == MINING_REWARD_INPUT: if has_mining_reward: raise Exception('There can only be one mining reward ' 'per block. Check block with hash: ' f'{block.hash}') has_mining_reward = True else: if trans.id in trans_ids: raise Exception( f'Transaction {trans.id} is not unique.') trans_ids.add(trans.id) historic_blockchain = Blockchain() historic_blockchain.chain = chain[0:i] historic_balance = Wallet.calculate_balance( historic_blockchain, trans.input['address']) if historic_balance != trans.input['amount']: raise Exception( f'Transaction {trans.id} has an invalid input amount.' ) Transaction.is_valid(trans)
def test_wallet_starting_balance(): wallet = Wallet() blockchain = Blockchain() assert Wallet.calculate_balance(blockchain, wallet.address) == STARTING_BALANCE