hash-of-wisdom/internal/controller/controller.go

122 lines
3.7 KiB
Go

package controller
import (
"context"
"encoding/json"
"fmt"
"hash-of-wisdom/internal/pow/challenge"
"hash-of-wisdom/internal/protocol"
"hash-of-wisdom/internal/quotes"
)
// WisdomService defines the interface for the wisdom service
type WisdomService interface {
GenerateChallenge(ctx context.Context, resource string) (*challenge.Challenge, error)
VerifySolution(ctx context.Context, solution *challenge.Solution) error
GetQuote(ctx context.Context) (*quotes.Quote, error)
}
// WisdomController handles the Word of Wisdom protocol flow
type WisdomController struct {
wisdomService WisdomService
codec *protocol.Codec
}
// NewWisdomController creates a new wisdom protocol controller
func NewWisdomController(wisdomService WisdomService) *WisdomController {
return &WisdomController{
wisdomService: wisdomService,
codec: protocol.NewCodec(),
}
}
// HandleMessage processes a protocol message and returns a response
func (c *WisdomController) HandleMessage(ctx context.Context, msg *protocol.Message) (*protocol.Message, error) {
switch msg.Type {
case protocol.ChallengeRequest:
return c.handleChallengeRequest(ctx, msg)
case protocol.SolutionRequest:
return c.handleSolutionRequest(ctx, msg)
default:
return c.createErrorResponse(protocol.ErrMalformedMessage,
fmt.Sprintf("unsupported message type: 0x%02x", msg.Type))
}
}
// handleChallengeRequest processes challenge requests
func (c *WisdomController) handleChallengeRequest(ctx context.Context, _ *protocol.Message) (*protocol.Message, error) {
challenge, err := c.wisdomService.GenerateChallenge(ctx, "quotes")
if err != nil {
return c.createErrorResponse(protocol.ErrServerError, "failed to generate challenge")
}
// Protocol requires direct challenge JSON (not wrapped)
payload := (*protocol.ChallengeResponsePayload)(challenge)
jsonData, err := json.Marshal(payload)
if err != nil {
return c.createErrorResponse(protocol.ErrServerError, "failed to marshal challenge")
}
return &protocol.Message{
Type: protocol.ChallengeResponse,
Payload: jsonData,
}, nil
}
// handleSolutionRequest processes solution requests
func (c *WisdomController) handleSolutionRequest(ctx context.Context, msg *protocol.Message) (*protocol.Message, error) {
// Parse solution request
var solutionReq protocol.SolutionRequestPayload
if err := json.Unmarshal(msg.Payload, &solutionReq); err != nil {
return c.createErrorResponse(protocol.ErrMalformedMessage, "invalid solution format")
}
// Create solution object
solution := &challenge.Solution{
Challenge: solutionReq.Challenge,
Nonce: solutionReq.Nonce,
}
// Verify solution
if err := c.wisdomService.VerifySolution(ctx, solution); err != nil {
return c.createErrorResponse(protocol.ErrInvalidSolution, "solution verification failed")
}
// Get quote
quote, err := c.wisdomService.GetQuote(ctx)
if err != nil {
return c.createErrorResponse(protocol.ErrServerError, "failed to get quote")
}
// Protocol requires direct quote JSON (not wrapped)
payload := (*protocol.QuoteResponsePayload)(quote)
jsonData, err := json.Marshal(payload)
if err != nil {
return c.createErrorResponse(protocol.ErrServerError, "failed to marshal quote")
}
return &protocol.Message{
Type: protocol.QuoteResponse,
Payload: jsonData,
}, nil
}
// createErrorResponse creates a protocol error response
func (c *WisdomController) createErrorResponse(code, message string) (*protocol.Message, error) {
errorPayload := &protocol.ErrorResponsePayload{
Code: code,
Message: message,
}
jsonData, err := json.Marshal(errorPayload)
if err != nil {
return nil, fmt.Errorf("failed to marshal error: %w", err)
}
return &protocol.Message{
Type: protocol.ErrorResponse,
Payload: jsonData,
}, nil
}