feat: added block routes

This commit is contained in:
darwincereska
2025-12-18 09:42:28 -05:00
parent 89e45128b6
commit 9bc861f1d1

View File

@@ -0,0 +1,280 @@
package org.ccoin.routes
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import org.ccoin.services.BlockService
import org.ccoin.services.ValidationService
fun Route.blockRoutes() {
route("/block") {
/** Get block by hash */
get("/{hash}") {
try {
val hash = call.parameters["hash"] ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Block hash parameter required"))
return@get
}
// Validate hash format
if (!ValidationService.validateBlockHash(hash)) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid block hash format"))
return@get
}
val block = BlockService.getBlock(hash)
if (block != null) {
call.respond(block)
} else {
call.respond(HttpStatusCode.NotFound, mapOf("error" to "Block not found"))
}
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get block")))
}
}
/** Get block by height */
get("/height/{height}") {
try {
val height = call.parameters["height"]?.toIntOrNull() ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Valid height parameter required"))
return@get
}
if (height < 0) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Height must be non-negative"))
return@get
}
val block = BlockService.getBlockByHeight(height)
if (block != null) {
call.respond(block)
} else {
call.respond(HttpStatusCode.NotFound, mapOf("error" to "Block not found at height $height"))
}
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get block by height")))
}
}
/** Check if block exists */
get("/{hash}/exists") {
try {
val hash = call.parameters["hash"] ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Block hash parameter required"))
return@get
}
// Validate hash format
if (!ValidationService.validateBlockHash(hash)) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid block hash format"))
return@get
}
val exists = BlockService.blockExists(hash)
call.respond(mapOf(
"hash" to hash,
"exists" to exists
))
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to check block existence")))
}
}
}
route("/blocks") {
/** Get latest blocks */
get("/latest") {
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
}
val blocks = BlockService.getLatestBlocks(limit)
call.respond(mapOf(
"blocks" to blocks,
"count" to blocks.size
))
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get latest blocks")))
}
}
/** Get blocks in height range */
get("/range") {
try {
val fromHeight = call.request.queryParameters["from"]?.toIntOrNull() ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Valid 'from' height parameter required"))
return@get
}
val toHeight = call.request.queryParameters["to"]?.toIntOrNull() ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Valid 'to' height parameter required"))
return@get
}
if (fromHeight < 0 || toHeight < 0) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Heights must be non-negative"))
return@get
}
if (fromHeight > toHeight) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "From height must be less than or equal to to height"))
return@get
}
if (toHeight - fromHeight > 1000) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Range too large (max 1000 blocks)"))
return@get
}
val blockRange = BlockService.getBlocksInRange(fromHeight, toHeight)
call.respond(blockRange)
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get blocks in range")))
}
}
/** Get blocks by miner address */
get("/miner/{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 blocks = BlockService.getBlocksByMiner(address, pageSize, offset)
call.respond(mapOf(
"blocks" to blocks,
"minerAddress" to address,
"pagination" to mapOf(
"currentPage" to page,
"pageSize" to pageSize,
"hasNext" to (blocks.size == pageSize),
"hasPrevious" to (page > 1)
)
))
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get blocks by miner")))
}
}
/** Get blocks by timestamp range */
get("/time-range") {
try {
val fromTime = call.request.queryParameters["from"]?.toLongOrNull() ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Valid 'from' timestamp parameter required"))
return@get
}
val toTime = call.request.queryParameters["to"]?.toLongOrNull() ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Valid 'to' timestamp parameter required"))
return@get
}
if (fromTime < 0 || toTime < 0) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Timestamps must be non-negative"))
return@get
}
if (fromTime > toTime) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "From time must be less than or equal to to time"))
return@get
}
// Limit to 30 days max
if (toTime - fromTime > 30 * 24 * 60 * 60) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Time range too large (max 30 days)"))
return@get
}
val blocks = BlockService.getBlocksByTimeRange(fromTime, toTime)
call.respond(mapOf(
"blocks" to blocks,
"fromTime" to fromTime,
"toTime" to toTime,
"count" to blocks.size
))
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get blocks by time range")))
}
}
/** Get blocks by difficulty */
get("/difficulty/{difficulty}") {
try {
val difficulty = call.parameters["difficulty"]?.toIntOrNull() ?: run {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Valid difficulty parameter required"))
return@get
}
if (!ValidationService.validateMiningDifficulty(difficulty)) {
call.respond(HttpStatusCode.BadRequest, mapOf("error" to "Invalid mining difficulty"))
return@get
}
val blocks = BlockService.getBlocksByDifficulty(difficulty)
call.respond(mapOf(
"blocks" to blocks,
"difficulty" to difficulty,
"count" to blocks.size
))
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get blocks by difficulty")))
}
}
/** Get blockchain statistics */
get("/stats") {
try {
val totalBlocks = BlockService.getTotalBlockCount()
val latestHeight = BlockService.getLatestBlockHeight()
val latestHash = BlockService.getLatestBlockHash()
val averageBlockTime = BlockService.getAverageBlockTime()
val totalRewards = BlockService.getTotalRewardsDistributed()
call.respond(mapOf(
"totalBlocks" to totalBlocks,
"latestHeight" to latestHeight,
"latestHash" to latestHash,
"averageBlockTime" to averageBlockTime,
"totalRewardsDistributed" to totalRewards
))
} catch (e: Exception) {
call.respond(HttpStatusCode.InternalServerError, mapOf("error" to (e.message ?: "Failed to get blockchain statistics")))
}
}
}
}