175 lines
3.8 KiB
Go
175 lines
3.8 KiB
Go
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, "<p>Invalid challenge.</p><a href='/'>Try again</a>")
|
|
return
|
|
}
|
|
|
|
hash := sha256.Sum256([]byte(challenge + nonce))
|
|
if checkDifficulty(hash[:], difficulty) {
|
|
fmt.Fprint(w, "<p>Success! Valid nonce.</p><a href='/'>Try again</a>")
|
|
} else {
|
|
fmt.Fprint(w, "<p>Invalid nonce.</p><a href='/'>Try again</a>")
|
|
}
|
|
}
|
|
|
|
var tmpl = template.Must(template.New("page").Parse(`
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<title>NOJSCAP demo</title>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<style>
|
|
body {
|
|
padding: 20px;
|
|
margin-bottom: 300px;
|
|
background: #cacaca;
|
|
color: black;
|
|
}
|
|
code {
|
|
white-space: break-spaces;
|
|
background: black;
|
|
color: white;
|
|
padding: 10px;
|
|
display: block;
|
|
max-width: 500px;
|
|
}
|
|
.h {
|
|
font-size: 21px;
|
|
font-weight: bolder;
|
|
width: 100%;
|
|
float: left;
|
|
}
|
|
button {
|
|
background: #006ed0;
|
|
color: white;
|
|
border: 1px solid #006ed0;
|
|
padding: 10px 20px;
|
|
margin-top: 10px;
|
|
}
|
|
input {
|
|
background: black;
|
|
color: white;
|
|
padding: 10px;
|
|
display: block;
|
|
max-width: 500px;
|
|
width: 100%;
|
|
border: 0px solid transparent;
|
|
font-family: monospace;
|
|
font-size: 12px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<h1>NOJSCAP demo</h1>
|
|
<p><a href="https://git.libroot.org/libroot/NOJSCAP/" target="_blank">https://git.libroot.org/libroot/NOJSCAP/</a></p>
|
|
<p>If you don't already have the <b><em>NOJSCAP</em></b> client:</p>
|
|
<code>$ git clone https://git.libroot.org/libroot/NOJSCAP/$ cd NOJSCAP/client/</code>
|
|
<p><code>$ python3 pow_client.py {{.Challenge}} {{.Difficulty}}</code></p>
|
|
<p><strong>Go:</strong></p>
|
|
<p><code>$ go run pow_client.go {{.Challenge}} {{.Difficulty}}</code></p>
|
|
<p><strong>Node.js:</strong></p>
|
|
<p><code>$ node pow_client.js {{.Challenge}} {{.Difficulty}}</code></p>
|
|
<p><strong>Rust:</strong></p>
|
|
<p><code>$ rustc pow_client.rs -o pow_client_rs$ pow_client_rs {{.Challenge}} {{.Difficulty}}</code></p>
|
|
<p><strong>C:</strong></p>
|
|
<p><code>$ gcc -O2 -o pow_client pow_client.c -lssl -lcrypto$ ./pow_client {{.Challenge}} {{.Difficulty}}</code></p>
|
|
<form method="POST">
|
|
<input type="hidden" name="challenge" value="{{.Challenge}}">
|
|
<label class="h">Nonce: <input name="nonce"></label>
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
</body>
|
|
</html>
|
|
`))
|
|
|
|
func main() {
|
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == http.MethodPost {
|
|
postHandler(w, r)
|
|
} else {
|
|
indexHandler(w, r)
|
|
}
|
|
})
|
|
fmt.Printf("Running on http://localhost:%d\n", port)
|
|
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
|
|
}
|
|
|