diff --git a/server/src/main/kotlin/org/ccoin/routes/MiningRoutes.kt b/server/src/main/kotlin/org/ccoin/routes/MiningRoutes.kt index e69de29..fbf87b0 100644 --- a/server/src/main/kotlin/org/ccoin/routes/MiningRoutes.kt +++ b/server/src/main/kotlin/org/ccoin/routes/MiningRoutes.kt @@ -0,0 +1,229 @@ +package org.ccoin.routes + +import io.ktor.http.* +import io.ktor.server.application.* +import io.ktor.server.request.* +import io.ktor.server.response.* +import io.ktor.server.routing.* +import org.ccoin.models.StartMiningRequest +import org.ccoin.models.SubmitMiningRequest +import org.ccoin.services.MiningService +import org.ccoin.services.ValidationService + +fun Route.miningRoutes() { + route("/mining") { + /** Start a mining job */ + post("/start") { + try { + val request = call.receive() + + // Validate miner address + if (!ValidationService.validateWalletAddress(request.minerAddress)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid miner address format")) + return@post + } + + // Validate difficulty if provided + if (request.difficulty != null && !ValidationService.validateMiningDifficulty(request.difficulty)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid mining difficulty")) + return@post + } + + val job = MiningService.startMining(request.minerAddress, request.difficulty) + call.respond(HttpStatusCode.Created, job) + + } catch (e: Exception) { + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to start mining job"))) + } + } + + /** Sumit mining result */ + post("/submit") { + try { + val request = call.receive() + + // Validate miner address + if (!ValidationService.validateWalletAddress(request.minerAddress)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid miner address format")) + return@post + } + + // Validate hash format + if (!ValidationService.validateBlockHash(request.hash)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid hash format")) + return@post + } + + // Validate previous hash format + if (!ValidationService.validateBlockHash(request.previousHash)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid previous hash format")) + return@post + } + + // Validate nonce + if (!ValidationService.validateMiningNonce(request.nonce)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid nonce")) + return@post + } + + // Validate timestamp + if (!ValidationService.validateTimestamp(request.timestamp)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid timestamp")) + return@post + } + + val block = MiningService.submitMiningResult( + request.minerAddress, + request.nonce, + request.hash, + request.previousHash, + request.timestamp + ) + + call.respond(HttpStatusCode.Created, block) + + } catch (e: Exception) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to (e.message ?: "Mining submission failed"))) + } + } + + /** Get current mining difficulty */ + get("/difficulty") { + try { + val difficulty = MiningService.getCurrentDifficulty() + call.respond(mapOf("difficulty" to difficulty)) + + } catch (e: Exception) { + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get difficulty"))) + } + } + + /** Get mining statistics for a miner */ + get("/stats/{address}") { + try { + val address = call.parameters["address"] ?: run { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Address parameter required")) + return@get + } + + // Validate address format + if (!ValidationService.validateWalletAddress(address)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid address format")) + return@get + } + + val stats = MiningService.getMinerStats(address) + call.respond(stats) + + } catch (e: Exception) { + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get miner statistics"))) + } + } + + /** Get network mining statistics */ + get("/network") { + try { + val hashRate = MiningService.getNetworkHashRate() + val averageBlockTime = MiningService.getAverageBlockTime() + val activeMiners = MiningService.getActiveMinersCount() + val difficulty = MiningService.getCurrentDifficulty() + + call.respond(mapOf( + "networkHashRate" to hashRate, + "averageBlockTime" to averageBlockTime, + "activeMiners" to activeMiners, + "currentDifficulty" to difficulty + )) + + } catch (e: Exception) { + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get network statistics"))) + } + } + + /** Get pending transactions available for mining */ + get("/pending-transactions") { + try { + val limit = call.request.queryParameters["limit"]?.toIntOrNull() ?: 100 + + if (limit in 0..1000) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Limit must be between 1 and 1000")) + return@get + } + + val transactions = MiningService.getPendingTransactionsForMining(limit) + call.respond(mapOf( + "transactions" to transactions, + "count" to transactions.size + )) + + } catch (e: Exception) { + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get pending transactions"))) + } + } + + /** Validate mining job */ + post("/validate") { + try { + val jobId = call.request.queryParameters["jobId"] ?: run { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Job ID parameter required")) + return@post + } + + val hash = call.request.queryParameters["hash"] ?: run { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Hash parameter required")) + return@post + } + + val nonce = call.request.queryParameters["nonce"]?.toLongOrNull() ?: run { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Valid nonce parameter required")) + return@post + } + + // Validate hash format + if (!ValidationService.validateBlockHash(hash)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid hash format")) + return@post + } + + // Validate nonce + if (!ValidationService.validateMiningNonce(nonce)) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid nonce")) + return@post + } + + val isValid = MiningService.validateMiningJob(jobId, hash, nonce) + call.respond(mapOf( + "jobId" to jobId, + "hash" to hash, + "nonce" to nonce, + "isValid" to isValid + )) + + } catch (e: Exception) { + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to validate mining job"))) + } + } + + /** Get mining leaderboard */ + get("/leaderboard") { + try { + val limit = call.request.queryParameters["limit"]?.toIntOrNull() ?: 10 + + if (limit in 0..100) { + call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Limit must be between 1 and 100")) + return@get + } + + // This would need a new service method to get top miners + // For now, return a placeholder response + call.respond(mapOf( + "message" to "Leaderboard endpoint - implementation needed", + "limit" to limit + )) + + } catch (e: Exception) { + call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get mining leaderboard"))) + } + } + } +}