diff --git a/internal/service/adapters.go b/internal/service/adapters.go new file mode 100644 index 0000000..a789c04 --- /dev/null +++ b/internal/service/adapters.go @@ -0,0 +1,16 @@ +package service + +import "hash-of-wisdom/internal/pow/challenge" + +// generatorAdapter adapts the real challenge.Generator to our interface +type generatorAdapter struct { + generator *challenge.Generator +} + +func NewGeneratorAdapter(generator *challenge.Generator) ChallengeGenerator { + return &generatorAdapter{generator: generator} +} + +func (a *generatorAdapter) GenerateChallenge() (*challenge.Challenge, error) { + return a.generator.GenerateChallenge() +} diff --git a/internal/service/wisdom.go b/internal/service/wisdom.go new file mode 100644 index 0000000..5cc2ca7 --- /dev/null +++ b/internal/service/wisdom.go @@ -0,0 +1,86 @@ +package service + +import ( + "context" + "errors" + "fmt" + "hash-of-wisdom/internal/pow/challenge" + "hash-of-wisdom/internal/quotes" +) + +var ( + ErrResourceRequired = errors.New("resource is required") + ErrUnsupportedResource = errors.New("unsupported resource") + ErrSolutionRequired = errors.New("solution is required") + ErrInvalidChallenge = errors.New("invalid challenge") + ErrInvalidSolution = errors.New("invalid proof of work solution") +) + +type ChallengeGenerator interface { + GenerateChallenge() (*challenge.Challenge, error) +} + +type ChallengeVerifier interface { + VerifyChallenge(ch *challenge.Challenge) error +} + +type QuoteService interface { + GetRandomQuote(ctx context.Context) (*quotes.Quote, error) +} + +type WisdomService struct { + challengeGenerator ChallengeGenerator + challengeVerifier ChallengeVerifier + quoteService QuoteService +} + +func NewWisdomService( + generator ChallengeGenerator, + verifier ChallengeVerifier, + quoteService QuoteService, +) *WisdomService { + return &WisdomService{ + challengeGenerator: generator, + challengeVerifier: verifier, + quoteService: quoteService, + } +} + +func (s *WisdomService) GenerateChallenge(ctx context.Context, resource string) (*challenge.Challenge, error) { + if resource == "" { + return nil, ErrResourceRequired + } + + if resource != "quotes" { + return nil, ErrUnsupportedResource + } + + ch, err := s.challengeGenerator.GenerateChallenge() + if err != nil { + return nil, fmt.Errorf("failed to generate challenge: %w", err) + } + + return ch, nil +} + +func (s *WisdomService) VerifySolution(ctx context.Context, solution *challenge.Solution) error { + if solution == nil { + return ErrSolutionRequired + } + + // Verify challenge authenticity and expiration + if err := s.challengeVerifier.VerifyChallenge(&solution.Challenge); err != nil { + return fmt.Errorf("%w: %v", ErrInvalidChallenge, err) + } + + // Verify PoW solution + if !solution.Verify() { + return ErrInvalidSolution + } + + return nil +} + +func (s *WisdomService) GetQuote(ctx context.Context) (*quotes.Quote, error) { + return s.quoteService.GetRandomQuote(ctx) +}