package solver import ( "context" "runtime" "sync/atomic" "hash-of-wisdom/internal/pow/challenge" ) // Solver handles parallel PoW solving with configurable parallelism type Solver struct { workers int } // SolverOption represents functional options for solver configuration type solverOption func(*Solver) // WithWorkers sets the number of parallel workers func WithWorkers(workers int) solverOption { return func(s *Solver) { s.workers = workers } } // NewSolver creates a new parallel PoW solver func NewSolver(options ...solverOption) *Solver { solver := &Solver{ workers: runtime.NumCPU(), // Default to CPU count } for _, option := range options { option(solver) } return solver } // Solve attempts to find a valid nonce for the given challenge func (s *Solver) Solve(ctx context.Context, ch *challenge.Challenge) (*challenge.Solution, error) { // Create cancellable context for workers ctx, cancel := context.WithCancel(ctx) defer cancel() // Channel to receive solution from any worker solutionCh := make(chan *challenge.Solution, 1) // Shared nonce counter for work distribution var nonceCounter atomic.Uint64 // Start parallel workers for i := range s.workers { go func(workerID int) { s.worker(ctx, ch, &nonceCounter, solutionCh) }(i) } // Wait for either solution or context cancellation select { case solution := <-solutionCh: return solution, nil case <-ctx.Done(): return nil, ctx.Err() } } // worker performs PoW computation in parallel func (s *Solver) worker(ctx context.Context, ch *challenge.Challenge, nonceCounter *atomic.Uint64, solutionCh chan<- *challenge.Solution) { for { select { case <-ctx.Done(): return default: // Get next nonce to try nonce := nonceCounter.Add(1) - 1 // Try this nonce if ch.VerifySolution(nonce) { // Found solution! Send it back solution := &challenge.Solution{ Challenge: *ch, Nonce: nonce, } select { case solutionCh <- solution: case <-ctx.Done(): } return } } } }