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
21 // BlockTimeInterval indicate product one block per 500 milliseconds
22 BlockTimeInterval = 500
23 // BlockNumEachNode indicate product three blocks per node in succession
28 errNotFoundConsensusNode = errors.New("can not found consensus node")
29 errNotFoundBlockNode = errors.New("can not find block node")
32 type consensusNode struct {
38 type consensusNodeSlice []*consensusNode
40 func (c consensusNodeSlice) Len() int { return len(c) }
41 func (c consensusNodeSlice) Less(i, j int) bool { return c[i].voteNum > c[j].voteNum }
42 func (c consensusNodeSlice) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
44 type consensusNodeManager struct {
46 blockIndex *state.BlockIndex
49 func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consensusNodeManager {
50 return &consensusNodeManager{
52 blockIndex: blockIndex,
56 func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*consensusNode, error) {
57 consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash)
62 node, exist := consensusNodeMap[pubkey]
64 return nil, errNotFoundConsensusNode
69 func (c *consensusNodeManager) isBlocker(block *types.Block, pubKey string) (bool, error) {
70 consensusNode, err := c.getConsensusNode(&block.PreviousBlockHash, pubKey)
71 if err != nil && err != errNotFoundConsensusNode {
75 if consensusNode == nil {
79 prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&block.PreviousBlockHash)
84 startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval
86 begin := getLastBlockTimeInTimeRange(startTimestamp, block.Timestamp, consensusNode.order)
87 end := begin + BlockNumEachNode*BlockTimeInterval
88 return block.Timestamp >= begin && block.Timestamp < end, nil
91 func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, prevBlockHash *bc.Hash) (uint64, uint64, error) {
92 consensusNode, err := c.getConsensusNode(prevBlockHash, hex.EncodeToString(pubkey))
97 prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(prevBlockHash)
102 startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval
104 nextLeaderTime, err := nextLeaderTimeHelper(startTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order)
109 return nextLeaderTime, nextLeaderTime + BlockNumEachNode*BlockTimeInterval, nil
112 func nextLeaderTimeHelper(startTime, now, nodeOrder uint64) (uint64, error) {
113 nextLeaderTimestamp := getLastBlockTimeInTimeRange(startTime, now, nodeOrder)
114 roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * BlockTimeInterval)
116 if now > nextLeaderTimestamp {
117 nextLeaderTimestamp += roundBlockTime
120 return nextLeaderTimestamp, nil
123 func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uint64 {
124 // One round of product block time for all consensus nodes
125 roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * BlockTimeInterval)
126 // The start time of the last round of product block
127 lastRoundStartTime := startTimestamp + (endTimestamp-startTimestamp)/roundBlockTime*roundBlockTime
128 // The time of product block of the consensus in last round
129 return lastRoundStartTime + order*(BlockNumEachNode*BlockTimeInterval)
132 func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) {
133 prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
134 if prevBlockNode == nil {
135 return nil, errNotFoundBlockNode
138 blockHeight := prevBlockNode.Height + 1
140 prevVoteRoundLastBlockHeight := blockHeight/RoundVoteBlockNums*RoundVoteBlockNums - 1
142 if blockHeight/RoundVoteBlockNums == 0 {
143 prevVoteRoundLastBlockHeight = 0
146 lastBlockNode := prevBlockNode.GetParent(prevVoteRoundLastBlockHeight)
147 if lastBlockNode == nil {
148 return nil, errNotFoundBlockNode
150 return lastBlockNode, nil
153 func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*consensusNode, error) {
154 prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
155 if prevBlockNode == nil {
156 return nil, errNotFoundBlockNode
159 seq := (prevBlockNode.Height + 1) / RoundVoteBlockNums
160 voteResult, err := c.store.GetVoteResult(seq)
162 // TODO find previous round vote
163 voteResult = &state.VoteResult{
165 NumOfVote: make(map[string]uint64),
170 lastBlockNode, err := c.getPrevRoundVoteLastBlock(prevBlockHash)
175 if err := c.reorganizeVoteResult(voteResult, lastBlockNode); err != nil {
179 if len(voteResult.NumOfVote) == 0 {
180 return initConsensusNodes(), nil
183 var nodes []*consensusNode
184 for pubkey, voteNum := range voteResult.NumOfVote {
185 if voteNum >= MinVoteNum {
186 nodes = append(nodes, &consensusNode{
192 // In principle, there is no need to sort all voting nodes.
193 // if there is a performance problem, consider the optimization later.
194 // TODO not consider the same number of votes
195 sort.Sort(consensusNodeSlice(nodes))
197 result := make(map[string]*consensusNode)
198 for i := 0; i < len(nodes) && i < NumOfConsensusNode; i++ {
200 node.order = uint64(i)
201 result[node.pubkey] = node
206 func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error {
207 genesisBlockHash := config.GenesisBlock().Hash()
208 mainChainNode := c.blockIndex.GetNode(&genesisBlockHash)
210 emptyHash := bc.Hash{}
211 if voteResult.LastBlockHash != emptyHash {
212 mainChainNode = c.blockIndex.GetNode(&voteResult.LastBlockHash)
213 if mainChainNode == nil {
214 return errNotFoundBlockNode
218 var attachNodes []*state.BlockNode
219 var detachNodes []*state.BlockNode
221 for forkChainNode != nil && mainChainNode != nil && forkChainNode.Hash != mainChainNode.Hash {
222 if forkChainNode.Height == mainChainNode.Height {
223 detachNodes = append(detachNodes, mainChainNode)
224 mainChainNode = mainChainNode.Parent
226 attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...)
227 forkChainNode = forkChainNode.Parent
230 for _, node := range detachNodes {
231 block, err := c.store.GetBlock(&node.Hash)
236 if err := c.detachBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
241 for _, node := range attachNodes {
242 block, err := c.store.GetBlock(&node.Hash)
247 if err := c.applyBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil {
254 func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) {
255 voteResult, err := c.getVoteResult(voteResultMap, block.Height)
260 emptyHash := bc.Hash{}
261 if voteResult.LastBlockHash != emptyHash && voteResult.LastBlockHash != block.PreviousBlockHash {
262 return errors.New("bbft append block error, the block parent hash is not equals last block hash of vote result")
265 for _, tx := range block.Transactions {
266 for _, input := range tx.Inputs {
267 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
272 pubkey := hex.EncodeToString(unVoteInput.Vote)
273 voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
275 return errVotingOperationOverFlow
278 for _, output := range tx.Outputs {
279 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
284 pubkey := hex.EncodeToString(voteOutput.Vote)
285 voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
287 return errVotingOperationOverFlow
292 voteResult.Finalized = (block.Height+1)%RoundVoteBlockNums == 0
296 func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.VoteResult, blockHeight uint64) (*state.VoteResult, error) {
298 // This round of voting prepares for the next round
299 seq := blockHeight/RoundVoteBlockNums + 1
300 voteResult := voteResultMap[seq]
301 if blockHeight == 0 {
302 voteResult = &state.VoteResult{
304 NumOfVote: make(map[string]uint64),
309 if voteResult == nil {
310 prevVoteResult := voteResultMap[seq-1]
311 if prevVoteResult != nil {
312 voteResult = &state.VoteResult{
314 NumOfVote: prevVoteResult.NumOfVote,
320 if voteResult == nil {
321 voteResult, err = c.store.GetVoteResult(seq)
322 if err != nil && err != ErrNotFoundVoteResult {
327 if voteResult == nil {
328 voteResult, err = c.store.GetVoteResult(seq - 1)
329 if err != nil && err != ErrNotFoundVoteResult {
333 if voteResult != nil {
334 // previous round voting must have finalized
335 if !voteResult.Finalized {
336 return nil, errors.New("previous round voting has not finalized")
340 voteResult.Finalized = false
341 voteResult.LastBlockHash = bc.Hash{}
345 if voteResult == nil {
346 return nil, errors.New("fail to get vote result")
349 voteResultMap[seq] = voteResult
350 return voteResult, nil
353 func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error {
354 voteSeq := block.Height / RoundVoteBlockNums
355 voteResult := voteResultMap[voteSeq]
357 if voteResult == nil {
358 voteResult, err := c.store.GetVoteResult(voteSeq)
362 voteResultMap[voteSeq] = voteResult
365 if voteResult.LastBlockHash != block.Hash() {
366 return errors.New("bbft detach block error, the block hash is not equals last block hash of vote result")
369 for _, tx := range block.Transactions {
370 for _, input := range tx.Inputs {
371 unVoteInput, ok := input.TypedInput.(*types.UnvoteInput)
376 pubkey := hex.EncodeToString(unVoteInput.Vote)
377 voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
379 return errVotingOperationOverFlow
382 for _, output := range tx.Outputs {
383 voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
388 pubkey := hex.EncodeToString(voteOutput.Vote)
389 voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
391 return errVotingOperationOverFlow
396 voteResult.Finalized = false
400 func initConsensusNodes() map[string]*consensusNode {
401 voteResult := map[string]*consensusNode{}
402 for i, pubkey := range config.CommonConfig.Federation.Xpubs {
403 pubkeyStr := pubkey.String()
404 voteResult[pubkeyStr] = &consensusNode{pubkey: pubkeyStr, voteNum: 0, order: uint64(i)}