package main import ( "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "html/template" "log" "net/http" "strconv" "sync" "time" ) const ( port = 3000 difficulty = 20 ) var ( challengeStore = make(map[string]time.Time) storeMutex sync.Mutex ) func generateChallenge() string { b := make([]byte, 12) _, err := rand.Read(b) if err != nil { panic(err) } return hex.EncodeToString(b) } func checkDifficulty(hash []byte, bits int) bool { i := 0 for bits > 0 { b := hash[i] if bits >= 8 { if b != 0 { return false } bits -= 8 } else { mask := byte(0xFF << (8 - bits)) if b&mask != 0 { return false } bits = 0 } i++ } return true } func indexHandler(w http.ResponseWriter, r *http.Request) { challenge := generateChallenge() storeMutex.Lock() challengeStore[challenge] = time.Now() storeMutex.Unlock() tmpl.Execute(w, map[string]string{ "Challenge": challenge, "Difficulty": strconv.Itoa(difficulty), }) } func postHandler(w http.ResponseWriter, r *http.Request) { challenge := r.FormValue("challenge") nonce := r.FormValue("nonce") storeMutex.Lock() _, ok := challengeStore[challenge] if ok { delete(challengeStore, challenge) } storeMutex.Unlock() if !ok { fmt.Fprint(w, "
Invalid challenge.
Try again") return } hash := sha256.Sum256([]byte(challenge + nonce)) if checkDifficulty(hash[:], difficulty) { fmt.Fprint(w, "Success! Valid nonce.
Try again") } else { fmt.Fprint(w, "Invalid nonce.
Try again") } } var tmpl = template.Must(template.New("page").Parse(`https://git.libroot.org/libroot/NOJSCAP/
If you don't already have the NOJSCAP client:
$ git clone https://git.libroot.org/libroot/NOJSCAP/$ cd NOJSCAP/client/
$ python3 pow_client.py {{.Challenge}} {{.Difficulty}}
Go:
$ go run pow_client.go {{.Challenge}} {{.Difficulty}}
Node.js:
$ node pow_client.js {{.Challenge}} {{.Difficulty}}
Rust:
$ rustc pow_client.rs -o pow_client_rs$ pow_client_rs {{.Challenge}} {{.Difficulty}}
C:
$ gcc -O2 -o pow_client pow_client.c -lssl -lcrypto$ ./pow_client {{.Challenge}} {{.Difficulty}}