|
| 1 | +# A built in module that provides functions from python standard library |
| 2 | +import functools |
| 3 | + |
| 4 | +MINING_REWARD = 10 |
| 5 | +genesis_block = { |
| 6 | + 'previous_hash': '', |
| 7 | + 'index': 0, |
| 8 | + 'transactions': [] |
| 9 | +} |
| 10 | +my_blockchain = [genesis_block] |
| 11 | +open_transactions = [] |
| 12 | +waiting_for_input = True |
| 13 | +owner = 'Nicolas' |
| 14 | +participants = set([owner]) |
| 15 | + |
| 16 | +def add__line(): |
| 17 | + print('------------------------------') |
| 18 | + |
| 19 | +def generate_options_menu(): |
| 20 | + add__line() |
| 21 | + print('Please choose an option:') |
| 22 | + print('1: Add a new transaction') |
| 23 | + print('2: Mine a new block') |
| 24 | + print('3: Output all blockchain blocks') |
| 25 | + print('4: Output all participants') |
| 26 | + print('h: Manipulate blockchain') |
| 27 | + print('q: Quit') |
| 28 | + add__line() |
| 29 | + |
| 30 | +def get_user_input(): |
| 31 | + user_input = input('Please enter your selection: ') |
| 32 | + add__line() |
| 33 | + return user_input |
| 34 | + |
| 35 | +def hash_block(block): |
| 36 | + """ Hash a block using its strucutre as base """ |
| 37 | + return str(block[key] for key in block) |
| 38 | + |
| 39 | +def mine_block(): |
| 40 | + last_block = my_blockchain[-1] |
| 41 | + hashed_block = hash_block(last_block) |
| 42 | + |
| 43 | + reward_transaction = { |
| 44 | + 'sender': 'MINING', |
| 45 | + 'recipient': owner, |
| 46 | + 'amount': MINING_REWARD |
| 47 | + } |
| 48 | + |
| 49 | + block = { |
| 50 | + 'previous_hash': hashed_block, |
| 51 | + 'index': len(my_blockchain), |
| 52 | + 'transactions': [open_transactions, reward_transaction] |
| 53 | + } |
| 54 | + |
| 55 | + my_blockchain.append(block) |
| 56 | + print('Block added!') |
| 57 | + add__line() |
| 58 | + return True |
| 59 | + |
| 60 | +def get_transaction_value(): |
| 61 | + """ Returns the input of the user (a new transaction amount and its recipient) as a tuple """ |
| 62 | + tx_recipient_input = input('Please enter the recipient of the transaction: ') |
| 63 | + tx_amount_input = float(input('Please enter your transaction input: ')) |
| 64 | + add__line() |
| 65 | + |
| 66 | + # When you return a tuple, it does not need to be enclosed in parentheses (is optional i guess) |
| 67 | + return tx_recipient_input, tx_amount_input |
| 68 | + |
| 69 | +def take_last_blockchain_value(): |
| 70 | + if len(my_blockchain) < 1: |
| 71 | + return None |
| 72 | + |
| 73 | + return my_blockchain[-1] |
| 74 | + |
| 75 | +def verify_transaction(transaction): |
| 76 | + sender_balance = get_balance(transaction['sender']) |
| 77 | + |
| 78 | + if sender_balance >= transaction['amount']: |
| 79 | + return True |
| 80 | + return False |
| 81 | + |
| 82 | +def add_transaction(sender, recipient, amount=1): |
| 83 | + """ |
| 84 | + Add a new transaction to the list of open transactions (which will be added to the next mined block) |
| 85 | +
|
| 86 | + Arguments: |
| 87 | + :sender: The sender of the coins. |
| 88 | + :recipient: The recipient of the coins. |
| 89 | + :amount: The amount of the transaction. |
| 90 | + """ |
| 91 | + |
| 92 | + new_transaction = { |
| 93 | + 'sender': sender, |
| 94 | + 'recipient': recipient, |
| 95 | + 'amount': amount |
| 96 | + } |
| 97 | + |
| 98 | + if verify_transaction(new_transaction): |
| 99 | + open_transactions.append(new_transaction) |
| 100 | + participants.add(sender) |
| 101 | + participants.add(recipient) |
| 102 | + else: |
| 103 | + print('Transaction failed! Not enough balance!') |
| 104 | + add__line() |
| 105 | + |
| 106 | +def return_all_blocks(): |
| 107 | + print('---Outputting all blocks---') |
| 108 | + |
| 109 | + for block in my_blockchain: |
| 110 | + print(f'Outputting block: {block}') |
| 111 | + add__line() |
| 112 | + |
| 113 | +def get_balance(participant): |
| 114 | + # final_balance = 0 |
| 115 | + sent_transactions = [[tx['amount'] for tx in block['transactions'] if tx['sender'] == participant] for block in my_blockchain] |
| 116 | + recieved_transactions = [[tx['amount'] for tx in block['transactions'] if tx['recipient'] == participant] for block in my_blockchain] |
| 117 | + open_sent_transactions = [tx['amount'] for tx in open_transactions if tx['sender'] == participant] |
| 118 | + |
| 119 | + sent_transactions.append(open_sent_transactions) |
| 120 | + # This function is a reducer that will sum up all the values in a list of lists |
| 121 | + # The lambda function is an anonymous function that takes two arguments, the first one is the accumulator (the sum of the previous values) and the second one is the current value |
| 122 | + sent_amounts = functools.reduce(lambda tx_sum, tx_amt: tx_sum + sum(tx_amt) if len(tx_amt) > 0 else tx_amt + 0, sent_transactions, 0) |
| 123 | + # for sent_amount in sent_transactions: |
| 124 | + # if len(sent_amount) > 0: |
| 125 | + # final_balance -= sent_amount |
| 126 | + |
| 127 | + recieved_amounts = functools.reduce(lambda tx_sum, tx_amt: tx_sum + sum(tx_amt) if len(tx_amt) > 0 else tx_amt + 0, recieved_transactions, 0) |
| 128 | + # for recieved_amount in recieved_transactions: |
| 129 | + # if len(recieved_amount) > 0: |
| 130 | + # final_balance += recieved_amount |
| 131 | + |
| 132 | + return recieved_amounts - sent_amounts |
| 133 | + |
| 134 | +def verify_chain(): |
| 135 | + """ The function helps to verify the integrity of the blockchain by checking if each block's previous hash matches the hash of the previous block. """ |
| 136 | + for (index, block) in enumerate(my_blockchain): |
| 137 | + if index == 0: |
| 138 | + continue |
| 139 | + if block['previous_hash'] != hash_block(my_blockchain[index - 1]): |
| 140 | + return False |
| 141 | + return True |
| 142 | + |
| 143 | +def verify_transactions(): |
| 144 | + """ The function verifies all open transactions to ensure they are valid. """ |
| 145 | + return all([tx for tx in open_transactions if not verify_transaction(tx)]) |
| 146 | + |
| 147 | +while waiting_for_input: |
| 148 | + generate_options_menu() |
| 149 | + |
| 150 | + user_choice = get_user_input() |
| 151 | + |
| 152 | + if user_choice == '1': |
| 153 | + tx_input_data = get_transaction_value() |
| 154 | + recipient, amount = tx_input_data |
| 155 | + add_transaction(owner, recipient, amount) |
| 156 | + elif user_choice == '2': |
| 157 | + if mine_block(): |
| 158 | + open_transactions = [] |
| 159 | + elif user_choice == '3': |
| 160 | + return_all_blocks() |
| 161 | + elif user_choice == '4': |
| 162 | + print(participants) |
| 163 | + elif user_choice == 'q': |
| 164 | + waiting_for_input = False |
| 165 | + elif user_choice == 'h': |
| 166 | + if len(my_blockchain) >= 1: |
| 167 | + my_blockchain[0] = [2.0] |
| 168 | + else: |
| 169 | + print('Invalid input, please choose a valid option') |
| 170 | + if not verify_chain(): |
| 171 | + print('Invalid blockchain!') |
| 172 | + waiting_for_input = False |
| 173 | + print(f"Balance of {owner}: {get_balance(owner)}") |
| 174 | +else: |
| 175 | + print('User left!') |
| 176 | + |
| 177 | +add__line() |
| 178 | +print('Done!') |
0 commit comments