github.com/codysnider/go-ethereum@v1.10.18-0.20220420071915-14f4ae99222a/consensus/ethash/sealer_test.go (about) 1 // Copyright 2018 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package ethash 18 19 import ( 20 "encoding/json" 21 "io/ioutil" 22 "math/big" 23 "net/http" 24 "net/http/httptest" 25 "strconv" 26 "testing" 27 "time" 28 29 "github.com/ethereum/go-ethereum/common" 30 "github.com/ethereum/go-ethereum/core/types" 31 "github.com/ethereum/go-ethereum/internal/testlog" 32 "github.com/ethereum/go-ethereum/log" 33 ) 34 35 // Tests whether remote HTTP servers are correctly notified of new work. 36 func TestRemoteNotify(t *testing.T) { 37 // Start a simple web server to capture notifications. 38 sink := make(chan [3]string) 39 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 40 blob, err := ioutil.ReadAll(req.Body) 41 if err != nil { 42 t.Errorf("failed to read miner notification: %v", err) 43 } 44 var work [3]string 45 if err := json.Unmarshal(blob, &work); err != nil { 46 t.Errorf("failed to unmarshal miner notification: %v", err) 47 } 48 sink <- work 49 })) 50 defer server.Close() 51 52 // Create the custom ethash engine. 53 ethash := NewTester([]string{server.URL}, false) 54 defer ethash.Close() 55 56 // Stream a work task and ensure the notification bubbles out. 57 header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} 58 block := types.NewBlockWithHeader(header) 59 60 ethash.Seal(nil, block, nil, nil) 61 select { 62 case work := <-sink: 63 if want := ethash.SealHash(header).Hex(); work[0] != want { 64 t.Errorf("work packet hash mismatch: have %s, want %s", work[0], want) 65 } 66 if want := common.BytesToHash(SeedHash(header.Number.Uint64())).Hex(); work[1] != want { 67 t.Errorf("work packet seed mismatch: have %s, want %s", work[1], want) 68 } 69 target := new(big.Int).Div(new(big.Int).Lsh(big.NewInt(1), 256), header.Difficulty) 70 if want := common.BytesToHash(target.Bytes()).Hex(); work[2] != want { 71 t.Errorf("work packet target mismatch: have %s, want %s", work[2], want) 72 } 73 case <-time.After(3 * time.Second): 74 t.Fatalf("notification timed out") 75 } 76 } 77 78 // Tests whether remote HTTP servers are correctly notified of new work. (Full pending block body / --miner.notify.full) 79 func TestRemoteNotifyFull(t *testing.T) { 80 // Start a simple web server to capture notifications. 81 sink := make(chan map[string]interface{}) 82 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 83 blob, err := ioutil.ReadAll(req.Body) 84 if err != nil { 85 t.Errorf("failed to read miner notification: %v", err) 86 } 87 var work map[string]interface{} 88 if err := json.Unmarshal(blob, &work); err != nil { 89 t.Errorf("failed to unmarshal miner notification: %v", err) 90 } 91 sink <- work 92 })) 93 defer server.Close() 94 95 // Create the custom ethash engine. 96 config := Config{ 97 PowMode: ModeTest, 98 NotifyFull: true, 99 Log: testlog.Logger(t, log.LvlWarn), 100 } 101 ethash := New(config, []string{server.URL}, false) 102 defer ethash.Close() 103 104 // Stream a work task and ensure the notification bubbles out. 105 header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)} 106 block := types.NewBlockWithHeader(header) 107 108 ethash.Seal(nil, block, nil, nil) 109 select { 110 case work := <-sink: 111 if want := "0x" + strconv.FormatUint(header.Number.Uint64(), 16); work["number"] != want { 112 t.Errorf("pending block number mismatch: have %v, want %v", work["number"], want) 113 } 114 if want := "0x" + header.Difficulty.Text(16); work["difficulty"] != want { 115 t.Errorf("pending block difficulty mismatch: have %s, want %s", work["difficulty"], want) 116 } 117 case <-time.After(3 * time.Second): 118 t.Fatalf("notification timed out") 119 } 120 } 121 122 // Tests that pushing work packages fast to the miner doesn't cause any data race 123 // issues in the notifications. 124 func TestRemoteMultiNotify(t *testing.T) { 125 // Start a simple web server to capture notifications. 126 sink := make(chan [3]string, 64) 127 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 128 blob, err := ioutil.ReadAll(req.Body) 129 if err != nil { 130 t.Errorf("failed to read miner notification: %v", err) 131 } 132 var work [3]string 133 if err := json.Unmarshal(blob, &work); err != nil { 134 t.Errorf("failed to unmarshal miner notification: %v", err) 135 } 136 sink <- work 137 })) 138 defer server.Close() 139 140 // Create the custom ethash engine. 141 ethash := NewTester([]string{server.URL}, false) 142 ethash.config.Log = testlog.Logger(t, log.LvlWarn) 143 defer ethash.Close() 144 145 // Provide a results reader. 146 // Otherwise the unread results will be logged asynchronously 147 // and this can happen after the test is finished, causing a panic. 148 results := make(chan *types.Block, cap(sink)) 149 150 // Stream a lot of work task and ensure all the notifications bubble out. 151 for i := 0; i < cap(sink); i++ { 152 header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)} 153 block := types.NewBlockWithHeader(header) 154 ethash.Seal(nil, block, results, nil) 155 } 156 157 for i := 0; i < cap(sink); i++ { 158 select { 159 case <-sink: 160 <-results 161 case <-time.After(10 * time.Second): 162 t.Fatalf("notification %d timed out", i) 163 } 164 } 165 } 166 167 // Tests that pushing work packages fast to the miner doesn't cause any data race 168 // issues in the notifications. Full pending block body / --miner.notify.full) 169 func TestRemoteMultiNotifyFull(t *testing.T) { 170 // Start a simple web server to capture notifications. 171 sink := make(chan map[string]interface{}, 64) 172 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { 173 blob, err := ioutil.ReadAll(req.Body) 174 if err != nil { 175 t.Errorf("failed to read miner notification: %v", err) 176 } 177 var work map[string]interface{} 178 if err := json.Unmarshal(blob, &work); err != nil { 179 t.Errorf("failed to unmarshal miner notification: %v", err) 180 } 181 sink <- work 182 })) 183 defer server.Close() 184 185 // Create the custom ethash engine. 186 config := Config{ 187 PowMode: ModeTest, 188 NotifyFull: true, 189 Log: testlog.Logger(t, log.LvlWarn), 190 } 191 ethash := New(config, []string{server.URL}, false) 192 defer ethash.Close() 193 194 // Provide a results reader. 195 // Otherwise the unread results will be logged asynchronously 196 // and this can happen after the test is finished, causing a panic. 197 results := make(chan *types.Block, cap(sink)) 198 199 // Stream a lot of work task and ensure all the notifications bubble out. 200 for i := 0; i < cap(sink); i++ { 201 header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)} 202 block := types.NewBlockWithHeader(header) 203 ethash.Seal(nil, block, results, nil) 204 } 205 206 for i := 0; i < cap(sink); i++ { 207 select { 208 case <-sink: 209 <-results 210 case <-time.After(10 * time.Second): 211 t.Fatalf("notification %d timed out", i) 212 } 213 } 214 } 215 216 // Tests whether stale solutions are correctly processed. 217 func TestStaleSubmission(t *testing.T) { 218 ethash := NewTester(nil, true) 219 defer ethash.Close() 220 api := &API{ethash} 221 222 fakeNonce, fakeDigest := types.BlockNonce{0x01, 0x02, 0x03}, common.HexToHash("deadbeef") 223 224 testcases := []struct { 225 headers []*types.Header 226 submitIndex int 227 submitRes bool 228 }{ 229 // Case1: submit solution for the latest mining package 230 { 231 []*types.Header{ 232 {ParentHash: common.BytesToHash([]byte{0xa}), Number: big.NewInt(1), Difficulty: big.NewInt(100000000)}, 233 }, 234 0, 235 true, 236 }, 237 // Case2: submit solution for the previous package but have same parent. 238 { 239 []*types.Header{ 240 {ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000000)}, 241 {ParentHash: common.BytesToHash([]byte{0xb}), Number: big.NewInt(2), Difficulty: big.NewInt(100000001)}, 242 }, 243 0, 244 true, 245 }, 246 // Case3: submit stale but acceptable solution 247 { 248 []*types.Header{ 249 {ParentHash: common.BytesToHash([]byte{0xc}), Number: big.NewInt(3), Difficulty: big.NewInt(100000000)}, 250 {ParentHash: common.BytesToHash([]byte{0xd}), Number: big.NewInt(9), Difficulty: big.NewInt(100000000)}, 251 }, 252 0, 253 true, 254 }, 255 // Case4: submit very old solution 256 { 257 []*types.Header{ 258 {ParentHash: common.BytesToHash([]byte{0xe}), Number: big.NewInt(10), Difficulty: big.NewInt(100000000)}, 259 {ParentHash: common.BytesToHash([]byte{0xf}), Number: big.NewInt(17), Difficulty: big.NewInt(100000000)}, 260 }, 261 0, 262 false, 263 }, 264 } 265 results := make(chan *types.Block, 16) 266 267 for id, c := range testcases { 268 for _, h := range c.headers { 269 ethash.Seal(nil, types.NewBlockWithHeader(h), results, nil) 270 } 271 if res := api.SubmitWork(fakeNonce, ethash.SealHash(c.headers[c.submitIndex]), fakeDigest); res != c.submitRes { 272 t.Errorf("case %d submit result mismatch, want %t, get %t", id+1, c.submitRes, res) 273 } 274 if !c.submitRes { 275 continue 276 } 277 select { 278 case res := <-results: 279 if res.Header().Nonce != fakeNonce { 280 t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce) 281 } 282 if res.Header().MixDigest != fakeDigest { 283 t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.Header().MixDigest) 284 } 285 if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() { 286 t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty) 287 } 288 if res.Header().Number.Uint64() != c.headers[c.submitIndex].Number.Uint64() { 289 t.Errorf("case %d block number mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Number.Uint64(), res.Header().Number.Uint64()) 290 } 291 if res.Header().ParentHash != c.headers[c.submitIndex].ParentHash { 292 t.Errorf("case %d block parent hash mismatch, want %s, get %s", id+1, c.headers[c.submitIndex].ParentHash.Hex(), res.Header().ParentHash.Hex()) 293 } 294 case <-time.NewTimer(time.Second).C: 295 t.Errorf("case %d fetch ethash result timeout", id+1) 296 } 297 } 298 }