package fetch import ( "context" "crypto/sha256" "encoding/hex" "fmt" "io" "net/http" "net/http/httptest" "strings" "sync/atomic" "testing" ) // TestProofOfWork: a host that answers 403 with the challenge page until a cookie is set is // paid once, verified the way the server would, and later requests pass. func TestProofOfWork(t *testing.T) { var solves, apiHits int32 srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case r.URL.Path == powPath && r.Method == http.MethodGet: fmt.Fprint(w, `{"c":0,"t":1,"r":"971bb96a00000000","s":"sig"}`) case r.URL.Path == powPath && r.Method == http.MethodPost: b, _ := io.ReadAll(r.Body) parts := strings.Split(string(b), "-") sum := sha256.Sum256([]byte(parts[0] + parts[2])) if len(parts) != 3 || parts[1] != "sig" || !strings.HasPrefix(hex.EncodeToString(sum[:]), "0000") { w.WriteHeader(http.StatusBadRequest) return } atomic.AddInt32(&solves, 1) http.SetCookie(w, &http.Cookie{Name: "berghain", Value: "ok", Path: "/"}) case r.URL.Path == "/api/0/version": atomic.AddInt32(&apiHits, 1) if c, err := r.Cookie("berghain"); err != nil || c.Value != "ok" { w.WriteHeader(http.StatusForbidden) fmt.Fprint(w, ``) return } fmt.Fprint(w, `{"version":"0.31"}`) } })) defer srv.Close() c := New(t.TempDir()) c.MinInterval = 0 for i := 0; i < 2; i++ { b, err := c.Get(context.Background(), srv.URL+"/api/0/version", NoCache()) if err != nil || string(b) != `{"version":"0.31"}` { t.Fatalf("get %d: %v %q", i, err, b) } } if solves != 1 || apiHits != 3 { t.Fatalf("solves=%d apiHits=%d, want one solve and one refused request", solves, apiHits) } if n, ok := solvePoWNonce("971bb96a00000000"); !ok || n != 21517 { t.Fatalf("nonce for a challenge seen on code.opensuse.org: %d %v (the browser found 21517)", n, ok) } }