Finished in the 4th place, great learning experience!
Category: Software
Subcategory: Java Backend
Difficulty: Medium
In this challenge you will build a Java backend application that simulates a simplified blockchain and cryptocurrency trading platform. The app includes user authentication, wallet management, transaction processing, live market data fetching, blockchain simulation (with mining and chain validation), digital key generation, and the implementation of Smart Contracts with digital signing.
Smart Contracts allow you to set rules on trading operations (for example, to prevent excessively large transactions by “anti-whale” mechanisms) by defining conditions (using a dynamic expression language) that must be met before a transaction is accepted. Each smart contract is associated with a liquidity wallet (for base assets like USDT) and is digitally signed using RSA.
For additional information on the proposed functioning of our blockchain, please download the file: Blockchain Guide
API Endpoint: https://faas-lon1-917a94a7.doserverless.co/api/v1/web/fn-3d8ede30-848f-4a7a-acc2-22ba0cd9a382/default/fake-market-prices
hackathon-caixabank-backend-java-blockchain
├── blockchain
│ ├── cookies.txt
│ ├── docker-compose.yml
│ ├── Dockerfile
│ ├── keys
│ ├── mvnw
│ ├── mvnw.cmd
│ ├── pom.xml
│ └── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── hackathon
│ │ │ └── blockchain
│ │ │ ├── BlockchainApplication.java
│ │ │ ├── config
│ │ │ │ └── CorsConfig.java
│ │ │ ├── controller
│ │ │ │ ├── DashboardController.java
│ │ │ │ └── HealthCheckController.java
│ │ │ ├── dto
│ │ │ ├── model
│ │ │ ├── repository
│ │ │ ├── service
│ │ │ │ ├── BlockchainService.java
│ │ │ │ ├── MarketDataService.java
│ │ │ │ ├── SmartContractEvaluationService.java
│ │ │ │ ├── UserService.java
│ │ │ │ ├── WalletKeyService.java
│ │ │ │ └── WalletService.java
│ │ │ └── utils
│ │ └── resources
│ │ └── application.properties
│ └── test
│ └── java
│ └── com
│ └── hackathon
│ └── blockchain
│ └── BlockchainApplicationTests.java
└── README.md
- Task 1: Dockerfile & Health Check
- Task 2: User Login, Register and Session Management
- Task 3: Wallet & Live Data Fetching
- Task 4: Transactions
- Task 5: Blockchain Simulation
- Task 6: Digital Signing
- Task 7: Smart Contracts
Please read the entire README and code carefully, as well as the detailed guides below, to fully understand the requirements of the challenge.
The first thing to do is to configure the Dockerfile to be able to test the application in containers.
A health check endpoint is provided to which a first request will be sent to check that the container is working properly.
Before doing the first push, you should make sure that this file works correctly, as all other tasks will be tested by attacking the endpoint generated by this container on port 3000.
The contents of the /target folder must not be used for this task.
- Objective:
Create a full authentication circuit including registration, login (with session creation) and logout. - Endpoints:
- POST /auth/register:
Request:Response (HTTP 200):{ "email": "user@example.com", "username": "user123", "password": "securePassword" }
{"message": "User registered and logged in successfully"}
- POST /auth/login:
Request:Response (HTTP 200):{ "username": "user@example.com", "password": "securePassword" }
Errors:{"message": "Login successful"}
- 401:
{"message": "❌ Invalid credentials"}
- 401:
- GET /auth/check-session:
Response (HTTP 200):Or 401 if no active session.{"user": {"username": "user123"}}
- POST /auth/logout:
Response (HTTP 200):{"message": "Logged out successfully"}
- POST /auth/register:
- Objective:
Implement wallet creation and live market data endpoints. - Endpoints:
- POST /wallet/create:
- Must be an authenticated request.
- New wallets start with a fiat balance of 100,000$.
- Response (HTTP 200):
{"message": "✅ Wallet successfully created! Address: <wallet_address>"}
- POST /wallet/generate-keys:
- Generates an RSA key pair for the wallet (PEM)
- Stores keys in the database and as files (in a
/keys
folder inside the container or volume). The name of the files are:wallet_<wallet_id>_private.pem
andwallet_<wallet_id>_public.pem
- Response (HTTP 200):
{ "message": "Keys generated/retrieved successfully for wallet id: <id>", "publicKey": "-----BEGIN PUBLIC KEY-----\n...-----END PUBLIC KEY-----\n", "absolutePath": "/abs/path/to/keys" }
- GET /market/prices:
Returns live (or simulated) market prices:{ "BTC": 35000.0, "ETH": 2500.0, "USDT": 1.0, "NCOIN": 10, "CCOIN": 10 }
- GET /market/price/{symbol}:
Returns a price for a specific asset:- HTTP 200:
{"message": "Current price of BTC: $35000.0"}
- HTTP 400 if not found.
- HTTP 200:
- POST /wallet/create:
- Objective:
Implement buying and selling of assets and maintain transaction history. - Endpoints:
- POST /wallet/buy:
- Authenticated request.
- Request (example):
{ "symbol": "BTC", "quantity": 0.5 }
- Important: Before processing the buy, the endpoint dynamically checks if the asset has a smart contract associated with its liquidity wallet (see Task 7 below). If such a contract exists and the transaction violates the contract condition, the transaction is blocked (and not saved in the DB).
- Response (HTTP 200):
{"message": "✅ Asset purchased successfully!"}
- Error:
{"message": "❌ Transaction blocked by smart contract conditions for BTC"}
- POST /wallet/sell:
- Similar to the buy endpoint, with dynamic validation against a smart contract if one exists.
- GET /wallet/balance:
- Returns wallet details:
{ "wallet_address": "<wallet_address>", "cash_balance": 100000.0, "net_worth": 150000.0, "assets": { "BTC": 0.5, "ETH": 3.0, ... } }
- Returns wallet details:
- GET /wallet/transactions:
- Returns transaction history, divided into
"sent"
and"received"
lists.
{ "sent": [ { "id": 1, "assetSymbol": "BTC", "amount": 0.5, "pricePerUnit": 35000.0, "type": "BUY", "timestamp": "2025-02-18T12:34:56.789Z", "status": "MINED", "fee": 0.0, "senderWalletId": 2, "receiverWalletId": 3 } ], "received": [ { "id": 2, "assetSymbol": "ETH", "amount": 2.0, "pricePerUnit": 2500.0, "type": "SELL", "timestamp": "2025-02-18T13:45:00.123Z", "status": "MINED", "fee": 0.0, "senderWalletId": 3, "receiverWalletId": 2 } ] }
- Returns transaction history, divided into
- POST /wallet/buy:
- Objective:
Simulate a blockchain by grouping pending transactions into blocks. - Endpoints:
- POST /blockchain/mine:
- Mines a new block containing pending transactions.
- Response (HTTP 200):
{"message": "Block mined: <block_hash>"}
- Error (HTTP 400):
{"message": "❌ No pending transactions to mine."}
- GET /blockchain:
- Returns the blockchain as an array of blocks.
[ { "id": 1, "blockIndex": 0, "timestamp": 1739921419757, "previousHash": "0", "nonce": 77403, "hash": "000054ba7a15300dcc48ccc86d6402f1086717bb0ff828a050892524fff8412a", "genesis": true }, { "id": 2, "blockIndex": 1, "timestamp": 1739926453603, "previousHash": "000054ba7a15300dcc48ccc86d6402f1086717bb0ff828a050892524fff8412a", "nonce": 62319, "hash": "0000f393638957ef5d7d835a9fe2e5654da67b9303abbd02ae8e82dc0762f896", "genesis": false }, { "id": 3, "blockIndex": 2, "timestamp": 1739926650238, "previousHash": "0000f393638957ef5d7d835a9fe2e5654da67b9303abbd02ae8e82dc0762f896", "nonce": 596, "hash": "00004bc719f169f3a0364172593e276e13a631f94084bc45ae4d95ecbdb83f57", "genesis": false } ]
- GET /blockchain/validate:
- Returns whether the blockchain is valid:
or if the chain is invalid,
{"message": "Blockchain valid: true"}
{"message": "Blockchain valid: false"}
- Returns whether the blockchain is valid:
- POST /blockchain/mine:
At the start of the application, a series of processes detailed below must be executed:
- Create wallets for the currencies with the following format: ‘LP-COINNAME’. These wallets do not have an initial balance of fiat money, they only have the related assets.
- As a liquidity pool, they are initially allocated a certain number of assets. BTC: 100000, ETH: 400000, USDT: 1000000, NCOIN: 10000000, CCOIN: 2000000.
- A genesis block will have to be created as the first block in the chain with no associated transactions.
- Objective:
Implement RSA-based digital signing for wallet keys and smart contracts. - Implementation:
- Each wallet generates an RSA key pair (via
/wallet/generate-keys
). - Smart contracts are signed using the issuer wallet’s private key.
- In the case of the smart contracts that will be created later by default for a coin at the startup of the application, these will be signed by a system key that is generated separately and without using the endpoint.
- Each wallet generates an RSA key pair (via
-
Objective:
Implement a Smart Contracts module that allows setting trading rules (e.g., anti-whale measures) using dynamic conditions (via SpEL) and digital signing.- Dynamic Validation:
When a user submits a buy (or sell) transaction, the endpoint dynamically checks if a smart contract exists for the asset’s liquidity pool.- It retrieves the liquidity wallet by address ("LP-" + symbol.toUpperCase()).
- It retrieves the smart contract linked to that wallet (via
issuerWalletId
). - It evaluates the condition using SpEL with variables (e.g.,
amount
andtxType
). - If the condition fails (e.g., for an anti-whale rule, if the purchase amount is too high), the endpoint returns an error (HTTP 400) and the transaction is not persisted.
- Manual Creation of Smart Contracts:
Additionally, a dedicated controller allows manually creating smart contracts.- Endpoint:
POST /contracts/create
- Request Body (JSON):
{ "name": "Contract example", "conditionExpression": "#amount > 10", "action": "CANCEL_TRANSACTION", "actionValue": 0.0, "issuerWalletId": 5 }
- Behavior:
The endpoint retrieves the private key of the issuer wallet (viaWalletKeyService
), signs the contract data, and stores the contract with its digital signature in the database. - Response:
Returns the smart contract object created in JSON, including thedigitalSignature
field.
- Endpoint:
- Validation Endpoint:
An endpoint to validate a smart contract’s signature:- Endpoint:
GET /contracts/validate/{id}
- Behavior:
Retrieves the smart contract by ID, obtains the public key of the issuer wallet, reconstructs the signed data, and verifies the digital signature. - Response:
o
{"message": "Smart contract is valid"}
{"message": "Smart contract is invalid"}
- Endpoint:
- Dynamic Validation:
-
Automatic Association:
At startup (via the StartupRunner), smart contract is automatically created for BTC liquidity pool wallet with predefined conditions (anti-whale: blockingBUY
transactions exceeding10,000 units
).
Endpoint | Method | Auth | Possible Status Codes | Response Example |
---|---|---|---|---|
GET /health |
GET | No | 200, 500 | 200 OK: "OK" |
User Endpoints | ||||
/auth/register |
POST | No | 200, 400, 500 | 200 OK: {"message": "User registered and logged in successfully"} |
/auth/login |
POST | No | 200, 400, 401, 500 | 200 OK: {"message": "Login successful"} |
/auth/check-session |
GET | Yes | 200, 401, 500 | 200 OK: {"user": {"username": "user123"}} |
/auth/logout |
POST | Yes | 200, 401 | 200 OK: {"message": "Logged out successfully"} |
Wallet & Key Endpoints | ||||
/wallet/create |
POST | Yes | 200, 401, 500 | 200 OK: {"message": "✅ Wallet successfully created! Address: <wallet_address>"} |
/wallet/generate-keys |
POST | Yes | 200, 401, 404, 500 | 200 OK: { "message": "Keys generated/retrieved successfully for wallet id: <id>", "publicKey": "-----BEGIN PUBLIC KEY-----\n...", "absolutePath": "/abs/path/to/keys" } |
/wallet/transactions |
GET | Yes | 200, 401, 404, 500 | 200 OK: { "sent": [ ... ], "received": [ ... ] } |
/wallet/balance |
GET | Yes | 200, 401, 404 | 200 OK: JSON con información de la wallet (address, cash_balance, net_worth, assets) |
/wallet/buy |
POST | Yes | 200, 400, 401, 500 | 200 OK: {"message": "✅ Asset purchased successfully!"} 400 Bad Request: {"message": "❌ Transaction blocked by smart contract conditions for BTC"} |
/wallet/sell |
POST | Yes | 200, 400, 401, 500 | 200 OK: {"message": "✅ Asset sold successfully!"} 400 Bad Request: {"message": "❌ Transaction blocked by smart contract conditions for BTC"} |
Blockchain Endpoints | ||||
/blockchain/mine |
POST | Optional | 200, 400, 500 | 200 OK: {"message": "Block mined: <block_hash>"} 400: {"message": "❌ No pending transactions to mine."} |
/blockchain |
GET | Optional | 200, 500 | 200 OK: JSON array de bloques |
/blockchain/validate |
GET | Optional | 200, 500 | 200 OK: {"message": "Blockchain valid: true"} |
Market & Dashboard Endpoints | ||||
/market/prices |
GET | No | 200, 500 | 200 OK: { "BTC": 35000.0, "ETH": 2500.0, "USDT": 1.0, ... } |
/market/price/{symbol} |
GET | No | 200, 400, 500 | 200 OK: {"message": "Current price of BTC: $35000.0"} 400: {"message": "❌ Asset not found or price unavailable: BTC"} |
/api/dashboard |
GET | Yes | 200, 401 | 200 OK: "Welcome to your dashboard, user123! Your registered email is: user@example.com" |
Smart Contract Endpoints | ||||
/contracts/create |
POST | Yes | 200, 400, 401, 500 | 200 OK: JSON del smart contract creado, incluyendo sus campos y la digitalSignature |
/contracts/validate/{id} |
GET | Optional | 200, 404, 500 | 200 OK: {"message": "Smart contract is valid"} o {"message": "Smart contract is invalid"} |
The application.properties file contains the configuration necessary for the correct functioning of the application.
The backend tests will simulate the interaction of a user directly with the API running in a container and exposed on port 3000
-
App start:
- Build the application in a Docker container on port 3000.
- The
/health
endpoint confirms that the container is running. - In the StartupRunner:
- Liquidity pools are initialized with predefined assets (BTC, ETH, USDT, etc.).
- Predefined Smart Contract for BTC is initialized.
- The blockchain is initialized (including the genesis block).
-
User Management:
- Users register (
/auth/register
), log in (/auth/login
), check session (/auth/check-session
) and log out (/auth/logout
).
- Users register (
-
Wallet & Key Management:
- Users create their personal wallets (
/wallet/create
). - RSA key pairs are generated and stored via
/wallet/generate-keys
. - Transaction history and wallet balance can be retrieved.
- Users create their personal wallets (
-
Trading & Blockchain Operations:
- Users execute buy (
/wallet/buy
) and sell (/wallet/sell
) orders. - Before processing, the system dynamically checks if a smart contract is associated to the liquidity wallet (e.g., "LP-BTC") and evaluates its condition (using SpEL with variables such as
amount
andtxType
). - If the condition fails (for example, if the transaction exceeds a predefined limit), the operation is blocked and returns an error without saving the transaction.
- Valid transactions are processed normally.
- Pending transactions are mined into new blocks via
/blockchain/mine
and the blockchain can be queried and validated.
- Users execute buy (
-
Market Data:
- Live market prices are fetched via
/market/prices
and/market/price/{symbol}
.
- Live market prices are fetched via
-
Smart Contracts:
- Manual Creation:
A dedicated endpoint (POST /contracts/create
) allows the manual creation of smart contracts by providing parameters (name, conditionExpression, action, actionValue, issuerWalletId). The contract is digitally signed using the issuer wallet’s private key. - Validation:
An endpoint (GET /contracts/validate/{id}
) validates a contract’s digital signature by reconstructing the data and verifying it with the issuer wallet’s public key.
- Manual Creation:
- Solve the proposed tasks.
- Continuously push the changes you have made.
- Wait for the results.
- Click submit challenge when you have reached your maximum score.
The final score will be given according to whether or not the objectives have been met.
In this case, the challenge will be evaluated on 1700 (1300 for backend tasks and 400 for code quality) points which are distributed as follows:
- Task 1: 50 points
- Task 2: 100 points
- Task 3: 150 points
- Task 4: 200 points
- Task 5: 300 points
- Task 6: 200 points
- Task 7: 300 points
- Code quality: 400 points
Q1: Can I change anything in the app?
A1: Yes, as it is a hackathon and the application is dockerised, you are free to modify anything within the project, except for the functions that are already predefined to validate the proper functioning of the blockchain and the validation of smart contracts, Dockerfile and docker-compose.
Q2: Can I add resources that are not in pom.xml?
A2: Yes, you can add new resources if necessary, but keep in mind that everything needed to develop it has already been added.
Q3: Is it completely necessary to do the Dockerfile configuration first?
A3: Yes. To ensure the integrity of the correction, a Dockerised environment is the safest way to go.
Q4: Can I participate in both the fullstack and backend challenges at the same time?
A4: No, you cannot participate in both challenges. You must choose only one challenge to compete in.
Q5: What is the latest version of README?
A5: The most recent version will always be the one that appears on the platform. In case there is something to correct in the readme, you can see the updated version on the NUWE website.