X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=protocol%2Fconsensus_node_manager.go;h=d55e7ccf044c25c3c08f7de95f1adac80ebdafa9;hb=ef940688d032a404007b1516d8a3e8737d7b3e35;hp=be5e6bb92df04d62c0de862d8ee06f1373834876;hpb=2d27cad41ef79da3a46142401b5efcf92133ea1a;p=bytom%2Fvapor.git diff --git a/protocol/consensus_node_manager.go b/protocol/consensus_node_manager.go index be5e6bb9..d55e7ccf 100644 --- a/protocol/consensus_node_manager.go +++ b/protocol/consensus_node_manager.go @@ -1,204 +1,169 @@ package protocol import ( - "encoding/hex" - "sort" - "sync" - "time" - + "github.com/vapor/consensus" "github.com/vapor/errors" + "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 = 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") - errVoteResultIsNotfinalized = errors.New("vote result is not finalized") - errPublicKeyIsNotConsensusNode = errors.New("public key is not consensus 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 { - consensusNodeMap map[string]*consensusNode - effectiveStartHeight uint64 - store Store - blockIndex *state.BlockIndex - sync.RWMutex +func (c *Chain) getBestConsensusResult() (*state.ConsensusResult, error) { + bestBlockHeader := c.bestBlockHeader + seq := state.CalcVoteSeq(bestBlockHeader.Height) + return c.getConsensusResult(seq, bestBlockHeader) } -func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consensusNodeManager { - return &consensusNodeManager{ - consensusNodeMap: make(map[string]*consensusNode), - effectiveStartHeight: 1, - store: store, - blockIndex: blockIndex, - } +func getBlockerOrder(startTimestamp, blockTimestamp, numOfConsensusNode uint64) uint64 { + // One round of product block time for all consensus nodes + roundBlockTime := consensus.ActiveNetParams.BlockNumEachNode * numOfConsensusNode * consensus.ActiveNetParams.BlockTimeInterval + // The start time of the last round of product block + lastRoundStartTime := startTimestamp + (blockTimestamp-startTimestamp)/roundBlockTime*roundBlockTime + // Order of blocker + return (blockTimestamp - lastRoundStartTime) / (consensus.ActiveNetParams.BlockNumEachNode * consensus.ActiveNetParams.BlockTimeInterval) } -func (c *consensusNodeManager) getConsensusNode(height uint64, pubkey string) (*consensusNode, error) { - defer c.RUnlock() - c.RLock() - if height >= c.effectiveStartHeight+roundVoteBlockNums { - return nil, errors.New("the vote has not been completed for the specified block height ") - } - - var err error - consensusNodeMap := c.consensusNodeMap - // query history vote result - if height < c.effectiveStartHeight { - consensusNodeMap, err = c.getConsensusNodesByVoteResult(height) - if err != nil { - return nil, err - } +func (c *Chain) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { + consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) + if err != nil { + return nil, err } node, exist := consensusNodeMap[pubkey] if !exist { - return node, errNotFoundConsensusNode + return nil, errNotFoundConsensusNode } return node, nil } -func (c *consensusNodeManager) isBlocker(height uint64, blockTimestamp uint64, pubkey string) (bool, error) { - prevVoteRoundLastBlock := c.blockIndex.NodeByHeight(height - 1) - startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval - - consensusNodeMap, err := c.getConsensusNodesByVoteResult(height) +func (c *Chain) getConsensusNodes(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) { + prevBlockHeader, err := c.store.GetBlockHeader(prevBlockHash) if err != nil { - return false, err + return nil, errNotFoundBlockNode } - blockerNode, exist := consensusNodeMap[pubkey] - if !exist { - return false, nil + bestBlockHeader := c.bestBlockHeader + preSeq := state.CalcVoteSeq(prevBlockHeader.Height+1) - 1 + if bestSeq := state.CalcVoteSeq(bestBlockHeader.Height); preSeq > bestSeq { + preSeq = bestSeq } - begin := getLastBlockTimeInTimeRange(startTimestamp, blockTimestamp, blockerNode.order) - end := begin + BlockNumEachNode*BlockTimeInterval - return blockTimestamp >= begin && blockTimestamp < end, nil -} + lastBlockHeader, err := c.getPrevRoundLastBlock(prevBlockHash) + if err != nil { + return nil, err + } -func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, bestBlockTimestamp, bestBlockHeight uint64) (uint64, uint64, error) { - defer c.RUnlock() - c.RLock() + consensusResult, err := c.getConsensusResult(preSeq, lastBlockHeader) + if err != nil { + return nil, err + } - startHeight := c.effectiveStartHeight - prevRoundLastBlock := c.blockIndex.NodeByHeight(startHeight - 1) - startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval - endTime := bestBlockTimestamp + (roundVoteBlockNums-bestBlockHeight%roundVoteBlockNums)*BlockTimeInterval + return consensusResult.ConsensusNodes() +} - consensusNode, exist := c.consensusNodeMap[hex.EncodeToString(pubkey)] - if !exist { - return 0, 0, errPublicKeyIsNotConsensusNode +// getConsensusResult return the vote result +// seq represent the sequence of vote +// blockHeader represent the chain in which the result of the vote is located +// Voting results need to be adjusted according to the chain +func (c *Chain) getConsensusResult(seq uint64, blockHeader *types.BlockHeader) (*state.ConsensusResult, error) { + consensusResult, err := c.store.GetConsensusResult(seq) + if err != nil { + return nil, err } - nextLeaderTime, err := nextLeaderTimeHelper(startTime, endTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order) - if err != nil { - return 0, 0, err + if err := c.reorganizeConsensusResult(consensusResult, blockHeader); err != nil { + return nil, err } - return nextLeaderTime, nextLeaderTime + BlockNumEachNode*BlockTimeInterval, nil + return consensusResult, nil } -func nextLeaderTimeHelper(startTime, endTime, now, nodeOrder uint64) (uint64, error) { - nextLeaderTimestamp := getLastBlockTimeInTimeRange(startTime, now, nodeOrder) - roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * BlockTimeInterval) +func (c *Chain) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*types.BlockHeader, error) { + blockHeader, err := c.store.GetBlockHeader(prevBlockHash) + if err != nil { + return nil, errNotFoundBlockNode + } - if int64(now-nextLeaderTimestamp) >= BlockNumEachNode*BlockTimeInterval { - nextLeaderTimestamp += roundBlockTime - if nextLeaderTimestamp >= endTime { - return 0, errHasNoChanceProductBlock + for blockHeader.Height%consensus.ActiveNetParams.RoundVoteBlockNums != 0 { + blockHeader, err = c.store.GetBlockHeader(&blockHeader.PreviousBlockHash) + if err != nil { + return nil, err } } - - return nextLeaderTimestamp, nil + return blockHeader, nil } -// updateConsensusNodes used to update consensus node after each round of voting -func (c *consensusNodeManager) updateConsensusNodes(bestBlockHeight uint64) error { - defer c.Unlock() - c.Lock() - - consensusNodeMap, err := c.getConsensusNodesByVoteResult(bestBlockHeight) - if err != nil && err != errVoteResultIsNotfinalized { +func (c *Chain) reorganizeConsensusResult(consensusResult *state.ConsensusResult, blockHeader *types.BlockHeader) error { + mainChainBlockHeader, err := c.store.GetBlockHeader(&consensusResult.BlockHash) + if err != nil { return err } - if err == errVoteResultIsNotfinalized { - return nil + attachBlockHeaders, detachBlockHeaders, err := c.calcReorganizeChain(blockHeader, mainChainBlockHeader) + if err != nil { + return err } - c.consensusNodeMap = consensusNodeMap - c.effectiveStartHeight = bestBlockHeight / roundVoteBlockNums * roundVoteBlockNums - return nil -} - -func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uint64 { - // One round of product block time for all consensus nodes - roundBlockTime := uint64(BlockNumEachNode * numOfConsensusNode * 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) -} + for _, bh := range detachBlockHeaders { + blockHash := bh.Hash() + block, err := c.store.GetBlock(&blockHash) + if err != nil { + return err + } -func (c *consensusNodeManager) getConsensusNodesByVoteResult(blockHeight uint64) (map[string]*consensusNode, error) { - defer c.RUnlock() - c.RLock() - if blockHeight >= c.effectiveStartHeight+roundVoteBlockNums { - return nil, errors.New("the given block height is greater than current vote start height") + if err := consensusResult.DetachBlock(block); err != nil { + return err + } } - if blockHeight >= c.effectiveStartHeight { - return c.consensusNodeMap, nil + for _, bh := range attachBlockHeaders { + blockHash := bh.Hash() + block, err := c.store.GetBlock(&blockHash) + if err != nil { + return err + } + + if err := consensusResult.ApplyBlock(block); err != nil { + return err + } } + return nil +} - voteResult, err := c.store.GetVoteResult(blockHeight / roundVoteBlockNums) +// GetBlocker return blocker by specified timestamp +func (c *Chain) GetBlocker(prevBlockHash *bc.Hash, timeStamp uint64) (string, error) { + consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) if err != nil { - return nil, err + return "", err } - if !voteResult.Finalized { - return nil, errVoteResultIsNotfinalized + prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash) + if err != nil { + return "", err } - var nodes []*consensusNode - for pubkey, voteNum := range voteResult.NumOfVote { - nodes = append(nodes, &consensusNode{ - pubkey: pubkey, - voteNum: voteNum, - }) + startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.ActiveNetParams.BlockTimeInterval + order := getBlockerOrder(startTimestamp, timeStamp, uint64(len(consensusNodeMap))) + for xPub, consensusNode := range consensusNodeMap { + if consensusNode.Order == order { + return xPub, 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)) - result := make(map[string]*consensusNode) - for i := 0; i < numOfConsensusNode; i++ { - node := nodes[i] - node.order = uint64(i) - result[node.pubkey] = node + // impossible occur + return "", errors.New("can not find blocker by given timestamp") +} + +// GetConsensusResultByHash return vote result by block hash +func (c *Chain) GetConsensusResultByHash(blockHash *bc.Hash) (*state.ConsensusResult, error) { + blockHeader, err := c.store.GetBlockHeader(blockHash) + if err != nil { + return nil, err } - return result, nil + return c.getConsensusResult(state.CalcVoteSeq(blockHeader.Height), blockHeader) }