OSDN Git Service

dcb71869b640e3544f55d17708ff013daa1460bb
[bytom/vapor.git] / netsync / chainmgr / fast_sync_test.go
1 package chainmgr
2
3 import (
4         "io/ioutil"
5         "os"
6         "reflect"
7         "sync"
8         "testing"
9         "time"
10
11         "github.com/vapor/consensus"
12         dbm "github.com/vapor/database/leveldb"
13         "github.com/vapor/errors"
14         "github.com/vapor/netsync/peers"
15         "github.com/vapor/protocol/bc"
16         "github.com/vapor/protocol/bc/types"
17         "github.com/vapor/test/mock"
18         "github.com/vapor/testutil"
19 )
20
21 func TestBlockLocator(t *testing.T) {
22         blocks := mockBlocks(nil, 500)
23         cases := []struct {
24                 bestHeight uint64
25                 wantHeight []uint64
26         }{
27                 {
28                         bestHeight: 0,
29                         wantHeight: []uint64{0},
30                 },
31                 {
32                         bestHeight: 1,
33                         wantHeight: []uint64{1, 0},
34                 },
35                 {
36                         bestHeight: 7,
37                         wantHeight: []uint64{7, 6, 5, 4, 3, 2, 1, 0},
38                 },
39                 {
40                         bestHeight: 10,
41                         wantHeight: []uint64{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
42                 },
43                 {
44                         bestHeight: 100,
45                         wantHeight: []uint64{100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 89, 85, 77, 61, 29, 0},
46                 },
47                 {
48                         bestHeight: 500,
49                         wantHeight: []uint64{500, 499, 498, 497, 496, 495, 494, 493, 492, 491, 489, 485, 477, 461, 429, 365, 237, 0},
50                 },
51         }
52
53         for i, c := range cases {
54                 mockChain := mock.NewChain(nil)
55                 fs := &fastSync{chain: mockChain}
56                 mockChain.SetBestBlockHeader(&blocks[c.bestHeight].BlockHeader)
57                 for i := uint64(0); i <= c.bestHeight; i++ {
58                         mockChain.SetBlockByHeight(i, blocks[i])
59                 }
60
61                 want := []*bc.Hash{}
62                 for _, i := range c.wantHeight {
63                         hash := blocks[i].Hash()
64                         want = append(want, &hash)
65                 }
66
67                 if got := fs.blockLocator(); !testutil.DeepEqual(got, want) {
68                         t.Errorf("case %d: got %v want %v", i, got, want)
69                 }
70         }
71 }
72
73 func TestFastBlockSync(t *testing.T) {
74         tmp, err := ioutil.TempDir(".", "")
75         if err != nil {
76                 t.Fatalf("failed to create temporary data folder: %v", err)
77         }
78         testDBA := dbm.NewDB("testdba", "leveldb", tmp)
79         testDBB := dbm.NewDB("testdbb", "leveldb", tmp)
80         defer func() {
81                 testDBA.Close()
82                 testDBB.Close()
83                 os.RemoveAll(tmp)
84         }()
85
86         maxSizeOfSyncSkeleton = 11
87         numOfBlocksSkeletonGap = 10
88         maxNumOfBlocksPerSync = numOfBlocksSkeletonGap * uint64(maxSizeOfSyncSkeleton-1)
89         fastSyncPivotGap = uint64(5)
90         minGapStartFastSync = uint64(6)
91
92         defer func() {
93                 maxSizeOfSyncSkeleton = 11
94                 numOfBlocksSkeletonGap = maxNumOfBlocksPerMsg
95                 maxNumOfBlocksPerSync = numOfBlocksSkeletonGap * uint64(maxSizeOfSyncSkeleton-1)
96                 fastSyncPivotGap = uint64(64)
97                 minGapStartFastSync = uint64(128)
98                 requireHeadersTimeout = 30 * time.Second
99         }()
100
101         baseChain := mockBlocks(nil, 300)
102
103         cases := []struct {
104                 syncTimeout time.Duration
105                 aBlocks     []*types.Block
106                 bBlocks     []*types.Block
107                 want        []*types.Block
108                 err         error
109         }{
110                 {
111                         syncTimeout: 30 * time.Second,
112                         aBlocks:     baseChain[:50],
113                         bBlocks:     baseChain[:301],
114                         want:        baseChain[:150],
115                         err:         nil,
116                 },
117                 {
118                         syncTimeout: 30 * time.Second,
119                         aBlocks:     baseChain[:2],
120                         bBlocks:     baseChain[:300],
121                         want:        baseChain[:102],
122                         err:         nil,
123                 },
124                 {
125                         syncTimeout: 30 * time.Second,
126                         aBlocks:     baseChain[:2],
127                         bBlocks:     baseChain[:53],
128                         want:        baseChain[:48],
129                         err:         nil,
130                 },
131                 {
132                         syncTimeout: 30 * time.Second,
133                         aBlocks:     baseChain[:2],
134                         bBlocks:     baseChain[:53],
135                         want:        baseChain[:48],
136                         err:         nil,
137                 },
138                 {
139                         syncTimeout: 30 * time.Second,
140                         aBlocks:     baseChain[:2],
141                         bBlocks:     baseChain[:10],
142                         want:        baseChain[:5],
143                         err:         nil,
144                 },
145                 {
146                         syncTimeout: 0 * time.Second,
147                         aBlocks:     baseChain[:50],
148                         bBlocks:     baseChain[:301],
149                         want:        baseChain[:50],
150                         err:         errSkeletonSize,
151                 },
152         }
153
154         for i, c := range cases {
155                 a := mockSync(c.aBlocks, nil, testDBA)
156                 b := mockSync(c.bBlocks, nil, testDBB)
157                 netWork := NewNetWork()
158                 netWork.Register(a, "192.168.0.1", "test node A", consensus.SFFullNode|consensus.SFFastSync)
159                 netWork.Register(b, "192.168.0.2", "test node B", consensus.SFFullNode|consensus.SFFastSync)
160                 if B2A, A2B, err := netWork.HandsShake(a, b); err != nil {
161                         t.Errorf("fail on peer hands shake %v", err)
162                 } else {
163                         go B2A.postMan()
164                         go A2B.postMan()
165                 }
166                 a.blockKeeper.syncPeer = a.peers.GetPeer("test node B")
167                 a.blockKeeper.fastSync.setSyncPeer(a.blockKeeper.syncPeer)
168
169                 requireHeadersTimeout = c.syncTimeout
170                 if err := a.blockKeeper.fastSync.process(); errors.Root(err) != c.err {
171                         t.Errorf("case %d: got %v want %v", i, err, c.err)
172                 }
173
174                 got := []*types.Block{}
175                 for i := uint64(0); i <= a.chain.BestBlockHeight(); i++ {
176                         block, err := a.chain.GetBlockByHeight(i)
177                         if err != nil {
178                                 t.Errorf("case %d got err %v", i, err)
179                         }
180                         got = append(got, block)
181                 }
182                 if !testutil.DeepEqual(got, c.want) {
183                         t.Errorf("case %d: got %v want %v", i, got, c.want)
184                 }
185         }
186 }
187
188 type mockFetcher struct {
189         baseChain  []*types.Block
190         peerStatus map[string][]*types.Block
191         peers      []string
192         testType   int
193 }
194
195 func (mf *mockFetcher) resetParameter() {
196         return
197 }
198
199 func (mf *mockFetcher) addSyncPeer(peerID string) {
200         return
201 }
202
203 func (mf *mockFetcher) requireBlock(peerID string, height uint64) (*types.Block, error) {
204         return nil, nil
205 }
206
207 func (mf *mockFetcher) parallelFetchBlocks(work []*fetchBlocksWork, downloadNotifyCh chan struct{}, ProcessStopCh chan struct{}, wg *sync.WaitGroup) {
208         return
209 }
210
211 func (mf *mockFetcher) parallelFetchHeaders(peers []*peers.Peer, locator []*bc.Hash, stopHash *bc.Hash, skip uint64) map[string][]*types.BlockHeader {
212         result := make(map[string][]*types.BlockHeader)
213         switch mf.testType {
214         case 1:
215                 result["peer1"] = []*types.BlockHeader{&mf.peerStatus["peer1"][1000].BlockHeader, &mf.peerStatus["peer1"][1100].BlockHeader, &mf.peerStatus["peer1"][1200].BlockHeader,
216                         &mf.peerStatus["peer1"][1300].BlockHeader, &mf.peerStatus["peer1"][1400].BlockHeader, &mf.peerStatus["peer1"][1500].BlockHeader,
217                         &mf.peerStatus["peer1"][1600].BlockHeader, &mf.peerStatus["peer1"][1700].BlockHeader, &mf.peerStatus["peer1"][1800].BlockHeader,
218                 }
219                 result["peer2"] = []*types.BlockHeader{&mf.peerStatus["peer2"][1000].BlockHeader, &mf.peerStatus["peer2"][1100].BlockHeader, &mf.peerStatus["peer2"][1200].BlockHeader,
220                         &mf.peerStatus["peer2"][1300].BlockHeader, &mf.peerStatus["peer2"][1400].BlockHeader, &mf.peerStatus["peer2"][1500].BlockHeader,
221                         &mf.peerStatus["peer2"][1600].BlockHeader, &mf.peerStatus["peer2"][1700].BlockHeader, &mf.peerStatus["peer2"][1800].BlockHeader,
222                 }
223
224         case 2:
225                 result["peer1"] = []*types.BlockHeader{}
226         case 3:
227         case 4:
228                 result["peer2"] = []*types.BlockHeader{&mf.peerStatus["peer2"][1000].BlockHeader, &mf.peerStatus["peer2"][1100].BlockHeader, &mf.peerStatus["peer2"][1200].BlockHeader,
229                         &mf.peerStatus["peer2"][1300].BlockHeader, &mf.peerStatus["peer2"][1400].BlockHeader, &mf.peerStatus["peer2"][1500].BlockHeader,
230                         &mf.peerStatus["peer2"][1600].BlockHeader, &mf.peerStatus["peer2"][1700].BlockHeader, &mf.peerStatus["peer2"][1800].BlockHeader,
231                 }
232         case 5:
233                 result["peer1"] = []*types.BlockHeader{&mf.peerStatus["peer1"][1000].BlockHeader, &mf.peerStatus["peer1"][1100].BlockHeader, &mf.peerStatus["peer1"][1200].BlockHeader,
234                         &mf.peerStatus["peer1"][1300].BlockHeader, &mf.peerStatus["peer1"][1400].BlockHeader, &mf.peerStatus["peer1"][1500].BlockHeader,
235                         &mf.peerStatus["peer1"][1600].BlockHeader, &mf.peerStatus["peer1"][1700].BlockHeader, &mf.peerStatus["peer1"][1800].BlockHeader,
236                 }
237                 result["peer2"] = []*types.BlockHeader{&mf.peerStatus["peer2"][1000].BlockHeader, &mf.peerStatus["peer2"][1100].BlockHeader, &mf.peerStatus["peer2"][1200].BlockHeader,
238                         &mf.peerStatus["peer2"][1300].BlockHeader, &mf.peerStatus["peer2"][1400].BlockHeader, &mf.peerStatus["peer2"][1500].BlockHeader,
239                         &mf.peerStatus["peer2"][1600].BlockHeader, &mf.peerStatus["peer2"][1700].BlockHeader,
240                 }
241         }
242         return result
243 }
244
245 func TestCreateFetchBlocksTasks(t *testing.T) {
246         baseChain := mockBlocks(nil, 1000)
247         chainX := append(baseChain, mockBlocks(baseChain[1000], 2000)...)
248         chainY := append(baseChain, mockBlocks(baseChain[1000], 1900)...)
249         peerStatus := make(map[string][]*types.Block)
250         peerStatus["peer1"] = chainX
251         peerStatus["peer2"] = chainY
252         type syncPeer struct {
253                 peer               *P2PPeer
254                 bestHeight         uint64
255                 irreversibleHeight uint64
256         }
257
258         cases := []struct {
259                 peers        []*syncPeer
260                 mainSyncPeer string
261                 testType     int
262                 wantTasks    []*fetchBlocksWork
263                 wantErr      error
264         }{
265                 // normal test
266                 {
267                         peers: []*syncPeer{
268                                 {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 1000},
269                                 {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 800, irreversibleHeight: 800},
270                         },
271                         mainSyncPeer: "peer1",
272                         testType:     1,
273                         wantTasks: []*fetchBlocksWork{
274                                 {&chainX[1000].BlockHeader, &chainX[1100].BlockHeader}, {&chainX[1100].BlockHeader, &chainX[1200].BlockHeader},
275                                 {&chainX[1200].BlockHeader, &chainX[1300].BlockHeader}, {&chainX[1300].BlockHeader, &chainX[1400].BlockHeader},
276                                 {&chainX[1400].BlockHeader, &chainX[1500].BlockHeader}, {&chainX[1500].BlockHeader, &chainX[1600].BlockHeader},
277                                 {&chainX[1600].BlockHeader, &chainX[1700].BlockHeader}, {&chainX[1700].BlockHeader, &chainX[1800].BlockHeader},
278                         },
279                         wantErr: nil,
280                 },
281                 // test no sync peer
282                 {
283                         peers:        []*syncPeer{},
284                         mainSyncPeer: "peer1",
285                         testType:     0,
286                         wantTasks:    nil,
287                         wantErr:      errNoSyncPeer,
288                 },
289                 // primary sync peer skeleton size error
290                 {
291                         peers: []*syncPeer{
292                                 {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 1000},
293                                 {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 800, irreversibleHeight: 800},
294                         },
295                         mainSyncPeer: "peer1",
296                         testType:     2,
297                         wantTasks:    nil,
298                         wantErr:      errSkeletonSize,
299                 },
300                 // no skeleton return
301                 {
302                         peers: []*syncPeer{
303                                 {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 1000},
304                                 {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 800, irreversibleHeight: 800},
305                         },
306                         mainSyncPeer: "peer1",
307                         testType:     3,
308                         wantTasks:    nil,
309                         wantErr:      errNoSkeletonFound,
310                 },
311                 // no main skeleton found
312                 {
313                         peers: []*syncPeer{
314                                 {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 1000},
315                                 {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 800, irreversibleHeight: 800},
316                         },
317                         mainSyncPeer: "peer1",
318                         testType:     4,
319                         wantTasks:    nil,
320                         wantErr:      errNoMainSkeleton,
321                 },
322                 // skeleton length mismatch
323                 {
324                         peers: []*syncPeer{
325                                 {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 1000},
326                                 {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 800, irreversibleHeight: 800},
327                         },
328                         mainSyncPeer: "peer1",
329                         testType:     5,
330                         wantTasks: []*fetchBlocksWork{
331                                 {&chainX[1000].BlockHeader, &chainX[1100].BlockHeader}, {&chainX[1100].BlockHeader, &chainX[1200].BlockHeader},
332                                 {&chainX[1200].BlockHeader, &chainX[1300].BlockHeader}, {&chainX[1300].BlockHeader, &chainX[1400].BlockHeader},
333                                 {&chainX[1400].BlockHeader, &chainX[1500].BlockHeader}, {&chainX[1500].BlockHeader, &chainX[1600].BlockHeader},
334                                 {&chainX[1600].BlockHeader, &chainX[1700].BlockHeader}, {&chainX[1700].BlockHeader, &chainX[1800].BlockHeader},
335                         },
336                         wantErr: nil,
337                 },
338         }
339
340         for i, c := range cases {
341                 peers := peers.NewPeerSet(NewPeerSet())
342                 for _, syncPeer := range c.peers {
343                         peers.AddPeer(syncPeer.peer)
344                         peers.SetStatus(syncPeer.peer.id, syncPeer.bestHeight, nil)
345                         peers.SetIrreversibleStatus(syncPeer.peer.id, syncPeer.irreversibleHeight, nil)
346                 }
347                 mockChain := mock.NewChain(nil)
348                 fs := newFastSync(mockChain, &mockFetcher{baseChain: baseChain, peerStatus: peerStatus, testType: c.testType}, nil, peers)
349                 fs.mainSyncPeer = fs.peers.GetPeer(c.mainSyncPeer)
350                 tasks, err := fs.createFetchBlocksTasks(baseChain[700])
351                 if err != c.wantErr {
352                         t.Errorf("case %d: got %v want %v", i, err, c.wantErr)
353                 }
354                 if !reflect.DeepEqual(tasks, c.wantTasks) {
355                         t.Errorf("case %d: got %v want %v", i, tasks, c.wantTasks)
356                 }
357         }
358 }