OSDN Git Service

orphan block num limit (#1579)
[bytom/bytom.git] / protocol / protocol.go
1 package protocol
2
3 import (
4         "sync"
5
6         log "github.com/sirupsen/logrus"
7
8         "github.com/bytom/config"
9         "github.com/bytom/errors"
10         "github.com/bytom/protocol/bc"
11         "github.com/bytom/protocol/bc/types"
12         "github.com/bytom/protocol/state"
13 )
14
15 const maxProcessBlockChSize = 1024
16
17 // Chain provides functions for working with the Bytom block chain.
18 type Chain struct {
19         index          *state.BlockIndex
20         orphanManage   *OrphanManage
21         txPool         *TxPool
22         store          Store
23         processBlockCh chan *processBlockMsg
24
25         cond     sync.Cond
26         bestNode *state.BlockNode
27 }
28
29 // NewChain returns a new Chain using store as the underlying storage.
30 func NewChain(store Store, txPool *TxPool) (*Chain, error) {
31         c := &Chain{
32                 orphanManage:   NewOrphanManage(),
33                 txPool:         txPool,
34                 store:          store,
35                 processBlockCh: make(chan *processBlockMsg, maxProcessBlockChSize),
36         }
37         c.cond.L = new(sync.Mutex)
38
39         storeStatus := store.GetStoreStatus()
40         if storeStatus == nil {
41                 if err := c.initChainStatus(); err != nil {
42                         return nil, err
43                 }
44                 storeStatus = store.GetStoreStatus()
45         }
46
47         var err error
48         if c.index, err = store.LoadBlockIndex(storeStatus.Height); err != nil {
49                 return nil, err
50         }
51
52         c.bestNode = c.index.GetNode(storeStatus.Hash)
53         c.index.SetMainChain(c.bestNode)
54         go c.blockProcesser()
55         return c, nil
56 }
57
58 func (c *Chain) initChainStatus() error {
59         genesisBlock := config.GenesisBlock()
60         txStatus := bc.NewTransactionStatus()
61         for i := range genesisBlock.Transactions {
62                 if err := txStatus.SetStatus(i, false); err != nil {
63                         return err
64                 }
65         }
66
67         if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
68                 return err
69         }
70
71         utxoView := state.NewUtxoViewpoint()
72         bcBlock := types.MapBlock(genesisBlock)
73         if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
74                 return err
75         }
76
77         node, err := state.NewBlockNode(&genesisBlock.BlockHeader, nil)
78         if err != nil {
79                 return err
80         }
81         return c.store.SaveChainStatus(node, utxoView)
82 }
83
84 // BestBlockHeight returns the current height of the blockchain.
85 func (c *Chain) BestBlockHeight() uint64 {
86         c.cond.L.Lock()
87         defer c.cond.L.Unlock()
88         return c.bestNode.Height
89 }
90
91 // BestBlockHash return the hash of the chain tail block
92 func (c *Chain) BestBlockHash() *bc.Hash {
93         c.cond.L.Lock()
94         defer c.cond.L.Unlock()
95         return &c.bestNode.Hash
96 }
97
98 // BestBlockHeader returns the chain tail block
99 func (c *Chain) BestBlockHeader() *types.BlockHeader {
100         node := c.index.BestNode()
101         return node.BlockHeader()
102 }
103
104 // InMainChain checks wheather a block is in the main chain
105 func (c *Chain) InMainChain(hash bc.Hash) bool {
106         return c.index.InMainchain(hash)
107 }
108
109 // CalcNextSeed return the seed for the given block
110 func (c *Chain) CalcNextSeed(preBlock *bc.Hash) (*bc.Hash, error) {
111         node := c.index.GetNode(preBlock)
112         if node == nil {
113                 return nil, errors.New("can't find preblock in the blockindex")
114         }
115         return node.CalcNextSeed(), nil
116 }
117
118 // CalcNextBits return the seed for the given block
119 func (c *Chain) CalcNextBits(preBlock *bc.Hash) (uint64, error) {
120         node := c.index.GetNode(preBlock)
121         if node == nil {
122                 return 0, errors.New("can't find preblock in the blockindex")
123         }
124         return node.CalcNextBits(), nil
125 }
126
127 // This function must be called with mu lock in above level
128 func (c *Chain) setState(node *state.BlockNode, view *state.UtxoViewpoint) error {
129         if err := c.store.SaveChainStatus(node, view); err != nil {
130                 return err
131         }
132
133         c.cond.L.Lock()
134         defer c.cond.L.Unlock()
135
136         c.index.SetMainChain(node)
137         c.bestNode = node
138
139         log.WithFields(log.Fields{"height": c.bestNode.Height, "hash": c.bestNode.Hash.String()}).Debug("chain best status has been update")
140         c.cond.Broadcast()
141         return nil
142 }
143
144 // BlockWaiter returns a channel that waits for the block at the given height.
145 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
146         ch := make(chan struct{}, 1)
147         go func() {
148                 c.cond.L.Lock()
149                 defer c.cond.L.Unlock()
150                 for c.bestNode.Height < height {
151                         c.cond.Wait()
152                 }
153                 ch <- struct{}{}
154         }()
155
156         return ch
157 }
158
159 // GetTxPool return chain txpool.
160 func (c *Chain) GetTxPool() *TxPool {
161         return c.txPool
162 }