OSDN Git Service

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