X-Git-Url: http://git.osdn.net/view?a=blobdiff_plain;f=protocol%2Fconsensus_node_manager.go;h=d55e7ccf044c25c3c08f7de95f1adac80ebdafa9;hb=8f3497631ce7cf7ae1e03200a9be9adecf4a9e57;hp=18fb23ba90b9736f35a0c8a20c523646f8585628;hpb=0f705655e257982edfc95526f58bf074462fd7ba;p=bytom%2Fvapor.git diff --git a/protocol/consensus_node_manager.go b/protocol/consensus_node_manager.go index 18fb23ba..d55e7ccf 100644 --- a/protocol/consensus_node_manager.go +++ b/protocol/consensus_node_manager.go @@ -1,60 +1,35 @@ 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 - MinVoteNum = 5000000 - - // 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") ) -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 +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{ - 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(prevBlockHash *bc.Hash, pubkey string) (*consensusNode, error) { - consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash) +func (c *Chain) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) { + consensusNodeMap, err := c.getConsensusNodes(prevBlockHash) if err != nil { return nil, err } @@ -66,342 +41,129 @@ 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 { - return false, err - } - - if consensusNode == nil { - return false, nil - } - - prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(&block.PreviousBlockHash) +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 } - startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval - - begin := getLastBlockTimeInTimeRange(startTimestamp, block.Timestamp, consensusNode.order) - end := begin + BlockNumEachNode*BlockTimeInterval - return block.Timestamp >= begin && block.Timestamp < end, nil -} - -func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, prevBlockHash *bc.Hash) (uint64, uint64, error) { - consensusNode, err := c.getConsensusNode(prevBlockHash, hex.EncodeToString(pubkey)) - if err != nil { - return 0, 0, err + bestBlockHeader := c.bestBlockHeader + preSeq := state.CalcVoteSeq(prevBlockHeader.Height+1) - 1 + if bestSeq := state.CalcVoteSeq(bestBlockHeader.Height); preSeq > bestSeq { + preSeq = bestSeq } - prevRoundLastBlock, err := c.getPrevRoundVoteLastBlock(prevBlockHash) + lastBlockHeader, err := c.getPrevRoundLastBlock(prevBlockHash) if err != nil { - return 0, 0, err + return nil, err } - startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval - - nextLeaderTime, err := nextLeaderTimeHelper(startTime, uint64(time.Now().UnixNano()/1e6), consensusNode.order) + consensusResult, err := c.getConsensusResult(preSeq, lastBlockHeader) 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 { - // 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) -} - -func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash) (*state.BlockNode, error) { - prevBlockNode := c.blockIndex.GetNode(prevBlockHash) - if prevBlockNode == nil { - return nil, errNotFoundBlockNode - } - - blockHeight := prevBlockNode.Height + 1 - - prevVoteRoundLastBlockHeight := blockHeight/RoundVoteBlockNums*RoundVoteBlockNums - 1 - // first round - if blockHeight/RoundVoteBlockNums == 0 { - prevVoteRoundLastBlockHeight = 0 + return nil, err } - lastBlockNode := prevBlockNode.GetParent(prevVoteRoundLastBlockHeight) - if lastBlockNode == nil { - return nil, errNotFoundBlockNode - } - return lastBlockNode, nil + return consensusResult.ConsensusNodes() } -func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*consensusNode, error) { - prevBlockNode := c.blockIndex.GetNode(prevBlockHash) - if prevBlockNode == nil { - return nil, errNotFoundBlockNode - } - - seq := (prevBlockNode.Height + 1) / RoundVoteBlockNums - voteResult, err := c.store.GetVoteResult(seq) - if err != nil { - // TODO find previous round vote - voteResult = &state.VoteResult{ - Seq: seq, - NumOfVote: make(map[string]uint64), - Finalized: false, - } - } - - lastBlockNode, err := c.getPrevRoundVoteLastBlock(prevBlockHash) +// 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 } - if err := c.reorganizeVoteResult(voteResult, lastBlockNode); err != nil { + if err := c.reorganizeConsensusResult(consensusResult, blockHeader); err != nil { return nil, err } - if len(voteResult.NumOfVote) == 0 { - return initConsensusNodes(), nil - } + return consensusResult, nil +} - var nodes []*consensusNode - for pubkey, voteNum := range voteResult.NumOfVote { - if voteNum >= MinVoteNum { - nodes = append(nodes, &consensusNode{ - pubkey: pubkey, - voteNum: voteNum, - }) - } +func (c *Chain) getPrevRoundLastBlock(prevBlockHash *bc.Hash) (*types.BlockHeader, error) { + blockHeader, err := c.store.GetBlockHeader(prevBlockHash) + if err != nil { + return nil, errNotFoundBlockNode } - // 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 < len(nodes) && i < NumOfConsensusNode; i++ { - node := nodes[i] - node.order = uint64(i) - result[node.pubkey] = node + for blockHeader.Height%consensus.ActiveNetParams.RoundVoteBlockNums != 0 { + blockHeader, err = c.store.GetBlockHeader(&blockHeader.PreviousBlockHash) + if err != nil { + return nil, err + } } - return result, nil + return blockHeader, nil } -func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error { - genesisBlockHash := config.GenesisBlock().Hash() - mainChainNode := c.blockIndex.GetNode(&genesisBlockHash) - - emptyHash := bc.Hash{} - if voteResult.LastBlockHash != emptyHash { - mainChainNode = c.blockIndex.GetNode(&voteResult.LastBlockHash) - if mainChainNode == nil { - return errNotFoundBlockNode - } +func (c *Chain) reorganizeConsensusResult(consensusResult *state.ConsensusResult, blockHeader *types.BlockHeader) error { + mainChainBlockHeader, err := c.store.GetBlockHeader(&consensusResult.BlockHash) + if err != nil { + return err } - var attachNodes []*state.BlockNode - var detachNodes []*state.BlockNode - - 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 + attachBlockHeaders, detachBlockHeaders, err := c.calcReorganizeChain(blockHeader, mainChainBlockHeader) + if err != nil { + return err } - for _, node := range detachNodes { - block, err := c.store.GetBlock(&node.Hash) + for _, bh := range detachBlockHeaders { + blockHash := bh.Hash() + block, err := c.store.GetBlock(&blockHash) if err != nil { return err } - if err := c.detachBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil { + if err := consensusResult.DetachBlock(block); err != nil { return err } } - for _, node := range attachNodes { - block, err := c.store.GetBlock(&node.Hash) + for _, bh := range attachBlockHeaders { + blockHash := bh.Hash() + block, err := c.store.GetBlock(&blockHash) if err != nil { return err } - if err := c.applyBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil { + if err := consensusResult.ApplyBlock(block); err != nil { return err } } return nil } -func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) { - voteResult, err := c.getVoteResult(voteResultMap, block.Height) +// 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 err - } - - 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") - } - - 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) 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/RoundVoteBlockNums + 1 - voteResult := voteResultMap[seq] - if blockHeight == 0 { - voteResult = &state.VoteResult{ - Seq: seq, - NumOfVote: make(map[string]uint64), - Finalized: false, - } + return "", err } - if voteResult == nil { - prevVoteResult := voteResultMap[seq-1] - if prevVoteResult != nil { - voteResult = &state.VoteResult{ - Seq: seq, - NumOfVote: prevVoteResult.NumOfVote, - Finalized: false, - } - } - } - - if voteResult == nil { - voteResult, err = c.store.GetVoteResult(seq) - if err != nil && err != ErrNotFoundVoteResult { - return nil, err - } - } - - if voteResult == nil { - voteResult, err = c.store.GetVoteResult(seq - 1) - if err != nil && err != ErrNotFoundVoteResult { - return nil, err - } - - if voteResult != nil { - // previous round voting must have finalized - if !voteResult.Finalized { - return nil, errors.New("previous round voting has not finalized") - } - - voteResult.Seq = seq - voteResult.Finalized = false - voteResult.LastBlockHash = bc.Hash{} - } - } - - 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 - 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") + prevVoteRoundLastBlock, err := c.getPrevRoundLastBlock(prevBlockHash) + if err != nil { + return "", 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.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 - } + 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 } } - voteResult.Finalized = false - return nil + // impossible occur + return "", errors.New("can not find blocker by given timestamp") } -func initConsensusNodes() 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)} +// 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 voteResult + return c.getConsensusResult(state.CalcVoteSeq(blockHeader.Height), blockHeader) }