From 46a0db054ffc2221096f118b9128cc62636211a6 Mon Sep 17 00:00:00 2001 From: niuxiaojie81 <85773309@qq.com> Date: Wed, 21 May 2025 14:57:28 +0800 Subject: [PATCH] =?UTF-8?q?prepareQC=E3=80=81viewChangeQC=E7=9A=84?= =?UTF-8?q?=E7=94=9F=E6=88=90=E5=92=8C=E6=A0=A1=E9=AA=8C=E5=9F=BA=E4=BA=8E?= =?UTF-8?q?=E8=8A=82=E7=82=B9=E6=8A=95=E7=A5=A8=E6=9D=83=E9=87=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- consensus/cbft/cbft.go | 92 ++++++++++++++++++++++++--- consensus/cbft/cbft_common_util.go | 8 ++- consensus/cbft/consensus_process.go | 27 ++++++-- consensus/cbft/sync_process.go | 19 +++--- consensus/cbft/validator/validator.go | 16 +++++ consensus/sdk.go | 3 +- core/cbfttypes/type.go | 27 ++++++++ sdk/simapp/app.go | 3 + 8 files changed, 168 insertions(+), 27 deletions(-) diff --git a/consensus/cbft/cbft.go b/consensus/cbft/cbft.go index 8203b1228..96f559041 100644 --- a/consensus/cbft/cbft.go +++ b/consensus/cbft/cbft.go @@ -22,14 +22,19 @@ import ( "crypto/elliptic" "encoding/json" "fmt" - "github.com/PlatONnetwork/PlatON-Go/common/base" - "github.com/PlatONnetwork/PlatON-Go/core/sdk" + "math/big" "reflect" "strings" "sync" "sync/atomic" "time" + "github.com/PlatONnetwork/PlatON-Go/common/base" + "github.com/PlatONnetwork/PlatON-Go/core/sdk" + + mapset "github.com/deckarep/golang-set" + "github.com/pkg/errors" + "github.com/PlatONnetwork/PlatON-Go/common" "github.com/PlatONnetwork/PlatON-Go/common/hexutil" "github.com/PlatONnetwork/PlatON-Go/consensus" @@ -59,8 +64,6 @@ import ( "github.com/PlatONnetwork/PlatON-Go/params" "github.com/PlatONnetwork/PlatON-Go/rpc" "github.com/PlatONnetwork/PlatON-Go/trie" - mapset "github.com/deckarep/golang-set" - "github.com/pkg/errors" ) const ( @@ -1379,6 +1382,10 @@ func (cbft *Cbft) threshold(num int) int { return num - (num-1)/3 } +func (cbft *Cbft) thresholdShares(num uint64) uint64 { + return num - (num-1)/3 +} + func (cbft *Cbft) commitBlock(commitBlock *types.Block, commitQC *ctypes.QuorumCert, lockBlock *types.Block, qcBlock *types.Block) { extra, err := ctypes.EncodeExtra(byte(cbftVersion), commitQC) if err != nil { @@ -1821,9 +1828,11 @@ func (cbft *Cbft) verifyPrepareQC(oriNum uint64, oriHash common.Hash, qc *ctypes return err } // check signature number - threshold := cbft.threshold(cbft.validatorPool.Len(qc.Epoch)) + //threshold := cbft.threshold(cbft.validatorPool.Len(qc.Epoch)) + //signsTotal := qc.Len() + threshold := cbft.thresholdShares(cbft.epochValidatorShares(qc.Epoch)) + signsTotal := cbft.PrepareQCShares(qc) - signsTotal := qc.Len() if signsTotal < threshold { return authFailedError{err: fmt.Errorf("block qc has small number of signature total:%d, threshold:%d", signsTotal, threshold)} } @@ -1855,9 +1864,12 @@ func (cbft *Cbft) validateViewChangeQC(viewChangeQC *ctypes.ViewChangeQC) error return fmt.Errorf("viewchangeQC exceed validator max limit, total:%d, threshold:%d", len(viewChangeQC.QCs), maxLimit) } // the threshold of validator on current epoch - threshold := cbft.threshold(maxLimit) + //threshold := cbft.threshold(maxLimit) // check signature number - signsTotal := viewChangeQC.Len() + //signsTotal := viewChangeQC.Len() + + threshold := cbft.thresholdShares(cbft.epochValidatorShares(vcEpoch)) + signsTotal := cbft.ViewChangeQCShares(viewChangeQC) if signsTotal < threshold { return fmt.Errorf("viewchange has small number of signature total:%d, threshold:%d", signsTotal, threshold) } @@ -1979,3 +1991,67 @@ func (cbft *Cbft) IsCurrentValidator() (*cbfttypes.ValidateNode, error) { func (cbft *Cbft) ValidatorLen(epoch uint64) int { return cbft.validatorPool.Len(epoch) } + +func (cbft *Cbft) SortedValidators(epoch uint64) cbfttypes.SortedValidatorNode { + return cbft.validatorPool.SortedValidators(epoch) +} + +func (cbft *Cbft) PrepareVoteSharesByIndex(blockIndex uint32) uint64 { + shares := new(big.Int) + ps := cbft.state.AllPrepareVoteByIndex(blockIndex) + currentValidators := cbft.SortedValidators(cbft.Epoch()) + + for nodeIndex, v := range currentValidators { + if _, e := ps[uint32(nodeIndex)]; e { + shares.Add(shares, v.Shares) + } + } + + return shares.Uint64() +} + +func (cbft *Cbft) ViewChangeShares() uint64 { + shares := new(big.Int) + vc := cbft.state.AllViewChange() + currentValidators := cbft.SortedValidators(cbft.Epoch()) + + for nodeIndex, v := range currentValidators { + if _, e := vc[uint32(nodeIndex)]; e { + shares.Add(shares, v.Shares) + } + } + + return shares.Uint64() +} + +func (cbft *Cbft) PrepareQCShares(qc *ctypes.QuorumCert) uint64 { + validators := cbft.SortedValidators(qc.Epoch) + + shares := new(big.Int) + for i := uint32(0); i < qc.ValidatorSet.Size(); i++ { + if qc.ValidatorSet.GetIndex(i) { + shares.Add(shares, validators[i].Shares) + } + } + return shares.Uint64() +} + +func (cbft *Cbft) ViewChangeQCShares(viewChangeQC *ctypes.ViewChangeQC) uint64 { + vcEpoch, _, _, _, _, _ := viewChangeQC.MaxBlock() + validators := cbft.SortedValidators(vcEpoch) + + shares := new(big.Int) + for _, qc := range viewChangeQC.QCs { + for i := uint32(0); i < qc.ValidatorSet.Size(); i++ { + if qc.ValidatorSet.GetIndex(i) { + shares.Add(shares, validators[i].Shares) + } + } + } + + return shares.Uint64() +} + +func (cbft *Cbft) epochValidatorShares(epoch uint64) uint64 { + return cbft.validatorPool.TotalShares(epoch) +} diff --git a/consensus/cbft/cbft_common_util.go b/consensus/cbft/cbft_common_util.go index 94a2ed0da..5f45535d8 100644 --- a/consensus/cbft/cbft_common_util.go +++ b/consensus/cbft/cbft_common_util.go @@ -22,11 +22,10 @@ import ( "net" "time" - "github.com/PlatONnetwork/PlatON-Go/ethdb" - "github.com/PlatONnetwork/PlatON-Go/p2p/enode" - "github.com/PlatONnetwork/PlatON-Go/core/cbfttypes" "github.com/PlatONnetwork/PlatON-Go/core/rawdb" + "github.com/PlatONnetwork/PlatON-Go/ethdb" + "github.com/PlatONnetwork/PlatON-Go/p2p/enode" "github.com/PlatONnetwork/PlatON-Go/consensus/cbft/network" "github.com/PlatONnetwork/PlatON-Go/consensus/cbft/protocols" @@ -296,6 +295,9 @@ func (m *mockConsensusApp) VerifyExtendData(ctx consensus.ConsensusContext, data func (m *mockConsensusApp) PrepareQC(ctx consensus.ConsensusContext, block *protocols.PrepareBlock, votes map[uint32]*protocols.PrepareVote) { } +func (m *mockConsensusApp) ViewChange(ctx consensus.ConsensusContext, validators []*cbfttypes.ValidateNode) { +} + func (m *mockConsensusApp) NewHeader(ctx consensus.ConsensusContext, header *types.Header) error { return nil } diff --git a/consensus/cbft/consensus_process.go b/consensus/cbft/consensus_process.go index cfb9e3f70..fdec24ba3 100644 --- a/consensus/cbft/consensus_process.go +++ b/consensus/cbft/consensus_process.go @@ -20,6 +20,8 @@ import ( "fmt" "time" + "github.com/pkg/errors" + "github.com/PlatONnetwork/PlatON-Go/common" "github.com/PlatONnetwork/PlatON-Go/common/math" "github.com/PlatONnetwork/PlatON-Go/consensus/cbft/context" @@ -32,7 +34,6 @@ import ( "github.com/PlatONnetwork/PlatON-Go/core/types" "github.com/PlatONnetwork/PlatON-Go/crypto/bls" "github.com/PlatONnetwork/PlatON-Go/log" - "github.com/pkg/errors" ) // OnPrepareBlock performs security rule verification,store in blockTree, @@ -206,7 +207,7 @@ func (cbft *Cbft) OnViewTimeout() { cbft.bridge.SendViewChange(viewChange) } - cbft.state.AddViewChange(uint32(node.Index), viewChange) + cbft.state.AddViewChange(node.Index, viewChange) cbft.network.Broadcast(viewChange) cbft.log.Info("Local add viewChange", "index", node.Index, "viewChange", viewChange.String(), "total", cbft.state.ViewChangeLen()) @@ -501,10 +502,14 @@ func (cbft *Cbft) findExecutableBlock() { func (cbft *Cbft) findQCBlock() { index := cbft.state.MaxQCIndex() next := index + 1 - size := cbft.state.PrepareVoteLenByIndex(next) + //size := cbft.state.PrepareVoteLenByIndex(next) + //prepareQC := func() bool { + // return size >= cbft.threshold(cbft.currentValidatorLen()) && cbft.state.HadSendPrepareVote().Had(next) + //} + shares := cbft.PrepareVoteSharesByIndex(next) prepareQC := func() bool { - return size >= cbft.threshold(cbft.currentValidatorLen()) && cbft.state.HadSendPrepareVote().Had(next) + return shares >= cbft.thresholdShares(cbft.epochValidatorShares(cbft.Epoch())) && cbft.state.HadSendPrepareVote().Had(next) } if prepareQC() { @@ -609,11 +614,15 @@ func (cbft *Cbft) tryChangeView() { return } + shares := cbft.ViewChangeShares() viewChangeQC := func() bool { - if cbft.state.ViewChangeLen() >= cbft.threshold(cbft.currentValidatorLen()) { + //if cbft.state.ViewChangeLen() >= cbft.threshold(cbft.currentValidatorLen()) { + // return true + //} + if shares >= cbft.thresholdShares(cbft.epochValidatorShares(cbft.Epoch())) { return true } - cbft.log.Debug("Try change view failed, had receive viewchange", "len", cbft.state.ViewChangeLen(), "view", cbft.state.ViewString()) + cbft.log.Debug("Try change view failed, had receive viewchange", "len", cbft.state.ViewChangeLen(), "shares", shares, "view", cbft.state.ViewString()) return false } @@ -728,7 +737,7 @@ func (cbft *Cbft) generateViewChange(qc *ctypes.QuorumCert) (*protocols.ViewChan ViewNumber: cbft.state.ViewNumber(), BlockHash: qc.BlockHash, BlockNumber: qc.BlockNumber, - ValidatorIndex: uint32(node.Index), + ValidatorIndex: node.Index, PrepareQC: qc, } if err := cbft.signMsgByBls(v); err != nil { @@ -774,6 +783,10 @@ func (cbft *Cbft) changeView(epoch, viewNumber uint64, block *types.Block, qc *c if !cbft.isLoading() { cbft.bridge.ConfirmViewChange(epoch, viewNumber, block, qc, viewChangeQC, preEpoch, preViewNumber) } + if cbft.app != nil { + cbft.app.ViewChange(context.NewContext(cbft, 0, nil), cbft.SortedValidators(cbft.Epoch())) + } + cbft.clearInvalidBlocks(block) cbft.evPool.Clear(epoch, viewNumber) // view change maybe lags behind the other nodes,active sync prepare block diff --git a/consensus/cbft/sync_process.go b/consensus/cbft/sync_process.go index 82fd2e8a9..8ac281d37 100644 --- a/consensus/cbft/sync_process.go +++ b/consensus/cbft/sync_process.go @@ -371,17 +371,18 @@ func (cbft *Cbft) OnGetPrepareVote(id string, msg *protocols.GetPrepareVote) err // Defining an array for receiving PrepareVote. votes := make([]*protocols.PrepareVote, 0, len(prepareVoteMap)) if prepareVoteMap != nil { - threshold := cbft.threshold(cbft.currentValidatorLen()) - remain := threshold - (cbft.currentValidatorLen() - int(msg.UnKnownSet.Size())) + //threshold := cbft.threshold(cbft.currentValidatorLen()) + //remain := threshold - (cbft.currentValidatorLen() - int(msg.UnKnownSet.Size())) for k, v := range prepareVoteMap { if msg.UnKnownSet.GetIndex(k) { votes = append(votes, v) } - // Limit response votes - if remain > 0 && len(votes) >= remain { - break - } + // 此处不做 vote shares 的 remain 计算,把对方缺失的 vote 全部返回 TODO + //// Limit response votes + //if remain > 0 && len(votes) >= remain { + // break + //} } } if len(votes) > 0 { @@ -677,7 +678,8 @@ func (cbft *Cbft) MissingViewChangeNodes() (v *protocols.GetViewChange, err erro vbits := utils.NewBitArray(uint32(length)) // enough qc or did not reach deadline - if len(allViewChange) >= cbft.threshold(length) || !cbft.state.IsDeadline() { + //if len(allViewChange) >= cbft.threshold(length) || !cbft.state.IsDeadline() { + if !cbft.state.IsDeadline() { v, err = nil, fmt.Errorf("no need sync viewchange") return } @@ -717,7 +719,8 @@ func (cbft *Cbft) MissingPrepareVote() (v *protocols.GetPrepareVote, err error) cbft.log.Debug("The length of prepare vote", "index", index, "size", size) // We need sync prepare votes when a long time not arrived QC. - if size < cbft.threshold(len) && time.Since(blockTime) >= syncPrepareVotesInterval { // need sync prepare votes + //if size < cbft.threshold(len) && time.Since(blockTime) >= syncPrepareVotesInterval { // need sync prepare votes + if time.Since(blockTime) >= syncPrepareVotesInterval { // need sync prepare votes knownVotes := cbft.state.AllPrepareVoteByIndex(index) unKnownSet := utils.NewBitArray(uint32(len)) for i := uint32(0); i < unKnownSet.Size(); i++ { diff --git a/consensus/cbft/validator/validator.go b/consensus/cbft/validator/validator.go index 72d7cce73..167b18cea 100644 --- a/consensus/cbft/validator/validator.go +++ b/consensus/cbft/validator/validator.go @@ -355,6 +355,22 @@ func (vp *ValidatorPool) Validators(epoch uint64) *cbfttypes.Validators { return vp.currentValidators } +func (vp *ValidatorPool) SortedValidators(epoch uint64) cbfttypes.SortedValidatorNode { + vp.lock.RLock() + defer vp.lock.RUnlock() + + validators := vp.Validators(epoch) + return validators.SortedValidators() +} + +func (vp *ValidatorPool) TotalShares(epoch uint64) uint64 { + vp.lock.RLock() + defer vp.lock.RUnlock() + + validators := vp.Validators(epoch) + return validators.TotalShares() +} + // VerifyHeader verify block's header. func (vp *ValidatorPool) VerifyHeader(header *types.Header) error { _, err := crypto.Ecrecover(header.SealHash().Bytes(), header.Signature()) diff --git a/consensus/sdk.go b/consensus/sdk.go index 3b5357914..d5d0a6440 100644 --- a/consensus/sdk.go +++ b/consensus/sdk.go @@ -26,6 +26,7 @@ type ConsensusContext interface { type ConsensusApp interface { ElectionApp ConsensusExtendApp + ConsensusViewChange } type ConsensusExtendApp interface { ExtendData(ctx ConsensusContext) []byte @@ -33,7 +34,7 @@ type ConsensusExtendApp interface { PrepareQC(ctx ConsensusContext, block *protocols.PrepareBlock, votes map[uint32]*protocols.PrepareVote) } type ConsensusViewChange interface { - ViewChange(epoch uint64, ViewNumber uint64, validators []*cbfttypes.ValidateNode) + ViewChange(ctx ConsensusContext, validators []*cbfttypes.ValidateNode) } type ElectionApp interface { NewHeader(ctx ConsensusContext, header *types.Header) error diff --git a/core/cbfttypes/type.go b/core/cbfttypes/type.go index 696195ee2..730f42390 100644 --- a/core/cbfttypes/type.go +++ b/core/cbfttypes/type.go @@ -23,6 +23,7 @@ import ( "errors" "fmt" "math" + "math/big" "sort" "github.com/PlatONnetwork/PlatON-Go/common" @@ -59,6 +60,7 @@ type ValidateNode struct { PubKey *ecdsa.PublicKey `json:"-"` NodeID enode.ID `json:"nodeID"` BlsPubKey *bls.PublicKey `json:"blsPubKey"` + Shares *big.Int `json:"shares"` } type ValidateNodeMap map[enode.ID]*ValidateNode @@ -74,6 +76,7 @@ type Validators struct { ValidBlockNumber uint64 `json:"validateBlockNumber"` sortedNodes SortedValidatorNode + totalShares *big.Int } func (vn *ValidateNode) String() string { @@ -115,6 +118,30 @@ func (vs *Validators) NodeList() []enode.ID { return nodeList } +func (vs *Validators) SortedValidators() SortedValidatorNode { + if len(vs.sortedNodes) == 0 { + vs.sort() + } + return vs.sortedNodes +} + +func (vs *Validators) TotalShares() uint64 { + if vs.totalShares != nil { + return vs.totalShares.Uint64() + } + + if len(vs.sortedNodes) == 0 { + vs.sort() + } + + shares := new(big.Int) + for _, v := range vs.sortedNodes { + shares.Add(shares, v.Shares) + } + vs.totalShares = shares + return shares.Uint64() +} + func (vs *Validators) NodeListByIndexes(indexes []uint32) ([]*ValidateNode, error) { if len(vs.sortedNodes) == 0 { vs.sort() diff --git a/sdk/simapp/app.go b/sdk/simapp/app.go index 22d5361e9..0ad6f0b40 100644 --- a/sdk/simapp/app.go +++ b/sdk/simapp/app.go @@ -102,6 +102,9 @@ func (app *SimApp) VerifyExtendData(ctx sdk.ConsensusContext, data []byte) (comm func (app *SimApp) PrepareQC(ctx sdk.ConsensusContext, block *protocols.PrepareBlock, votes map[uint32]*protocols.PrepareVote) { } +func (app *SimApp) ViewChange(ctx sdk.ConsensusContext, validators []*cbfttypes.ValidateNode) { +} + func (app *SimApp) NewHeader(ctx sdk.ConsensusContext, header *types.Header) error { return nil }