OSDN Git Service

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