OSDN Git Service

edit code while reviw (#253)
[bytom/vapor.git] / protocol / protocol.go
1 package protocol
2
3 import (
4         "sync"
5
6         log "github.com/sirupsen/logrus"
7
8         "github.com/vapor/common"
9         "github.com/vapor/config"
10         "github.com/vapor/event"
11         "github.com/vapor/protocol/bc"
12         "github.com/vapor/protocol/bc/types"
13         "github.com/vapor/protocol/state"
14 )
15
16 const maxProcessBlockChSize = 1024
17
18 // Chain provides functions for working with the Bytom block chain.
19 type Chain struct {
20         orphanManage   *OrphanManage
21         txPool         *TxPool
22         store          Store
23         processBlockCh chan *processBlockMsg
24
25         signatureCache  *common.Cache
26         eventDispatcher *event.Dispatcher
27
28         cond               sync.Cond
29         bestBlockHeader    *types.BlockHeader // the last block on current main chain
30         lastIrrBlockHeader *types.BlockHeader // the last irreversible block
31 }
32
33 // NewChain returns a new Chain using store as the underlying storage.
34 func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*Chain, error) {
35         c := &Chain{
36                 orphanManage:    NewOrphanManage(),
37                 txPool:          txPool,
38                 store:           store,
39                 signatureCache:  common.NewCache(maxSignatureCacheSize),
40                 eventDispatcher: eventDispatcher,
41                 processBlockCh:  make(chan *processBlockMsg, maxProcessBlockChSize),
42         }
43         c.cond.L = new(sync.Mutex)
44
45         storeStatus := store.GetStoreStatus()
46         if storeStatus == nil {
47                 if err := c.initChainStatus(); err != nil {
48                         return nil, err
49                 }
50                 storeStatus = store.GetStoreStatus()
51         }
52
53         var err error
54         c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash)
55         if err != nil {
56                 return nil, err
57         }
58
59         c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
60         if err != nil {
61                 return nil, err
62         }
63         go c.blockProcesser()
64         return c, nil
65 }
66
67 func (c *Chain) initChainStatus() error {
68         genesisBlock := config.GenesisBlock()
69         txStatus := bc.NewTransactionStatus()
70         for i := range genesisBlock.Transactions {
71                 if err := txStatus.SetStatus(i, false); err != nil {
72                         return err
73                 }
74         }
75
76         if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
77                 return err
78         }
79
80         utxoView := state.NewUtxoViewpoint()
81         bcBlock := types.MapBlock(genesisBlock)
82         if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
83                 return err
84         }
85
86         consensusResults := []*state.ConsensusResult{&state.ConsensusResult{
87                 Seq:            0,
88                 NumOfVote:      make(map[string]uint64),
89                 CoinbaseReward: make(map[string]uint64),
90                 BlockHash:      genesisBlock.Hash(),
91                 BlockHeight:    0,
92         }}
93
94         genesisBlockHeader := &genesisBlock.BlockHeader
95         return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults)
96 }
97
98 // BestBlockHeight returns the current height of the blockchain.
99 func (c *Chain) BestBlockHeight() uint64 {
100         c.cond.L.Lock()
101         defer c.cond.L.Unlock()
102         return c.bestBlockHeader.Height
103 }
104
105 // BestBlockHash return the hash of the main chain tail block
106 func (c *Chain) BestBlockHash() *bc.Hash {
107         c.cond.L.Lock()
108         defer c.cond.L.Unlock()
109         bestHash := c.bestBlockHeader.Hash()
110         return &bestHash
111 }
112
113 // LastIrreversibleHeader returns the chain last irreversible block header
114 func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
115         c.cond.L.Lock()
116         defer c.cond.L.Unlock()
117         return c.lastIrrBlockHeader
118 }
119
120 // BestBlockHeader returns the chain best block header
121 func (c *Chain) BestBlockHeader() *types.BlockHeader {
122         c.cond.L.Lock()
123         defer c.cond.L.Unlock()
124         return c.bestBlockHeader
125 }
126
127 // InMainChain checks wheather a block is in the main chain
128 func (c *Chain) InMainChain(hash bc.Hash) bool {
129         blockHeader, err := c.store.GetBlockHeader(&hash)
130         if err != nil {
131                 return false
132         }
133
134         blockHash, err := c.store.GetMainChainHash(blockHeader.Height)
135         if err != nil {
136                 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height")
137                 return false
138         }
139         return *blockHash == hash
140 }
141
142 // trace back to the tail of the chain from the given block header
143 func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
144         longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
145
146         for len(workQueue) > 0 {
147                 currentHeader := workQueue[0]
148                 currentHash := currentHeader.Hash()
149                 workQueue = workQueue[1:]
150                 hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
151                 if err != nil {
152                         return nil, err
153                 }
154
155                 for _, h := range hashes {
156                         if header, err := c.store.GetBlockHeader(h); err != nil {
157                                 return nil, err
158                         } else if header.PreviousBlockHash == currentHash {
159                                 if longestTail.Height < header.Height {
160                                         longestTail = header
161                                 }
162                                 workQueue = append(workQueue, header)
163                         }
164                 }
165         }
166         return longestTail, nil
167 }
168
169 // This function must be called with mu lock in above level
170 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
171         if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
172                 return err
173         }
174
175         c.bestBlockHeader = blockHeader
176         c.lastIrrBlockHeader = irrBlockHeader
177
178         blockHash := blockHeader.Hash()
179         log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
180         c.cond.Broadcast()
181         return nil
182 }
183
184 // BlockWaiter returns a channel that waits for the block at the given height.
185 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
186         ch := make(chan struct{}, 1)
187         go func() {
188                 c.cond.L.Lock()
189                 defer c.cond.L.Unlock()
190                 for c.bestBlockHeader.Height < height {
191                         c.cond.Wait()
192                 }
193                 ch <- struct{}{}
194         }()
195
196         return ch
197 }
198
199 // GetTxPool return chain txpool.
200 func (c *Chain) GetTxPool() *TxPool {
201         return c.txPool
202 }