OSDN Git Service

fix bug (#160)
[bytom/vapor.git] / protocol / consensus_node_manager.go
1 package protocol
2
3 import (
4         "github.com/vapor/config"
5         "github.com/vapor/consensus"
6         "github.com/vapor/errors"
7         "github.com/vapor/protocol/bc"
8         "github.com/vapor/protocol/state"
9 )
10
11 var (
12         errNotFoundConsensusNode = errors.New("can not found consensus node")
13         errNotFoundBlockNode     = errors.New("can not find block node")
14 )
15
16 type consensusNodeManager struct {
17         store      Store
18         blockIndex *state.BlockIndex
19 }
20
21 func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consensusNodeManager {
22         return &consensusNodeManager{
23                 store:      store,
24                 blockIndex: blockIndex,
25         }
26 }
27
28 func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) {
29         consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
30         if err != nil {
31                 return nil, err
32         }
33
34         node, exist := consensusNodeMap[pubkey]
35         if !exist {
36                 return nil, errNotFoundConsensusNode
37         }
38         return node, nil
39 }
40
41 func (c *consensusNodeManager) isBlocker(prevBlockHash *bc.Hash, pubKey string, timeStamp uint64) (bool, error) {
42         consensusNodeMap, err := c.getConsensusNodes(prevBlockHash)
43         if err != nil {
44                 return false, err
45         }
46
47         consensusNode := consensusNodeMap[pubKey]
48         if consensusNode == nil {
49                 return false, nil
50         }
51
52         prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash)
53         if err != nil {
54                 return false, err
55         }
56
57         startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval
58         begin := getLastBlockTimeInTimeRange(startTimestamp, timeStamp, consensusNode.Order, uint64(len(consensusNodeMap)))
59         end := begin + consensus.BlockNumEachNode*consensus.BlockTimeInterval
60         return timeStamp >= begin && timeStamp < end, nil
61 }
62
63 func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order, numOfConsensusNode uint64) uint64 {
64         // One round of product block time for all consensus nodes
65         roundBlockTime := consensus.BlockNumEachNode * numOfConsensusNode * consensus.BlockTimeInterval
66         // The start time of the last round of product block
67         lastRoundStartTime := startTimestamp + (endTimestamp-startTimestamp)/roundBlockTime*roundBlockTime
68         // The time of product block of the consensus in last round
69         return lastRoundStartTime + order*(consensus.BlockNumEachNode*consensus.BlockTimeInterval)
70 }
71
72 func (c *consensusNodeManager) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) {
73         node := c.blockIndex.GetNode(prevBlockHash)
74         if node == nil {
75                 return nil, errNotFoundBlockNode
76         }
77
78         for node.Height%consensus.RoundVoteBlockNums != 0 {
79                 node = node.Parent
80         }
81         return node, nil
82 }
83
84 func (c *consensusNodeManager) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) {
85         prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
86         if prevBlockNode == nil {
87                 return nil, errNotFoundBlockNode
88         }
89
90         preSeq := state.CalcVoteSeq(prevBlockNode.Height+1) - 1
91         if bestSeq := state.CalcVoteSeq(c.blockIndex.BestNode().Height); preSeq > bestSeq {
92                 preSeq = bestSeq
93         }
94
95         voteResult, err := c.store.GetVoteResult(preSeq)
96         if err != nil {
97                 return nil, err
98         }
99
100         lastBlockNode, err := c.getPrevRoundLastBlock(prevBlockHash)
101         if err != nil {
102                 return nil, err
103         }
104
105         if err := c.reorganizeVoteResult(voteResult, lastBlockNode); err != nil {
106                 return nil, err
107         }
108
109         result, err := voteResult.ConsensusNodes()
110         if err != nil {
111                 return nil, err
112         }
113
114         if len(result) != 0 {
115                 return result, nil
116         }
117         return federationNodes(), nil
118 }
119
120 func (c *consensusNodeManager) getBestVoteResult() (*state.VoteResult, error) {
121         blockNode := c.blockIndex.BestNode()
122         seq := state.CalcVoteSeq(blockNode.Height)
123         voteResult, err := c.store.GetVoteResult(seq)
124         if err != nil {
125                 return nil, err
126         }
127
128         if err := c.reorganizeVoteResult(voteResult, blockNode); err != nil {
129                 return nil, err
130         }
131
132         return voteResult, nil
133 }
134
135 func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, node *state.BlockNode) error {
136         mainChainNode := c.blockIndex.GetNode(&voteResult.BlockHash)
137         var attachNodes []*state.BlockNode
138         var detachNodes []*state.BlockNode
139         for forkChainNode := node; mainChainNode != forkChainNode; forkChainNode = forkChainNode.Parent {
140                 if forkChainNode.Height == mainChainNode.Height {
141                         detachNodes = append(detachNodes, mainChainNode)
142                         mainChainNode = mainChainNode.Parent
143                 }
144                 attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...)
145         }
146
147         for _, node := range detachNodes {
148                 block, err := c.store.GetBlock(&node.Hash)
149                 if err != nil {
150                         return err
151                 }
152
153                 if err := voteResult.DetachBlock(block); err != nil {
154                         return err
155                 }
156         }
157
158         for _, node := range attachNodes {
159                 block, err := c.store.GetBlock(&node.Hash)
160                 if err != nil {
161                         return err
162                 }
163
164                 if err := voteResult.ApplyBlock(block); err != nil {
165                         return err
166                 }
167         }
168         return nil
169 }
170
171 func federationNodes() map[string]*state.ConsensusNode {
172         voteResult := map[string]*state.ConsensusNode{}
173         for i, xpub := range config.CommonConfig.Federation.Xpubs {
174                 voteResult[xpub.String()] = &state.ConsensusNode{XPub: xpub, VoteNum: 0, Order: uint64(i)}
175         }
176         return voteResult
177 }