OSDN Git Service

fix vote result (#94)
authormuscle_boy <shenao.78@163.com>
Wed, 29 May 2019 09:26:48 +0000 (17:26 +0800)
committerPaladz <yzhu101@uottawa.ca>
Wed, 29 May 2019 09:26:48 +0000 (17:26 +0800)
* fix vote result

* bug fix

* bug fix

* opt code'

* opt code

* bug fix

* bug fix

proposal/blockproposer/blockproposer.go
protocol/bbft.go
protocol/consensus_node_manager.go
protocol/protocol.go
protocol/state/blockindex.go
protocol/state/vote_result.go

index e6ec2b9..0743b87 100644 (file)
@@ -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
index ac6b434..3a59e47 100644 (file)
@@ -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)
index be5e6bb..33cc83c 100644 (file)
@@ -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
+}
index 57547d7..9eac945 100644 (file)
@@ -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
index f1810b6..3bccd84 100644 (file)
@@ -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()
index 5719a5c..bf914b4 100644 (file)
@@ -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
 }