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