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