OSDN Git Service

tmp save (#123)
authorPaladz <yzhu101@uottawa.ca>
Tue, 4 Jun 2019 06:06:34 +0000 (14:06 +0800)
committerGitHub <noreply@github.com>
Tue, 4 Jun 2019 06:06:34 +0000 (14:06 +0800)
* tmp save

* keep edit

* keep edit

* keep edit

* edit for run

* edit the config

consensus/general.go
netsync/consensusmgr/block_fetcher_test.go
netsync/consensusmgr/handle.go
protocol/bbft.go
protocol/block.go
protocol/consensus_node_manager.go
protocol/protocol.go
protocol/state/vote_result.go

index 910bce4..47650f6 100644 (file)
@@ -23,10 +23,10 @@ const (
        VotePendingBlockNumber = uint64(10000)
 
        //DPOS parameter
-       NumOfConsensusNode = 7
-       BlockNumEachNode   = 3
-       RoundVoteBlockNums = NumOfConsensusNode * BlockNumEachNode * 100
-       MinVoteNum         = 5000000
+       NumOfConsensusNode = 10
+       BlockNumEachNode   = 12
+       RoundVoteBlockNums = NumOfConsensusNode * BlockNumEachNode * 10
+       MinVoteNum         = 10000000
 
        // BlockTimeInterval indicate product one block per 500 milliseconds
        BlockTimeInterval = 500
index b02b802..f7d6936 100644 (file)
@@ -45,7 +45,7 @@ func (c *chain) ProcessBlock(block *types.Block) (bool, error) {
        return false, nil
 }
 
-func (c *chain) ProcessBlockSignature(signature []byte, pubkey [64]byte, blockHeight uint64, blockHash *bc.Hash) error {
+func (c *chain) ProcessBlockSignature(signature []byte, pubkey [64]byte, blockHash *bc.Hash) error {
        return nil
 }
 
index 5443e76..67fec0c 100644 (file)
@@ -23,7 +23,7 @@ type Chain interface {
        BestBlockHeight() uint64
        GetHeaderByHash(*bc.Hash) (*types.BlockHeader, error)
        ProcessBlock(*types.Block) (bool, error)
-       ProcessBlockSignature(signature []byte, pubkey [64]byte, blockHeight uint64, blockHash *bc.Hash) error
+       ProcessBlockSignature(signature []byte, pubkey [64]byte, blockHash *bc.Hash) error
 }
 
 type blockMsg struct {
@@ -95,7 +95,7 @@ func (m *Manager) handleBlockProposeMsg(peerID string, msg *BlockProposeMsg) {
 
 func (m *Manager) handleBlockSignatureMsg(peerID string, msg *BlockSignatureMsg) {
        blockHash := bc.NewHash(msg.BlockHash)
-       if err := m.chain.ProcessBlockSignature(msg.Signature, msg.PubKey, msg.Height, &blockHash); err != nil {
+       if err := m.chain.ProcessBlockSignature(msg.Signature, msg.PubKey, &blockHash); err != nil {
                m.peers.AddBanScore(peerID, 20, 0, err.Error())
                return
        }
index 679af2b..5667bc6 100644 (file)
@@ -7,8 +7,6 @@ import (
        log "github.com/sirupsen/logrus"
 
        "github.com/vapor/config"
-       "github.com/vapor/crypto/ed25519"
-       "github.com/vapor/crypto/ed25519/chainkd"
        "github.com/vapor/errors"
        "github.com/vapor/protocol/bc"
        "github.com/vapor/protocol/bc/types"
@@ -25,18 +23,24 @@ var (
        errInvalidSignature        = errors.New("the signature of block is invalid")
 )
 
-func (c *Chain) isIrreversible(block *types.Block) bool {
-       consensusNodes, err := c.consensusNodeManager.getConsensusNodesByVoteResult(&block.PreviousBlockHash)
+func signCacheKey(blockHash, pubkey string) string {
+       return fmt.Sprintf("%s:%s", blockHash, pubkey)
+}
+
+func (c *Chain) isIrreversible(blockNode *state.BlockNode) bool {
+       consensusNodes, err := c.consensusNodeManager.getConsensusNodesByVoteResult(&blockNode.Parent.Hash)
        if err != nil {
                return false
        }
 
-       signNum, err := c.validateSign(block)
-       if err != nil {
-               return false
+       signCount := 0
+       for i := 0; i < len(consensusNodes); i++ {
+               if ok, _ := blockNode.BlockWitness.Test(uint32(i)); ok {
+                       signCount++
+               }
        }
 
-       return signNum > (uint64(len(consensusNodes)) * 2 / 3)
+       return signCount > len(consensusNodes)*2/3
 }
 
 // NextLeaderTime returns the start time of the specified public key as the next leader node
@@ -44,55 +48,41 @@ func (c *Chain) IsBlocker(prevBlockHash *bc.Hash, pubkey string, timeStamp uint6
        return c.consensusNodeManager.isBlocker(prevBlockHash, pubkey, timeStamp)
 }
 
-func (c *Chain) ApplyBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) (err error) {
-       return c.consensusNodeManager.applyBlock(voteResultMap, block)
-}
-
-func (c *Chain) DetachBlock(voteResultMap map[uint64]*state.VoteResult, block *types.Block) error {
-       return c.consensusNodeManager.detachBlock(voteResultMap, block)
-}
-
 // ProcessBlockSignature process the received block signature messages
 // return whether a block become irreversible, if so, the chain module must update status
-func (c *Chain) ProcessBlockSignature(signature []byte, xPub [64]byte, blockHeight uint64, blockHash *bc.Hash) error {
-       block, err := c.consensusNodeManager.store.GetBlock(blockHash)
-       if err != nil {
-               // block is not exist, save the signature
-               key := fmt.Sprintf("%s:%s", blockHash.String(), hex.EncodeToString(xPub[:]))
-               c.signatureCache.Add(key, signature)
-               return err
+func (c *Chain) ProcessBlockSignature(signature []byte, xPub [64]byte, blockHash *bc.Hash) error {
+       xpubStr := hex.EncodeToString(xPub[:])
+       blockNode := c.index.GetNode(blockHash)
+       // save the signature if the block is not exist
+       if blockNode == nil {
+               cacheKey := signCacheKey(blockHash.String(), xpubStr)
+               c.signatureCache.Add(cacheKey, signature)
+               return nil
        }
 
-       consensusNode, err := c.consensusNodeManager.getConsensusNode(&block.PreviousBlockHash, hex.EncodeToString(xPub[:]))
+       consensusNode, err := c.consensusNodeManager.getConsensusNode(&blockNode.Parent.Hash, xpubStr)
        if err != nil {
                return err
        }
 
-       if chainkd.XPub(xPub).Verify(blockHash.Bytes(), signature) {
+       if consensusNode.XPub.Verify(blockHash.Bytes(), signature) {
                return errInvalidSignature
        }
 
-       isDoubleSign, err := c.checkDoubleSign(consensusNode.order, blockHeight, *blockHash)
+       isDoubleSign, err := c.checkDoubleSign(consensusNode.Order, blockNode.Height, *blockHash)
        if err != nil {
                return err
        }
 
        if isDoubleSign {
-               log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "xPub": hex.EncodeToString(xPub[:])}).Warn("the consensus node double sign the same height of different block")
                return errDoubleSignBlock
        }
 
-       orphanBlock, ok := c.orphanManage.Get(blockHash)
-       if ok {
-               orphanBlock.Witness[consensusNode.order] = signature
-               return nil
-       }
-
-       if err := c.updateBlockSignature(block, consensusNode.order, signature); err != nil {
+       if err := c.updateBlockSignature(&blockNode.Hash, consensusNode.Order, signature); err != nil {
                return err
        }
 
-       if c.isIrreversible(block) && blockHeight > c.bestIrreversibleNode.Height {
+       if c.isIrreversible(blockNode) && blockNode.Height > c.bestIrreversibleNode.Height {
                bestIrreversibleNode := c.index.GetNode(blockHash)
                if err := c.store.SaveChainNodeStatus(c.bestNode, bestIrreversibleNode); err != nil {
                        return err
@@ -103,78 +93,65 @@ func (c *Chain) ProcessBlockSignature(signature []byte, xPub [64]byte, blockHeig
        return nil
 }
 
-// ValidateBlock verify whether the block is valid
-func (c *Chain) ValidateBlock(block *types.Block) error {
-       signNum, err := c.validateSign(block)
-       if err != nil {
-               return err
-       }
-
-       if signNum == 0 {
-               return errors.New("no valid signature")
-       }
-       return nil
-}
-
 // validateSign verify the signatures of block, and return the number of correct signature
 // if some signature is invalid, they will be reset to nil
 // if the block has not the signature of blocker, it will return error
 func (c *Chain) validateSign(block *types.Block) (uint64, error) {
-       var correctSignNum uint64
        consensusNodeMap, err := c.consensusNodeManager.getConsensusNodesByVoteResult(&block.PreviousBlockHash)
        if err != nil {
                return 0, err
        }
 
        hasBlockerSign := false
+       signCount := uint64(0)
+       blockHash := block.Hash()
        for pubKey, node := range consensusNodeMap {
-               if len(block.Witness) <= int(node.order) {
+               if len(block.Witness) <= int(node.Order) {
                        continue
                }
 
-               blockHash := block.Hash()
-               if block.Witness[node.order] == nil {
-                       key := fmt.Sprintf("%s:%s", blockHash.String(), pubKey)
-                       signature, ok := c.signatureCache.Get(key)
-                       if ok {
-                               block.Witness[node.order] = signature.([]byte)
+               if block.Witness[node.Order] == nil {
+                       cachekey := signCacheKey(blockHash.String(), pubKey)
+                       if signature, ok := c.signatureCache.Get(cachekey); ok {
+                               block.Witness[node.Order] = signature.([]byte)
+                       } else {
+                               continue
                        }
                }
 
-               pubKeyBytes, err := hex.DecodeString(pubKey)
+               if ok := node.XPub.Verify(blockHash.Bytes(), block.Witness[node.Order]); !ok {
+                       block.Witness[node.Order] = nil
+                       continue
+               }
+
+               isDoubleSign, err := c.checkDoubleSign(node.Order, block.Height, block.Hash())
                if err != nil {
                        return 0, err
                }
 
-               if ed25519.Verify(ed25519.PublicKey(pubKeyBytes[:32]), blockHash.Bytes(), block.Witness[node.order]) {
-                       isDoubleSign, err := c.checkDoubleSign(node.order, block.Height, block.Hash())
-                       if err != nil {
-                               return 0, err
-                       }
+               if isDoubleSign {
+                       // Consensus node is signed twice with the same block height, discard the signature
+                       log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubKey": pubKey}).Warn("the consensus node double sign the same height of different block")
+                       block.Witness[node.Order] = nil
+                       continue
+               }
 
-                       if isDoubleSign {
-                               log.WithFields(log.Fields{"module": logModule, "blockHash": blockHash.String(), "pubKey": pubKey}).Warn("the consensus node double sign the same height of different block")
-                               // Consensus node is signed twice with the same block height, discard the signature
-                               block.Witness[node.order] = nil
-                       } else {
-                               correctSignNum++
-                               isBlocker, err := c.consensusNodeManager.isBlocker(&block.PreviousBlockHash, pubKey, block.Timestamp)
-                               if err != nil {
-                                       return 0, err
-                               }
-                               if isBlocker {
-                                       hasBlockerSign = true
-                               }
-                       }
-               } else {
-                       // discard the invalid signature
-                       block.Witness[node.order] = nil
+               signCount++
+               isBlocker, err := c.consensusNodeManager.isBlocker(&block.PreviousBlockHash, pubKey, block.Timestamp)
+               if err != nil {
+                       return 0, err
+               }
+
+               if isBlocker {
+                       hasBlockerSign = true
                }
+
        }
+
        if !hasBlockerSign {
                return 0, errors.New("the block has no signature of the blocker")
        }
-       return correctSignNum, nil
+       return signCount, nil
 }
 
 func (c *Chain) checkDoubleSign(nodeOrder, blockHeight uint64, blockHash bc.Hash) (bool, error) {
@@ -184,13 +161,7 @@ func (c *Chain) checkDoubleSign(nodeOrder, blockHeight uint64, blockHash bc.Hash
                        continue
                }
                if ok, err := blockNode.BlockWitness.Test(uint32(nodeOrder)); err != nil && ok {
-                       block, err := c.consensusNodeManager.store.GetBlock(&blockHash)
-                       if err != nil {
-                               return false, err
-                       }
-
-                       // reset nil to discard signature
-                       if err := c.updateBlockSignature(block, nodeOrder, nil); err != nil {
+                       if err := c.updateBlockSignature(&blockHash, nodeOrder, nil); err != nil {
                                return false, err
                        }
 
@@ -216,23 +187,21 @@ func (c *Chain) SignBlock(block *types.Block) ([]byte, error) {
        blockNodes := c.consensusNodeManager.blockIndex.NodesByHeight(block.Height)
        for _, blockNode := range blockNodes {
                // Has already signed the same height block
-               if ok, err := blockNode.BlockWitness.Test(uint32(node.order)); err != nil && ok {
+               if ok, err := blockNode.BlockWitness.Test(uint32(node.Order)); err != nil && ok {
                        return nil, nil
                }
        }
 
-       signature := block.Witness[node.order]
+       signature := block.Witness[node.Order]
        if len(signature) == 0 {
                signature = xprv.Sign(block.Hash().Bytes())
-               block.Witness[node.order] = signature
+               block.Witness[node.Order] = signature
        }
        return signature, nil
 }
 
-func (c *Chain) updateBlockSignature(block *types.Block, nodeOrder uint64, signature []byte) error {
-       blockHash := block.Hash()
-       blockNode := c.consensusNodeManager.blockIndex.GetNode(&blockHash)
-
+func (c *Chain) updateBlockSignature(blockHash *bc.Hash, nodeOrder uint64, signature []byte) error {
+       blockNode := c.consensusNodeManager.blockIndex.GetNode(blockHash)
        if len(signature) != 0 {
                if err := blockNode.BlockWitness.Set(uint32(nodeOrder)); err != nil {
                        return err
@@ -243,8 +212,13 @@ func (c *Chain) updateBlockSignature(block *types.Block, nodeOrder uint64, signa
                }
        }
 
+       block, err := c.store.GetBlock(blockHash)
+       if err != nil {
+               return err
+       }
+
        block.Witness[nodeOrder] = signature
-       txStatus, err := c.consensusNodeManager.store.GetTransactionStatus(&blockHash)
+       txStatus, err := c.consensusNodeManager.store.GetTransactionStatus(blockHash)
        if err != nil {
                return err
        }
index 29dc3c7..e3f0f83 100644 (file)
@@ -91,12 +91,12 @@ func (c *Chain) connectBlock(block *types.Block) (err error) {
        }
 
        voteResultMap := make(map[uint64]*state.VoteResult)
-       if err := c.ApplyBlock(voteResultMap, block); err != nil {
+       if err := c.consensusNodeManager.applyBlock(voteResultMap, block); err != nil {
                return err
        }
 
        node := c.index.GetNode(&bcBlock.ID)
-       if c.isIrreversible(block) && block.Height > irreversibleNode.Height {
+       if c.isIrreversible(node) && block.Height > irreversibleNode.Height {
                irreversibleNode = node
        }
 
@@ -140,7 +140,7 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error {
                        return err
                }
 
-               if err := c.DetachBlock(voteResultMap, b); err != nil {
+               if err := c.consensusNodeManager.detachBlock(voteResultMap, b); err != nil {
                        return err
                }
 
@@ -167,11 +167,11 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error {
                        return err
                }
 
-               if err := c.ApplyBlock(voteResultMap, b); err != nil {
+               if err := c.consensusNodeManager.applyBlock(voteResultMap, b); err != nil {
                        return err
                }
 
-               if c.isIrreversible(b) && b.Height > irreversibleNode.Height {
+               if c.isIrreversible(attachNode) && b.Height > irreversibleNode.Height {
                        irreversibleNode = attachNode
                }
 
@@ -183,7 +183,7 @@ func (c *Chain) reorganizeChain(node *state.BlockNode) error {
 
 // SaveBlock will validate and save block into storage
 func (c *Chain) saveBlock(block *types.Block) error {
-       if err := c.ValidateBlock(block); err != nil {
+       if _, err := c.validateSign(block); err != nil {
                return errors.Sub(ErrBadBlock, err)
        }
 
@@ -282,20 +282,11 @@ func (c *Chain) processBlock(block *types.Block) (bool, error) {
                return c.orphanManage.BlockExist(&blockHash), nil
        }
 
-       parent := c.index.GetNode(&block.PreviousBlockHash)
-       if parent == nil {
+       if parent := c.index.GetNode(&block.PreviousBlockHash); parent == nil {
                c.orphanManage.Add(block)
                return true, nil
        }
 
-       forkPointBlock := parent
-       for !c.index.InMainchain(forkPointBlock.Hash) {
-               forkPointBlock = forkPointBlock.Parent
-       }
-       if forkPointBlock.Height < c.bestIrreversibleNode.Height {
-               return false, errors.New("the block impossible to be block of main chain")
-       }
-
        if err := c.saveBlock(block); err != nil {
                return false, err
        }
index 282923c..fd7de37 100644 (file)
@@ -1,13 +1,9 @@
 package protocol
 
 import (
-       "encoding/hex"
-       "sort"
-
        "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"
@@ -18,18 +14,6 @@ var (
        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
@@ -42,7 +26,7 @@ func newConsensusNodeManager(store Store, blockIndex *state.BlockIndex) *consens
        }
 }
 
-func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*consensusNode, error) {
+func (c *consensusNodeManager) getConsensusNode(prevBlockHash *bc.Hash, pubkey string) (*state.ConsensusNode, error) {
        consensusNodeMap, err := c.getConsensusNodesByVoteResult(prevBlockHash)
        if err != nil {
                return nil, err
@@ -72,7 +56,7 @@ func (c *consensusNodeManager) isBlocker(prevBlockHash *bc.Hash, pubKey string,
        }
 
        startTimestamp := prevVoteRoundLastBlock.Timestamp + consensus.BlockTimeInterval
-       begin := getLastBlockTimeInTimeRange(startTimestamp, timeStamp, consensusNode.order, len(consensusNodeMap))
+       begin := getLastBlockTimeInTimeRange(startTimestamp, timeStamp, consensusNode.Order, len(consensusNodeMap))
        end := begin + consensus.BlockNumEachNode*consensus.BlockTimeInterval
        return timeStamp >= begin && timeStamp < end, nil
 }
@@ -107,7 +91,7 @@ func (c *consensusNodeManager) getPrevRoundVoteLastBlock(prevBlockHash *bc.Hash)
        return lastBlockNode, nil
 }
 
-func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*consensusNode, error) {
+func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.Hash) (map[string]*state.ConsensusNode, error) {
        prevBlockNode := c.blockIndex.GetNode(prevBlockHash)
        if prevBlockNode == nil {
                return nil, errNotFoundBlockNode
@@ -120,7 +104,6 @@ func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.H
                voteResult = &state.VoteResult{
                        Seq:       seq,
                        NumOfVote: make(map[string]uint64),
-                       Finalized: false,
                }
        }
 
@@ -137,27 +120,7 @@ func (c *consensusNodeManager) getConsensusNodesByVoteResult(prevBlockHash *bc.H
                return initConsensusNodes(), nil
        }
 
-       var nodes []*consensusNode
-       for pubkey, voteNum := range voteResult.NumOfVote {
-               if voteNum >= consensus.MinVoteNum {
-                       nodes = append(nodes, &consensusNode{
-                               pubkey:  pubkey,
-                               voteNum: voteNum,
-                       })
-               }
-       }
-       // 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 < consensus.NumOfConsensusNode; i++ {
-               node := nodes[i]
-               node.order = uint64(i)
-               result[node.pubkey] = node
-       }
-       return result, nil
+       return voteResult.ConsensusNodes()
 }
 
 func (c *consensusNodeManager) reorganizeVoteResult(voteResult *state.VoteResult, forkChainNode *state.BlockNode) error {
@@ -214,40 +177,7 @@ func (c *consensusNodeManager) applyBlock(voteResultMap map[uint64]*state.VoteRe
                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)%consensus.RoundVoteBlockNums == 0
-       return nil
+       return voteResult.ApplyBlock(block)
 }
 
 func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.VoteResult, blockHeight uint64) (*state.VoteResult, error) {
@@ -259,7 +189,6 @@ func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.Vot
                voteResult = &state.VoteResult{
                        Seq:       seq,
                        NumOfVote: make(map[string]uint64),
-                       Finalized: false,
                }
        }
 
@@ -269,7 +198,6 @@ func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.Vot
                        voteResult = &state.VoteResult{
                                Seq:       seq,
                                NumOfVote: prevVoteResult.NumOfVote,
-                               Finalized: false,
                        }
                }
        }
@@ -288,13 +216,7 @@ func (c *consensusNodeManager) getVoteResult(voteResultMap map[uint64]*state.Vot
                }
 
                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{}
                }
        }
@@ -319,46 +241,14 @@ func (c *consensusNodeManager) detachBlock(voteResultMap map[uint64]*state.VoteR
                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
+       voteResult.DetachBlock(block)
        return nil
 }
 
-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)}
+func initConsensusNodes() 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)}
        }
        return voteResult
 }
index 384c54b..b893e8a 100644 (file)
@@ -86,7 +86,7 @@ func (c *Chain) initChainStatus() error {
        }
 
        voteResultMap := make(map[uint64]*state.VoteResult)
-       if err := c.ApplyBlock(voteResultMap, genesisBlock); err != nil {
+       if err := c.consensusNodeManager.applyBlock(voteResultMap, genesisBlock); err != nil {
                return err
        }
 
index bf914b4..acbd58f 100644 (file)
 package state
 
 import (
+       "encoding/hex"
+       "sort"
+
+       "github.com/vapor/consensus"
+       "github.com/vapor/crypto/ed25519/chainkd"
+       "github.com/vapor/errors"
+       "github.com/vapor/math/checked"
        "github.com/vapor/protocol/bc"
+       "github.com/vapor/protocol/bc/types"
 )
 
+var errVotingOperationOverFlow = errors.New("voting operation result overflow")
+
+type ConsensusNode struct {
+       XPub    chainkd.XPub
+       VoteNum uint64
+       Order   uint64
+}
+
+type byVote []*ConsensusNode
+
+func (c byVote) Len() int { return len(c) }
+func (c byVote) Less(i, j int) bool {
+       return c[i].VoteNum > c[j].VoteNum || (c[i].VoteNum == c[j].VoteNum && c[i].XPub.String() > c[j].XPub.String())
+}
+func (c byVote) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
+
 // 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
 // Finalized indicates whether this vote is finalized
 type VoteResult struct {
-       Seq             uint64
-       NumOfVote       map[string]uint64
-       LastBlockHash   bc.Hash
-       Finalized       bool
+       Seq           uint64
+       NumOfVote     map[string]uint64
+       LastBlockHash bc.Hash
+}
+
+func (v *VoteResult) ApplyBlock(block *types.Block) error {
+       if v.LastBlockHash != block.PreviousBlockHash {
+               return errors.New("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)
+                       v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], unVoteInput.Amount)
+                       if !ok {
+                               return errVotingOperationOverFlow
+                       }
+
+                       if v.NumOfVote[pubkey] == 0 {
+                               delete(v.NumOfVote, pubkey)
+                       }
+               }
+
+               for _, output := range tx.Outputs {
+                       voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
+                       if !ok {
+                               continue
+                       }
+
+                       pubkey := hex.EncodeToString(voteOutput.Vote)
+                       if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], voteOutput.Amount); !ok {
+                               return errVotingOperationOverFlow
+                       }
+               }
+       }
+
+       v.LastBlockHash = block.Hash()
+       return nil
+}
+
+func (v *VoteResult) ConsensusNodes() (map[string]*ConsensusNode, error) {
+       var nodes []*ConsensusNode
+       for pubkey, voteNum := range v.NumOfVote {
+               if voteNum >= consensus.MinVoteNum {
+                       var xpub chainkd.XPub
+                       if err := xpub.UnmarshalText([]byte(pubkey)); err != nil {
+                               return nil, err
+                       }
+
+                       nodes = append(nodes, &ConsensusNode{XPub: xpub, VoteNum: voteNum})
+               }
+       }
+       // In principle, there is no need to sort all voting nodes.
+       // if there is a performance problem, consider the optimization later.
+       sort.Sort(byVote(nodes))
+       result := make(map[string]*ConsensusNode)
+       for i := 0; i < len(nodes) && i < consensus.NumOfConsensusNode; i++ {
+               nodes[i].Order = uint64(i)
+               result[nodes[i].XPub.String()] = nodes[i]
+       }
+       return result, nil
+}
+
+func (v *VoteResult) DetachBlock(block *types.Block) error {
+       if v.LastBlockHash != block.Hash() {
+               return errors.New("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)
+                       if v.NumOfVote[pubkey], ok = checked.AddUint64(v.NumOfVote[pubkey], unVoteInput.Amount); !ok {
+                               return errVotingOperationOverFlow
+                       }
+               }
+
+               for _, output := range tx.Outputs {
+                       voteOutput, ok := output.TypedOutput.(*types.VoteTxOutput)
+                       if !ok {
+                               continue
+                       }
+
+                       pubkey := hex.EncodeToString(voteOutput.Vote)
+                       v.NumOfVote[pubkey], ok = checked.SubUint64(v.NumOfVote[pubkey], voteOutput.Amount)
+                       if !ok {
+                               return errVotingOperationOverFlow
+                       }
+
+                       if v.NumOfVote[pubkey] == 0 {
+                               delete(v.NumOfVote, pubkey)
+                       }
+               }
+       }
+
+       v.LastBlockHash = block.PreviousBlockHash
+       return nil
 }