Skip to content

Commit

Permalink
Merge dev -> main (#6)
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
  • Loading branch information
hamdi3 authored Sep 21, 2024
1 parent b03aa16 commit bca98f8
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 7 deletions.
45 changes: 39 additions & 6 deletions src/app/routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,31 +14,64 @@ def home():
return jsonify({"message": "Welcome to Flask!"})


wallets = {}


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

public_key_str = binascii.hexlify(
public_key.exportKey(format='DER')).decode('ascii')
private_key_str = binascii.hexlify(
private_key.exportKey(format='DER')).decode('ascii')

# 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
}

response = {
'private_key': binascii.hexlify(private_key.exportKey(format='DER')).decode('ascii'),
'public_key': binascii.hexlify(public_key.exportKey(format='DER')).decode('ascii')
'private_key': private_key_str,
'public_key': public_key_str
}

return jsonify(response), 200


@bp.route('/generate/transaction', methods=['POST'])
def generate_transaction():

sender_address = request.form['sender_address']
sender_private_key = request.form['sender_private_key']
recipient_address = request.form['recipient_address']
value = request.form['amount']
value = float(request.form['amount'])

# Check if sender and recipient exist
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:
return jsonify({'error': 'Insufficient balance.'}), 400

# Create transaction
transaction = Transaction(
sender_address, sender_private_key, recipient_address, value)

response = {'transaction': transaction.to_dict(
), 'signature': transaction.sign_transaction()}
# Sign transaction
response = {
'transaction': transaction.to_dict(),
'signature': transaction.sign_transaction()
}

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

return jsonify(response), 200
106 changes: 105 additions & 1 deletion tests/test_routes.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def test_home(self):
self.assertEqual(response.status_code, 200)
self.assertIn(b'Welcome to Flask!', response.data)

# Patch the wallets dictionary with an empty dictionary
@patch.dict('src.app.routes.wallets', {}, clear=True)
@patch('src.app.routes.RSA.generate') # Mock RSA.generate
@patch('src.app.routes.Crypto.Random.new') # Mock Crypto.Random.new().read
def test_new_wallet(self, mock_random_new, mock_rsa_generate):
Expand Down Expand Up @@ -57,20 +59,36 @@ def test_new_wallet(self, mock_random_new, mock_rsa_generate):
binascii.hexlify(b'public_key_mock').decode('ascii')
)

# Check that the new wallet is stored in the wallets dictionary (from the module)
from src.app.routes import wallets # Import wallets after patching
self.assertIn(response_json['public_key'], wallets)
self.assertEqual(wallets[response_json['public_key']]['private_key'],
response_json['private_key'])
self.assertEqual(wallets[response_json['public_key']]['balance'], 100)

# Ensure that the appropriate methods were called
mock_rsa_generate.assert_called_once_with(1024, mock_random_gen)
mock_private_key.publickey.assert_called_once()
mock_private_key.exportKey.assert_called_once_with(format='DER')
mock_public_key.exportKey.assert_called_once_with(format='DER')

# Patch the wallets dictionary with an empty dictionary
@patch.dict('src.app.routes.wallets', {}, clear=True)
@patch('src.app.routes.Transaction') # Mock the Transaction class
def test_generate_transaction(self, mock_transaction):
# Add mock wallets with initial balances
from src.app.routes import wallets # Import wallets after patching
wallets['sender_address_123'] = {
'private_key': 'private_key_abc123', 'balance': 200}
wallets['recipient_address_456'] = {
'private_key': 'private_key_xyz456', 'balance': 50}

# Mock form data for the POST request
form_data = {
'sender_address': 'sender_address_123',
'sender_private_key': 'private_key_abc123',
'recipient_address': 'recipient_address_456',
'amount': '100'
'amount': 100
}

# Mock transaction instance and its methods
Expand Down Expand Up @@ -112,6 +130,92 @@ def test_generate_transaction(self, mock_transaction):
mock_transaction_instance.to_dict.assert_called_once()
mock_transaction_instance.sign_transaction.assert_called_once()

# Check that balances were updated correctly
self.assertEqual(wallets['sender_address_123']
['balance'], 100) # 200 - 100
self.assertEqual(wallets['recipient_address_456']
['balance'], 150) # 50 + 100

# Patch the wallets dictionary with an empty dictionary
@patch.dict('src.app.routes.wallets', {}, clear=True)
def test_generate_transaction_insufficient_balance(self):
# Add mock wallets with low balance for sender
from src.app.routes import wallets # Import wallets after patching
wallets['sender_address_123'] = {
'private_key': 'private_key_abc123', 'balance': 50}
wallets['recipient_address_456'] = {
'private_key': 'private_key_xyz456', 'balance': 50}

# Mock form data for the POST request
form_data = {
'sender_address': 'sender_address_123',
'sender_private_key': 'private_key_abc123',
'recipient_address': 'recipient_address_456',
'amount': '100' # More than sender's balance
}

# Send POST request to /generate/transaction route
response = self.client.post('/generate/transaction', data=form_data)

# Validate the response status code and content
response_json = response.get_json()
self.assertEqual(response.status_code, 400)
self.assertEqual(response_json['error'], 'Insufficient balance.')

# Patch the wallets dictionary with an empty dictionary
@patch.dict('src.app.routes.wallets', {}, clear=True)
def test_generate_transaction_sender_address_not_exist(self):
# Add a mock wallet for the recipient only
from src.app.routes import wallets # Import wallets after patching
wallets['recipient_address_456'] = {
'private_key': 'private_key_xyz456', 'balance': 50}

# Mock form data for the POST request
form_data = {
'sender_address': 'non_existent_sender',
'sender_private_key': 'private_key_abc123',
'recipient_address': 'recipient_address_456',
'amount': '100'
}

# Send POST request to /generate/transaction route
response = self.client.post('/generate/transaction', data=form_data)

# Parse the JSON response
response_json = response.get_json()

# Validate the response status code and content
self.assertEqual(response.status_code, 400)
self.assertEqual(response_json['error'],
'Sender address does not exist.')

# Patch the wallets dictionary with an empty dictionary
@patch.dict('src.app.routes.wallets', {}, clear=True)
def test_generate_transaction_recipient_address_not_exist(self):
# Add a mock wallet for the sender only
from src.app.routes import wallets # Import wallets after patching
wallets['sender_address_123'] = {
'private_key': 'private_key_abc123', 'balance': 200}

# Mock form data for the POST request
form_data = {
'sender_address': 'sender_address_123',
'sender_private_key': 'private_key_abc123',
'recipient_address': 'non_existent_recipient',
'amount': '100'
}

# Send POST request to /generate/transaction route
response = self.client.post('/generate/transaction', data=form_data)

# Parse the JSON response
response_json = response.get_json()

# Validate the response status code and content
self.assertEqual(response.status_code, 400)
self.assertEqual(response_json['error'],
'Recipient address does not exist.')


if __name__ == '__main__':
unittest.main()

0 comments on commit bca98f8

Please sign in to comment.