Skip to content

Commit f35d472

Browse files
authored
consensus: make smr run with a single validator. (#284)
* consensus: make smr run with a single validator.
1 parent a5c5c91 commit f35d472

3 files changed

Lines changed: 115 additions & 32 deletions

File tree

bcs/consensus/xpoa/kernel_contract.go

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,25 @@ func (x *xpoaConsensus) methodEditValidates(contractCtx contract.KContext) (*con
3333
if err != nil {
3434
return common.NewContractErrResponse(common.StatusBadRequest, "invalid acl: pls check accept value."), err
3535
}
36-
if !x.isAuthAddress(aks, acceptValue) {
36+
37+
curValiBytes, err := contractCtx.Get(x.election.bindContractBucket,
38+
[]byte(fmt.Sprintf("%d_%s", x.election.consensusVersion, validateKeys)))
39+
curVali, err := func() ([]string, error) {
40+
if err != nil || curValiBytes == nil {
41+
return x.election.initValidators, nil
42+
}
43+
var curValiKey ProposerInfo
44+
err = json.Unmarshal(curValiBytes, &curValiKey)
45+
if err != nil {
46+
x.log.Error("Unmarshal error")
47+
return nil, err
48+
}
49+
return curValiKey.Address, nil
50+
}()
51+
if err != nil {
52+
return common.NewContractErrResponse(common.StatusBadRequest, err.Error()), err
53+
}
54+
if !x.isAuthAddress(curVali, aks, acceptValue, x.election.enableBFT) {
3755
return common.NewContractErrResponse(common.StatusBadRequest, aclErr.Error()), aclErr
3856
}
3957

@@ -87,10 +105,18 @@ func (x *xpoaConsensus) methodGetValidates(contractCtx contract.KContext) (*cont
87105
}
88106

89107
// isAuthAddress 判断输入aks是否能在贪心下仍能满足签名数量>33%(Chained-BFT装载) or 50%(一般情况)
90-
func (x *xpoaConsensus) isAuthAddress(aks map[string]float64, threshold float64) bool {
108+
func (x *xpoaConsensus) isAuthAddress(validators []string, aks map[string]float64, threshold float64, enableBFT bool) bool {
109+
// 0. 是否是单个候选人
110+
if len(validators) == 1 {
111+
weight, ok := aks[validators[0]]
112+
if !ok {
113+
return false
114+
}
115+
return weight >= threshold
116+
}
91117
// 1. 判断aks中的地址是否是当前集合地址
92118
for addr, _ := range aks {
93-
if !Find(addr, x.election.validators) {
119+
if !Find(addr, validators) {
94120
return false
95121
}
96122
}
@@ -106,15 +132,14 @@ func (x *xpoaConsensus) isAuthAddress(aks map[string]float64, threshold float64)
106132
greedyCount := 0
107133
sum := threshold
108134
for i := 0; i < len(aks); i++ {
109-
if sum > 0 {
110-
sum -= s[i].Weight
111-
greedyCount++
112-
continue
135+
if sum <= 0 {
136+
break
113137
}
114-
break
138+
sum -= s[i].Weight
139+
greedyCount++
115140
}
116-
if !x.election.enableBFT {
117-
return greedyCount >= len(x.election.validators)/2+1
141+
if !enableBFT {
142+
return greedyCount >= len(validators)/2+1
118143
}
119-
return CalFault(int64(greedyCount), int64(len(x.election.validators)))
144+
return CalFault(int64(greedyCount), int64(len(validators)))
120145
}

bcs/consensus/xpoa/kernel_contract_test.go

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,15 @@ var (
1212
"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN": 0.5,
1313
"WNWk3ekXeM5M2232dY2uCJmEqWhfQiDYT": 0.5,
1414
}
15-
)
16-
17-
func TestIsAuthAddress(t *testing.T) {
18-
cCtx, err := prepare(getXpoaConsensusConf())
19-
if err != nil {
20-
t.Error("prepare error", "error", err)
21-
return
22-
}
23-
i := NewXpoaConsensus(*cCtx, getConfig(getXpoaConsensusConf()))
24-
xpoa, ok := i.(*xpoaConsensus)
25-
if !ok {
26-
t.Error("transfer err.")
15+
aks2 = map[string]float64{
16+
"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN": 0.5,
17+
"WNWk3ekXeM5M2232dY2uCJmEqWhfQiDYT": 0.6,
2718
}
28-
if !xpoa.isAuthAddress(aks, 0.6) {
29-
t.Error("isAuthAddress err.")
19+
aks3 = map[string]float64{
20+
"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN": 0.4,
21+
"WNWk3ekXeM5M2232dY2uCJmEqWhfQiDYT": 0.6,
3022
}
31-
}
23+
)
3224

3325
func NewEditArgs() map[string][]byte {
3426
a := make(map[string][]byte)
@@ -85,3 +77,36 @@ func TestMethodGetValidates(t *testing.T) {
8577
return
8678
}
8779
}
80+
81+
func TestIsAuthAddress(t *testing.T) {
82+
cCtx, err := prepare(getXpoaConsensusConf())
83+
if err != nil {
84+
t.Error("prepare error", "error", err)
85+
return
86+
}
87+
i := NewXpoaConsensus(*cCtx, getConfig(getXpoaConsensusConf()))
88+
xpoa, ok := i.(*xpoaConsensus)
89+
if !ok {
90+
t.Error("transfer err.")
91+
return
92+
}
93+
v1 := []string{"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN", "WNWk3ekXeM5M2232dY2uCJmEqWhfQiDYT"}
94+
if !xpoa.isAuthAddress(v1, aks, 0.6, false) {
95+
t.Error("isAuthAddress err.")
96+
return
97+
}
98+
v2 := []string{"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN"}
99+
if xpoa.isAuthAddress(v2, aks2, 0.6, true) {
100+
t.Error("isAuthAddress err.")
101+
return
102+
}
103+
v3 := []string{"WNWk3ekXeM5M2232dY2uCJmEqWhfQiDYT"}
104+
if !xpoa.isAuthAddress(v3, aks2, 0.6, true) {
105+
t.Error("isAuthAddress err.")
106+
return
107+
}
108+
v4 := []string{"dpzuVdosQrF2kmzumhVeFQZa1aYcdgFpN", "WNWk3ekXeM5M2232dY2uCJmEqWhfQiDYT"}
109+
if !xpoa.isAuthAddress(v4, aks2, 0.7, true) {
110+
t.Error("isAuthAddress err.")
111+
}
112+
}

kernel/consensus/base/driver/chained-bft/smr.go

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,11 @@ func (s *Smr) ResetProposerStatus(tipBlock cctx.BlockInterface,
219219
s.mtx.Lock()
220220
defer s.mtx.Unlock()
221221

222-
if bytes.Equal(s.getHighQC().GetProposalId(), tipBlock.GetBlockid()) {
222+
if bytes.Equal(s.getHighQC().GetProposalId(), tipBlock.GetBlockid()) &&
223+
s.validNewHighQC(tipBlock.GetBlockid(), validators) {
223224
// 此处需要获取带签名的完整Justify
224225
return false, s.getCompleteHighQC(), nil
225226
}
226-
// 单个节点不存在投票验证的hotstuff流程,因此返回true
227-
if len(validators) == 1 {
228-
return false, nil, nil
229-
}
230227

231228
// 从当前TipBlock开始往前追溯,交给smr根据状态进行回滚。
232229
// 在本地状态树上找到指代TipBlock的QC,若找不到,则在状态树上找和TipBlock同一分支上的最近值
@@ -384,10 +381,43 @@ func (s *Smr) ProcessProposal(viewNumber int64, proposalID []byte, parentID []by
384381
}
385382
go s.p2p.SendMessage(createNewBCtx(), netMsg, p2p.WithAccounts(s.removeLocalValidator(validatesIpInfo)))
386383
s.localProposal.Store(utils.F(proposalID), proposal.Timestamp)
387-
s.log.Debug("smr:ProcessProposal::new proposal has been made", "address", s.address, "proposalID", utils.F(proposalID))
384+
// 若为单候选人情况,则此处需要特殊处理,矿工需要给自己提前签名
385+
if len(validatesIpInfo) == 1 {
386+
s.voteToSelf(viewNumber, proposalID, parentQuorumCert)
387+
}
388+
s.log.Debug("smr:ProcessProposal::new proposal has been made", "address", s.address, "proposalID", utils.F(proposalID), "target", validatesIpInfo)
388389
return nil
389390
}
390391

392+
func (s *Smr) voteToSelf(viewNumber int64, proposalID []byte, parent storage.QuorumCertInterface) {
393+
selfVote := &storage.VoteInfo{
394+
ProposalId: proposalID,
395+
ProposalView: viewNumber,
396+
ParentId: parent.GetProposalId(),
397+
}
398+
selfLedgerInfo := &storage.LedgerCommitInfo{
399+
VoteInfoHash: proposalID,
400+
}
401+
selfQC := storage.NewQuorumCert(selfVote, selfLedgerInfo, nil)
402+
selfSign, err := s.cryptoClient.SignVoteMsg(proposalID)
403+
if err != nil {
404+
s.log.Error("smr::voteProposal::voteToSelf error", "err", err)
405+
return
406+
}
407+
s.qcVoteMsgs.LoadOrStore(utils.F(proposalID), []*chainedBftPb.QuorumCertSign{selfSign})
408+
selfNode := &storage.ProposalNode{
409+
In: selfQC,
410+
}
411+
if err := s.qcTree.UpdateQcStatus(selfNode); err != nil {
412+
s.log.Error("smr::voteProposal::updateQcStatus error", "err", err)
413+
return
414+
}
415+
// 更新本地smr状态机
416+
s.pacemaker.AdvanceView(selfQC)
417+
s.qcTree.UpdateHighQC(proposalID)
418+
s.log.Debug("smr:voteProposal::done local voting", "address", s.address, "proposalID", utils.F(proposalID))
419+
}
420+
391421
// reloadJustifyQC 与LibraBFT不同,返回一个指定的parentQC
392422
func (s *Smr) reloadJustifyQC(parentID []byte) (storage.QuorumCertInterface, error) {
393423
// 第一次proposal,highQC==rootQC==genesisQC
@@ -695,6 +725,9 @@ func (s *Smr) validNewHighQC(inProposalId []byte, validators []string) bool {
695725
if !ok {
696726
return false
697727
}
728+
if len(validators) == 1 {
729+
return len(signs) == len(validators)
730+
}
698731
return s.saftyrules.CalVotesThreshold(len(signs), len(validators))
699732
}
700733

0 commit comments

Comments
 (0)