122 lines
2.9 KiB
Go
122 lines
2.9 KiB
Go
package proxy
|
|
|
|
import (
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// CustomDiffBucketStats tracks per-custom-difficulty share outcomes.
|
|
type CustomDiffBucketStats struct {
|
|
Accepted uint64 `json:"accepted"`
|
|
Rejected uint64 `json:"rejected"`
|
|
Invalid uint64 `json:"invalid"`
|
|
Expired uint64 `json:"expired"`
|
|
HashesTotal uint64 `json:"hashes_total"`
|
|
}
|
|
|
|
// CustomDiffBuckets records share totals grouped by miner custom difficulty.
|
|
//
|
|
// buckets := NewCustomDiffBuckets(true)
|
|
// buckets.OnAccept(Event{Miner: &Miner{customDiff: 50000}, Diff: 25000})
|
|
type CustomDiffBuckets struct {
|
|
enabled bool
|
|
buckets map[uint64]*CustomDiffBucketStats
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// NewCustomDiffBuckets creates a per-difficulty share tracker.
|
|
func NewCustomDiffBuckets(enabled bool) *CustomDiffBuckets {
|
|
return &CustomDiffBuckets{
|
|
enabled: enabled,
|
|
buckets: make(map[uint64]*CustomDiffBucketStats),
|
|
}
|
|
}
|
|
|
|
// SetEnabled toggles recording without discarding any collected buckets.
|
|
func (b *CustomDiffBuckets) SetEnabled(enabled bool) {
|
|
if b == nil {
|
|
return
|
|
}
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
b.enabled = enabled
|
|
}
|
|
|
|
// OnAccept records an accepted share for the miner's custom difficulty bucket.
|
|
func (b *CustomDiffBuckets) OnAccept(e Event) {
|
|
if b == nil || !b.enabled || e.Miner == nil {
|
|
return
|
|
}
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
bucket := b.bucketLocked(e.Miner.customDiff)
|
|
bucket.Accepted++
|
|
if e.Expired {
|
|
bucket.Expired++
|
|
}
|
|
if e.Diff > 0 {
|
|
bucket.HashesTotal += e.Diff
|
|
}
|
|
}
|
|
|
|
// OnReject records a rejected share for the miner's custom difficulty bucket.
|
|
func (b *CustomDiffBuckets) OnReject(e Event) {
|
|
if b == nil || !b.enabled || e.Miner == nil {
|
|
return
|
|
}
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
bucket := b.bucketLocked(e.Miner.customDiff)
|
|
bucket.Rejected++
|
|
if isInvalidShareReason(e.Error) {
|
|
bucket.Invalid++
|
|
}
|
|
}
|
|
|
|
// Snapshot returns a copy of the current bucket totals.
|
|
//
|
|
// summary := buckets.Snapshot()
|
|
func (b *CustomDiffBuckets) Snapshot() map[uint64]CustomDiffBucketStats {
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
if !b.enabled || len(b.buckets) == 0 {
|
|
return nil
|
|
}
|
|
out := make(map[uint64]CustomDiffBucketStats, len(b.buckets))
|
|
for diff, bucket := range b.buckets {
|
|
if bucket == nil {
|
|
continue
|
|
}
|
|
out[diff] = *bucket
|
|
}
|
|
return out
|
|
}
|
|
|
|
func (b *CustomDiffBuckets) bucketLocked(diff uint64) *CustomDiffBucketStats {
|
|
if b.buckets == nil {
|
|
b.buckets = make(map[uint64]*CustomDiffBucketStats)
|
|
}
|
|
bucket, ok := b.buckets[diff]
|
|
if !ok {
|
|
bucket = &CustomDiffBucketStats{}
|
|
b.buckets[diff] = bucket
|
|
}
|
|
return bucket
|
|
}
|
|
|
|
func isInvalidShareReason(reason string) bool {
|
|
reason = strings.ToLower(reason)
|
|
if reason == "" {
|
|
return false
|
|
}
|
|
return strings.Contains(reason, "low diff") ||
|
|
strings.Contains(reason, "lowdifficulty") ||
|
|
strings.Contains(reason, "low difficulty") ||
|
|
strings.Contains(reason, "malformed") ||
|
|
strings.Contains(reason, "difficulty") ||
|
|
strings.Contains(reason, "invalid") ||
|
|
strings.Contains(reason, "nonce")
|
|
}
|