github.com/bloxroute-labs/bor@v0.1.4/consensus/ethash/sealer_test.go (about) 1 package ethash 2 3 import ( 4 "encoding/json" 5 "io/ioutil" 6 "math/big" 7 "net" 8 "net/http" 9 "testing" 10 "time" 11 12 "github.com/maticnetwork/bor/common" 13 "github.com/maticnetwork/bor/core/types" 14 ) 15 16 // Tests whether remote HTTP servers are correctly notified of new work. 17 func TestRemoteNotify(t *testing.T) { 18 // Start a simple webserver to capture notifications 19 sink := make(chan [3]string) 20 21 server := &http.Server{ 22 Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 23 blob, err := ioutil.ReadAll(req.Body) 24 if err != nil { 25 t.Fatalf("failed to read miner notification: %v", err) 26 } 27 var work [3]string 28 if err := json.Unmarshal(blob, &work); err != nil { 29 t.Fatalf("failed to unmarshal miner notification: %v", err) 30 } 31 sink <- work 32 }), 33 } 34 // Open a custom listener to extract its local address 35 listener, err := net.Listen("tcp", "localhost:0") 36 if err != nil { 37 t.Fatalf("failed to open notification server: %v", err) 38 } 39 defer listener.Close() 40 41 go server.Serve(listener) 42 43 // Wait for server to start listening 44 var tries int 45 for tries = 0; tries < 10; tries++ { 46 conn, _ := net.DialTimeout("tcp", listener.Addr().String(), 1*time.Second) 47 if conn != nil { 48 break 49 } 50 } 51 if tries == 10 { 52 t.Fatal("tcp listener not ready for more than 10 seconds") 53 } 54 55 // Create the custom ethash engine 56 ethash := NewTester([]string{"http://" + listener.Addr().String()}, false) 57 defer ethash.Close() 58 59 // Stream a work task and ensure the notification bubbles out 60 header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} 61 block := types.NewBlockWithHeader(header) 62 63 ethash.Seal(nil, block, nil, nil) 64 select { 65 case work := <-sink: 66 if want := ethash.SealHash(header).Hex(); work[0] != want { 67 t.Errorf("work packet hash mismatch: have %s, want %s", work[0], want) 68 } 69 if want := common.BytesToHash(SeedHash(header.Number.Uint64())).Hex(); work[1] != want { 70 t.Errorf("work packet seed mismatch: have %s, want %s", work[1], want) 71 } 72 target := new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), header.Difficulty) 73 if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want { 74 t.Errorf("work packet target mismatch: have %s, want %s", work[2], want) 75 } 76 case <-time.After(3 * time.Second): 77 t.Fatalf("notification timed out") 78 } 79 } 80 81 // Tests that pushing work packages fast to the miner doesn't cause any data race 82 // issues in the notifications. 83 func TestRemoteMultiNotify(t *testing.T) { 84 // Start a simple webserver to capture notifications 85 sink := make(chan [3]string, 64) 86 87 server := &http.Server{ 88 Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 89 blob, err := ioutil.ReadAll(req.Body) 90 if err != nil { 91 t.Fatalf("failed to read miner notification: %v", err) 92 } 93 var work [3]string 94 if err := json.Unmarshal(blob, &work); err != nil { 95 t.Fatalf("failed to unmarshal miner notification: %v", err) 96 } 97 sink <- work 98 }), 99 } 100 // Open a custom listener to extract its local address 101 listener, err := net.Listen("tcp", "localhost:0") 102 if err != nil { 103 t.Fatalf("failed to open notification server: %v", err) 104 } 105 defer listener.Close() 106 107 go server.Serve(listener) 108 109 // Create the custom ethash engine 110 ethash := NewTester([]string{"http://" + listener.Addr().String()}, false) 111 defer ethash.Close() 112 113 // Stream a lot of work task and ensure all the notifications bubble out 114 for i := 0; i < cap(sink); i++ { 115 header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)} 116 block := types.NewBlockWithHeader(header) 117 118 ethash.Seal(nil, block, nil, nil) 119 } 120 for i := 0; i < cap(sink); i++ { 121 select { 122 case <-sink: 123 case <-time.After(3 * time.Second): 124 t.Fatalf("notification %d timed out", i) 125 } 126 } 127 } 128 129 // Tests whether stale solutions are correctly processed. 130 func TestStaleSubmission(t *testing.T) { 131 ethash := NewTester(nil, true) 132 defer ethash.Close() 133 api := &API{ethash} 134 135 fakeNonce, fakeDigest := types.BlockNonce{0x01, 0x02, 0x03}, common.HexToHash("deadbeef") 136 137 testcases := []struct { 138 headers []*types.Header 139 submitIndex int 140 submitRes bool 141 }{ 142 // Case1: submit solution for the latest mining package 143 { 144 []*types.Header{ 145 {ParentHash: common.BytesToHash([]byte{0xa}), Number: big.NewInt(1), Difficulty: big.NewInt(100000000)}, 146 }, 147 0, 148 true, 149 }, 150 // Case2: submit solution for the previous package but have same parent. 151 { 152 []*types.Header{ 153 {ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000000)}, 154 {ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000001)}, 155 }, 156 0, 157 true, 158 }, 159 // Case3: submit stale but acceptable solution 160 { 161 []*types.Header{ 162 {ParentHash: common.BytesToHash([]byte{0xc}), Number: big.NewInt(3), Difficulty: big.NewInt(100000000)}, 163 {ParentHash: common.BytesToHash([]byte{0xd}), Number: big.NewInt(9), Difficulty: big.NewInt(100000000)}, 164 }, 165 0, 166 true, 167 }, 168 // Case4: submit very old solution 169 { 170 []*types.Header{ 171 {ParentHash: common.BytesToHash([]byte{0xe}), Number: big.NewInt(10), Difficulty: big.NewInt(100000000)}, 172 {ParentHash: common.BytesToHash([]byte{0xf}), Number: big.NewInt(17), Difficulty: big.NewInt(100000000)}, 173 }, 174 0, 175 false, 176 }, 177 } 178 results := make(chan *types.Block, 16) 179 180 for id, c := range testcases { 181 for _, h := range c.headers { 182 ethash.Seal(nil, types.NewBlockWithHeader(h), results, nil) 183 } 184 if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes { 185 t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res) 186 } 187 if !c.submitRes { 188 continue 189 } 190 select { 191 case res := <-results: 192 if res.Header().Nonce != fakeNonce { 193 t.Errorf("case %d block nonce mismatch, want %s, get %s", id+1, fakeNonce, res.Header().Nonce) 194 } 195 if res.Header().MixDigest != fakeDigest { 196 t.Errorf("case %d block digest mismatch, want %s, get %s", id+1, fakeDigest, res.Header().MixDigest) 197 } 198 if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() { 199 t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty) 200 } 201 if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() { 202 t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64()) 203 } 204 if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash { 205 t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex()) 206 } 207 case <-time.NewTimer(time.Second).C: 208 t.Errorf("case %d fetch ethash result timeout", id+1) 209 } 210 } 211 }