X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=protocol%2Fconsensus_node_manager.go;h=fd7de372a611882cab66bdd6ea8bedbacf4e499d;hb=644c2afa58cfef47dbb1df817399b1da4d353aa0;hp=e90ee3c22b6721a9c633d70e69e371c1287ce721;hpb=e7c3686fa6506c1762ae88fff9cdc33dadfa0bcd;p=bytom%2Fvapor.git diff --git a/protocol/consensus_node_manager.go b/protocol/consensus_node_manager.go index e90ee3c2..fd7de372 100644 --- a/protocol/consensus_node_manager.go +++ b/protocol/consensus_node_manager.go @@ -1,45 +1,19 @@ package protocol import ( - "encoding/hex" - "sort" - "time" - "github.com/vapor/config" + "github.com/vapor/consensus" "github.com/vapor/errors" - "github.com/vapor/math/checked" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" "github.com/vapor/protocol/state" ) -const ( - NumOfConsensusNode = 21 - roundVoteBlockNums = 1000 - - // BlockTimeInterval indicate product one block per 500 milliseconds - BlockTimeInterval = 500 - // BlockNumEachNode indicate product three blocks per node in succession - BlockNumEachNode = 3 -) - var ( - errNotFoundConsensusNode = errors.New("can not found consensus node") - errNotFoundBlockNode = errors.New("can not find block node") + errNotFoundConsensusNode = errors.New("can not found consensus node") + errNotFoundBlockNode = errors.New("can not find block node") ) -type consensusNode struct { - pubkey string - voteNum uint64 - order uint64 -} - -type consensusNodeSlice []*consensusNode - -func (c consensusNodeSlice) Len() int { return len(c) } -func (c consensusNodeSlice) Less(i, j int) bool { return c[i].voteNum > c[j].voteNum } -func (c consensusNodeSlice) Swap(i, j int) { c[i], c[j] = c[j], c[i] } - type consensusNodeManager struct { store Store blockIndex *state.BlockIndex @@ -52,7 +26,7 @@ func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consens } } -func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*consensusNode, error) { +func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash) if err != nil { return nil, err @@ -65,72 +39,35 @@ func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey s return node, nil } -func (c *consensusNodeManager) isBlocker(block *types.Block, pubKey string) (bool, error) { - consensusNode, err := c.getConsensusNode(&block.PreviousBlockHash, pubKey) - if err != nil && err != errNotFoundConsensusNode { +func (c *consensusNodeManager) isBlocker(prevBlockHash *bc.Hash, pubKey string, timeStamp uint64) (bool, error) { + consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash) + if err != nil { return false, err } + consensusNode := consensusNodeMap[pubKey] if consensusNode == nil { return false, nil } - prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&block.PreviousBlockHash) + prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(prevBlockHash) if err != nil { return false, err } - startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval - - begin := getLastBlockTimeInTimeRange(startTimestamp, block.Timestamp, consensusNode.order) - end := begin + BlockNumEachNode*BlockTimeInterval - return block.Timestamp >= begin && block.Timestamp < end, nil + startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval + begin := getLastBlockTimeInTimeRange(startTimestamp, timeStamp, consensusNode.Order, len(consensusNodeMap)) + end := begin + consensus.BlockNumEachNode*consensus.BlockTimeInterval + return timeStamp >= begin && timeStamp < end, nil } -func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, bestBlockHash *bc.Hash) (uint64, uint64, error) { - bestBlockNode := c.blockIndex.GetNode(bestBlockHash) - if bestBlockNode == nil { - return 0, 0, errNotFoundBlockNode - } - - consensusNode, err := c.getConsensusNode(&bestBlockNode.Parent.Hash, hex.EncodeToString(pubkey)) - if err != nil { - return 0, 0, err - } - - prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&bestBlockNode.Parent.Hash) - if err != nil { - return 0, 0, err - } - - startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval - - nextLeaderTime, err := nextLeaderTimeHelper(startTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order) - if err != nil { - return 0, 0, err - } - - return nextLeaderTime, nextLeaderTime + BlockNumEachNode*BlockTimeInterval, nil -} - -func nextLeaderTimeHelper(startTime, now, nodeOrder uint64) (uint64, error) { - nextLeaderTimestamp := getLastBlockTimeInTimeRange(startTime, now, nodeOrder) - roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * BlockTimeInterval) - - if now > nextLeaderTimestamp { - nextLeaderTimestamp += roundBlockTime - } - - return nextLeaderTimestamp, nil -} - -func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uint64 { +func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64, numOfConsensusNode int) uint64 { // One round of product block time for all consensus nodes - roundBlockTime := uint64(BlockNumEachNode * NumOfConsensusNode * BlockTimeInterval) + roundBlockTime := uint64(consensus.BlockNumEachNode * numOfConsensusNode * consensus.BlockTimeInterval) // The start time of the last round of product block lastRoundStartTime := startTimestamp + (endTimestamp-startTimestamp)/roundBlockTime*roundBlockTime // The time of product block of the consensus in last round - return lastRoundStartTime + order*(BlockNumEachNode*BlockTimeInterval) + return lastRoundStartTime + order*(consensus.BlockNumEachNode*consensus.BlockTimeInterval) } func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) { @@ -138,12 +75,12 @@ func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) if prevBlockNode == nil { return nil, errNotFoundBlockNode } - + blockHeight := prevBlockNode.Height + 1 - prevVoteRoundLastBlockHeight := blockHeight/roundVoteBlockNums*roundVoteBlockNums - 1 + prevVoteRoundLastBlockHeight := blockHeight/consensus.RoundVoteBlockNums*consensus.RoundVoteBlockNums - 1 // first round - if blockHeight / roundVoteBlockNums == 0 { + if blockHeight/consensus.RoundVoteBlockNums == 0 { prevVoteRoundLastBlockHeight = 0 } @@ -154,24 +91,19 @@ func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) return lastBlockNode, nil } -func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*consensusNode, error) { +func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) { prevBlockNode := c.blockIndex.GetNode(prevBlockHash) if prevBlockNode == nil { return nil, errNotFoundBlockNode } - seq := (prevBlockNode.Height + 1) / roundVoteBlockNums - if seq == 0 { - return initVoteResult(), nil - } - + seq := (prevBlockNode.Height + 1) / consensus.RoundVoteBlockNums voteResult, err := c.store.GetVoteResult(seq) if err != nil { - // fail to find vote result, try to construct + // TODO find previous round vote voteResult = &state.VoteResult{ Seq: seq, NumOfVote: make(map[string]uint64), - Finalized: false, } } @@ -184,29 +116,17 @@ func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.H return nil, err } - var nodes []*consensusNode - for pubkey, voteNum := range voteResult.NumOfVote { - nodes = append(nodes, &consensusNode{ - pubkey: pubkey, - voteNum: voteNum, - }) - } - // In principle, there is no need to sort all voting nodes. - // if there is a performance problem, consider the optimization later. - // TODO not consider the same number of votes - sort.Sort(consensusNodeSlice(nodes)) - - result := make(map[string]*consensusNode) - for i := 0; i < NumOfConsensusNode; i++ { - node := nodes[i] - node.order = uint64(i) - result[node.pubkey] = node + if len(voteResult.NumOfVote) == 0 { + return initConsensusNodes(), nil } - return result, nil + + return voteResult.ConsensusNodes() } func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error { - var mainChainNode *state.BlockNode + genesisBlockHash := config.GenesisBlock().Hash() + mainChainNode := c.blockIndex.GetNode(&genesisBlockHash) + emptyHash := bc.Hash{} if voteResult.LastBlockHash != emptyHash { mainChainNode = c.blockIndex.GetNode(&voteResult.LastBlockHash) @@ -218,14 +138,13 @@ func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult var attachNodes []*state.BlockNode var detachNodes []*state.BlockNode - for forkChainNode.Hash != mainChainNode.Hash && forkChainNode.Height >= (voteResult.Seq-1)*roundVoteBlockNums { - attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...) - forkChainNode = forkChainNode.Parent - - if mainChainNode != nil && forkChainNode.Height == mainChainNode.Height { + for forkChainNode != nil && mainChainNode != nil && forkChainNode.Hash != mainChainNode.Hash { + if forkChainNode.Height == mainChainNode.Height { detachNodes = append(detachNodes, mainChainNode) mainChainNode = mainChainNode.Parent } + attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...) + forkChainNode = forkChainNode.Parent } for _, node := range detachNodes { @@ -253,63 +172,65 @@ func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult } func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) { - voteSeq := block.Height / roundVoteBlockNums - voteResult := voteResultMap[voteSeq] + voteResult, err := c.getVoteResult(voteResultMap, block.Height) + if err != nil { + return err + } - if voteResult == nil { - voteResult, err = c.store.GetVoteResult(voteSeq) - if err != nil && err != ErrNotFoundVoteResult { - return err + return voteResult.ApplyBlock(block) +} + +func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.VoteResult, blockHeight uint64) (*state.VoteResult, error) { + var err error + // This round of voting prepares for the next round + seq := blockHeight/consensus.RoundVoteBlockNums + 1 + voteResult := voteResultMap[seq] + if blockHeight == 0 { + voteResult = &state.VoteResult{ + Seq: seq, + NumOfVote: make(map[string]uint64), } } if voteResult == nil { - voteResult = &state.VoteResult{ - Seq: voteSeq, - NumOfVote: make(map[string]uint64), + prevVoteResult := voteResultMap[seq-1] + if prevVoteResult != nil { + voteResult = &state.VoteResult{ + Seq: seq, + NumOfVote: prevVoteResult.NumOfVote, + } } } - voteResultMap[voteSeq] = voteResult - - emptyHash := bc.Hash{} - if voteResult.LastBlockHash != emptyHash && voteResult.LastBlockHash != block.PreviousBlockHash { - return errors.New("bbft append block error, the block parent hash is not equals last block hash of vote result") + if voteResult == nil { + voteResult, err = c.store.GetVoteResult(seq) + if err != nil && err != ErrNotFoundVoteResult { + return nil, err + } } - for _, tx := range block.Transactions { - for _, input := range tx.Inputs { - unVoteInput, ok := input.TypedInput.(*types.UnvoteInput) - if !ok { - continue - } - - pubkey := hex.EncodeToString(unVoteInput.Vote) - voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount) - if !ok { - return errVotingOperationOverFlow - } + if voteResult == nil { + voteResult, err = c.store.GetVoteResult(seq - 1) + if err != nil && err != ErrNotFoundVoteResult { + return nil, err } - for _, output := range tx.Outputs { - voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput) - if !ok { - continue - } - pubkey := hex.EncodeToString(voteOutput.Vote) - voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount) - if !ok { - return errVotingOperationOverFlow - } + if voteResult != nil { + voteResult.Seq = seq + voteResult.LastBlockHash = bc.Hash{} } } - voteResult.Finalized = (block.Height+1)%roundVoteBlockNums == 0 - return nil + if voteResult == nil { + return nil, errors.New("fail to get vote result") + } + + voteResultMap[seq] = voteResult + return voteResult, nil } func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error { - voteSeq := block.Height / roundVoteBlockNums + voteSeq := block.Height / consensus.RoundVoteBlockNums voteResult := voteResultMap[voteSeq] if voteResult == nil { @@ -320,46 +241,14 @@ func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteR voteResultMap[voteSeq] = voteResult } - if voteResult.LastBlockHash != block.Hash() { - return errors.New("bbft detach block error, the block hash is not equals last block hash of vote result") - } - - for _, tx := range block.Transactions { - for _, input := range tx.Inputs { - unVoteInput, ok := input.TypedInput.(*types.UnvoteInput) - if !ok { - continue - } - - pubkey := hex.EncodeToString(unVoteInput.Vote) - voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount) - if !ok { - return errVotingOperationOverFlow - } - } - for _, output := range tx.Outputs { - voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput) - if !ok { - continue - } - - pubkey := hex.EncodeToString(voteOutput.Vote) - voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount) - if !ok { - return errVotingOperationOverFlow - } - } - } - - voteResult.Finalized = false + voteResult.DetachBlock(block) return nil } -func initVoteResult() map[string]*consensusNode { - voteResult := map[string]*consensusNode{} - for i, pubkey := range config.CommonConfig.Federation.Xpubs { - pubkeyStr := pubkey.String() - voteResult[pubkeyStr] = &consensusNode{pubkey: pubkeyStr, voteNum: 0, order: uint64(i)} +func initConsensusNodes() map[string]*state.ConsensusNode { + voteResult := map[string]*state.ConsensusNode{} + for i, xpub := range config.CommonConfig.Federation.Xpubs { + voteResult[xpub.String()] = &state.ConsensusNode{XPub: xpub, VoteNum: 0, Order: uint64(i)} } return voteResult }