199 lines
5.5 KiB
Go
199 lines
5.5 KiB
Go
package http
|
|
|
|
import (
|
|
"context"
|
|
"encoding/hex"
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
grpcadapter "github.com/rwadurian/mpc-system/services/account/adapters/output/grpc"
|
|
)
|
|
|
|
// MPCHandler handles MPC-related HTTP requests
|
|
type MPCHandler struct {
|
|
sessionCoordinatorClient *grpcadapter.SessionCoordinatorClient
|
|
}
|
|
|
|
// NewMPCHandler creates a new MPCHandler
|
|
func NewMPCHandler(sessionCoordinatorClient *grpcadapter.SessionCoordinatorClient) *MPCHandler {
|
|
return &MPCHandler{
|
|
sessionCoordinatorClient: sessionCoordinatorClient,
|
|
}
|
|
}
|
|
|
|
// RegisterRoutes registers MPC routes
|
|
func (h *MPCHandler) RegisterRoutes(router *gin.RouterGroup) {
|
|
mpc := router.Group("/mpc")
|
|
{
|
|
mpc.POST("/keygen", h.CreateKeygenSession)
|
|
mpc.POST("/sign", h.CreateSigningSession)
|
|
mpc.GET("/sessions/:id", h.GetSessionStatus)
|
|
}
|
|
}
|
|
|
|
// CreateKeygenSessionRequest represents a keygen session creation request
|
|
type CreateKeygenSessionRequest struct {
|
|
ThresholdN int json:"threshold_n" binding:"required,min=2,max=10"
|
|
ThresholdT int json:"threshold_t" binding:"required,min=1"
|
|
Participants []ParticipantRequest json:"participants" binding:"required,min=2"
|
|
}
|
|
|
|
// ParticipantRequest represents a participant in a request
|
|
type ParticipantRequest struct {
|
|
PartyID string json:"party_id" binding:"required"
|
|
DeviceType string json:"device_type" binding:"required"
|
|
DeviceID string json:"device_id,omitempty"
|
|
}
|
|
|
|
// CreateKeygenSession handles creating a new keygen session
|
|
func (h *MPCHandler) CreateKeygenSession(c *gin.Context) {
|
|
var req CreateKeygenSessionRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Validate threshold
|
|
if req.ThresholdT > req.ThresholdN {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "threshold_t cannot be greater than threshold_n"})
|
|
return
|
|
}
|
|
|
|
if len(req.Participants) != req.ThresholdN {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "number of participants must equal threshold_n"})
|
|
return
|
|
}
|
|
|
|
// Convert participants
|
|
participants := make([]grpcadapter.ParticipantInfo, len(req.Participants))
|
|
for i, p := range req.Participants {
|
|
participants[i] = grpcadapter.ParticipantInfo{
|
|
PartyID: p.PartyID,
|
|
DeviceType: p.DeviceType,
|
|
DeviceID: p.DeviceID,
|
|
}
|
|
}
|
|
|
|
// Call gRPC service
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := h.sessionCoordinatorClient.CreateKeygenSession(
|
|
ctx,
|
|
int32(req.ThresholdN),
|
|
int32(req.ThresholdT),
|
|
participants,
|
|
600, // 10 minutes expiry
|
|
)
|
|
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"session_id": resp.SessionID,
|
|
"session_type": "keygen",
|
|
"threshold_n": req.ThresholdN,
|
|
"threshold_t": req.ThresholdT,
|
|
"join_tokens": resp.JoinTokens,
|
|
"status": "created",
|
|
})
|
|
}
|
|
|
|
// CreateSigningSessionRequest represents a signing session creation request
|
|
type CreateSigningSessionRequest struct {
|
|
AccountID string json:"account_id" binding:"required"
|
|
MessageHash string json:"message_hash" binding:"required"
|
|
Participants []ParticipantRequest json:"participants" binding:"required,min=2"
|
|
}
|
|
|
|
// CreateSigningSession handles creating a new signing session
|
|
func (h *MPCHandler) CreateSigningSession(c *gin.Context) {
|
|
var req CreateSigningSessionRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
// Decode message hash
|
|
messageHash, err := hex.DecodeString(req.MessageHash)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "invalid message hash format"})
|
|
return
|
|
}
|
|
|
|
// Convert participants
|
|
participants := make([]grpcadapter.ParticipantInfo, len(req.Participants))
|
|
for i, p := range req.Participants {
|
|
participants[i] = grpcadapter.ParticipantInfo{
|
|
PartyID: p.PartyID,
|
|
DeviceType: p.DeviceType,
|
|
DeviceID: p.DeviceID,
|
|
}
|
|
}
|
|
|
|
// Determine threshold (should come from account configuration)
|
|
// For now, use len(participants) as threshold
|
|
thresholdT := int32(len(req.Participants))
|
|
|
|
// Call gRPC service
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := h.sessionCoordinatorClient.CreateSigningSession(
|
|
ctx,
|
|
thresholdT,
|
|
participants,
|
|
messageHash,
|
|
600, // 10 minutes expiry
|
|
)
|
|
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, gin.H{
|
|
"session_id": resp.SessionID,
|
|
"session_type": "sign",
|
|
"account_id": req.AccountID,
|
|
"message_hash": req.MessageHash,
|
|
"threshold_t": thresholdT,
|
|
"join_tokens": resp.JoinTokens,
|
|
"status": "created",
|
|
})
|
|
}
|
|
|
|
// GetSessionStatus handles querying session status
|
|
func (h *MPCHandler) GetSessionStatus(c *gin.Context) {
|
|
sessionID := c.Param("id")
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
defer cancel()
|
|
|
|
resp, err := h.sessionCoordinatorClient.GetSessionStatus(ctx, sessionID)
|
|
if err != nil {
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
response := gin.H{
|
|
"session_id": sessionID,
|
|
"status": resp.Status,
|
|
"completed_parties": resp.CompletedParties,
|
|
"total_parties": resp.TotalParties,
|
|
}
|
|
|
|
if len(resp.PublicKey) > 0 {
|
|
response["public_key"] = hex.EncodeToString(resp.PublicKey)
|
|
}
|
|
|
|
if len(resp.Signature) > 0 {
|
|
response["signature"] = hex.EncodeToString(resp.Signature)
|
|
}
|
|
|
|
c.JSON(http.StatusOK, response)
|
|
}
|