Compare commits
2 Commits
35a73c340c
...
89e45128b6
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
89e45128b6 | ||
|
|
3c097af03d |
@@ -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<StartMiningRequest>()
|
||||||
|
|
||||||
|
// 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<SubmitMiningRequest>()
|
||||||
|
|
||||||
|
// 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")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,210 @@
|
|||||||
|
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.SendTransactionRequest
|
||||||
|
import org.ccoin.services.TransactionService
|
||||||
|
import org.ccoin.services.ValidationService
|
||||||
|
|
||||||
|
fun Route.transactionRoutes() {
|
||||||
|
route("/transaction") {
|
||||||
|
/** Send a transaction */
|
||||||
|
post("/send") {
|
||||||
|
try {
|
||||||
|
val request = call.receive<SendTransactionRequest>()
|
||||||
|
|
||||||
|
// Validate transaction data
|
||||||
|
val validation = ValidationService.validateTransaction(
|
||||||
|
request.fromAddress,
|
||||||
|
request.toAddress,
|
||||||
|
request.amount,
|
||||||
|
request.fee,
|
||||||
|
request.memo
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!validation.isValid) {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to validation.getErrorMessage()))
|
||||||
|
return@post
|
||||||
|
}
|
||||||
|
|
||||||
|
val transaction = TransactionService.sendTransaction(
|
||||||
|
request.fromAddress,
|
||||||
|
request.toAddress,
|
||||||
|
request.amount,
|
||||||
|
request.fee,
|
||||||
|
request.memo
|
||||||
|
)
|
||||||
|
|
||||||
|
call.respond(HttpStatusCode.Created, transaction)
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to (e.message ?: "Transaction failed")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get transaction by hash */
|
||||||
|
get("/{hash}") {
|
||||||
|
try {
|
||||||
|
val hash = call.parameters["hash"] ?: run {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Transaction hash parameter required"))
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate hash format
|
||||||
|
if (!ValidationService.validateTransactionHash(hash)) {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid transaction hash format"))
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
|
||||||
|
val transaction = TransactionService.getTransaction(hash)
|
||||||
|
if (transaction != null) {
|
||||||
|
call.respond(transaction)
|
||||||
|
} else {
|
||||||
|
call.respond(HttpStatusCode.NotFound, mapOf("error" to "Transaction not found"))
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get transaction")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get transaction history for an address */
|
||||||
|
get("/history/{address}") {
|
||||||
|
try {
|
||||||
|
val address = call.parameters["address"] ?: run {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Address parameter required"))
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
|
||||||
|
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
|
||||||
|
val pageSize = call.request.queryParameters["pageSize"]?.toIntOrNull() ?: 50
|
||||||
|
|
||||||
|
// Validate address format
|
||||||
|
if (!ValidationService.validateWalletAddress(address)) {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid address format"))
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate pagination
|
||||||
|
val validation = ValidationService.validatePagination(page, pageSize)
|
||||||
|
if (!validation.isValid) {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to validation.getErrorMessage()))
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
|
||||||
|
val offset = (page - 1) * pageSize
|
||||||
|
val transactions = TransactionService.getTransactionHistory(address, pageSize, offset)
|
||||||
|
val totalCount = TransactionService.getTransactionCountForAddress(address)
|
||||||
|
|
||||||
|
call.respond(mapOf(
|
||||||
|
"transactions" to transactions,
|
||||||
|
"address" to address,
|
||||||
|
"pagination" to mapOf(
|
||||||
|
"currentPage" to page,
|
||||||
|
"pageSize" to pageSize,
|
||||||
|
"totalItems" to totalCount,
|
||||||
|
"totalPages" to ((totalCount + pageSize - 1) / pageSize),
|
||||||
|
"hasNext" to (offset + pageSize < totalCount),
|
||||||
|
"hasPrevious" to (page > 1)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get transaction history")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get pending transactions */
|
||||||
|
get("/pending") {
|
||||||
|
try {
|
||||||
|
val transactions = TransactionService.getPendingTransactions()
|
||||||
|
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")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get transaction count for address */
|
||||||
|
get("/count/{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 count = TransactionService.getTransactionCountForAddress(address)
|
||||||
|
call.respond(mapOf(
|
||||||
|
"address" to address,
|
||||||
|
"transactionCount" to count
|
||||||
|
))
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get transaction count")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get all transactions with pagination */
|
||||||
|
get("/list") {
|
||||||
|
try {
|
||||||
|
val page = call.request.queryParameters["page"]?.toIntOrNull() ?: 1
|
||||||
|
val pageSize = call.request.queryParameters["pageSize"]?.toIntOrNull() ?: 50
|
||||||
|
val status = call.request.queryParameters["status"] // Optional filter
|
||||||
|
|
||||||
|
// Validate pagination
|
||||||
|
val validation = ValidationService.validatePagination(page, pageSize)
|
||||||
|
if (!validation.isValid) {
|
||||||
|
call.respond(HttpStatusCode.BadRequest, mapOf("error" to validation.getErrorMessage()))
|
||||||
|
return@get
|
||||||
|
}
|
||||||
|
|
||||||
|
// For now, just get pending transactions as an example
|
||||||
|
// You could extend this to support status filtering
|
||||||
|
val transactions = if (status == "pending") {
|
||||||
|
TransactionService.getPendingTransactions()
|
||||||
|
} else {
|
||||||
|
// This would need a new service method for all transactions
|
||||||
|
TransactionService.getPendingTransactions() // Placeholder
|
||||||
|
}
|
||||||
|
|
||||||
|
call.respond(mapOf(
|
||||||
|
"transactions" to transactions,
|
||||||
|
"count" to transactions.size,
|
||||||
|
"status" to status
|
||||||
|
))
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get transactions")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get network transaction statistics */
|
||||||
|
get("/stats") {
|
||||||
|
try {
|
||||||
|
val totalCount = TransactionService.getTotalTransactionCount()
|
||||||
|
val pendingCount = TransactionService.getPendingTransactions().size
|
||||||
|
|
||||||
|
call.respond(mapOf(
|
||||||
|
"totalTransactions" to totalCount,
|
||||||
|
"pendingTransactions" to pendingCount,
|
||||||
|
"confirmedTransactions" to (totalCount - pendingCount)
|
||||||
|
))
|
||||||
|
|
||||||
|
} catch (e: Exception) {
|
||||||
|
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get transaction statistics")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user