Skip to content

Commit

Permalink
Merge: dev -> main (#8)
Browse files Browse the repository at this point in the history
* FIXED

Fixed imports for PKCS1 to use it a signature instead of a cipher

* ADDED

Integrated Flask into it and implemented the routes

* ADDED

Added routes tests

* ADDED

Added Flask to req.

* ADDED

Added docker support

* ADDED

Added simple transaction validators

* UPDATE

Updated the tests for transaction validators

* UPDATE

Updated the route tests to cover the cases of walets not being found

* UPDATE

Updated tests to improve the coverage

* UPDATE:

Removed the transaction class and added the blockchain class

* UPDATE & ADD

Updated the routes based on the new blockchain class & added more routes

* UPDATE

Updated the tests

* FIX

Removed the version from the docker-compose.yaml file since it's obsolete

* UPDATE

Updated the requirements.txt file

* FIX

Code fixes
  • Loading branch information
hamdi3 authored Sep 25, 2024
1 parent 7d8de33 commit 1d73a8e
Show file tree
Hide file tree
Showing 8 changed files with 751 additions and 213 deletions.
2 changes: 0 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: '0.1'

services:
# Service for running tests
test:
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pycryptodome==3.20.0
Flask==3.0.3
Flask==3.0.3
requests==2.32.3
131 changes: 112 additions & 19 deletions src/app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from Crypto.PublicKey import RSA
from flask import Blueprint, jsonify, request

from transactions import Transaction
from blockchain import Blockchain, MINING_REWARD, MINING_SENDER


bp = Blueprint('routes', __name__)
Expand All @@ -15,12 +15,13 @@ def home():


wallets = {}
blockchain = Blockchain()


@bp.route('/wallet/new', methods=['GET'])
@bp.route('/wallet/new', methods=['POST'])
def new_wallet():
random_gen = Crypto.Random.new().read
private_key = RSA.generate(1024, random_gen)
private_key = RSA.generate(3072, random_gen)
public_key = private_key.publickey()

public_key_str = binascii.hexlify(
Expand All @@ -31,7 +32,7 @@ def new_wallet():
# Save wallet in a dictionary with initial balance of 0
wallets[public_key_str] = {
'private_key': private_key_str,
'balance': 100 # Starting each wallet with a 100
'balance': 0.0 # Starting each wallet with a 0
}

response = {
Expand All @@ -42,36 +43,128 @@ def new_wallet():
return jsonify(response), 200


@bp.route('/generate/transaction', methods=['POST'])
def generate_transaction():
@bp.route('/transactions/new', methods=['POST'])
def new_transaction():
sender_address = request.form['sender_address']
sender_private_key = request.form['sender_private_key']
recipient_address = request.form['recipient_address']
value = float(request.form['amount'])
amount = float(request.form['amount'])

# Check if sender and recipient exist
# Dynamically calculate the sender's balance
sender_balance = blockchain.get_available_balance(address=sender_address)

# Check if sender and recipient exist (in the current context wallets dictionary)
if sender_address not in wallets:
return jsonify({'error': 'Sender address does not exist.'}), 400

if recipient_address not in wallets:
return jsonify({'error': 'Recipient address does not exist.'}), 400

# Check if sender has enough balance
if wallets[sender_address]['balance'] < value:
# Check if sender has enough balance dynamically
if sender_balance < amount:
return jsonify({'error': 'Insufficient balance.'}), 400

# Create transaction
transaction = Transaction(
sender_address, sender_private_key, recipient_address, value)
# Create a new Transaction
transaction_result = blockchain.submit_transaction(
sender_address, sender_private_key, recipient_address, amount
)

if not transaction_result:
response = {'message': 'Invalid Transaction!'}
return jsonify(response), 406
else:
response = {
'message': f'Transaction will be added to Block {str(transaction_result)}'
}
return jsonify(response), 201


@bp.route('/transactions/get', methods=['GET'])
def get_transactions():
# Get pending transactions from transactions pool
transactions = blockchain.transactions

response = {'transactions': transactions}
return jsonify(response), 200


# Sign transaction
@bp.route('/chain', methods=['GET'])
def full_chain():
response = {
'transaction': transaction.to_dict(),
'signature': transaction.sign_transaction()
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200


@bp.route('/mine', methods=['POST'])
def mine():
miner_address = request.form['miner_address']

# We run the proof of work algorithm to get the next proof...
last_block = blockchain.chain[-1]
nonce = blockchain.proof_of_work()

# Submit a reward transaction without a private key (since it's from the system)
blockchain.submit_transaction(
sender_address=MINING_SENDER, # System address for mining rewards
sender_private_key=None, # No private key needed for mining rewards
recipient_address=miner_address, # Miner receives the reward
value=MINING_REWARD # The reward for mining the block
)

# Forge the new Block by adding it to the chain
previous_hash = blockchain.hash(last_block)
block = blockchain.create_block(nonce, previous_hash)

response = {
'message': "New Block Forged",
'block_number': block['block_number'],
'transactions': block['transactions'],
'nonce': block['nonce'],
'previous_hash': block['previous_hash'],
'miner_balance': blockchain.get_balance(miner_address)
}
return jsonify(response), 200


@bp.route('/nodes/register', methods=['POST'])
def register_nodes():
values = request.form
nodes = values.get('nodes').replace(" ", "").split(',')

if nodes is None:
return "Error: Please supply a valid list of nodes", 400

for node in nodes:
blockchain.register_node(node)

response = {
'message': 'New nodes have been added',
'total_nodes': [node for node in blockchain.nodes],
}
return jsonify(response), 201


@bp.route('/nodes/resolve', methods=['GET'])
def consensus():
replaced = blockchain.resolve_conflicts()

if replaced:
response = {
'message': 'Our chain was replaced',
'new_chain': blockchain.chain
}
else:
response = {
'message': 'Our chain is authoritative',
'chain': blockchain.chain
}
return jsonify(response), 200

# Subtract value from sender and add it to recipient
wallets[sender_address]['balance'] -= value
wallets[recipient_address]['balance'] += value

@bp.route('/nodes/get', methods=['GET'])
def get_nodes():
nodes = list(blockchain.nodes)
response = {'nodes': nodes}
return jsonify(response), 200
Loading

0 comments on commit 1d73a8e

Please sign in to comment.