93 lines
2.6 KiB
Go
93 lines
2.6 KiB
Go
package proxy
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"encoding/hex"
|
|
"strconv"
|
|
)
|
|
|
|
// Job holds the current work unit received from a pool. Immutable once assigned.
|
|
//
|
|
// j := proxy.Job{
|
|
// Blob: "0707d5ef...b01",
|
|
// JobID: "4BiGm3/RgGQzgkTI",
|
|
// Target: "b88d0600",
|
|
// Algo: "cn/r",
|
|
// }
|
|
type Job struct {
|
|
Blob string `json:"blob"` // hex-encoded block template (160 hex chars = 80 bytes)
|
|
JobID string `json:"job_id"` // pool-assigned identifier
|
|
Target string `json:"target"` // 8-char hex little-endian uint32 difficulty target
|
|
Algo string `json:"algo"` // algorithm e.g. "cn/r", "rx/0"; "" if not negotiated
|
|
Height uint64 `json:"height"` // block height (0 if pool did not provide)
|
|
SeedHash string `json:"seed_hash"` // RandomX seed hash hex (empty if not RandomX)
|
|
ClientID string `json:"id"` // pool session ID that issued this job (for stale detection)
|
|
}
|
|
|
|
// IsValid returns true if Blob and JobID are non-empty.
|
|
//
|
|
// if !job.IsValid() { return }
|
|
func (j Job) IsValid() bool {
|
|
return j.Blob != "" && j.JobID != ""
|
|
}
|
|
|
|
// BlobWithFixedByte returns a copy of Blob with hex characters at positions 78-79
|
|
// (blob byte index 39) replaced by the two-digit lowercase hex of fixedByte.
|
|
//
|
|
// partitioned := job.BlobWithFixedByte(0x2A) // chars 78-79 become "2a"
|
|
func (j Job) BlobWithFixedByte(fixedByte uint8) string {
|
|
if len(j.Blob) < 80 {
|
|
return j.Blob
|
|
}
|
|
|
|
blob := []byte(j.Blob)
|
|
blob[78] = lowerHexDigit(fixedByte >> 4)
|
|
blob[79] = lowerHexDigit(fixedByte & 0x0F)
|
|
return string(blob)
|
|
}
|
|
|
|
// DifficultyFromTarget converts the 8-char little-endian hex Target field to a uint64 difficulty.
|
|
//
|
|
// diff := job.DifficultyFromTarget() // "b88d0600" → ~100000
|
|
func (j Job) DifficultyFromTarget() uint64 {
|
|
if len(j.Target) != 8 {
|
|
return 0
|
|
}
|
|
|
|
targetBytes, errorValue := hex.DecodeString(j.Target)
|
|
if errorValue != nil || len(targetBytes) != 4 {
|
|
return 0
|
|
}
|
|
|
|
targetValue := binary.LittleEndian.Uint32(targetBytes)
|
|
if targetValue == 0 {
|
|
return 0
|
|
}
|
|
|
|
return uint64(^uint32(0) / targetValue)
|
|
}
|
|
|
|
// TargetForDifficulty converts a difficulty back to the 8-char little-endian target field.
|
|
//
|
|
// target := proxy.TargetForDifficulty(100000)
|
|
func TargetForDifficulty(difficulty uint64) string {
|
|
if difficulty <= 1 {
|
|
return "ffffffff"
|
|
}
|
|
|
|
targetValue := uint64(^uint32(0)) / difficulty
|
|
if targetValue == 0 {
|
|
targetValue = 1
|
|
}
|
|
if targetValue > uint64(^uint32(0)) {
|
|
targetValue = uint64(^uint32(0))
|
|
}
|
|
|
|
targetBytes := make([]byte, 4)
|
|
binary.LittleEndian.PutUint32(targetBytes, uint32(targetValue))
|
|
return hex.EncodeToString(targetBytes)
|
|
}
|
|
|
|
func lowerHexDigit(value uint8) byte {
|
|
return strconv.FormatUint(uint64(value), 16)[0]
|
|
}
|