135 lines
3.6 KiB
Go
135 lines
3.6 KiB
Go
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
|
|
}
|