API Endpoints
En el vasto ecosistema de KodeChain, donde múltiples componentes interactúan en armonía, la API REST emerge como el lenguaje universal que permite la comunicación entre nodos, aplicaciones y usuarios. Implementada a través de Gin y documentada con Swagger, esta interfaz transforma operaciones técnicas complejas en endpoints HTTP intuitivos y bien definidos.
La Arquitectura de la API
Gin Framework: El Motor HTTP
// En api/server.go
func NewServer() *Server {
gin.SetMode(gin.ReleaseMode)
r := gin.Default()
// Middleware de logging
r.Use(gin.Logger())
r.Use(gin.Recovery())
// Configuración de CORS
r.Use(cors.Default())
return &Server{
router: r,
endpoints: make(map[string]Endpoint),
}
}
Gin proporciona la base sólida sobre la que se construye toda la comunicación HTTP del sistema.
Estructura de Endpoints Organizada
// En api/endpoints/
├── blockchain.go // Estado de la blockchain
├── transactions.go // Gestión de transacciones
├── validator_registration.go // Registro de validadores
├── staking.go // Sistema de staking
├── sync.go // Sincronización
└── upnp.go // Configuración UPnP
Cada archivo se enfoca en un dominio específico, manteniendo la organización y mantenibilidad.
Categorías de Endpoints
Blockchain Status
GET /api/blockchain/status
Obtiene el estado general de ambas cadenas (DPOS y PBFT).
func (s *Server) getBlockchainStatus(c *gin.Context) {
status := map[string]interface{}{
"dpos": s.getChainStatus("DPOS"),
"pbft": s.getChainStatus("PBFT"),
"timestamp": time.Now().Unix(),
}
c.JSON(200, status)
}
Respuesta:
{
"dpos": {
"height": 1500,
"last_block": "0xabc123...",
"validators": 6,
"status": "active"
},
"pbft": {
"height": 1498,
"last_block": "0xdef456...",
"validators": 4,
"status": "active"
},
"timestamp": 1640995200
}
Gestión de Transacciones
POST /api/transaction/create
Crea una nueva transacción y la añade al mempool.
func (s *Server) createTransaction(c *gin.Context) {
var req CreateTransactionRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Validar transacción
if err := s.validateTransaction(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Crear y firmar transacción
tx := s.createTransactionFromRequest(&req)
// Añadir al mempool
if err := s.mempool.AddTransaction(tx); err != nil {
c.JSON(500, gin.H{"error": "Failed to add to mempool"})
return
}
c.JSON(201, gin.H{
"success": true,
"transaction_hash": tx.Hash,
"mempool_size": s.mempool.Size(),
})
}
Parámetros:
{
"from": "0x1000000000000000000000000000000000000001",
"to": "0x1000000000000000000000000000000000000002",
"amount": 1000000000000000000,
"type": "transfer",
"data": ""
}
Sistema de Staking
POST /api/staking/delegate/register
Registra un nuevo validador en el sistema de staking.
func (s *Server) registerDelegate(c *gin.Context) {
var req DelegateRegistrationRequest
if err := c.ShouldBindJSON(&req); err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
// Verificar stake mínimo
if req.StakeAmount < MIN_STAKE {
c.JSON(400, gin.H{"error": "Stake amount below minimum"})
return
}
// Registrar en staking contract
nodeID, err := s.stakingContract.RegisterDelegate(
req.DelegateAddress,
req.StakeAmount,
req.ConsensusType,
)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{
"success": true,
"node_id": nodeID,
"message": "Delegate registered successfully",
})
}
Sincronización entre Nodos
GET /api/sync/height
Obtiene la altura actual de la blockchain para un tipo de consenso específico.
func (s *Server) getBlockHeight(c *gin.Context) {
consensusType := c.GetHeader("X-Consensus-Type")
if consensusType == "" {
c.JSON(400, gin.H{"error": "X-Consensus-Type header required"})
return
}
height, err := s.blockchain.GetHeight(consensusType)
if err != nil {
c.JSON(500, gin.H{"error": err.Error()})
return
}
c.JSON(200, gin.H{
"height": height,
"consensus_type": consensusType,
"timestamp": time.Now().Unix(),
})
}
Headers requeridos:
X-Consensus-Type: "DPOS" o "PBFT"
Middleware y Seguridad
Rate Limiting
func RateLimitMiddleware() gin.HandlerFunc {
limiter := tollbooth.NewLimiter(10, nil) // 10 requests por segundo
return func(c *gin.Context) {
httpError := tollbooth.LimitByRequest(limiter, c.Writer, c.Request)
if httpError != nil {
c.JSON(httpError.StatusCode, gin.H{
"error": "Rate limit exceeded",
"retry_after": httpError.Message,
})
c.Abort()
return
}
c.Next()
}
}
Protege contra abusos y ataques de denegación de servicio.
Autenticación y Autorización
func AuthMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
auth := c.GetHeader("Authorization")
if auth == "" {
c.JSON(401, gin.H{"error": "Authorization header required"})
c.Abort()
return
}
// Verificar token de autenticación
if !s.validateAuthToken(auth) {
c.JSON(403, gin.H{"error": "Invalid authentication token"})
c.Abort()
return
}
c.Next()
}
}
Asegura que solo usuarios autorizados puedan acceder a endpoints sensibles.
Headers Especiales
X-Consensus-Type
Especifica qué cadena (DPOS o PBFT) debe manejar la solicitud.
consensusType := c.GetHeader("X-Consensus-Type")
switch consensusType {
case "DPOS":
// Manejar con lógica DPOS
case "PBFT":
// Manejar con lógica PBFT
default:
c.JSON(400, gin.H{"error": "Invalid consensus type"})
return
}
Content-Type
Siempre debe ser application/json para requests con body.
r.POST("/api/transaction/create", func(c *gin.Context) {
if c.ContentType() != "application/json" {
c.JSON(400, gin.H{"error": "Content-Type must be application/json"})
return
}
// Procesar request...
})
Manejo de Errores
Estructura de Respuestas de Error
type ErrorResponse struct {
Error string `json:"error"`
Code int `json:"code"`
Details string `json:"details,omitempty"`
}
Códigos de Estado HTTP
200 OK: Operación exitosa201 Created: Recurso creado exitosamente400 Bad Request: Parámetros inválidos401 Unauthorized: Autenticación requerida403 Forbidden: Permisos insuficientes404 Not Found: Recurso no encontrado405 Method Not Allowed: Método HTTP no soportado409 Conflict: Conflicto con estado actual500 Internal Server Error: Error del servidor
Documentación con Swagger
Generación Automática
//go:generate swag init -g main.go -o docs/
El comando genera automáticamente la documentación Swagger desde los comentarios en el código.
Comentarios de Documentación
// getBlockchainStatus godoc
// @Summary Get blockchain status
// @Description Get the status of both DPOS and PBFT chains
// @Tags blockchain
// @Accept json
// @Produce json
// @Success 200 {object} map[string]interface{}
// @Router /api/blockchain/status [get]
func (s *Server) getBlockchainStatus(c *gin.Context) {
// Implementación...
}
Los comentarios especiales generan la documentación Swagger automáticamente.
Testing de Endpoints
Tests Unitarios
func TestCreateTransaction(t *testing.T) {
// Setup
server := NewServer()
req := CreateTransactionRequest{
From: "0x1000...001",
To: "0x1000...002",
Amount: 1000000000000000000,
}
// Execute
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request = &http.Request{
Method: "POST",
Body: io.NopCloser(strings.NewReader(toJSON(req))),
}
server.createTransaction(c)
// Assert
assert.Equal(t, 201, w.Code)
var response map[string]interface{}
json.Unmarshal(w.Body.Bytes(), &response)
assert.True(t, response["success"].(bool))
}
Tests de Integración
func TestFullTransactionFlow(t *testing.T) {
// Iniciar servidor completo
server := setupTestServer()
// Crear transacción
txResp := createTestTransaction(server)
assert.NotEmpty(t, txResp["transaction_hash"])
// Verificar en mempool
mempoolResp := getMempoolStats(server)
assert.Greater(t, mempoolResp["size"], 0)
// Esperar minado
time.Sleep(3 * time.Second)
// Verificar en blockchain
blockResp := getLatestBlock(server)
assert.Contains(t, blockResp["transactions"], txResp["transaction_hash"])
}
Monitoreo y Métricas
Métricas de Rendimiento
func (s *Server) metricsMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
path := c.Request.URL.Path
c.Next()
duration := time.Since(start)
status := c.Writer.Status()
// Registrar métricas
s.metrics.RecordRequest(path, status, duration)
}
}
Health Checks
func (s *Server) healthCheck(c *gin.Context) {
health := map[string]interface{}{
"status": "healthy",
"timestamp": time.Now().Unix(),
"version": s.version,
"uptime": time.Since(s.startTime).String(),
}
// Verificar componentes críticos
if err := s.checkDatabase(); err != nil {
health["database"] = "unhealthy"
health["status"] = "degraded"
}
if err := s.checkConsensus(); err != nil {
health["consensus"] = "unhealthy"
health["status"] = "degraded"
}
statusCode := 200
if health["status"] != "healthy" {
statusCode = 503
}
c.JSON(statusCode, health)
}
El Rol en la Arquitectura
Puerta de Entrada al Sistema
La API REST actúa como la interfaz principal entre:
- Usuarios finales: Aplicaciones web y móviles
- Otros nodos: Comunicación P2P
- Herramientas de desarrollo: SDKs y bibliotecas
- Sistemas de monitoreo: Dashboards y alertas
Abstracción de Complejidad
// Para el usuario: simple llamada HTTP
POST /api/transaction/create
// Detrás de escena: compleja lógica distribuida
func createTransaction() {
// Validar transacción
// Verificar balances
// Firmar con criptografía cuántica
// Añadir al mempool distribuido
// Propagar a otros nodos
// Esperar confirmación de consenso
}
Escalabilidad y Mantenibilidad
La arquitectura modular permite:
- Añadir nuevos endpoints: Sin afectar existentes
- Versionado de API: Soporte para múltiples versiones
- Rate limiting granular: Por endpoint o usuario
- Logging detallado: Para debugging y auditoría
El Arte de la Comunicación
Cada endpoint, cada parámetro, cada código de respuesta representa un contrato entre el sistema y sus usuarios. La API REST de KodeChain no solo expone funcionalidad; establece un lenguaje común que hace que la complejidad de la blockchain sea accesible y utilizable.
Desde el primer gin.Default() hasta la respuesta JSON final, la API transforma operaciones técnicas en interacciones intuitivas, permitiendo que desarrolladores y usuarios construyan sobre KodeChain con confianza y facilidad.