OSDN Git Service

fix a dead lock bug
[bytom/bytom.git] / protocol / block.go
1 package protocol
2
3 import (
4         log "github.com/sirupsen/logrus"
5
6         "github.com/bytom/errors"
7         "github.com/bytom/protocol/bc"
8         "github.com/bytom/protocol/bc/legacy"
9         "github.com/bytom/protocol/state"
10         "github.com/bytom/protocol/validation"
11 )
12
13 var (
14         // ErrBadBlock is returned when a block is invalid.
15         ErrBadBlock = errors.New("invalid block")
16
17         // ErrStaleState is returned when the Chain does not have a current
18         // blockchain state.
19         ErrStaleState = errors.New("stale blockchain state")
20
21         // ErrBadStateRoot is returned when the computed assets merkle root
22         // disagrees with the one declared in a block header.
23         ErrBadStateRoot = errors.New("invalid state merkle root")
24 )
25
26 func (c *Chain) BlockExist(hash *bc.Hash) bool {
27         return c.orphanManage.BlockExist(hash) || c.store.BlockExist(hash)
28 }
29
30 func (c *Chain) GetBlockByHash(hash *bc.Hash) (*legacy.Block, error) {
31         return c.store.GetBlock(hash)
32 }
33
34 func (c *Chain) GetBlockByHeight(height uint64) (*legacy.Block, error) {
35         c.state.cond.L.Lock()
36         hash, ok := c.state.mainChain[height]
37         c.state.cond.L.Unlock()
38         if !ok {
39                 return nil, errors.New("can't find block in given hight")
40         }
41         return c.GetBlockByHash(hash)
42 }
43
44 // ValidateBlock validates an incoming block in advance of applying it
45 // to a snapshot (with ApplyValidBlock) and committing it to the
46 // blockchain (with CommitAppliedBlock).
47 func (c *Chain) ValidateBlock(block, prev *legacy.Block) error {
48         blockEnts := legacy.MapBlock(block)
49         prevEnts := legacy.MapBlock(prev)
50         if err := validation.ValidateBlock(blockEnts, prevEnts); err != nil {
51                 return errors.Sub(ErrBadBlock, err)
52         }
53         return nil
54 }
55
56 // ApplyValidBlock creates an updated snapshot without validating the
57 // block.
58 func (c *Chain) ConnectBlock(block *legacy.Block) error {
59         newSnapshot := state.Copy(c.state.snapshot)
60         if err := newSnapshot.ApplyBlock(legacy.MapBlock(block)); err != nil {
61                 return err
62         }
63
64         blockHash := block.Hash()
65         if err := c.setState(block, newSnapshot, map[uint64]*bc.Hash{block.Height: &blockHash}); err != nil {
66                 return err
67         }
68
69         for _, tx := range block.Transactions {
70                 c.txPool.RemoveTransaction(&tx.Tx.ID)
71         }
72         return nil
73 }
74
75 func (c *Chain) getReorganizeBlocks(block *legacy.Block) ([]*legacy.Block, []*legacy.Block) {
76         attachBlocks := []*legacy.Block{}
77         detachBlocks := []*legacy.Block{}
78         ancestor := block
79
80         for !c.InMainchain(ancestor) {
81                 attachBlocks = append([]*legacy.Block{ancestor}, attachBlocks...)
82                 ancestor, _ = c.GetBlockByHash(&ancestor.PreviousBlockHash)
83         }
84
85         for d := c.state.block; d.Hash() != ancestor.Hash(); d, _ = c.GetBlockByHash(&d.PreviousBlockHash) {
86                 detachBlocks = append(detachBlocks, d)
87         }
88
89         return attachBlocks, detachBlocks
90 }
91
92 func (c *Chain) reorganizeChain(block *legacy.Block) error {
93         attachBlocks, detachBlocks := c.getReorganizeBlocks(block)
94         newSnapshot := state.Copy(c.state.snapshot)
95         chainChanges := map[uint64]*bc.Hash{}
96
97         for _, d := range detachBlocks {
98                 if err := newSnapshot.DetachBlock(legacy.MapBlock(d)); err != nil {
99                         return err
100                 }
101         }
102
103         for _, a := range attachBlocks {
104                 if err := newSnapshot.ApplyBlock(legacy.MapBlock(a)); err != nil {
105                         return err
106                 }
107                 aHash := a.Hash()
108                 chainChanges[a.Height] = &aHash
109         }
110
111         return c.setState(block, newSnapshot, chainChanges)
112 }
113
114 func (c *Chain) SaveBlock(block *legacy.Block) error {
115         preBlock, _ := c.GetBlockByHash(&block.PreviousBlockHash)
116         if err := c.ValidateBlock(block, preBlock); err != nil {
117                 return err
118         }
119         if err := c.store.SaveBlock(block); err != nil {
120                 return err
121         }
122
123         preorphans, ok := c.orphanManage.preOrphans[block.Hash()]
124         if !ok {
125                 return nil
126         }
127         for _, preorphan := range preorphans {
128                 orphanBlock, ok := c.orphanManage.Get(preorphan)
129                 if !ok {
130                         continue
131                 }
132                 c.SaveBlock(orphanBlock)
133                 c.orphanManage.Delete(preorphan)
134         }
135         return nil
136 }
137
138 func (c *Chain) ProcessBlock(block *legacy.Block) (bool, error) {
139         blockHash := block.Hash()
140         if c.BlockExist(&blockHash) {
141                 log.WithFields(log.Fields{"hash": blockHash.String()}).Info("Skip process due to block already been handled")
142                 return false, nil
143         }
144         if !c.BlockExist(&block.PreviousBlockHash) {
145                 log.WithFields(log.Fields{"hash": blockHash.String()}).Info("Add to orphan block setg")
146                 c.orphanManage.Add(block)
147                 return true, nil
148         }
149         if err := c.SaveBlock(block); err != nil {
150                 return false, err
151         }
152
153         c.state.cond.L.Lock()
154         if c.state.block.Hash() == block.PreviousBlockHash {
155                 defer c.state.cond.L.Unlock()
156                 return false, c.ConnectBlock(block)
157         }
158
159         if block.Height > c.state.height && block.Bits >= c.state.block.Bits {
160                 defer c.state.cond.L.Unlock()
161                 return false, c.reorganizeChain(block)
162         }
163         c.state.cond.L.Unlock()
164         return false, nil
165 }