Skip to content

Commit

Permalink
Merge: dev -> main (#9)
Browse files Browse the repository at this point in the history
* ADD & UPDATE

- Added OpenAI(Swagger) to document the api.
- Updated the routes to use JSON instead of forms (for Consistency, flexibility, scalability).

* UPDATE

Updated the requirements.txt

* UPDATE

- Updated the routes tests to accept JSON instead of forms
- Increased coverage
  • Loading branch information
hamdi3 authored Sep 27, 2024
1 parent 1d73a8e commit 95db182
Show file tree
Hide file tree
Showing 5 changed files with 655 additions and 64 deletions.
6 changes: 4 additions & 2 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pycryptodome==3.20.0
pycryptodome==3.20.0
Flask==3.0.3
requests==2.32.3
requests==2.32.3
flask-cors==5.0.0
flask-swagger-ui==4.11.1
11 changes: 10 additions & 1 deletion src/app/__init__.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
from flask import Flask
from flask_cors import CORS


def create_app():
app = Flask(__name__)

from .routes import bp as routes_bp
from .routes import bp as routes_bp, swaggerui_blueprint, SWAGGER_URL

# Register the routes blueprint
app.register_blueprint(routes_bp)

# Register the Swagger UI blueprint
app.register_blueprint(swaggerui_blueprint, url_prefix=SWAGGER_URL)

# Enable CORS for the entire app
CORS(app)

return app
56 changes: 47 additions & 9 deletions src/app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,24 @@
import Crypto
from Crypto.PublicKey import RSA
from flask import Blueprint, jsonify, request
from flask_swagger_ui import get_swaggerui_blueprint

from blockchain import Blockchain, MINING_REWARD, MINING_SENDER


# Swagger UI setup
SWAGGER_URL = '/swagger'
API_URL = '/static/openapi.yaml' # OpenAPI specification location

swaggerui_blueprint = get_swaggerui_blueprint(
SWAGGER_URL, # Swagger UI served at /swagger
API_URL, # OpenAPI specification served at /static/openapi.yaml
config={ # Swagger UI configuration
'app_name': "ChainAlchemy API Documentation"
}
)


bp = Blueprint('routes', __name__)


Expand Down Expand Up @@ -45,10 +59,23 @@ def new_wallet():

@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']
amount = float(request.form['amount'])
# Parse JSON payload
data = request.get_json()

# Ensure all required fields are present in the JSON body
required_fields = ['sender_address', 'sender_private_key', 'recipient_address', 'amount']
for field in required_fields:
if field not in data:
return jsonify({'error': f"Missing required field: {field}"}), 400

sender_address = data['sender_address']
sender_private_key = data['sender_private_key']
recipient_address = data['recipient_address']

try:
amount = float(data['amount'])
except ValueError:
return jsonify({'error': 'Amount must be a number.'}), 400

# Dynamically calculate the sender's balance
sender_balance = blockchain.get_available_balance(address=sender_address)
Expand Down Expand Up @@ -99,7 +126,14 @@ def full_chain():

@bp.route('/mine', methods=['POST'])
def mine():
miner_address = request.form['miner_address']
# Parse JSON payload
data = request.get_json()

# Ensure the 'miner_address' field is present
if 'miner_address' not in data:
return jsonify({'error': 'Missing required field: miner_address'}), 400

miner_address = data['miner_address']

# We run the proof of work algorithm to get the next proof...
last_block = blockchain.chain[-1]
Expand Down Expand Up @@ -130,12 +164,16 @@ def mine():

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

# Ensure the 'nodes' field is present and valid
if 'nodes' not in data or not isinstance(data['nodes'], list) or len(data['nodes']) == 0:
return jsonify({'error': 'Missing or invalid required field: nodes (must be a non-empty list)'}), 400

if nodes is None:
return "Error: Please supply a valid list of nodes", 400
nodes = data['nodes']

# Register each node in the blockchain
for node in nodes:
blockchain.register_node(node)

Expand Down
243 changes: 243 additions & 0 deletions src/app/static/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
openapi: 3.0.0
info:
title: ChainAlchemy API
description: This API allows interaction with the blockchain network, including wallet creation, transactions, mining, and node management.
version: 1.0.0

servers:
- url: http://localhost:5000

paths:
/:
get:
summary: Home route
description: A simple welcome message from the API.
responses:
'200':
description: A welcome message from Flask.
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Welcome to Flask!

/wallet/new:
post:
summary: Create a new wallet
description: Generates a new RSA key pair and stores the wallet in the system with an initial balance of 0.
responses:
'200':
description: The newly generated wallet, including private and public keys.
content:
application/json:
schema:
type: object
properties:
private_key:
type: string
description: The private key for the wallet.
public_key:
type: string
description: The public key for the wallet.

/transactions/new:
post:
summary: Submit a new transaction
description: Submits a transaction between two wallets, transferring a specific amount from the sender to the recipient.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
sender_address:
type: string
description: The sender's public key.
sender_private_key:
type: string
description: The sender's private key.
recipient_address:
type: string
description: The recipient's public key.
amount:
type: number
description: The amount to be transferred.
responses:
'201':
description: The transaction will be added to the blockchain.
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: Transaction will be added to Block 1
'400':
description: Error due to insufficient balance or invalid addresses.
'406':
description: Invalid transaction.

/transactions/get:
get:
summary: Get all pending transactions
description: Retrieves the list of pending transactions from the transactions pool.
responses:
'200':
description: A list of pending transactions.
content:
application/json:
schema:
type: object
properties:
transactions:
type: array
items:
type: object
properties:
sender:
type: string
recipient:
type: string
amount:
type: number

/chain:
get:
summary: Retrieve the full blockchain
description: Returns the full blockchain, including all blocks.
responses:
'200':
description: A JSON object containing the entire blockchain.
content:
application/json:
schema:
type: object
properties:
chain:
type: array
items:
type: object
properties:
block_number:
type: integer
transactions:
type: array
items:
type: object
nonce:
type: integer
previous_hash:
type: string
length:
type: integer
description: The number of blocks in the chain.

/mine:
post:
summary: Mine a new block
description: Mines a new block using proof of work, adding it to the blockchain.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
miner_address:
type: string
description: The public key of the miner who will receive the mining reward.
responses:
'200':
description: A JSON object containing details of the newly forged block.
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "New Block Forged"
block_number:
type: integer
transactions:
type: array
items:
type: object
nonce:
type: integer
previous_hash:
type: string
miner_balance:
type: number

/nodes/register:
post:
summary: Register new nodes
description: Registers a list of new nodes to the blockchain network.
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
nodes:
type: string
description: A comma-separated list of node addresses.
responses:
'201':
description: A confirmation message showing the total nodes added.
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "New nodes have been added"
total_nodes:
type: array
items:
type: string

/nodes/resolve:
get:
summary: Resolve conflicts between nodes
description: Initiates the consensus algorithm to resolve conflicts and ensure the blockchain is consistent across nodes.
responses:
'200':
description: The chain was either replaced or found authoritative.
content:
application/json:
schema:
type: object
properties:
message:
type: string
example: "Our chain was replaced"
new_chain:
type: array
items:
type: object

/nodes/get:
get:
summary: Get all registered nodes
description: Retrieves a list of all nodes registered in the blockchain network.
responses:
'200':
description: A list of all registered nodes.
content:
application/json:
schema:
type: object
properties:
nodes:
type: array
items:
type: string
Loading

0 comments on commit 95db182

Please sign in to comment.