X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=protocol%2Fconsensus_node_manager.go;h=9d842d044f5c28fb2c561f8d37c49dcc3a4a9477;hb=55ad7001a67ae4fa6587703446999f4cc3096e92;hp=7667e46d82b4e4e1bedc57f28d892dad26220392;hpb=f7e2219e11c2f9aa52db4d7f4281f42fc12be39f;p=bytom%2Fvapor.git diff --git a/protocol/consensus_node_manager.go b/protocol/consensus_node_manager.go index 7667e46d..9d842d04 100644 --- a/protocol/consensus_node_manager.go +++ b/protocol/consensus_node_manager.go @@ -1,45 +1,18 @@ 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 ( - errHasNoChanceProductBlock = errors.New("the node has no chance to product a block in this round of voting") - 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,8 +25,8 @@ func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consens } } -func (c *consensusNodeManager) getConsensusNode(blockHash *bc.Hash, pubkey string) (*consensusNode, error) { - consensusNodeMap, err := c.getConsensusNodesByVoteResult(blockHash) +func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { + consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) if err != nil { return nil, err } @@ -65,110 +38,66 @@ func (c *consensusNodeManager) getConsensusNode(blockHash *bc.Hash, pubkey strin return node, nil } -func (c *consensusNodeManager) isBlocker(blockHash *bc.Hash, pubkey string) (bool, error) { - blockNode := c.blockIndex.GetNode(blockHash) - if blockNode == nil { - return false, errNotFoundBlockNode - } - - consensusNode, err := c.getConsensusNode(blockHash, pubkey) - if err != nil && err != errNotFoundConsensusNode { +func (c *consensusNodeManager) isBlocker(prevBlockHash *bc.Hash, pubKey string, timeStamp uint64) (bool, error) { + consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) + if err != nil { return false, err } + consensusNode := consensusNodeMap[pubKey] if consensusNode == nil { return false, nil } - prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(blockNode) + prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash) if err != nil { return false, err } - startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval - - begin := getLastBlockTimeInTimeRange(startTimestamp, blockNode.Timestamp, consensusNode.order) - end := begin + BlockNumEachNode*BlockTimeInterval - return blockNode.Timestamp >= begin && blockNode.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(bestBlockHash, hex.EncodeToString(pubkey)) - if err != nil { - return 0, 0, err - } - - prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(bestBlockNode) - if err != nil { - return 0, 0, nil - } - - startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval - endTime := startTime + roundVoteBlockNums*BlockTimeInterval - - nextLeaderTime, err := nextLeaderTimeHelper(startTime, endTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order) - if err != nil { - return 0, 0, err - } - - return nextLeaderTime, nextLeaderTime + BlockNumEachNode*BlockTimeInterval, nil + startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval + begin := getLastBlockTimeInTimeRange(startTimestamp, timeStamp, consensusNode.Order, uint64(len(consensusNodeMap))) + end := begin + consensus.BlockNumEachNode*consensus.BlockTimeInterval + return timeStamp >= begin && timeStamp < end, nil } -func nextLeaderTimeHelper(startTime, endTime, now, nodeOrder uint64) (uint64, error) { - nextLeaderTimestamp := getLastBlockTimeInTimeRange(startTime, now, nodeOrder) - roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * BlockTimeInterval) - - if int64(now-nextLeaderTimestamp) >= BlockNumEachNode*BlockTimeInterval { - nextLeaderTimestamp += roundBlockTime - if nextLeaderTimestamp >= endTime { - return 0, errHasNoChanceProductBlock - } - } - - return nextLeaderTimestamp, nil -} - -func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uint64 { +func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order, numOfConsensusNode uint64) uint64 { // One round of product block time for all consensus nodes - roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * BlockTimeInterval) + roundBlockTime := 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(blockNode *state.BlockNode) (*state.BlockNode, error) { - prevVoteRoundLastBlockHeight := blockNode.Height/roundVoteBlockNums*roundVoteBlockNums - 1 - lastBlockNode := blockNode.GetParent(prevVoteRoundLastBlockHeight) - if lastBlockNode == nil { +func (c *consensusNodeManager) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) { + node := c.blockIndex.GetNode(prevBlockHash) + if node == nil { return nil, errNotFoundBlockNode } - return lastBlockNode, nil + + for node.Height%consensus.RoundVoteBlockNums != 0 { + node = node.Parent + } + return node, nil } -func (c *consensusNodeManager) getConsensusNodesByVoteResult(blockHash *bc.Hash) (map[string]*consensusNode, error) { - blockNode := c.blockIndex.GetNode(blockHash) - if blockNode == nil { +func (c *consensusNodeManager) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) { + prevBlockNode := c.blockIndex.GetNode(prevBlockHash) + if prevBlockNode == nil { return nil, errNotFoundBlockNode } - seq := blockNode.Height / roundVoteBlockNums - voteResult, err := c.store.GetVoteResult(seq) + preSeq := state.CalcVoteSeq(prevBlockNode.Height + 1) - 1 + if bestSeq := state.CalcVoteSeq(c.blockIndex.BestNode().Height); preSeq > bestSeq { + preSeq = bestSeq + } + + voteResult, err := c.store.GetVoteResult(preSeq) if err != nil { - // fail to find vote result, try to construct - voteResult = &state.VoteResult{ - Seq: seq, - NumOfVote: make(map[string]uint64), - Finalized: false, - } + return nil, err } - lastBlockNode, err := c.getPrevRoundVoteLastBlock(blockNode) + lastBlockNode, err := c.getPrevRoundLastBlock(prevBlockHash) if err != nil { return nil, err } @@ -177,48 +106,37 @@ func (c *consensusNodeManager) getConsensusNodesByVoteResult(blockHash *bc.Hash) return nil, err } - var nodes []*consensusNode - for pubkey, voteNum := range voteResult.NumOfVote { - nodes = append(nodes, &consensusNode{ - pubkey: pubkey, - voteNum: voteNum, - }) + if len(voteResult.NumOfVote) == 0 { + return federationNodes(), nil } - // 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)) + return voteResult.ConsensusNodes() +} - result := make(map[string]*consensusNode) - for i := 0; i < numOfConsensusNode; i++ { - node := nodes[i] - node.order = uint64(i) - result[node.pubkey] = node +func (c *consensusNodeManager) getBestVoteResult() (*state.VoteResult, error) { + blockNode := c.blockIndex.BestNode() + seq := state.CalcVoteSeq(blockNode.Height) + voteResult, err := c.store.GetVoteResult(seq) + if err != nil { + return nil, err } - return result, nil -} -func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error { - var mainChainNode *state.BlockNode - emptyHash := bc.Hash{} - if voteResult.LastBlockHash != emptyHash { - mainChainNode = c.blockIndex.GetNode(&voteResult.LastBlockHash) - if mainChainNode == nil { - return errNotFoundBlockNode - } + if err := c.reorganizeVoteResult(voteResult, blockNode); err != nil { + return nil, err } + return voteResult, nil +} + +func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, node *state.BlockNode) error { + mainChainNode := c.blockIndex.GetNode(&voteResult.BlockHash) 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 := node; mainChainNode != forkChainNode; forkChainNode = forkChainNode.Parent { + if forkChainNode.Height == mainChainNode.Height { detachNodes = append(detachNodes, mainChainNode) mainChainNode = mainChainNode.Parent } + attachNodes = append([]*state.BlockNode{forkChainNode}, attachNodes...) } for _, node := range detachNodes { @@ -227,7 +145,7 @@ func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult return err } - if err := c.detachBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil { + if err := voteResult.DetachBlock(block); err != nil { return err } } @@ -238,112 +156,17 @@ func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult return err } - if err := c.applyBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil { + if err := voteResult.ApplyBlock(block); err != nil { return err } } return nil } -func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) { - voteSeq := block.Height / roundVoteBlockNums - voteResult := voteResultMap[voteSeq] - - if voteResult == nil { - voteResult, err = c.store.GetVoteResult(voteSeq) - if err != nil && err != ErrNotFoundVoteResult { - return err - } - } - - if voteResult == nil { - voteResult = &state.VoteResult{ - Seq: voteSeq, - NumOfVote: make(map[string]uint64), - LastBlockHash: block.Hash(), - } +func federationNodes() 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)} } - - voteResultMap[voteSeq] = voteResult - - if voteResult.LastBlockHash != block.PreviousBlockHash { - return errors.New("bbft append block error, the block parent 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.SubUint64(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.AddUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount) - if !ok { - return errVotingOperationOverFlow - } - } - } - - voteResult.Finalized = (block.Height+1)%roundVoteBlockNums == 0 - return nil -} - -func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error { - voteSeq := block.Height / roundVoteBlockNums - voteResult := voteResultMap[voteSeq] - - if voteResult == nil { - voteResult, err := c.store.GetVoteResult(voteSeq) - if err != nil { - return err - } - 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 - return nil + return voteResult }