8 "github.com/vapor/errors"
9 "github.com/vapor/math/checked"
10 "github.com/vapor/protocol/bc"
11 "github.com/vapor/protocol/bc/types"
12 "github.com/vapor/protocol/state"
16 numOfConsensusNode = 21
17 roundVoteBlockNums = 1000
19 // BlockTimeInterval indicate product one block per 500 milliseconds
20 BlockTimeInterval = 500
21 // BlockNumEachNode indicate product three blocks per node in succession
26 errHasNoChanceProductBlock = errors.New("the node has no chance to product a block in this round of voting")
27 errNotFoundConsensusNode = errors.New("can not found consensus node")
28 errNotFoundBlockNode = errors.New("can not find block node")
31 type consensusNode struct {
37 type consensusNodeSlice []*consensusNode
39 func (c consensusNodeSlice) Len() int { return len(c) }
40 func (c consensusNodeSlice) Less(i, j int) bool { return c[i].voteNum > c[j].voteNum }
41 func (c consensusNodeSlice) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
43 type consensusNodeManager struct {
45 blockIndex *state.BlockIndex
48 func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consensusNodeManager {
49 return &consensusNodeManager{
51 blockIndex: blockIndex,
55 func (c *consensusNodeManager) getConsensusNode(blockHash *bc.Hash, pubkey string) (*consensusNode, error) {
56 consensusNodeMap, err := c.getConsensusNodesByVoteResult(blockHash)
61 node, exist := consensusNodeMap[pubkey]
63 return nil, errNotFoundConsensusNode
68 func (c *consensusNodeManager) isBlocker(blockHash *bc.Hash, pubkey string) (bool, error) {
69 blockNode := c.blockIndex.GetNode(blockHash)
71 return false, errNotFoundBlockNode
74 consensusNode, err := c.getConsensusNode(blockHash, pubkey)
75 if err != nil && err != errNotFoundConsensusNode {
79 if consensusNode == nil {
83 prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(blockNode)
88 startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval
90 begin := getLastBlockTimeInTimeRange(startTimestamp, blockNode.Timestamp, consensusNode.order)
91 end := begin + BlockNumEachNode*BlockTimeInterval
92 return blockNode.Timestamp >= begin && blockNode.Timestamp < end, nil
95 func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, bestBlockHash *bc.Hash) (uint64, uint64, error) {
96 bestBlockNode := c.blockIndex.GetNode(bestBlockHash)
97 if bestBlockNode == nil {
98 return 0, 0, errNotFoundBlockNode
101 consensusNode, err := c.getConsensusNode(bestBlockHash, hex.EncodeToString(pubkey))
106 prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(bestBlockNode)
111 startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval
112 endTime := startTime + roundVoteBlockNums*BlockTimeInterval
114 nextLeaderTime, err := nextLeaderTimeHelper(startTime, endTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order)
119 return nextLeaderTime, nextLeaderTime + BlockNumEachNode*BlockTimeInterval, nil
122 func nextLeaderTimeHelper(startTime, endTime, now, nodeOrder uint64) (uint64, error) {
123 nextLeaderTimestamp := getLastBlockTimeInTimeRange(startTime, now, nodeOrder)
124 roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * BlockTimeInterval)
126 if int64(now-nextLeaderTimestamp) >= BlockNumEachNode*BlockTimeInterval {
127 nextLeaderTimestamp += roundBlockTime
128 if nextLeaderTimestamp >= endTime {
129 return 0, errHasNoChanceProductBlock
133 return nextLeaderTimestamp, nil
136 func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uint64 {
137 // One round of product block time for all consensus nodes
138 roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * BlockTimeInterval)
139 // The start time of the last round of product block
140 lastRoundStartTime := startTimestamp + (endTimestamp-startTimestamp)/roundBlockTime*roundBlockTime
141 // The time of product block of the consensus in last round
142 return lastRoundStartTime + order*(BlockNumEachNode*BlockTimeInterval)
145 func (c *consensusNodeManager) getPrevRoundVoteLastBlock(blockNode *state.BlockNode) (*state.BlockNode, error) {
146 prevVoteRoundLastBlockHeight := blockNode.Height/roundVoteBlockNums*roundVoteBlockNums - 1
147 lastBlockNode := blockNode.GetParent(prevVoteRoundLastBlockHeight)
148 if lastBlockNode == nil {
149 return nil, errNotFoundBlockNode
151 return lastBlockNode, nil
154 func (c *consensusNodeManager) getConsensusNodesByVoteResult(blockHash *bc.Hash) (map[string]*consensusNode, error) {
155 blockNode := c.blockIndex.GetNode(blockHash)
156 if blockNode == nil {
157 return nil, errNotFoundBlockNode
160 seq := blockNode.Height / roundVoteBlockNums
161 voteResult, err := c.store.GetVoteResult(seq)
163 // fail to find vote result, try to construct
164 voteResult = &state.VoteResult{
166 NumOfVote: make(map[string]uint64),
171 lastBlockNode, err := c.getPrevRoundVoteLastBlock(blockNode)
176 if err := c.reorganizeVoteResult(voteResult, lastBlockNode); err != nil {
180 var nodes []*consensusNode
181 for pubkey, voteNum := range voteResult.NumOfVote {
182 nodes = append(nodes, &consensusNode{
187 // In principle, there is no need to sort all voting nodes.
188 // if there is a performance problem, consider the optimization later.
189 // TODO not consider the same number of votes
190 sort.Sort(consensusNodeSlice(nodes))
192 result := make(map[string]*consensusNode)
193 for i := 0; i < numOfConsensusNode; i++ {
195 node.order = uint64(i)
196 result[node.pubkey] = node
201 func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error {
202 var mainChainNode *state.BlockNode
203 emptyHash := bc.Hash{}
204 if voteResult.LastBlockHash != emptyHash {
205 mainChainNode = c.blockIndex.GetNode(&voteResult.LastBlockHash)
206 if mainChainNode == nil {
207 return errNotFoundBlockNode
211 var attachNodes []*state.BlockNode
212 var detachNodes []*state.BlockNode
214 for forkChainNode.Hash != mainChainNode.Hash && forkChainNode.Height >= (voteResult.Seq-1)*roundVoteBlockNums {
215 attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...)
216 forkChainNode = forkChainNode.Parent
218 if mainChainNode != nil && forkChainNode.Height == mainChainNode.Height {
219 detachNodes = append(detachNodes, mainChainNode)
220 mainChainNode = mainChainNode.Parent
224 for _, node := range detachNodes {
225 block, err := c.store.GetBlock(&node.Hash)
230 if err := c.detachBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
235 for _, node := range attachNodes {
236 block, err := c.store.GetBlock(&node.Hash)
241 if err := c.applyBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
248 func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) {
249 voteSeq := block.Height / roundVoteBlockNums
250 voteResult := voteResultMap[voteSeq]
252 if voteResult == nil {
253 voteResult, err = c.store.GetVoteResult(voteSeq)
254 if err != nil && err != ErrNotFoundVoteResult {
259 if voteResult == nil {
260 voteResult = &state.VoteResult{
262 NumOfVote: make(map[string]uint64),
263 LastBlockHash: block.Hash(),
267 voteResultMap[voteSeq] = voteResult
269 if voteResult.LastBlockHash != block.PreviousBlockHash {
270 return errors.New("bbft append block error, the block parent hash is not equals last block hash of vote result")
273 for _, tx := range block.Transactions {
274 for _, input := range tx.Inputs {
275 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
280 pubkey := hex.EncodeToString(unVoteInput.Vote)
281 voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
283 return errVotingOperationOverFlow
286 for _, output := range tx.Outputs {
287 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
292 pubkey := hex.EncodeToString(voteOutput.Vote)
293 voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
295 return errVotingOperationOverFlow
300 voteResult.Finalized = (block.Height+1)%roundVoteBlockNums == 0
304 func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error {
305 voteSeq := block.Height / roundVoteBlockNums
306 voteResult := voteResultMap[voteSeq]
308 if voteResult == nil {
309 voteResult, err := c.store.GetVoteResult(voteSeq)
313 voteResultMap[voteSeq] = voteResult
316 if voteResult.LastBlockHash != block.Hash() {
317 return errors.New("bbft detach block error, the block hash is not equals last block hash of vote result")
320 for _, tx := range block.Transactions {
321 for _, input := range tx.Inputs {
322 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
327 pubkey := hex.EncodeToString(unVoteInput.Vote)
328 voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
330 return errVotingOperationOverFlow
333 for _, output := range tx.Outputs {
334 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
339 pubkey := hex.EncodeToString(voteOutput.Vote)
340 voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
342 return errVotingOperationOverFlow
347 voteResult.Finalized = false