Add pow config

This commit is contained in:
Savely Krendelhoff 2025-08-22 18:57:48 +07:00
parent c5dcc2acce
commit 859bd989be
No known key found for this signature in database
GPG key ID: F70DFD34F40238DE

View file

@ -0,0 +1,147 @@
package challenge
import (
"crypto/rand"
"fmt"
"time"
)
// Config holds configuration for the PoW system
type Config struct {
DefaultDifficulty int // Default difficulty in bits (e.g., 4)
MaxDifficulty int // Maximum allowed difficulty (e.g., 10)
MinDifficulty int // Minimum allowed difficulty (e.g., 3)
ChallengeTTL time.Duration // Time-to-live for challenges (e.g., 5 minutes)
HMACSecret []byte // Secret key for HMAC signing
Resource string // Resource identifier (e.g., "quotes")
RandomBytes int // Number of random bytes in challenge (e.g., 6)
LoadAdjustmentBits int // Extra difficulty bits when server under load
FailurePenaltyBits int // Extra difficulty bits per failure group
MaxFailurePenaltyBits int // Maximum failure penalty bits
}
// configOption represents a functional option for Config
type configOption func(*Config)
// WithDefaultDifficulty sets the default difficulty
func WithDefaultDifficulty(difficulty int) configOption {
return func(c *Config) {
c.DefaultDifficulty = difficulty
}
}
// WithMaxDifficulty sets the maximum difficulty
func WithMaxDifficulty(difficulty int) configOption {
return func(c *Config) {
c.MaxDifficulty = difficulty
}
}
// WithMinDifficulty sets the minimum difficulty
func WithMinDifficulty(difficulty int) configOption {
return func(c *Config) {
c.MinDifficulty = difficulty
}
}
// WithChallengeTTL sets the challenge time-to-live
func WithChallengeTTL(ttl time.Duration) configOption {
return func(c *Config) {
c.ChallengeTTL = ttl
}
}
// WithHMACSecret sets the HMAC secret
func WithHMACSecret(secret []byte) configOption {
return func(c *Config) {
c.HMACSecret = secret
}
}
// WithResource sets the resource identifier
func WithResource(resource string) configOption {
return func(c *Config) {
c.Resource = resource
}
}
// WithRandomBytes sets the number of random bytes
func WithRandomBytes(bytes int) configOption {
return func(c *Config) {
c.RandomBytes = bytes
}
}
// WithLoadAdjustmentBits sets the load adjustment bits
func WithLoadAdjustmentBits(bits int) configOption {
return func(c *Config) {
c.LoadAdjustmentBits = bits
}
}
// WithFailurePenaltyBits sets the failure penalty bits
func WithFailurePenaltyBits(bits int) configOption {
return func(c *Config) {
c.FailurePenaltyBits = bits
}
}
// WithMaxFailurePenaltyBits sets the maximum failure penalty bits
func WithMaxFailurePenaltyBits(bits int) configOption {
return func(c *Config) {
c.MaxFailurePenaltyBits = bits
}
}
// NewConfig creates and validates a new PoW configuration with defaults
func NewConfig(opts ...configOption) (*Config, error) {
// Generate default HMAC secret
secret := make([]byte, 32)
rand.Read(secret)
// Create config with defaults
config := &Config{
DefaultDifficulty: 4,
MaxDifficulty: 10,
MinDifficulty: 3,
ChallengeTTL: 5 * time.Minute,
HMACSecret: secret,
Resource: "quotes",
RandomBytes: 6,
LoadAdjustmentBits: 1,
FailurePenaltyBits: 2,
MaxFailurePenaltyBits: 6,
}
// Apply options
for _, opt := range opts {
opt(config)
}
// Validate configuration
if config.RandomBytes <= 0 || config.RandomBytes > 32 {
return nil, fmt.Errorf("%w: random bytes must be between 1-32, got %d", ErrInvalidConfig, config.RandomBytes)
}
if config.MinDifficulty < 1 {
return nil, fmt.Errorf("%w: minimum difficulty must be >= 1, got %d", ErrInvalidConfig, config.MinDifficulty)
}
if config.MaxDifficulty < config.MinDifficulty {
return nil, fmt.Errorf("%w: maximum difficulty (%d) must be >= minimum difficulty (%d)", ErrInvalidConfig, config.MaxDifficulty, config.MinDifficulty)
}
if config.DefaultDifficulty < config.MinDifficulty || config.DefaultDifficulty > config.MaxDifficulty {
return nil, fmt.Errorf("%w: default difficulty (%d) must be between min (%d) and max (%d)", ErrInvalidConfig, config.DefaultDifficulty, config.MinDifficulty, config.MaxDifficulty)
}
if len(config.HMACSecret) == 0 {
return nil, fmt.Errorf("%w: HMAC secret cannot be empty", ErrInvalidConfig)
}
if config.ChallengeTTL <= 0 {
return nil, fmt.Errorf("%w: challenge TTL must be positive, got %v", ErrInvalidConfig, config.ChallengeTTL)
}
return config, nil
}