OSDN Git Service

45cefc89d2cb866444ce47b814a4bebe4730b9f1
[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/errors"
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         processBlockCh chan *processBlockMsg
24
25         cond       sync.Cond
26         bestNode   *state.BlockNode
27         Authoritys map[string]string
28         position   uint64
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) SetAuthoritys(authoritys map[string]string) {
61         c.Authoritys = authoritys
62 }
63
64 func (c *Chain) SetPosition(position uint64) {
65         c.position = position
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         node, err := state.NewBlockNode(&genesisBlock.BlockHeader, nil)
88         if err != nil {
89                 return err
90         }
91         return c.store.SaveChainStatus(node, utxoView)
92 }
93
94 // BestBlockHeight returns the current height of the blockchain.
95 func (c *Chain) BestBlockHeight() uint64 {
96         c.cond.L.Lock()
97         defer c.cond.L.Unlock()
98         return c.bestNode.Height
99 }
100
101 // BestBlockHash return the hash of the chain tail block
102 func (c *Chain) BestBlockHash() *bc.Hash {
103         c.cond.L.Lock()
104         defer c.cond.L.Unlock()
105         return &c.bestNode.Hash
106 }
107
108 // BestBlockHeader returns the chain tail block
109 func (c *Chain) BestBlockHeader() *types.BlockHeader {
110         node := c.index.BestNode()
111         return node.BlockHeader()
112 }
113
114 // InMainChain checks wheather a block is in the main chain
115 func (c *Chain) InMainChain(hash bc.Hash) bool {
116         return c.index.InMainchain(hash)
117 }
118
119 // CalcNextSeed return the seed for the given block
120 func (c *Chain) CalcNextSeed(preBlock *bc.Hash) (*bc.Hash, error) {
121         node := c.index.GetNode(preBlock)
122         if node == nil {
123                 return nil, errors.New("can't find preblock in the blockindex")
124         }
125         return node.CalcNextSeed(), nil
126 }
127
128 // CalcNextBits return the seed for the given block
129 func (c *Chain) CalcNextBits(preBlock *bc.Hash) (uint64, error) {
130         node := c.index.GetNode(preBlock)
131         if node == nil {
132                 return 0, errors.New("can't find preblock in the blockindex")
133         }
134         return node.CalcNextBits(), nil
135 }
136
137 // This function must be called with mu lock in above level
138 func (c *Chain) setState(node *state.BlockNode, view *state.UtxoViewpoint) error {
139         if err := c.store.SaveChainStatus(node, view); err != nil {
140                 return err
141         }
142
143         c.cond.L.Lock()
144         defer c.cond.L.Unlock()
145
146         c.index.SetMainChain(node)
147         c.bestNode = node
148
149         log.WithFields(log.Fields{"height": c.bestNode.Height, "hash": c.bestNode.Hash.String()}).Debug("chain best status has been update")
150         c.cond.Broadcast()
151         return nil
152 }
153
154 // BlockWaiter returns a channel that waits for the block at the given height.
155 func (c *Chain) BlockWaiter(height uint64) <-chan struct{} {
156         ch := make(chan struct{}, 1)
157         go func() {
158                 c.cond.L.Lock()
159                 defer c.cond.L.Unlock()
160                 for c.bestNode.Height < height {
161                         c.cond.Wait()
162                 }
163                 ch <- struct{}{}
164         }()
165
166         return ch
167 }
168
169 // GetTxPool return chain txpool.
170 func (c *Chain) GetTxPool() *TxPool {
171         return c.txPool
172 }