From ba520ccc18f0295b79d4ea2edb36864fcf3081a4 Mon Sep 17 00:00:00 2001 From: muscle_boy Date: Wed, 29 May 2019 17:26:48 +0800 Subject: [PATCH] fix vote result (#94) * fix vote result * bug fix * bug fix * opt code' * opt code * bug fix * bug fix --- proposal/blockproposer/blockproposer.go | 3 +- protocol/bbft.go | 129 ++------------ protocol/consensus_node_manager.go | 303 +++++++++++++++++++++++--------- protocol/protocol.go | 4 - protocol/state/blockindex.go | 18 ++ protocol/state/vote_result.go | 7 +- 6 files changed, 263 insertions(+), 201 deletions(-) diff --git a/proposal/blockproposer/blockproposer.go b/proposal/blockproposer/blockproposer.go index e6ec2b9a..0743b872 100644 --- a/proposal/blockproposer/blockproposer.go +++ b/proposal/blockproposer/blockproposer.go @@ -52,8 +52,9 @@ out: } bestBlockHeader := b.chain.BestBlockHeader() + bestBlockHash := bestBlockHeader.Hash() var pubKey []byte - timeStart, timeEnd, err := b.chain.GetBBFT().NextLeaderTimeRange(pubKey, bestBlockHeader.Timestamp, bestBlockHeader.Height) + timeStart, timeEnd, err := b.chain.GetBBFT().NextLeaderTimeRange(pubKey, &bestBlockHash) if err != nil { log.WithFields(log.Fields{"module": logModule, "error": err, "pubKey": hex.EncodeToString(pubKey)}).Debug("fail on get next leader time range") continue diff --git a/protocol/bbft.go b/protocol/bbft.go index ac6b4345..3a59e47f 100644 --- a/protocol/bbft.go +++ b/protocol/bbft.go @@ -11,7 +11,6 @@ import ( "github.com/vapor/crypto/ed25519/chainkd" "github.com/vapor/errors" "github.com/vapor/event" - "github.com/vapor/math/checked" "github.com/vapor/protocol/bc" "github.com/vapor/protocol/bc/types" "github.com/vapor/protocol/state" @@ -44,8 +43,8 @@ func newBbft(store Store, blockIndex *state.BlockIndex, orphanManage *OrphanMana } // IsConsensusPubkey determine whether a public key is a consensus node at a specified height -func (b *bbft) IsConsensusPubkey(height uint64, pubkey []byte) (bool, error) { - node, err := b.consensusNodeManager.getConsensusNode(height, hex.EncodeToString(pubkey)) +func (b *bbft) IsConsensusPubkey(blockHash *bc.Hash, pubkey []byte) (bool, error) { + node, err := b.consensusNodeManager.getConsensusNode(blockHash, hex.EncodeToString(pubkey)) if err != nil && err != errNotFoundConsensusNode { return false, err } @@ -62,122 +61,23 @@ func (b *bbft) isIrreversible(block *types.Block) bool { } // NextLeaderTime returns the start time of the specified public key as the next leader node -func (b *bbft) NextLeaderTimeRange(pubkey []byte, bestBlockTimestamp, bestBlockHeight uint64) (uint64, uint64, error) { - return b.consensusNodeManager.nextLeaderTimeRange(pubkey, bestBlockTimestamp, bestBlockHeight) +func (b *bbft) NextLeaderTimeRange(pubkey []byte, bestBlockHash *bc.Hash) (uint64, uint64, error) { + return b.consensusNodeManager.nextLeaderTimeRange(pubkey, bestBlockHash) } func (b *bbft) ApplyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) { - voteSeq := block.Height / roundVoteBlockNums - voteResult := voteResultMap[voteSeq] - - if voteResult == nil { - store := b.consensusNodeManager.store - voteResult, err = store.GetVoteResult(voteSeq) - if err != nil && err != ErrNotFoundVoteResult { - return err - } - } - - if voteResult == nil { - voteResult = &state.VoteResult{ - Seq: voteSeq, - NumOfVote: make(map[string]uint64), - LastBlockHeight: block.Height, - } - } - - voteResultMap[voteSeq] = voteResult - - if voteResult.LastBlockHeight+1 != block.Height { - return errors.New("bbft append block error, the block height is not equals last block height plus 1 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.LastBlockHeight++ - voteResult.Finalized = (block.Height+1)%roundVoteBlockNums == 0 - return nil + return b.consensusNodeManager.applyBlock(voteResultMap, block) } func (b *bbft) DetachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error { - voteSeq := block.Height / roundVoteBlockNums - voteResult := voteResultMap[voteSeq] - - if voteResult == nil { - store := b.consensusNodeManager.store - voteResult, err := store.GetVoteResult(voteSeq) - if err != nil { - return err - } - voteResultMap[voteSeq] = voteResult - } - - if voteResult.LastBlockHeight != block.Height { - return errors.New("bbft detach block error, the block height is not equals last block height 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.LastBlockHeight-- - voteResult.Finalized = false - return nil + return b.consensusNodeManager.detachBlock(voteResultMap, block) } // ProcessBlockSignature process the received block signature messages // return once a block become irreversible, whether it's height greater than best block height // if so, the chain module must update status func (b *bbft) ProcessBlockSignature(signature, pubkey []byte, blockHeight uint64, blockHash *bc.Hash) (bool, error) { - consensusNode, err := b.consensusNodeManager.getConsensusNode(blockHeight, hex.EncodeToString(pubkey)) + consensusNode, err := b.consensusNodeManager.getConsensusNode(blockHash, hex.EncodeToString(pubkey)) if err != nil { return false, err } @@ -235,7 +135,8 @@ func (b *bbft) ValidateBlock(block *types.Block) error { // if the block has not the signature of blocker, it will return error func (b *bbft) validateSign(block *types.Block) (uint64, error) { var correctSignNum uint64 - consensusNodeMap, err := b.consensusNodeManager.getConsensusNodesByVoteResult(block.Height) + blockHash := block.Hash() + consensusNodeMap, err := b.consensusNodeManager.getConsensusNodesByVoteResult(&blockHash) if err != nil { return 0, err } @@ -267,7 +168,7 @@ func (b *bbft) validateSign(block *types.Block) (uint64, error) { block.Witness[node.order] = nil } else { correctSignNum++ - isBlocker, err := b.consensusNodeManager.isBlocker(block.Height, block.Timestamp, pubkey) + isBlocker, err := b.consensusNodeManager.isBlocker(&blockHash, pubkey) if err != nil { return 0, err } @@ -302,6 +203,8 @@ func (b *bbft) checkDoubleSign(nodeOrder, blockHeight uint64, blockHash bc.Hash) if err := b.updateBlockSignature(block, nodeOrder, nil); err != nil { return false, err } + + return true, nil } } return false, nil @@ -311,7 +214,8 @@ func (b *bbft) checkDoubleSign(nodeOrder, blockHeight uint64, blockHash bc.Hash) func (b *bbft) SignBlock(block *types.Block) ([]byte, error) { var xprv chainkd.XPrv xpub := [64]byte(xprv.XPub()) - node, err := b.consensusNodeManager.getConsensusNode(block.Height, hex.EncodeToString(xpub[:])) + blockHash := block.Hash() + node, err := b.consensusNodeManager.getConsensusNode(&blockHash, hex.EncodeToString(xpub[:])) if err != nil && err != errNotFoundConsensusNode { return nil, err } @@ -325,11 +229,6 @@ func (b *bbft) SignBlock(block *types.Block) ([]byte, error) { return signature, nil } -// UpdateConsensusNodes used to update consensus node after each round of voting -func (b *bbft) UpdateConsensusNodes(blockHeight uint64) error { - return b.consensusNodeManager.updateConsensusNodes(blockHeight) -} - func (b *bbft) updateBlockSignature(block *types.Block, nodeOrder uint64, signature []byte) error { blockHash := block.Hash() blockNode := b.consensusNodeManager.blockIndex.GetNode(&blockHash) diff --git a/protocol/consensus_node_manager.go b/protocol/consensus_node_manager.go index be5e6bb9..33cc83c9 100644 --- a/protocol/consensus_node_manager.go +++ b/protocol/consensus_node_manager.go @@ -3,10 +3,12 @@ package protocol import ( "encoding/hex" "sort" - "sync" "time" "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" ) @@ -16,14 +18,14 @@ const ( // BlockTimeInterval indicate product one block per 500 milliseconds BlockTimeInterval = 500 - BlockNumEachNode = 3 + // 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") - errVoteResultIsNotfinalized = errors.New("vote result is not finalized") - errPublicKeyIsNotConsensusNode = errors.New("public key is not consensus node") + 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") ) type consensusNode struct { @@ -39,79 +41,76 @@ func (c consensusNodeSlice) Less(i, j int) bool { return c[i].voteNum > c[j].vot 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 + store Store + blockIndex *state.BlockIndex } func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consensusNodeManager { return &consensusNodeManager{ - consensusNodeMap: make(map[string]*consensusNode), - effectiveStartHeight: 1, - store: store, - blockIndex: blockIndex, + store: store, + blockIndex: blockIndex, } } -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 *consensusNodeManager) getConsensusNode(blockHash *bc.Hash, pubkey string) (*consensusNode, error) { + consensusNodeMap, err := c.getConsensusNodesByVoteResult(blockHash) + 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 +func (c *consensusNodeManager) isBlocker(blockHash *bc.Hash, pubkey string) (bool, error) { + blockNode := c.blockIndex.GetNode(blockHash) + if blockNode == nil { + return false, errNotFoundBlockNode + } - consensusNodeMap, err := c.getConsensusNodesByVoteResult(height) - if err != nil { + consensusNode, err := c.getConsensusNode(blockHash, pubkey) + if err != nil && err != errNotFoundConsensusNode { return false, err } - blockerNode, exist := consensusNodeMap[pubkey] - if !exist { + if consensusNode == nil { return false, nil } - begin := getLastBlockTimeInTimeRange(startTimestamp, blockTimestamp, blockerNode.order) + prevVoteRoundLastBlock, err := c.getPrevRoundVoteLastBlock(blockNode) + if err != nil { + return false, err + } + + startTimestamp := prevVoteRoundLastBlock.Timestamp + BlockTimeInterval + + begin := getLastBlockTimeInTimeRange(startTimestamp, blockNode.Timestamp, consensusNode.order) end := begin + BlockNumEachNode*BlockTimeInterval - return blockTimestamp >= begin && blockTimestamp < end, nil + return blockNode.Timestamp >= begin && blockNode.Timestamp < end, nil } -func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, bestBlockTimestamp, bestBlockHeight uint64) (uint64, uint64, error) { - defer c.RUnlock() - c.RLock() +func (c *consensusNodeManager) nextLeaderTimeRange(pubkey []byte, bestBlockHash *bc.Hash) (uint64, uint64, error) { + bestBlockNode := c.blockIndex.GetNode(bestBlockHash) + if bestBlockNode == nil { + return 0, 0, errNotFoundBlockNode + } - startHeight := c.effectiveStartHeight - prevRoundLastBlock := c.blockIndex.NodeByHeight(startHeight - 1) - startTime := prevRoundLastBlock.Timestamp + BlockTimeInterval - endTime := bestBlockTimestamp + (roundVoteBlockNums-bestBlockHeight%roundVoteBlockNums)*BlockTimeInterval + consensusNode, err := c.getConsensusNode(bestBlockHash, hex.EncodeToString(pubkey)) + if err != nil { + return 0, 0, err + } - consensusNode, exist := c.consensusNodeMap[hex.EncodeToString(pubkey)] - if !exist { - return 0, 0, errPublicKeyIsNotConsensusNode + 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 @@ -134,25 +133,6 @@ func nextLeaderTimeHelper(startTime, endTime, now, nodeOrder uint64) (uint64, er return nextLeaderTimestamp, 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 { - return err - } - - if err == errVoteResultIsNotfinalized { - return nil - } - - 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) @@ -162,24 +142,39 @@ func getLastBlockTimeInTimeRange(startTimestamp, endTimestamp, order uint64) uin return lastRoundStartTime + order*(BlockNumEachNode*BlockTimeInterval) } -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") +func (c *consensusNodeManager) getPrevRoundVoteLastBlock(blockNode *state.BlockNode) (*state.BlockNode, error) { + prevVoteRoundLastBlockHeight := blockNode.Height/roundVoteBlockNums*roundVoteBlockNums - 1 + lastBlockNode := c.blockIndex.NodeByHeightInSameChain(&blockNode.Hash, prevVoteRoundLastBlockHeight) + if blockNode == nil { + return nil, errNotFoundBlockNode + } + return lastBlockNode, nil +} + +func (c *consensusNodeManager) getConsensusNodesByVoteResult(blockHash *bc.Hash) (map[string]*consensusNode, error) { + blockNode := c.blockIndex.GetNode(blockHash) + if blockNode == nil { + return nil, errNotFoundBlockNode } - if blockHeight >= c.effectiveStartHeight { - return c.consensusNodeMap, nil + seq := blockNode.Height / roundVoteBlockNums + voteResult, err := c.store.GetVoteResult(seq) + if err != nil { + // fail to find vote result, try to construct + voteResult = &state.VoteResult{ + Seq: seq, + NumOfVote: make(map[string]uint64), + Finalized: false, + } } - voteResult, err := c.store.GetVoteResult(blockHeight / roundVoteBlockNums) + lastBlockNode, err := c.getPrevRoundVoteLastBlock(blockNode) if err != nil { return nil, err } - if !voteResult.Finalized { - return nil, errVoteResultIsNotfinalized + if err := c.reorganizeVoteResult(voteResult, lastBlockNode); err != nil { + return nil, err } var nodes []*consensusNode @@ -202,3 +197,153 @@ func (c *consensusNodeManager) getConsensusNodesByVoteResult(blockHeight uint64) } 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 + } + } + + var attachBlocks []*types.Block + var detachBlocks []*types.Block + + for forkChainNode.Hash != mainChainNode.Hash && forkChainNode.Height >= (voteResult.Seq-1)*roundVoteBlockNums { + attachBlock, err := c.store.GetBlock(&forkChainNode.Hash) + if err != nil { + return err + } + + attachBlocks = append([]*types.Block{attachBlock}, attachBlocks...) + forkChainNode = forkChainNode.Parent + + if mainChainNode != nil && forkChainNode.Height == mainChainNode.Height { + detachBlock, err := c.store.GetBlock(&mainChainNode.Hash) + if err != nil { + return err + } + + detachBlocks = append(detachBlocks, detachBlock) + mainChainNode = mainChainNode.Parent + } + } + + for _, block := range detachBlocks { + if err := c.detachBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, block); err != nil { + return err + } + } + + for _, block := range attachBlocks { + if err := c.applyBlock(map[uint64]*state.VoteResult{voteResult.Seq: voteResult}, 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(), + } + } + + 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 +} diff --git a/protocol/protocol.go b/protocol/protocol.go index 57547d7b..9eac9451 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -118,10 +118,6 @@ func (c *Chain) setState(node *state.BlockNode, irreversibleNode *state.BlockNod c.cond.L.Lock() defer c.cond.L.Unlock() - if err := c.bbft.UpdateConsensusNodes(node.Height); err != nil { - return err - } - c.index.SetMainChain(node) c.bestNode = node c.bestIrreversibleNode = irreversibleNode diff --git a/protocol/state/blockindex.go b/protocol/state/blockindex.go index f1810b6f..3bccd841 100644 --- a/protocol/state/blockindex.go +++ b/protocol/state/blockindex.go @@ -118,6 +118,24 @@ func (bi *BlockIndex) GetNode(hash *bc.Hash) *BlockNode { return bi.index[*hash] } +// NodeByHeightInSameChain return the node of specified height +// And the node satisfies the same chain as the node that specifies the hash +// Height of the block of the specified node hash must greater than height parameter +func (bi *BlockIndex) NodeByHeightInSameChain(nodeHash *bc.Hash, height uint64) *BlockNode { + bi.RLock() + defer bi.RUnlock() + + blockNode := bi.index[*nodeHash] + prevBlockNode := blockNode + for prevBlockNode != nil && prevBlockNode.Height != height { + if prevBlockNode.Height < height { + return nil + } + prevBlockNode = bi.index[prevBlockNode.Parent.Hash] + } + return prevBlockNode +} + func (bi *BlockIndex) BestNode() *BlockNode { bi.RLock() defer bi.RUnlock() diff --git a/protocol/state/vote_result.go b/protocol/state/vote_result.go index 5719a5c4..bf914b49 100644 --- a/protocol/state/vote_result.go +++ b/protocol/state/vote_result.go @@ -1,13 +1,16 @@ package state +import ( + "github.com/vapor/protocol/bc" +) + // VoteResult represents a snapshot of each round of DPOS voting // Seq indicates the sequence of current votes, which start from zero // NumOfVote indicates the number of votes each consensus node receives, the key of map represent public key -// LastBlockHeight indicates the last voted block height // Finalized indicates whether this vote is finalized type VoteResult struct { Seq uint64 NumOfVote map[string]uint64 - LastBlockHeight uint64 + LastBlockHash bc.Hash Finalized bool } -- 2.11.0