OSDN Git Service

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