Skip to content

Commit 086ce30

Browse files
committed
Restore frontend (backend interlude) functionality
1 parent 34d49b9 commit 086ce30

5 files changed

Lines changed: 102 additions & 5 deletions

File tree

backend/app/__init__.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import random
44

55
from flask import Flask, jsonify, request
6+
from flask_cors import CORS
67

78
from backend.blockchain.blockchain import Blockchain
89
from backend.wallet.wallet import Wallet
@@ -11,8 +12,9 @@
1112
from backend.pubsub import PubSub
1213

1314
app = Flask(__name__)
15+
CORS(app, resources={ r'/*': { 'origins': 'http://localhost:3000' } })
1416
blockchain = Blockchain()
15-
wallet = Wallet()
17+
wallet = Wallet(blockchain)
1618
transaction_pool = TransactionPool()
1719
pubsub = PubSub(blockchain, transaction_pool)
1820

@@ -24,9 +26,23 @@ def route_default():
2426
def route_blockchain():
2527
return jsonify(blockchain.to_json())
2628

29+
@app.route('/blockchain/range')
30+
def route_blockchain_range():
31+
# http://localhost:5050/blockchain/range?start=2&end=5
32+
start = int(request.args.get('start'))
33+
end = int(request.args.get('end'))
34+
35+
return jsonify(blockchain.to_json()[::-1][start:end])
36+
37+
@app.route('/blockchain/length')
38+
def route_blockchain_length():
39+
return jsonify(len(blockchain.chain))
40+
2741
@app.route('/blockchain/mine')
2842
def route_blockchain_mine():
29-
blockchain.add_block(transaction_pool.transaction_data())
43+
transaction_data = transaction_pool.transaction_data()
44+
transaction_data.append(Transaction.reward_transaction(wallet).to_json())
45+
blockchain.add_block(transaction_data)
3046
block = blockchain.chain[-1]
3147
pubsub.broadcast_block(block)
3248
transaction_pool.clear_blockchain_transactions(blockchain)
@@ -52,9 +68,28 @@ def route_wallet_transact():
5268
)
5369

5470
pubsub.broadcast_transaction(transaction)
71+
transaction_pool.set_transaction(transaction)
5572

5673
return jsonify(transaction.to_json())
5774

75+
@app.route('/wallet/info')
76+
def route_wallet_info():
77+
return jsonify({ 'address': wallet.address, 'balance': wallet.balance })
78+
79+
@app.route('/known-addresses')
80+
def route_known_addresses():
81+
known_addresses = set()
82+
83+
for block in blockchain.chain:
84+
for transaction in block.data:
85+
known_addresses.update(transaction['output'].keys())
86+
87+
return jsonify(list(known_addresses))
88+
89+
@app.route('/transactions')
90+
def route_transactions():
91+
return jsonify(transaction_pool.transaction_data())
92+
5893
ROOT_PORT = 5050
5994
PORT = ROOT_PORT
6095

@@ -70,5 +105,17 @@ def route_wallet_transact():
70105
except Exception as e:
71106
print(f'\n -- Error synchronizing: {e}')
72107

73-
app.run(port=5050)
108+
if os.environ.get('SEED_DATA') == 'True':
109+
for i in range(10):
110+
blockchain.add_block([
111+
Transaction(Wallet(), Wallet().address, random.randint(2, 50)).to_json(),
112+
Transaction(Wallet(), Wallet().address, random.randint(2, 50)).to_json()
113+
])
114+
115+
for i in range(3):
116+
transaction_pool.set_transaction(
117+
Transaction(Wallet(), Wallet().address, random.randint(2, 50))
118+
)
119+
120+
app.run(port=PORT)
74121

backend/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@
66
MINE_RATE = 4 * SECONDS
77

88
STARTING_BALANCE = 1000
9+
10+
MINING_REWARD = 50
11+
MINING_REWARD_INPUT = { 'address': '*--official-mining-reward--*' }

backend/wallet/transaction.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import uuid
33

44
from backend.wallet.wallet import Wallet
5+
from backend.config import MINING_REWARD, MINING_REWARD_INPUT
56

67
class Transaction:
78
"""
@@ -88,6 +89,11 @@ def is_valid_transaction(transaction):
8889
Validate a transaction.
8990
Raise an exception for invalid transactions.
9091
"""
92+
if transaction.input == MINING_REWARD_INPUT:
93+
if list(transaction.output.values()) != [MINING_REWARD]:
94+
raise Exception('Invalid mining reward')
95+
return
96+
9197
output_total = sum(transaction.output.values())
9298

9399
if transaction.input['amount'] != output_total:
@@ -100,6 +106,16 @@ def is_valid_transaction(transaction):
100106
):
101107
raise Exception('Invalid signature')
102108

109+
@staticmethod
110+
def reward_transaction(miner_wallet):
111+
"""
112+
Generate a reward transaction that award the miner.
113+
"""
114+
output = {}
115+
output[miner_wallet.address] = MINING_REWARD
116+
117+
return Transaction(input=MINING_REWARD_INPUT, output=output)
118+
103119
def main():
104120
transaction = Transaction(Wallet(), 'recipient', 15)
105121
print(f'transaction.__dict__: {transaction.__dict__}')

backend/wallet/wallet.py

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,20 @@ class Wallet:
1717
Keeps track of the miner's balance.
1818
Allows a miner to authorize transactions.
1919
"""
20-
def __init__(self):
20+
def __init__(self, blockchain=None):
21+
self.blockchain = blockchain
2122
self.address = str(uuid.uuid4())[0:8]
22-
self.balance = STARTING_BALANCE
2323
self.private_key = ec.generate_private_key(
2424
ec.SECP256K1(),
2525
default_backend()
2626
)
2727
self.public_key = self.private_key.public_key()
2828
self.serialize_public_key()
2929

30+
@property
31+
def balance(self):
32+
return Wallet.calculate_balance(self.blockchain, self.address)
33+
3034
def sign(self, data):
3135
"""
3236
Generate a signature based on the data using the local private key.
@@ -68,6 +72,31 @@ def verify(public_key, data, signature):
6872
except InvalidSignature:
6973
return False
7074

75+
@staticmethod
76+
def calculate_balance(blockchain, address):
77+
"""
78+
Calculate the balance of the given address considering the transaction
79+
data within the blockchain.
80+
81+
The balance is found by adding the output values that belong to the
82+
address since the most recent transaction by that address.
83+
"""
84+
balance = STARTING_BALANCE
85+
86+
if not blockchain:
87+
return balance
88+
89+
for block in blockchain.chain:
90+
for transaction in block.data:
91+
if transaction['input']['address'] == address:
92+
# Any time the address conducts a new transaction it resets
93+
# its balance
94+
balance = transaction['output'][address]
95+
elif address in transaction['output']:
96+
balance += transaction['output'][address]
97+
98+
return balance
99+
71100
def main():
72101
wallet = Wallet()
73102
print(f'wallet.__dict__: {wallet.__dict__}')

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
pytest==8.4.2
22
Flask==3.1.2
3+
Flask-Cors==3.0.8
34
pubnub==10.4.1
45
requests==2.32.5
6+
cryptography==46.0.3

0 commit comments

Comments
 (0)