OSDN Git Service

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