Golang implemented sidechain for Bytom
修订版 | be230601dbfe48d6d088854d553e4f71e7d435ff (tree) |
---|---|
时间 | 2019-07-29 20:28:42 |
作者 | Yahtoo Ma <yahtoo.ma@gmai...> |
Commiter | Yahtoo Ma |
netsync add test case
@@ -112,7 +112,7 @@ func (bk *blockKeeper) locateHeaders(locator []*bc.Hash, stopHash *bc.Hash, skip | ||
112 | 112 | headers := make([]*types.BlockHeader, 0) |
113 | 113 | stopHeader, err := bk.chain.GetHeaderByHash(stopHash) |
114 | 114 | if err != nil { |
115 | - return headers, nil | |
115 | + return headers, err | |
116 | 116 | } |
117 | 117 | |
118 | 118 | if !bk.chain.InMainChain(*stopHash) || stopHeader.Height < startHeader.Height { |
@@ -191,16 +191,13 @@ func (bk *blockKeeper) start() { | ||
191 | 191 | } |
192 | 192 | |
193 | 193 | func (bk *blockKeeper) checkSyncType() int { |
194 | - peer := bk.peers.BestIrreversiblePeer(consensus.SFFullNode | consensus.SFFastSync) | |
195 | - if peer == nil { | |
196 | - log.WithFields(log.Fields{"module": logModule}).Debug("can't find fast sync peer") | |
197 | - return noNeedSync | |
198 | - } | |
199 | - | |
200 | 194 | bestHeight := bk.chain.BestBlockHeight() |
201 | - if peerIrreversibleHeight := peer.IrreversibleHeight(); peerIrreversibleHeight >= bestHeight+minGapStartFastSync { | |
202 | - bk.fastSync.setSyncPeer(peer) | |
203 | - return fastSyncType | |
195 | + peer := bk.peers.BestIrreversiblePeer(consensus.SFFullNode | consensus.SFFastSync) | |
196 | + if peer != nil { | |
197 | + if peerIrreversibleHeight := peer.IrreversibleHeight(); peerIrreversibleHeight >= bestHeight+minGapStartFastSync { | |
198 | + bk.fastSync.setSyncPeer(peer) | |
199 | + return fastSyncType | |
200 | + } | |
204 | 201 | } |
205 | 202 | |
206 | 203 | peer = bk.peers.BestPeer(consensus.SFFullNode) |
@@ -209,8 +206,7 @@ func (bk *blockKeeper) checkSyncType() int { | ||
209 | 206 | return noNeedSync |
210 | 207 | } |
211 | 208 | |
212 | - peerHeight := peer.Height() | |
213 | - if peerHeight > bestHeight { | |
209 | + if peer.Height() > bestHeight { | |
214 | 210 | bk.syncPeer = peer |
215 | 211 | return regularSyncType |
216 | 212 | } |
@@ -230,6 +226,8 @@ func (bk *blockKeeper) startSync() bool { | ||
230 | 226 | log.WithFields(log.Fields{"module": logModule, "err": err}).Warning("fail on regularBlockSync") |
231 | 227 | return false |
232 | 228 | } |
229 | + default: | |
230 | + return false | |
233 | 231 | } |
234 | 232 | |
235 | 233 | return true |
@@ -11,17 +11,108 @@ import ( | ||
11 | 11 | dbm "github.com/vapor/database/leveldb" |
12 | 12 | "github.com/vapor/errors" |
13 | 13 | msgs "github.com/vapor/netsync/messages" |
14 | + "github.com/vapor/netsync/peers" | |
15 | + "github.com/vapor/protocol" | |
14 | 16 | "github.com/vapor/protocol/bc" |
15 | 17 | "github.com/vapor/protocol/bc/types" |
16 | 18 | "github.com/vapor/test/mock" |
17 | 19 | "github.com/vapor/testutil" |
18 | 20 | ) |
19 | 21 | |
22 | +func TestCheckSyncType(t *testing.T) { | |
23 | + tmp, err := ioutil.TempDir(".", "") | |
24 | + if err != nil { | |
25 | + t.Fatalf("failed to create temporary data folder: %v", err) | |
26 | + } | |
27 | + fastSyncDB := dbm.NewDB("testdb", "leveldb", tmp) | |
28 | + defer func() { | |
29 | + fastSyncDB.Close() | |
30 | + os.RemoveAll(tmp) | |
31 | + }() | |
32 | + | |
33 | + blocks := mockBlocks(nil, 50) | |
34 | + chain := mock.NewChain(nil) | |
35 | + chain.SetBestBlockHeader(&blocks[len(blocks)-1].BlockHeader) | |
36 | + for _, block := range blocks { | |
37 | + chain.SetBlockByHeight(block.Height, block) | |
38 | + } | |
39 | + | |
40 | + type syncPeer struct { | |
41 | + peer *P2PPeer | |
42 | + bestHeight uint64 | |
43 | + irreversibleHeight uint64 | |
44 | + } | |
45 | + | |
46 | + cases := []struct { | |
47 | + peers []*syncPeer | |
48 | + syncType int | |
49 | + }{ | |
50 | + { | |
51 | + peers: []*syncPeer{}, | |
52 | + syncType: noNeedSync, | |
53 | + }, | |
54 | + { | |
55 | + peers: []*syncPeer{ | |
56 | + {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 500}, | |
57 | + {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 50, irreversibleHeight: 50}, | |
58 | + }, | |
59 | + syncType: fastSyncType, | |
60 | + }, | |
61 | + { | |
62 | + peers: []*syncPeer{ | |
63 | + {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 100}, | |
64 | + {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 500, irreversibleHeight: 50}, | |
65 | + }, | |
66 | + syncType: regularSyncType, | |
67 | + }, | |
68 | + { | |
69 | + peers: []*syncPeer{ | |
70 | + {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 51, irreversibleHeight: 50}, | |
71 | + }, | |
72 | + syncType: regularSyncType, | |
73 | + }, | |
74 | + { | |
75 | + peers: []*syncPeer{ | |
76 | + {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 30, irreversibleHeight: 30}, | |
77 | + }, | |
78 | + syncType: noNeedSync, | |
79 | + }, | |
80 | + { | |
81 | + peers: []*syncPeer{ | |
82 | + {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode}, bestHeight: 1000, irreversibleHeight: 1000}, | |
83 | + }, | |
84 | + syncType: regularSyncType, | |
85 | + }, | |
86 | + { | |
87 | + peers: []*syncPeer{ | |
88 | + {peer: &P2PPeer{id: "peer1", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 1000, irreversibleHeight: 50}, | |
89 | + {peer: &P2PPeer{id: "peer2", flag: consensus.SFFullNode | consensus.SFFastSync}, bestHeight: 800, irreversibleHeight: 800}, | |
90 | + }, | |
91 | + syncType: fastSyncType, | |
92 | + }, | |
93 | + } | |
94 | + | |
95 | + for i, c := range cases { | |
96 | + peers := peers.NewPeerSet(NewPeerSet()) | |
97 | + blockKeeper := newBlockKeeper(chain, peers, fastSyncDB) | |
98 | + for _, syncPeer := range c.peers { | |
99 | + blockKeeper.peers.AddPeer(syncPeer.peer) | |
100 | + blockKeeper.peers.SetStatus(syncPeer.peer.id, syncPeer.bestHeight, nil) | |
101 | + blockKeeper.peers.SetIrreversibleStatus(syncPeer.peer.id, syncPeer.irreversibleHeight, nil) | |
102 | + } | |
103 | + gotType := blockKeeper.checkSyncType() | |
104 | + if c.syncType != gotType { | |
105 | + t.Errorf("case %d: got %d want %d", i, gotType, c.syncType) | |
106 | + } | |
107 | + } | |
108 | +} | |
109 | + | |
20 | 110 | func TestRegularBlockSync(t *testing.T) { |
21 | 111 | baseChain := mockBlocks(nil, 50) |
22 | 112 | chainX := append(baseChain, mockBlocks(baseChain[50], 60)...) |
23 | 113 | chainY := append(baseChain, mockBlocks(baseChain[50], 70)...) |
24 | 114 | chainZ := append(baseChain, mockBlocks(baseChain[50], 200)...) |
115 | + chainE := append(baseChain, mockErrorBlocks(baseChain[50], 200, 60)...) | |
25 | 116 | |
26 | 117 | cases := []struct { |
27 | 118 | syncTimeout time.Duration |
@@ -58,6 +149,20 @@ func TestRegularBlockSync(t *testing.T) { | ||
58 | 149 | want: chainZ[:180], |
59 | 150 | err: nil, |
60 | 151 | }, |
152 | + { | |
153 | + syncTimeout: 0 * time.Second, | |
154 | + aBlocks: chainX[:52], | |
155 | + bBlocks: chainZ, | |
156 | + want: chainX[:52], | |
157 | + err: errRequestTimeout, | |
158 | + }, | |
159 | + { | |
160 | + syncTimeout: 30 * time.Second, | |
161 | + aBlocks: chainX[:52], | |
162 | + bBlocks: chainE, | |
163 | + want: chainE[:60], | |
164 | + err: protocol.ErrBadStateRoot, | |
165 | + }, | |
61 | 166 | } |
62 | 167 | tmp, err := ioutil.TempDir(".", "") |
63 | 168 | if err != nil { |
@@ -84,6 +189,7 @@ func TestRegularBlockSync(t *testing.T) { | ||
84 | 189 | go A2B.postMan() |
85 | 190 | } |
86 | 191 | |
192 | + requireBlockTimeout = c.syncTimeout | |
87 | 193 | a.blockKeeper.syncPeer = a.peers.GetPeer("test node B") |
88 | 194 | if err := a.blockKeeper.regularBlockSync(); errors.Root(err) != c.err { |
89 | 195 | t.Errorf("case %d: got %v want %v", i, err, c.err) |
@@ -308,11 +414,19 @@ func TestLocateBlocks(t *testing.T) { | ||
308 | 414 | locator []uint64 |
309 | 415 | stopHash bc.Hash |
310 | 416 | wantHeight []uint64 |
417 | + wantErr error | |
311 | 418 | }{ |
312 | 419 | { |
313 | 420 | locator: []uint64{20}, |
314 | 421 | stopHash: blocks[100].Hash(), |
315 | 422 | wantHeight: []uint64{20, 21, 22, 23, 24}, |
423 | + wantErr: nil, | |
424 | + }, | |
425 | + { | |
426 | + locator: []uint64{20}, | |
427 | + stopHash: bc.NewHash([32]byte{0x01, 0x02}), | |
428 | + wantHeight: []uint64{}, | |
429 | + wantErr: mock.ErrFoundHeaderByHash, | |
316 | 430 | }, |
317 | 431 | } |
318 | 432 |
@@ -334,7 +448,11 @@ func TestLocateBlocks(t *testing.T) { | ||
334 | 448 | want = append(want, blocks[i]) |
335 | 449 | } |
336 | 450 | |
337 | - got, _ := bk.locateBlocks(locator, &c.stopHash) | |
451 | + got, err := bk.locateBlocks(locator, &c.stopHash) | |
452 | + if err != c.wantErr { | |
453 | + t.Errorf("case %d: got %v want err = %v", i, err, c.wantErr) | |
454 | + } | |
455 | + | |
338 | 456 | if !testutil.DeepEqual(got, want) { |
339 | 457 | t.Errorf("case %d: got %v want %v", i, got, want) |
340 | 458 | } |
@@ -358,7 +476,7 @@ func TestLocateHeaders(t *testing.T) { | ||
358 | 476 | stopHash *bc.Hash |
359 | 477 | skip uint64 |
360 | 478 | wantHeight []uint64 |
361 | - err bool | |
479 | + err error | |
362 | 480 | }{ |
363 | 481 | { |
364 | 482 | chainHeight: 100, |
@@ -366,7 +484,7 @@ func TestLocateHeaders(t *testing.T) { | ||
366 | 484 | stopHash: &blocksHash[100], |
367 | 485 | skip: 0, |
368 | 486 | wantHeight: []uint64{90, 91, 92, 93, 94, 95, 96, 97, 98, 99}, |
369 | - err: false, | |
487 | + err: nil, | |
370 | 488 | }, |
371 | 489 | { |
372 | 490 | chainHeight: 100, |
@@ -374,28 +492,28 @@ func TestLocateHeaders(t *testing.T) { | ||
374 | 492 | stopHash: &blocksHash[24], |
375 | 493 | skip: 0, |
376 | 494 | wantHeight: []uint64{20, 21, 22, 23, 24}, |
377 | - err: false, | |
495 | + err: nil, | |
378 | 496 | }, |
379 | 497 | { |
380 | 498 | chainHeight: 100, |
381 | 499 | locator: []uint64{20}, |
382 | 500 | stopHash: &blocksHash[20], |
383 | 501 | wantHeight: []uint64{20}, |
384 | - err: false, | |
502 | + err: nil, | |
385 | 503 | }, |
386 | 504 | { |
387 | 505 | chainHeight: 100, |
388 | 506 | locator: []uint64{20}, |
389 | 507 | stopHash: &blocksHash[120], |
390 | 508 | wantHeight: []uint64{}, |
391 | - err: false, | |
509 | + err: mock.ErrFoundHeaderByHash, | |
392 | 510 | }, |
393 | 511 | { |
394 | 512 | chainHeight: 100, |
395 | 513 | locator: []uint64{120, 70}, |
396 | 514 | stopHash: &blocksHash[78], |
397 | 515 | wantHeight: []uint64{70, 71, 72, 73, 74, 75, 76, 77, 78}, |
398 | - err: false, | |
516 | + err: nil, | |
399 | 517 | }, |
400 | 518 | { |
401 | 519 | chainHeight: 100, |
@@ -403,7 +521,7 @@ func TestLocateHeaders(t *testing.T) { | ||
403 | 521 | stopHash: &blocksHash[10], |
404 | 522 | skip: 10, |
405 | 523 | wantHeight: []uint64{}, |
406 | - err: false, | |
524 | + err: nil, | |
407 | 525 | }, |
408 | 526 | { |
409 | 527 | chainHeight: 100, |
@@ -411,7 +529,7 @@ func TestLocateHeaders(t *testing.T) { | ||
411 | 529 | stopHash: &blocksHash[80], |
412 | 530 | skip: 10, |
413 | 531 | wantHeight: []uint64{15, 26, 37, 48, 59, 70, 80}, |
414 | - err: false, | |
532 | + err: nil, | |
415 | 533 | }, |
416 | 534 | { |
417 | 535 | chainHeight: 100, |
@@ -419,7 +537,7 @@ func TestLocateHeaders(t *testing.T) { | ||
419 | 537 | stopHash: &blocksHash[100], |
420 | 538 | skip: 9, |
421 | 539 | wantHeight: []uint64{0, 10, 20, 30, 40, 50, 60, 70, 80, 90}, |
422 | - err: false, | |
540 | + err: nil, | |
423 | 541 | }, |
424 | 542 | } |
425 | 543 |
@@ -442,7 +560,7 @@ func TestLocateHeaders(t *testing.T) { | ||
442 | 560 | } |
443 | 561 | |
444 | 562 | got, err := bk.locateHeaders(locator, c.stopHash, c.skip, maxNumOfHeadersPerMsg) |
445 | - if err != nil != c.err { | |
563 | + if err != c.err { | |
446 | 564 | t.Errorf("case %d: got %v want err = %v", i, err, c.err) |
447 | 565 | } |
448 | 566 | if !testutil.DeepEqual(got, want) { |
@@ -8,6 +8,8 @@ import ( | ||
8 | 8 | "time" |
9 | 9 | |
10 | 10 | dbm "github.com/vapor/database/leveldb" |
11 | + "github.com/vapor/netsync/peers" | |
12 | + "github.com/vapor/protocol/bc/types" | |
11 | 13 | "github.com/vapor/test/mock" |
12 | 14 | ) |
13 | 15 |
@@ -21,30 +23,52 @@ func TestBlockProcess(t *testing.T) { | ||
21 | 23 | testDB := dbm.NewDB("testdb", "leveldb", tmp) |
22 | 24 | defer testDB.Close() |
23 | 25 | |
26 | + cases := []struct { | |
27 | + blocks []*types.Block | |
28 | + startHeight uint64 | |
29 | + stopHeight uint64 | |
30 | + }{ | |
31 | + { | |
32 | + blocks: mockBlocks(nil, 200), | |
33 | + startHeight: 100, | |
34 | + stopHeight: 200, | |
35 | + }, | |
36 | + { | |
37 | + blocks: mockBlocks(nil, 200), | |
38 | + startHeight: 110, | |
39 | + stopHeight: 100, | |
40 | + }, | |
41 | + { | |
42 | + blocks: mockErrorBlocks(nil, 200, 150), | |
43 | + startHeight: 100, | |
44 | + stopHeight: 149, | |
45 | + }, | |
46 | + } | |
24 | 47 | s := newStorage(testDB) |
25 | 48 | mockChain := mock.NewChain(nil) |
26 | - blockNum := 200 | |
27 | - blocks := mockBlocks(nil, uint64(blockNum)) | |
28 | - for i := 0; i <= blockNum/2; i++ { | |
29 | - mockChain.SetBlockByHeight(uint64(i), blocks[i]) | |
30 | - mockChain.SetBestBlockHeader(&blocks[i].BlockHeader) | |
31 | - } | |
49 | + for i, c := range cases { | |
50 | + for i := 0; i <= len(c.blocks)/2; i++ { | |
51 | + mockChain.SetBlockByHeight(uint64(i), c.blocks[i]) | |
52 | + mockChain.SetBestBlockHeader(&c.blocks[i].BlockHeader) | |
53 | + } | |
32 | 54 | |
33 | - if err := s.writeBlocks("testPeer", blocks); err != nil { | |
34 | - t.Fatal(err) | |
35 | - } | |
55 | + if err := s.writeBlocks("testPeer", c.blocks); err != nil { | |
56 | + t.Fatal(err) | |
57 | + } | |
58 | + | |
59 | + bp := newBlockProcessor(mockChain, s, peers.NewPeerSet(nil)) | |
60 | + downloadNotifyCh := make(chan struct{}, 1) | |
61 | + ProcessStopCh := make(chan struct{}) | |
62 | + var wg sync.WaitGroup | |
63 | + go func() { | |
64 | + time.Sleep(1 * time.Second) | |
65 | + close(downloadNotifyCh) | |
66 | + }() | |
67 | + wg.Add(1) | |
36 | 68 | |
37 | - bp := newBlockProcessor(mockChain, s, nil) | |
38 | - downloadNotifyCh := make(chan struct{}, 1) | |
39 | - ProcessStopCh := make(chan struct{}) | |
40 | - var wg sync.WaitGroup | |
41 | - go func() { | |
42 | - time.Sleep(1 * time.Second) | |
43 | - close(downloadNotifyCh) | |
44 | - }() | |
45 | - wg.Add(1) | |
46 | - bp.process(downloadNotifyCh, ProcessStopCh, uint64(blockNum/2), &wg) | |
47 | - if bp.chain.BestBlockHeight() != uint64(blockNum) { | |
48 | - t.Fatalf("TestBlockProcess fail: got %d want %d", bp.chain.BestBlockHeight(), blockNum) | |
69 | + bp.process(downloadNotifyCh, ProcessStopCh, c.startHeight, &wg) | |
70 | + if bp.chain.BestBlockHeight() != c.stopHeight { | |
71 | + t.Fatalf("TestBlockProcess index: %d fail: got %d want %d", i, bp.chain.BestBlockHeight(), c.stopHeight) | |
72 | + } | |
49 | 73 | } |
50 | 74 | } |
@@ -161,6 +161,33 @@ func mockBlocks(startBlock *types.Block, height uint64) []*types.Block { | ||
161 | 161 | return blocks |
162 | 162 | } |
163 | 163 | |
164 | +func mockErrorBlocks(startBlock *types.Block, height uint64, errBlockHeight uint64) []*types.Block { | |
165 | + blocks := []*types.Block{} | |
166 | + indexBlock := &types.Block{} | |
167 | + if startBlock == nil { | |
168 | + indexBlock = &types.Block{BlockHeader: types.BlockHeader{Version: uint64(rand.Uint32())}} | |
169 | + blocks = append(blocks, indexBlock) | |
170 | + } else { | |
171 | + indexBlock = startBlock | |
172 | + } | |
173 | + | |
174 | + for indexBlock.Height < height { | |
175 | + block := &types.Block{ | |
176 | + BlockHeader: types.BlockHeader{ | |
177 | + Height: indexBlock.Height + 1, | |
178 | + PreviousBlockHash: indexBlock.Hash(), | |
179 | + Version: uint64(rand.Uint32()), | |
180 | + }, | |
181 | + } | |
182 | + if block.Height == errBlockHeight { | |
183 | + block.TransactionsMerkleRoot = bc.NewHash([32]byte{0x1}) | |
184 | + } | |
185 | + blocks = append(blocks, block) | |
186 | + indexBlock = block | |
187 | + } | |
188 | + return blocks | |
189 | +} | |
190 | + | |
164 | 191 | func mockSync(blocks []*types.Block, mempool *mock.Mempool, fastSyncDB dbm.DB) *Manager { |
165 | 192 | chain := mock.NewChain(mempool) |
166 | 193 | peers := peers.NewPeerSet(NewPeerSet()) |
@@ -673,6 +673,15 @@ func (ps *PeerSet) SetStatus(peerID string, height uint64, hash *bc.Hash) { | ||
673 | 673 | peer.SetBestStatus(height, hash) |
674 | 674 | } |
675 | 675 | |
676 | +func (ps *PeerSet) SetIrreversibleStatus(peerID string, height uint64, hash *bc.Hash) { | |
677 | + peer := ps.GetPeer(peerID) | |
678 | + if peer == nil { | |
679 | + return | |
680 | + } | |
681 | + | |
682 | + peer.SetIrreversibleStatus(height, hash) | |
683 | +} | |
684 | + | |
676 | 685 | func (ps *PeerSet) Size() int { |
677 | 686 | ps.mtx.RLock() |
678 | 687 | defer ps.mtx.RUnlock() |
@@ -4,10 +4,16 @@ import ( | ||
4 | 4 | "errors" |
5 | 5 | "math/rand" |
6 | 6 | |
7 | + "github.com/vapor/protocol" | |
7 | 8 | "github.com/vapor/protocol/bc" |
8 | 9 | "github.com/vapor/protocol/bc/types" |
9 | 10 | ) |
10 | 11 | |
12 | +var ( | |
13 | + ErrFoundHeaderByHash = errors.New("can't find header by hash") | |
14 | + ErrFoundHeaderByHeight = errors.New("can't find header by height") | |
15 | +) | |
16 | + | |
11 | 17 | type mempool interface { |
12 | 18 | AddTx(tx *types.Tx) |
13 | 19 | } |
@@ -64,7 +70,7 @@ func (c *Chain) GetBlockByHeight(height uint64) (*types.Block, error) { | ||
64 | 70 | func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) { |
65 | 71 | block, ok := c.blockMap[*hash] |
66 | 72 | if !ok { |
67 | - return nil, errors.New("can't find block") | |
73 | + return nil, ErrFoundHeaderByHash | |
68 | 74 | } |
69 | 75 | return &block.BlockHeader, nil |
70 | 76 | } |
@@ -72,7 +78,7 @@ func (c *Chain) GetHeaderByHash(hash *bc.Hash) (*types.BlockHeader, error) { | ||
72 | 78 | func (c *Chain) GetHeaderByHeight(height uint64) (*types.BlockHeader, error) { |
73 | 79 | block, ok := c.heightMap[height] |
74 | 80 | if !ok { |
75 | - return nil, errors.New("can't find block") | |
81 | + return nil, ErrFoundHeaderByHeight | |
76 | 82 | } |
77 | 83 | return &block.BlockHeader, nil |
78 | 84 | } |
@@ -107,6 +113,10 @@ func (c *Chain) InMainChain(hash bc.Hash) bool { | ||
107 | 113 | } |
108 | 114 | |
109 | 115 | func (c *Chain) ProcessBlock(block *types.Block) (bool, error) { |
116 | + if block.TransactionsMerkleRoot == bc.NewHash([32]byte{0x1}) { | |
117 | + return false, protocol.ErrBadStateRoot | |
118 | + } | |
119 | + | |
110 | 120 | if c.bestBlockHeader.Hash() == block.PreviousBlockHash { |
111 | 121 | c.heightMap[block.Height] = block |
112 | 122 | c.blockMap[block.Hash()] = block |