package main import ( "crypto" _ "crypto/sha1" "encoding/base64" "fmt" "math/rand" "os" "time" ) func main() { g := Generator{ Difficulty: 25, } c := g.RandomChallenge() c.Solve() fmt.Println(c.String()) } type Challenge struct { Input [48]byte Date [20]byte Difficulty byte Solution int64 bytes []byte } type Generator struct { Difficulty byte } 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), Difficulty: g.Difficulty, } } // 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() for { s := c.Validate() if s { fmt.Fprintf(os.Stderr, "%s: found solution %d in %dms\n", c.String(), c.Solution, time.Since(timer).Milliseconds()) break } if c.Solution > 0xffffffffffffff { fmt.Fprintf(os.Stderr, "%s: could not find solution!", c.String()) } c.Solution++ } } func (c *Challenge) CheckValue(v int64) bool { for i := range 8 { c.bytes[75-i] = byte(v >> (i * 8)) } sum := make([]byte, 20) hash := crypto.SHA1.New() hash.Write(c.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 base64.URLEncoding.EncodeToString(bytes) }