import (
"encoding/hex"
+ "fmt"
"time"
+ "github.com/golang/groupcache/lru"
+ log "github.com/sirupsen/logrus"
+
"github.com/vapor/crypto/ed25519"
"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"
"github.com/vapor/protocol/state"
)
+const (
+ maxSignatureCacheSize = 10000
+)
+
var (
errVotingOperationOverFlow = errors.New("voting operation result overflow")
+ errDoubleSignBlock = errors.New("the consensus is double sign in same height of different block")
+ errInvalidSignature = errors.New("the signature of block is invalid")
)
type bbft struct {
consensusNodeManager *consensusNodeManager
+ orphanManage *OrphanManage
+ signatureCache *lru.Cache
}
-func newBbft(store Store, blockIndex *state.BlockIndex) *bbft {
+func newBbft(store Store, blockIndex *state.BlockIndex, orphanManage *OrphanManage) *bbft {
return &bbft{
+ orphanManage: orphanManage,
consensusNodeManager: newConsensusNodeManager(store, blockIndex),
+ signatureCache: lru.New(maxSignatureCacheSize),
}
}
if !ok {
continue
}
-
+
pubkey := hex.EncodeToString(unVoteInput.Vote)
voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
if !ok {
if !ok {
continue
}
-
+
pubkey := hex.EncodeToString(unVoteInput.Vote)
voteResult.NumOfVote[pubkey], ok = checked.AddUint64(voteResult.NumOfVote[pubkey], unVoteInput.Amount)
if !ok {
if !ok {
continue
}
-
+
pubkey := hex.EncodeToString(voteOutput.Vote)
voteResult.NumOfVote[pubkey], ok = checked.SubUint64(voteResult.NumOfVote[pubkey], voteOutput.Amount)
if !ok {
return nil
}
+// 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))
+ if err != nil {
+ return false, err
+ }
+
+ if !ed25519.Verify(ed25519.PublicKey(pubkey), blockHash.Bytes(), signature) {
+ return false, errInvalidSignature
+ }
+
+ isDoubleSign, err := b.checkDoubleSign(consensusNode.order, blockHeight, *blockHash)
+ if err != nil {
+ return false, err
+ }
+
+ 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")
+ return false, errDoubleSignBlock
+ }
+
+ orphanBlock, ok := b.orphanManage.Get(blockHash)
+ if ok {
+ orphanBlock.Witness[consensusNode.order] = signature
+ return false, nil
+ }
+
+ block, err := b.consensusNodeManager.store.GetBlock(blockHash)
+ if err != nil {
+ // block is not exist, save the signature
+ key := fmt.Sprintf("%s:%s", blockHash.String(), hex.EncodeToString(pubkey))
+ b.signatureCache.Add(key, signature)
+ return false, err
+ }
+
+ if err := b.updateBlockSignature(block, consensusNode.order, signature); err != nil {
+ return false, err
+ }
+
+ return b.isIrreversible(block) && blockHeight > b.consensusNodeManager.blockIndex.BestNode().Height, nil
+}
+
// ValidateBlock verify whether the block is valid
func (b *bbft) ValidateBlock(block *types.Block) error {
signNum, err := b.validateSign(block)
continue
}
- blocks := b.consensusNodeManager.blockIndex.NodesByHeight(block.Height)
- for _, b := range blocks {
- if b.Hash == block.Hash() {
- continue
- }
- if ok, err := b.BlockWitness.Test(uint32(node.order)); err != nil && ok {
- // Consensus node is signed twice with the same block height, discard the signature
- block.Witness[node.order] = nil
- break
+ blockHash := block.Hash()
+ if block.Witness[node.order] == nil {
+ key := fmt.Sprintf("%s:%s", blockHash.String(), pubkey)
+ signature, ok := b.signatureCache.Get(key)
+ if ok {
+ block.Witness[node.order] = signature.([]byte)
}
}
- if ed25519.Verify(ed25519.PublicKey(pubkey), block.Hash().Bytes(), block.Witness[node.order]) {
- correctSignNum++
- isBlocker, err := b.consensusNodeManager.isBlocker(block.Height, block.Timestamp, pubkey)
+ if ed25519.Verify(ed25519.PublicKey(pubkey), blockHash.Bytes(), block.Witness[node.order]) {
+ isDoubleSign, err := b.checkDoubleSign(node.order, block.Height, block.Hash())
if err != nil {
return 0, err
}
- if isBlocker {
- hasBlockerSign = true
+
+ 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 := b.consensusNodeManager.isBlocker(block.Height, block.Timestamp, pubkey)
+ if err != nil {
+ return 0, err
+ }
+ if isBlocker {
+ hasBlockerSign = true
+ }
}
} else {
// discard the invalid signature
return correctSignNum, nil
}
+func (b *bbft) checkDoubleSign(nodeOrder, blockHeight uint64, blockHash bc.Hash) (bool, error) {
+ blockNodes := b.consensusNodeManager.blockIndex.NodesByHeight(blockHeight)
+ for _, blockNode := range blockNodes {
+ if blockNode.Hash == blockHash {
+ continue
+ }
+ if ok, err := blockNode.BlockWitness.Test(uint32(nodeOrder)); err != nil && ok {
+ block, err := b.consensusNodeManager.store.GetBlock(&blockHash)
+ if err != nil {
+ return false, err
+ }
+
+ // reset nil to discard signature
+ if err := b.updateBlockSignature(block, nodeOrder, nil); err != nil {
+ return false, err
+ }
+ }
+ }
+ return false, nil
+}
+
// SignBlock signing the block if current node is consensus node
func (b *bbft) SignBlock(block *types.Block) error {
var xprv chainkd.XPrv
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)
+
+ if len(signature) != 0 {
+ if err := blockNode.BlockWitness.Set(uint32(nodeOrder)); err != nil {
+ return err
+ }
+ } else {
+ if err := blockNode.BlockWitness.Clean(uint32(nodeOrder)); err != nil {
+ return err
+ }
+ }
+
+ block.Witness[nodeOrder] = signature
+ txStatus, err := b.consensusNodeManager.store.GetTransactionStatus(&blockHash)
+ if err != nil {
+ return err
+ }
+
+ return b.consensusNodeManager.store.SaveBlock(block, txStatus)
+}