feat: added block routes
This commit is contained in:
@@ -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")))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user