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