OSDN Git Service

Modify interface function
[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/errors"
11         "github.com/vapor/event"
12         "github.com/vapor/protocol/bc"
13         "github.com/vapor/protocol/bc/types"
14         "github.com/vapor/protocol/state"
15 )
16
17 const (
18         maxProcessBlockChSize = 1024
19         maxKnownTxs           = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
20 )
21
22 type Protocoler interface {
23         Name() string
24         ChainStatus() (uint64, *bc.Hash, error)
25         ValidateBlock(block *types.Block) error
26         ValidateTxs(txs []*types.Tx) error
27         ApplyBlock(block *types.Block) error
28         DetachBlock(block *types.Block) error
29 }
30
31 // Chain provides functions for working with the Bytom block chain.
32 type Chain struct {
33         orphanManage   *OrphanManage
34         txPool         *TxPool
35         store          Store
36         processBlockCh chan *processBlockMsg
37         subProtocols   []Protocoler
38
39         signatureCache  *common.Cache
40         eventDispatcher *event.Dispatcher
41
42         cond               sync.Cond
43         bestBlockHeader    *types.BlockHeader // the last block on current main chain
44         lastIrrBlockHeader *types.BlockHeader // the last irreversible block
45
46         knownTxs *common.OrderedSet
47 }
48
49 // NewChain returns a new Chain using store as the underlying storage.
50 func NewChain(store Store, txPool *TxPool, subProtocols []Protocoler, eventDispatcher *event.Dispatcher) (*Chain, error) {
51         knownTxs, _ := common.NewOrderedSet(maxKnownTxs)
52         c := &Chain{
53                 orphanManage:    NewOrphanManage(),
54                 txPool:          txPool,
55                 store:           store,
56                 subProtocols:    subProtocols,
57                 signatureCache:  common.NewCache(maxSignatureCacheSize),
58                 eventDispatcher: eventDispatcher,
59                 processBlockCh:  make(chan *processBlockMsg, maxProcessBlockChSize),
60                 knownTxs:        knownTxs,
61         }
62         c.cond.L = new(sync.Mutex)
63
64         storeStatus := store.GetStoreStatus()
65         if storeStatus == nil {
66                 if err := c.initChainStatus(); err != nil {
67                         return nil, err
68                 }
69                 storeStatus = store.GetStoreStatus()
70         }
71
72         var err error
73         c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash)
74         if err != nil {
75                 return nil, err
76         }
77
78         c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
79         if err != nil {
80                 return nil, err
81         }
82
83         for _, p := range c.subProtocols {
84                 if err := c.syncProtocolStatus(p); err != nil {
85                         return nil, errors.Wrap(err, p.Name(), "sync sub protocol status")
86                 }
87         }
88
89         go c.blockProcesser()
90         return c, nil
91 }
92
93 func (c *Chain) initChainStatus() error {
94         genesisBlock := config.GenesisBlock()
95         txStatus := bc.NewTransactionStatus()
96         for i := range genesisBlock.Transactions {
97                 if err := txStatus.SetStatus(i, false); err != nil {
98                         return err
99                 }
100         }
101
102         if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
103                 return err
104         }
105
106         utxoView := state.NewUtxoViewpoint()
107         bcBlock := types.MapBlock(genesisBlock)
108         if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
109                 return err
110         }
111
112         consensusResults := []*state.ConsensusResult{&state.ConsensusResult{
113                 Seq:            0,
114                 NumOfVote:      make(map[string]uint64),
115                 CoinbaseReward: make(map[string]uint64),
116                 BlockHash:      genesisBlock.Hash(),
117                 BlockHeight:    0,
118         }}
119
120         genesisBlockHeader := &genesisBlock.BlockHeader
121         return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults)
122 }
123
124 // BestBlockHeight returns the current height of the blockchain.
125 func (c *Chain) BestBlockHeight() uint64 {
126         c.cond.L.Lock()
127         defer c.cond.L.Unlock()
128         return c.bestBlockHeader.Height
129 }
130
131 // BestBlockHash return the hash of the main chain tail block
132 func (c *Chain) BestBlockHash() *bc.Hash {
133         c.cond.L.Lock()
134         defer c.cond.L.Unlock()
135         bestHash := c.bestBlockHeader.Hash()
136         return &bestHash
137 }
138
139 // LastIrreversibleHeader returns the chain last irreversible block header
140 func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
141         c.cond.L.Lock()
142         defer c.cond.L.Unlock()
143         return c.lastIrrBlockHeader
144 }
145
146 // BestBlockHeader returns the chain best block header
147 func (c *Chain) BestBlockHeader() *types.BlockHeader {
148         c.cond.L.Lock()
149         defer c.cond.L.Unlock()
150         return c.bestBlockHeader
151 }
152
153 // InMainChain checks wheather a block is in the main chain
154 func (c *Chain) InMainChain(hash bc.Hash) bool {
155         blockHeader, err := c.store.GetBlockHeader(&hash)
156         if err != nil {
157                 return false
158         }
159
160         blockHash, err := c.store.GetMainChainHash(blockHeader.Height)
161         if err != nil {
162                 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height")
163                 return false
164         }
165         return *blockHash == hash
166 }
167
168 // trace back to the tail of the chain from the given block header
169 func (c *Chain) traceLongestChainTail(blockHeader *types.BlockHeader) (*types.BlockHeader, error) {
170         longestTail, workQueue := blockHeader, []*types.BlockHeader{blockHeader}
171
172         for ; len(workQueue) > 0; workQueue = workQueue[1:] {
173                 currentHeader := workQueue[0]
174                 currentHash := currentHeader.Hash()
175                 hashes, err := c.store.GetBlockHashesByHeight(currentHeader.Height + 1)
176                 if err != nil {
177                         return nil, err
178                 }
179
180                 for _, h := range hashes {
181                         if header, err := c.store.GetBlockHeader(h); err != nil {
182                                 return nil, err
183                         } else if header.PreviousBlockHash == currentHash {
184                                 if longestTail.Height < header.Height {
185                                         longestTail = header
186                                 }
187                                 workQueue = append(workQueue, header)
188                         }
189                 }
190         }
191         return longestTail, nil
192 }
193
194 func (c *Chain) hasSeenTx(tx *types.Tx) bool {
195         return c.knownTxs.Has(tx.ID.String())
196 }
197
198 func (c *Chain) markTransactions(txs ...*types.Tx) {
199         for _, tx := range txs {
200                 c.knownTxs.Add(tx.ID.String())
201         }
202 }
203
204 func (c *Chain) syncProtocolStatus(subProtocol Protocoler) error {
205         protocolHeight, protocolHash, err := subProtocol.ChainStatus()
206         if err != nil {
207                 return errors.Wrap(err, "failed on get sub protocol status")
208         }
209
210         if protocolHeight == c.BestBlockHeight() && protocolHash == c.BestBlockHash() {
211                 return nil
212         }
213
214         for !c.InMainChain(*protocolHash) {
215                 block, err := c.GetBlockByHash(protocolHash)
216                 if err != nil {
217                         return errors.Wrap(err, subProtocol.Name(), "can't get block by hash in chain")
218                 }
219
220                 if err := subProtocol.DetachBlock(block); err != nil {
221                         return errors.Wrap(err, subProtocol.Name(), "sub protocol detach block err")
222                 }
223
224                 protocolHeight, protocolHash, err = subProtocol.ChainStatus()
225                 if err != nil {
226                         return errors.Wrap(err, "failed on get sub protocol status")
227                 }
228         }
229
230         for height := protocolHeight + 1; height <= c.BestBlockHeight(); height++ {
231                 block, err := c.GetBlockByHeight(height)
232                 if err != nil {
233                         return errors.Wrap(err, subProtocol.Name(), "can't get block by height in chain")
234                 }
235
236                 if err := subProtocol.ApplyBlock(block); err != nil {
237                         return errors.Wrap(err, subProtocol.Name(), "sub protocol apply block err")
238                 }
239
240                 protocolHeight, protocolHash, err = subProtocol.ChainStatus()
241                 if err != nil {
242                         return errors.Wrap(err, "failed on get sub protocol status")
243                 }
244
245                 if *protocolHash != block.Hash() {
246                         return errors.Wrap(errors.New("sub protocol status sync err"), subProtocol.Name())
247                 }
248         }
249
250         return nil
251 }
252
253 // This function must be called with mu lock in above level
254 func (c *Chain) setState(blockHeader, irrBlockHeader *types.BlockHeader, mainBlockHeaders []*types.BlockHeader, view *state.UtxoViewpoint, consensusResults []*state.ConsensusResult) error {
255         if err := c.store.SaveChainStatus(blockHeader, irrBlockHeader, mainBlockHeaders, view, consensusResults); err != nil {
256                 return err
257         }
258
259         c.bestBlockHeader = blockHeader
260         c.lastIrrBlockHeader = irrBlockHeader
261
262         blockHash := blockHeader.Hash()
263         log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
264         c.cond.Broadcast()
265         return nil
266 }
267
268 // BlockWaiter returns a channel that waits for the block at the given height.
269 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
270         ch := make(chan struct{}, 1)
271         go func() {
272                 c.cond.L.Lock()
273                 defer c.cond.L.Unlock()
274                 for c.bestBlockHeader.Height < height {
275                         c.cond.Wait()
276                 }
277                 ch <- struct{}{}
278         }()
279
280         return ch
281 }
282
283 // GetTxPool return chain txpool.
284 func (c *Chain) GetTxPool() *TxPool {
285         return c.txPool
286 }