package main import ( "context" "crypto" _ "crypto/sha1" "encoding/hex" "fmt" "math/rand" "os" "runtime" "time" ) func main() { g := Generator{ Difficulty: 25, Timeout: 10 * time.Second, } c := g.RandomChallenge() c.Solve() if c.Validate() { fmt.Println(c.String()) } } type Challenge struct { Input [48]byte Date [20]byte Solution int64 bytes []byte Generator } type Generator struct { Difficulty byte Timeout time.Duration } func (g *Generator) RandomChallenge() Challenge { bytes := make([]byte, 48) for i, _ := range bytes { bytes[i] = byte(rand.Int()) } today := time.Now().UTC() date := today.Format("2006-01-02") hash := crypto.SHA1.New().Sum([]byte(date)) return Challenge{ Input: [48]byte(bytes), Date: [20]byte(hash), Generator: *g, } } // using a lookup table for this is super goofy but i'm lazy and it's 6am var prefixLookup = []int{ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } func (c *Challenge) Solve() { c.bytes = make([]byte, 76) c.bytes = append(c.bytes, c.Input[:]...) c.bytes = append(c.bytes, c.Date[:]...) timer := time.Now() solution := make(chan int64) ctx, cancel := context.WithCancel(context.Background()) count := runtime.NumCPU() for i := range count { go func(id int, ctx context.Context) { i := int64(id) defer cancel() for { select { case <-ctx.Done(): return default: s := c.CheckValue(i) if s { solution <- i fmt.Fprintf(os.Stderr, "[thread %d] ", id) cancel() break } if c.Solution > 0xffffffffffffff { fmt.Fprintf(os.Stderr, "%s (thread %d): could not find solution!\n", c.String(), id) cancel() break } if time.Since(timer) > c.Timeout { fmt.Fprintf(os.Stderr, "%s (thread %d): timed out!\n", c.String(), id) cancel() break } i += int64(count) } } }(i, ctx) } select { case c.Solution = <-solution: cancel() fmt.Fprintf(os.Stderr, "%s: found solution %d in %dms\n", c.String(), c.Solution, time.Since(timer).Milliseconds()) break case <-ctx.Done(): cancel() break } } func (c *Challenge) CheckValue(v int64) bool { bytes := make([]byte, len(c.bytes)) copy(bytes, c.bytes) for i := range 8 { bytes[75-i] = byte(v >> (i * 8)) } sum := make([]byte, 20) hash := crypto.SHA1.New() hash.Write(bytes) sum = hash.Sum(nil) count := 0 for _, c := range sum { p := prefixLookup[c] count += p if p != 8 { break } } return count >= int(c.Difficulty) } func (c *Challenge) Validate() bool { return c.CheckValue(c.Solution) } func (c *Challenge) String() string { bytes := make([]byte, 0) bytes = append(bytes, c.Input[:]...) bytes = append(bytes, c.Date[:]...) bytes = append(bytes, make([]byte, 8)...) bytes[68] = c.Difficulty for i := range 7 { bytes[75-i] = byte(c.Solution >> (i * 8)) } return hex.EncodeToString(bytes) }