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