hash-of-wisdom/cmd/client/main.go

135 lines
3.6 KiB
Go
Raw Normal View History

2025-08-23 09:26:49 +03:00
package main
import (
"context"
"flag"
"fmt"
"log"
"net"
"time"
"hash-of-wisdom/internal/pow/challenge"
"hash-of-wisdom/internal/pow/solver"
"hash-of-wisdom/internal/protocol"
)
func main() {
serverAddr := flag.String("addr", "localhost:8080", "server address")
flag.Parse()
fmt.Printf("Connecting to Word of Wisdom server at %s\n", *serverAddr)
// Step 1: Get challenge
challengeResp, err := requestChallenge(*serverAddr)
if err != nil {
log.Fatalf("Failed to get challenge: %v", err)
}
fmt.Printf("Received challenge with difficulty %d\n", challengeResp.Challenge.Difficulty)
// Step 2: Solve challenge
fmt.Println("Solving challenge...")
start := time.Now()
s := solver.NewSolver()
solution, err := s.Solve(context.Background(), challengeResp.Challenge)
if err != nil {
log.Fatalf("Failed to solve challenge: %v", err)
}
solveTime := time.Since(start)
fmt.Printf("Challenge solved in %v with nonce %d\n", solveTime, solution.Nonce)
// Step 3: Submit solution and get quote
err = submitSolution(*serverAddr, challengeResp.Challenge, solution.Nonce)
if err != nil {
log.Fatalf("Failed to submit solution: %v", err)
}
}
func requestChallenge(serverAddr string) (*protocol.ChallengeResponse, error) {
// Connect with timeout
conn, err := net.DialTimeout("tcp", serverAddr, 5*time.Second)
if err != nil {
return nil, fmt.Errorf("failed to connect: %w", err)
}
defer conn.Close()
// Request challenge
fmt.Println("Requesting challenge...")
challengeReq := &protocol.ChallengeRequest{}
if err := challengeReq.Encode(conn); err != nil {
return nil, fmt.Errorf("failed to send challenge request: %w", err)
}
// Receive challenge
decoder := protocol.NewMessageDecoder()
msg, err := decoder.Decode(conn)
if err != nil {
return nil, fmt.Errorf("failed to receive challenge: %w", err)
}
if msg.Type != protocol.ChallengeResponseType {
return nil, fmt.Errorf("unexpected response type: %v", msg.Type)
}
// Parse challenge
challengeResp := &protocol.ChallengeResponse{}
if err := challengeResp.Decode(msg.PayloadStream); err != nil {
return nil, fmt.Errorf("failed to parse challenge: %w", err)
}
return challengeResp, nil
}
func submitSolution(serverAddr string, chall *challenge.Challenge, nonce uint64) error {
// Connect with timeout
conn, err := net.DialTimeout("tcp", serverAddr, 5*time.Second)
if err != nil {
return fmt.Errorf("failed to connect: %w", err)
}
defer conn.Close()
// Submit solution
solutionReq := &protocol.SolutionRequest{
Challenge: *chall,
Nonce: nonce,
}
fmt.Println("Submitting solution...")
if err := solutionReq.Encode(conn); err != nil {
return fmt.Errorf("failed to send solution: %w", err)
}
// Receive quote or error
decoder := protocol.NewMessageDecoder()
msg, err := decoder.Decode(conn)
if err != nil {
return fmt.Errorf("failed to receive response: %w", err)
}
switch msg.Type {
case protocol.QuoteResponseType:
solutionResp := &protocol.SolutionResponse{}
if err := solutionResp.Decode(msg.PayloadStream); err != nil {
return fmt.Errorf("failed to parse quote: %w", err)
}
fmt.Printf("\nQuote received:\n\"%s\"\n— %s\n", solutionResp.Quote.Text, solutionResp.Quote.Author)
case protocol.ErrorResponseType:
errorResp := &protocol.ErrorResponse{}
if err := errorResp.Decode(msg.PayloadStream); err != nil {
fmt.Println("Error: Contact administrator")
return nil
}
if errorResp.Code == protocol.ErrServerError {
fmt.Println("Error: Contact administrator")
} else {
fmt.Printf("Error: %s\n", errorResp.Message)
}
default:
return fmt.Errorf("unexpected response type: %v", msg.Type)
}
return nil
}