6 log "github.com/sirupsen/logrus"
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"
17 maxProcessBlockChSize = 1024
18 maxKnownTxs = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
21 // Chain provides functions for working with the Bytom block chain.
23 orphanManage *OrphanManage
26 processBlockCh chan *processBlockMsg
28 signatureCache *common.Cache
29 eventDispatcher *event.Dispatcher
32 bestBlockHeader *types.BlockHeader // the last block on current main chain
33 lastIrrBlockHeader *types.BlockHeader // the last irreversible block
35 knownTxs *common.OrderedSet
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)
42 orphanManage: NewOrphanManage(),
45 signatureCache: common.NewCache(maxSignatureCacheSize),
46 eventDispatcher: eventDispatcher,
47 processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize),
50 c.cond.L = new(sync.Mutex)
52 storeStatus := store.GetStoreStatus()
53 if storeStatus == nil {
54 if err := c.initChainStatus(); err != nil {
57 storeStatus = store.GetStoreStatus()
61 c.bestBlockHeader, err = c.store.GetBlockHeader(storeStatus.Hash)
66 c.lastIrrBlockHeader, err = c.store.GetBlockHeader(storeStatus.IrreversibleHash)
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 {
83 if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
87 utxoView := state.NewUtxoViewpoint()
88 bcBlock := types.MapBlock(genesisBlock)
89 if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
93 consensusResults := []*state.ConsensusResult{&state.ConsensusResult{
95 NumOfVote: make(map[string]uint64),
96 CoinbaseReward: make(map[string]uint64),
97 BlockHash: genesisBlock.Hash(),
101 genesisBlockHeader := &genesisBlock.BlockHeader
102 return c.store.SaveChainStatus(genesisBlockHeader, genesisBlockHeader, []*types.BlockHeader{genesisBlockHeader}, utxoView, consensusResults)
105 // BestBlockHeight returns the current height of the blockchain.
106 func (c *Chain) BestBlockHeight() uint64 {
108 defer c.cond.L.Unlock()
109 return c.bestBlockHeader.Height
112 // BestBlockHash return the hash of the main chain tail block
113 func (c *Chain) BestBlockHash() *bc.Hash {
115 defer c.cond.L.Unlock()
116 bestHash := c.bestBlockHeader.Hash()
120 // LastIrreversibleHeader returns the chain last irreversible block header
121 func (c *Chain) LastIrreversibleHeader() *types.BlockHeader {
123 defer c.cond.L.Unlock()
124 return c.lastIrrBlockHeader
127 // BestBlockHeader returns the chain best block header
128 func (c *Chain) BestBlockHeader() *types.BlockHeader {
130 defer c.cond.L.Unlock()
131 return c.bestBlockHeader
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)
141 blockHash, err := c.store.GetMainChainHash(blockHeader.Height)
143 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height}).Debug("not contain block hash in main chain for specified height")
146 return *blockHash == hash
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}
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)
161 for _, h := range hashes {
162 if header, err := c.store.GetBlockHeader(h); err != nil {
164 } else if header.PreviousBlockHash == currentHash {
165 if longestTail.Height < header.Height {
168 workQueue = append(workQueue, header)
172 return longestTail, nil
175 func (c *Chain) hasSeenTx(tx *types.Tx) bool {
176 return c.knownTxs.Has(tx.ID.String())
179 func (c *Chain) markTransactions(txs ...*types.Tx) {
180 for _, tx := range txs {
181 c.knownTxs.Add(tx.ID.String())
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 {
191 c.bestBlockHeader = blockHeader
192 c.lastIrrBlockHeader = irrBlockHeader
194 blockHash := blockHeader.Hash()
195 log.WithFields(log.Fields{"module": logModule, "height": blockHeader.Height, "hash": blockHash.String()}).Debug("chain best status has been update")
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)
205 defer c.cond.L.Unlock()
206 for c.bestBlockHeader.Height < height {
215 // GetTxPool return chain txpool.
216 func (c *Chain) GetTxPool() *TxPool {