OSDN Git Service

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