95 lines
2 KiB
Go
95 lines
2 KiB
Go
|
|
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
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|