8 "github.com/vapor/config"
9 "github.com/vapor/errors"
10 "github.com/vapor/math/checked"
11 "github.com/vapor/protocol/bc"
12 "github.com/vapor/protocol/bc/types"
13 "github.com/vapor/protocol/state"
17 NumOfConsensusNode = 21
18 roundVoteBlockNums = 1000
20 // BlockTimeInterval indicate product one block per 500 milliseconds
21 BlockTimeInterval = 500
22 // BlockNumEachNode indicate product three blocks per node in succession
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(prevBlockHash *bc.Hash, pubkey string) (*consensusNode, error) {
56 consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash)
61 node, exist := consensusNodeMap[pubkey]
63 return nil, errNotFoundConsensusNode
68 func (c *consensusNodeManager) isBlocker(block *types.Block, pubKey string) (bool, error) {
69 consensusNode, err := c.getConsensusNode(&block.PreviousBlockHash, pubKey)
70 if err != nil && err != errNotFoundConsensusNode {
74 if consensusNode == nil {
78 prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&block.PreviousBlockHash)
83 startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval
85 begin := getLastBlockTimeInTimeRange(startTimestamp, block.Timestamp, consensusNode.order)
86 end := begin + BlockNumEachNode*BlockTimeInterval
87 return block.Timestamp >= begin && block.Timestamp < end, nil
90 func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, bestBlockHash *bc.Hash) (uint64, uint64, error) {
91 bestBlockNode := c.blockIndex.GetNode(bestBlockHash)
92 if bestBlockNode == nil {
93 return 0, 0, errNotFoundBlockNode
96 parentHash := bestBlockNode.Hash
97 if bestBlockNode.Height > 0 {
98 parentHash = bestBlockNode.Parent.Hash
101 consensusNode, err := c.getConsensusNode(&parentHash, hex.EncodeToString(pubkey))
106 prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&parentHash)
111 startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval
113 nextLeaderTime, err := nextLeaderTimeHelper(startTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order)
118 return nextLeaderTime, nextLeaderTime + BlockNumEachNode*BlockTimeInterval, nil
121 func nextLeaderTimeHelper(startTime, now, nodeOrder uint64) (uint64, error) {
122 nextLeaderTimestamp := getLastBlockTimeInTimeRange(startTime, now, nodeOrder)
123 roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * BlockTimeInterval)
125 if now > nextLeaderTimestamp {
126 nextLeaderTimestamp += roundBlockTime
129 return nextLeaderTimestamp, nil
132 func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uint64 {
133 // One round of product block time for all consensus nodes
134 roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * BlockTimeInterval)
135 // The start time of the last round of product block
136 lastRoundStartTime := startTimestamp + (endTimestamp-startTimestamp)/roundBlockTime*roundBlockTime
137 // The time of product block of the consensus in last round
138 return lastRoundStartTime + order*(BlockNumEachNode*BlockTimeInterval)
141 func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) {
142 prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
143 if prevBlockNode == nil {
144 return nil, errNotFoundBlockNode
147 blockHeight := prevBlockNode.Height + 1
149 prevVoteRoundLastBlockHeight := blockHeight/roundVoteBlockNums*roundVoteBlockNums - 1
151 if blockHeight/roundVoteBlockNums == 0 {
152 prevVoteRoundLastBlockHeight = 0
155 lastBlockNode := prevBlockNode.GetParent(prevVoteRoundLastBlockHeight)
156 if lastBlockNode == nil {
157 return nil, errNotFoundBlockNode
159 return lastBlockNode, nil
162 func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*consensusNode, error) {
163 prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
164 if prevBlockNode == nil {
165 return nil, errNotFoundBlockNode
168 seq := (prevBlockNode.Height + 1) / roundVoteBlockNums
170 return initVoteResult(), nil
173 voteResult, err := c.store.GetVoteResult(seq)
175 // fail to find vote result, try to construct
176 voteResult = &state.VoteResult{
178 NumOfVote: make(map[string]uint64),
183 lastBlockNode, err := c.getPrevRoundVoteLastBlock(prevBlockHash)
188 if err := c.reorganizeVoteResult(voteResult, lastBlockNode); err != nil {
192 var nodes []*consensusNode
193 for pubkey, voteNum := range voteResult.NumOfVote {
194 nodes = append(nodes, &consensusNode{
199 // In principle, there is no need to sort all voting nodes.
200 // if there is a performance problem, consider the optimization later.
201 // TODO not consider the same number of votes
202 sort.Sort(consensusNodeSlice(nodes))
204 result := make(map[string]*consensusNode)
205 for i := 0; i < len(nodes) && i < NumOfConsensusNode; i++ {
207 node.order = uint64(i)
208 result[node.pubkey] = node
213 func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error {
214 var mainChainNode *state.BlockNode
215 emptyHash := bc.Hash{}
216 if voteResult.LastBlockHash != emptyHash {
217 mainChainNode = c.blockIndex.GetNode(&voteResult.LastBlockHash)
218 if mainChainNode == nil {
219 return errNotFoundBlockNode
223 var attachNodes []*state.BlockNode
224 var detachNodes []*state.BlockNode
226 for forkChainNode.Hash != mainChainNode.Hash && forkChainNode.Height >= (voteResult.Seq-1)*roundVoteBlockNums {
227 attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...)
228 forkChainNode = forkChainNode.Parent
230 if mainChainNode != nil && forkChainNode.Height == mainChainNode.Height {
231 detachNodes = append(detachNodes, mainChainNode)
232 mainChainNode = mainChainNode.Parent
236 for _, node := range detachNodes {
237 block, err := c.store.GetBlock(&node.Hash)
242 if err := c.detachBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
247 for _, node := range attachNodes {
248 block, err := c.store.GetBlock(&node.Hash)
253 if err := c.applyBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
260 func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) {
261 voteResult, err := c.getVoteResult(voteResultMap, block.Height)
266 emptyHash := bc.Hash{}
267 if voteResult.LastBlockHash != emptyHash && voteResult.LastBlockHash != block.PreviousBlockHash {
268 return errors.New("bbft append block error, the block parent hash is not equals last block hash of vote result")
271 for _, tx := range block.Transactions {
272 for _, input := range tx.Inputs {
273 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
278 pubkey := hex.EncodeToString(unVoteInput.Vote)
279 voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
281 return errVotingOperationOverFlow
284 for _, output := range tx.Outputs {
285 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
290 pubkey := hex.EncodeToString(voteOutput.Vote)
291 voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
293 return errVotingOperationOverFlow
298 voteResult.Finalized = (block.Height+1)%roundVoteBlockNums == 0
302 func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.VoteResult, blockHeight uint64) (*state.VoteResult, error) {
304 seq := blockHeight / roundVoteBlockNums
305 voteResult := voteResultMap[seq]
306 if blockHeight == 0 {
307 voteResult = &state.VoteResult{
309 NumOfVote: make(map[string]uint64),
314 if voteResult == nil {
315 prevVoteResult := voteResultMap[seq-1]
316 if prevVoteResult != nil {
317 voteResult = &state.VoteResult{
319 NumOfVote: prevVoteResult.NumOfVote,
325 if voteResult == nil {
326 voteResult, err = c.store.GetVoteResult(seq)
327 if err != nil && err != ErrNotFoundVoteResult {
332 if voteResult == nil {
333 voteResult, err := c.store.GetVoteResult(seq - 1)
334 if err != nil && err != ErrNotFoundVoteResult {
338 if voteResult != nil {
339 // previous round voting must have finalized
340 if !voteResult.Finalized {
341 return nil, errors.New("previous round voting has not finalized")
344 voteResult.Finalized = false
345 voteResult.LastBlockHash = bc.Hash{}
349 if voteResult == nil {
350 return nil, errors.New("fail to get vote result")
353 voteResultMap[seq] = voteResult
354 return voteResult, nil
357 func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error {
358 voteSeq := block.Height / roundVoteBlockNums
359 voteResult := voteResultMap[voteSeq]
361 if voteResult == nil {
362 voteResult, err := c.store.GetVoteResult(voteSeq)
366 voteResultMap[voteSeq] = voteResult
369 if voteResult.LastBlockHash != block.Hash() {
370 return errors.New("bbft detach block error, the block hash is not equals last block hash of vote result")
373 for _, tx := range block.Transactions {
374 for _, input := range tx.Inputs {
375 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
380 pubkey := hex.EncodeToString(unVoteInput.Vote)
381 voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
383 return errVotingOperationOverFlow
386 for _, output := range tx.Outputs {
387 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
392 pubkey := hex.EncodeToString(voteOutput.Vote)
393 voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
395 return errVotingOperationOverFlow
400 voteResult.Finalized = false
404 func initVoteResult() map[string]*consensusNode {
405 voteResult := map[string]*consensusNode{}
406 for i, pubkey := range config.CommonConfig.Federation.Xpubs {
407 pubkeyStr := pubkey.String()
408 voteResult[pubkeyStr] = &consensusNode{pubkey: pubkeyStr, voteNum: 0, order: uint64(i)}