OSDN Git Service

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