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