package ethash import ( "encoding/json" "io/ioutil" "math/big" "net" "net/http" "testing" "time" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/types" ) // Tests whether remote HTTP servers are correctly notified of new work. func TestRemoteNotify(t *testing.T) { // Start a simple webserver to capture notifications sink := make(chan [3]string) server := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { blob, err := ioutil.ReadAll(req.Body) if err != nil { t.Fatalf("failed to read miner notification: %v", err) } var work [3]string if err := json.Unmarshal(blob, &work); err != nil { t.Fatalf("failed to unmarshal miner notification: %v", err) } sink <- work }), } // Open a custom listener to extract its local address listener, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("failed to open notification server: %v", err) } defer listener.Close() go server.Serve(listener) // Create the custom ethash engine ethash := NewTester([]string{"http://" + listener.Addr().String()}) defer ethash.Close() // Stream a work task and ensure the notification bubbles out header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} block := types.NewBlockWithHeader(header) ethash.Seal(nil, block, nil) select { case work := <-sink: if want := header.HashNoNonce().Hex(); work[0] != want { t.Errorf("work packet hash mismatch: have %s, want %s", work[0], want) } if want := common.BytesToHash(SeedHash(header.Number.Uint64())).Hex(); work[1] != want { t.Errorf("work packet seed mismatch: have %s, want %s", work[1], want) } target := new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), header.Difficulty) if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want { t.Errorf("work packet target mismatch: have %s, want %s", work[2], want) } case <-time.After(time.Second): t.Fatalf("notification timed out") } } // Tests that pushing work packages fast to the miner doesn't cause any daa race // issues in the notifications. func TestRemoteMultiNotify(t *testing.T) { // Start a simple webserver to capture notifications sink := make(chan [3]string, 64) server := &http.Server{ Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { blob, err := ioutil.ReadAll(req.Body) if err != nil { t.Fatalf("failed to read miner notification: %v", err) } var work [3]string if err := json.Unmarshal(blob, &work); err != nil { t.Fatalf("failed to unmarshal miner notification: %v", err) } sink <- work }), } // Open a custom listener to extract its local address listener, err := net.Listen("tcp", "localhost:0") if err != nil { t.Fatalf("failed to open notification server: %v", err) } defer listener.Close() go server.Serve(listener) // Create the custom ethash engine ethash := NewTester([]string{"http://" + listener.Addr().String()}) defer ethash.Close() // Stream a lot of work task and ensure all the notifications bubble out for i := 0; i < cap(sink); i++ { header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)} block := types.NewBlockWithHeader(header) ethash.Seal(nil, block, nil) } for i := 0; i < cap(sink); i++ { select { case <-sink: case <-time.After(250 * time.Millisecond): t.Fatalf("notification %d timed out", i) } } }