OSDN Git Service

Accumulate vote (#104)
[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         "github.com/vapor/event"
10         "github.com/vapor/protocol/bc"
11         "github.com/vapor/protocol/bc/types"
12         "github.com/vapor/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         bbft           *bbft
24         processBlockCh chan *processBlockMsg
25
26         cond                 sync.Cond
27         bestNode             *state.BlockNode
28         bestIrreversibleNode *state.BlockNode
29 }
30
31 // NewChain returns a new Chain using store as the underlying storage.
32 func NewChain(store Store, txPool *TxPool, eventDispatcher *event.Dispatcher) (*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         c.bbft = newBbft(store, nil, c.orphanManage, eventDispatcher)
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.bestIrreversibleNode = c.index.GetNode(storeStatus.IrreversibleHash)
57         c.index.SetMainChain(c.bestNode)
58         c.bbft.SetBlockIndex(c.index)
59         go c.blockProcesser()
60         return c, nil
61 }
62
63 func (c *Chain) initChainStatus() error {
64         genesisBlock := config.GenesisBlock()
65         txStatus := bc.NewTransactionStatus()
66         for i := range genesisBlock.Transactions {
67                 if err := txStatus.SetStatus(i, false); err != nil {
68                         return err
69                 }
70         }
71
72         if err := c.store.SaveBlock(genesisBlock, txStatus); err != nil {
73                 return err
74         }
75
76         utxoView := state.NewUtxoViewpoint()
77         bcBlock := types.MapBlock(genesisBlock)
78         if err := utxoView.ApplyBlock(bcBlock, txStatus); err != nil {
79                 return err
80         }
81
82         voteResultMap := make(map[uint64]*state.VoteResult)
83         if err := c.bbft.ApplyBlock(voteResultMap, genesisBlock); err != nil {
84                 return err
85         }
86
87         node, err := state.NewBlockNode(&genesisBlock.BlockHeader, nil)
88         if err != nil {
89                 return err
90         }
91
92         return c.store.SaveChainStatus(node, node, utxoView, voteResultMap)
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 // This function must be called with mu lock in above level
121 func (c *Chain) setState(node *state.BlockNode, irreversibleNode *state.BlockNode, view *state.UtxoViewpoint, voteMap map[uint64]*state.VoteResult) error {
122         if err := c.store.SaveChainStatus(node, irreversibleNode, view, voteMap); err != nil {
123                 return err
124         }
125
126         c.cond.L.Lock()
127         defer c.cond.L.Unlock()
128
129         c.index.SetMainChain(node)
130         c.bestNode = node
131         c.bestIrreversibleNode = irreversibleNode
132
133         log.WithFields(log.Fields{"module": logModule, "height": c.bestNode.Height, "hash": c.bestNode.Hash.String()}).Debug("chain best status has been update")
134         c.cond.Broadcast()
135         return nil
136 }
137
138 // BlockWaiter returns a channel that waits for the block at the given height.
139 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
140         ch := make(chan struct{}, 1)
141         go func() {
142                 c.cond.L.Lock()
143                 defer c.cond.L.Unlock()
144                 for c.bestNode.Height < height {
145                         c.cond.Wait()
146                 }
147                 ch <- struct{}{}
148         }()
149
150         return ch
151 }
152
153 // GetTxPool return chain txpool.
154 func (c *Chain) GetTxPool() *TxPool {
155         return c.txPool
156 }
157
158 // GetBBFT return chain bbft
159 func (c *Chain) GetBBFT() *bbft {
160         return c.bbft
161 }