Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(consensus): fast consensus path implementation #1253

Merged
merged 11 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,9 @@ func makeLocalGenesis(w wallet.Wallet) *genesis.Genesis {
crypto.TreasuryAddress: acc,
}

vals := make([]*validator.Validator, 4)
for i := 0; i < 4; i++ {
genValNum := 6
vals := make([]*validator.Validator, genValNum)
for i := 0; i < genValNum; i++ {
info := w.AddressInfo(w.AddressInfos()[i].Address)
pub, _ := bls.PublicKeyFromString(info.PublicKey)
vals[i] = validator.NewValidator(pub, int32(i))
Expand Down
4 changes: 2 additions & 2 deletions committee/committee_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ func TestContains(t *testing.T) {
func TestProposer(t *testing.T) {
ts := testsuite.NewTestSuite(t)

cmt, _ := ts.GenerateTestCommittee(4)
cmt, _ := ts.GenerateTestCommittee(6)

assert.Equal(t, cmt.Proposer(0).Number(), int32(0))
assert.Equal(t, cmt.Proposer(3).Number(), int32(3))
assert.Equal(t, cmt.Proposer(4).Number(), int32(0))
assert.Equal(t, cmt.Proposer(6).Number(), int32(0))

cmt.Update(0, nil)
assert.Equal(t, cmt.Proposer(0).Number(), int32(1))
Expand Down
2 changes: 1 addition & 1 deletion consensus/commit.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func (s *commitState) decide() {
certBlock := roundProposal.Block()
precommits := s.log.PrecommitVoteSet(s.round)
votes := precommits.BlockVotes(certBlock.Hash())
cert := s.makeCertificate(votes)
cert := s.makeBlockCertificate(votes)
err := s.bcState.CommitBlock(certBlock, cert)
if err != nil {
s.logger.Error("committing block failed", "block", certBlock, "error", err)
Expand Down
2 changes: 1 addition & 1 deletion consensus/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func DefaultConfig() *Config {
func (conf *Config) BasicCheck() error {
if conf.ChangeProposerTimeout <= 0 {
return ConfigError{
Reason: "timeout for change proposer must be greater than zero",
Reason: "change proposer timeout must be greater than zero",
}
}
if conf.ChangeProposerDelta <= 0 {
Expand Down
4 changes: 2 additions & 2 deletions consensus/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ func TestDefaultConfigCheck(t *testing.T) {
assert.ErrorIs(t, c2.BasicCheck(), ConfigError{Reason: "change proposer delta must be greater than zero"})

c3.ChangeProposerTimeout = 0 * time.Second
assert.ErrorIs(t, c3.BasicCheck(), ConfigError{Reason: "timeout for change proposer must be greater than zero"})
assert.ErrorIs(t, c3.BasicCheck(), ConfigError{Reason: "change proposer timeout must be greater than zero"})

c4.ChangeProposerTimeout = -1 * time.Second
assert.ErrorIs(t, c4.BasicCheck(), ConfigError{Reason: "timeout for change proposer must be greater than zero"})
assert.ErrorIs(t, c4.BasicCheck(), ConfigError{Reason: "change proposer timeout must be greater than zero"})

c5.MinimumAvailabilityScore = 1.5
assert.ErrorIs(t, c5.BasicCheck(), ConfigError{Reason: "minimum availability score can't be negative or more than 1"})
Expand Down
26 changes: 23 additions & 3 deletions consensus/consensus.go
Original file line number Diff line number Diff line change
Expand Up @@ -400,13 +400,25 @@ func (cs *consensus) broadcastVote(v *vote.Vote) {
message.NewVoteMessage(v))
}

func (cs *consensus) announceNewBlock(blk *block.Block, cert *certificate.Certificate) {
func (cs *consensus) announceNewBlock(blk *block.Block, cert *certificate.BlockCertificate) {
go cs.mediator.OnBlockAnnounce(cs)
cs.broadcaster(cs.valKey.Address(),
message.NewBlockAnnounceMessage(blk, cert))
}

func (cs *consensus) makeCertificate(votes map[crypto.Address]*vote.Vote) *certificate.Certificate {
func (cs *consensus) makeBlockCertificate(votes map[crypto.Address]*vote.Vote,
) *certificate.BlockCertificate {
cert := certificate.NewBlockCertificate(cs.height, cs.round, false)
cert.SetSignature(cs.signersInfo(votes))

return cert
}

// signersInfo processes a map of votes from validators and provides these information:
// - A list of all validators' numbers eligible to vote in this step.
// - A list of absentee validators' numbers who did not vote in this step.
// - An aggregated signature generated from the signatures of participating validators.
func (cs *consensus) signersInfo(votes map[crypto.Address]*vote.Vote) ([]int32, []int32, *bls.Signature) {
vals := cs.validators
committers := make([]int32, len(vals))
absentees := make([]int32, 0)
Expand All @@ -425,7 +437,15 @@ func (cs *consensus) makeCertificate(votes map[crypto.Address]*vote.Vote) *certi

aggSig := bls.SignatureAggregate(sigs...)

return certificate.NewCertificate(cs.height, cs.round, committers, absentees, aggSig)
return committers, absentees, aggSig
}

func (cs *consensus) makeVoteCertificate(votes map[crypto.Address]*vote.Vote,
) *certificate.VoteCertificate {
cert := certificate.NewVoteCertificate(cs.height, cs.round)
cert.SetSignature(cs.signersInfo(votes))

return cert
}

// IsActive checks if the consensus is in an active state and participating in the consensus algorithm.
Expand Down
75 changes: 40 additions & 35 deletions consensus/consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,23 +274,23 @@ func (td *testData) addPrecommitVote(cons *consensus, blockHash hash.Hash, heigh
}

func (td *testData) addCPPreVote(cons *consensus, blockHash hash.Hash, height uint32, round int16,
cpRound int16, cpVal vote.CPValue, just vote.Just, valID int,
cpVal vote.CPValue, just vote.Just, valID int,
) {
v := vote.NewCPPreVote(blockHash, height, round, cpRound, cpVal, just, td.valKeys[valID].Address())
v := vote.NewCPPreVote(blockHash, height, round, 0, cpVal, just, td.valKeys[valID].Address())
td.addVote(cons, v, valID)
}

func (td *testData) addCPMainVote(cons *consensus, blockHash hash.Hash, height uint32, round int16,
cpRound int16, cpVal vote.CPValue, just vote.Just, valID int,
cpVal vote.CPValue, just vote.Just, valID int,
) {
v := vote.NewCPMainVote(blockHash, height, round, cpRound, cpVal, just, td.valKeys[valID].Address())
v := vote.NewCPMainVote(blockHash, height, round, 0, cpVal, just, td.valKeys[valID].Address())
td.addVote(cons, v, valID)
}

func (td *testData) addCPDecidedVote(cons *consensus, blockHash hash.Hash, height uint32, round int16,
cpRound int16, cpVal vote.CPValue, just vote.Just, valID int,
cpVal vote.CPValue, just vote.Just, valID int,
) {
v := vote.NewCPDecidedVote(blockHash, height, round, cpRound, cpVal, just, td.valKeys[valID].Address())
v := vote.NewCPDecidedVote(blockHash, height, round, 0, cpVal, just, td.valKeys[valID].Address())
td.addVote(cons, v, valID)
}

Expand Down Expand Up @@ -337,23 +337,23 @@ func (*testData) enterNextRound(cons *consensus) {
cons.lk.Unlock()
}

func (td *testData) commitBlockForAllStates(t *testing.T) (*block.Block, *certificate.Certificate) {
func (td *testData) commitBlockForAllStates(t *testing.T) (*block.Block, *certificate.BlockCertificate) {
t.Helper()

height := td.consX.bcState.LastBlockHeight()
var err error
p := td.makeProposal(t, height+1, 0)
prop := td.makeProposal(t, height+1, 0)

sb := certificate.BlockCertificateSignBytes(p.Block().Hash(), height+1, 0)
cert := certificate.NewBlockCertificate(height+1, 0, false)
sb := cert.SignBytes(prop.Block().Hash())
sig1 := td.consX.valKey.Sign(sb)
sig2 := td.consY.valKey.Sign(sb)
sig3 := td.consB.valKey.Sign(sb)
sig4 := td.consP.valKey.Sign(sb)

sig := bls.SignatureAggregate(sig1, sig2, sig3, sig4)
cert := certificate.NewCertificate(height+1, 0,
[]int32{tIndexX, tIndexY, tIndexB, tIndexP}, []int32{}, sig)
blk := p.Block()
cert.SetSignature([]int32{tIndexX, tIndexY, tIndexB, tIndexP}, []int32{}, sig)
blk := prop.Block()

err = td.consX.bcState.CommitBlock(blk, cert)
assert.NoError(t, err)
Expand Down Expand Up @@ -549,43 +549,48 @@ func TestPickRandomVote(t *testing.T) {

td.enterNewHeight(td.consP)
assert.Nil(t, td.consP.PickRandomVote(0))
cpRound := int16(1)
cpRound := int16(0)

// === make valid certificate
sbPreVote := certificate.BlockCertificateSignBytes(hash.UndefHash, 1, 0)
sbPreVote = append(sbPreVote, util.StringToBytes(vote.VoteTypeCPPreVote.String())...)
sbPreVote = append(sbPreVote, util.Int16ToSlice(cpRound)...)
sbPreVote = append(sbPreVote, byte(vote.CPValueOne))
preVoteCommitters := []int32{}
preVoteSigs := []*bls.Signature{}
for i, val := range td.consP.validators {
preVoteJust := &vote.JustInitYes{}
preVote := vote.NewCPPreVote(hash.UndefHash, 1, 0, cpRound, vote.CPValueYes, preVoteJust, val.Address())
sbPreVote := preVote.SignBytes()

sbMainVote := certificate.BlockCertificateSignBytes(hash.UndefHash, 1, 0)
sbMainVote = append(sbMainVote, util.StringToBytes(vote.VoteTypeCPMainVote.String())...)
sbMainVote = append(sbMainVote, util.Int16ToSlice(cpRound)...)
sbMainVote = append(sbMainVote, byte(vote.CPValueOne))
preVoteCommitters = append(preVoteCommitters, val.Number())
preVoteSigs = append(preVoteSigs, td.valKeys[i].Sign(sbPreVote))
}
preVoteAggSig := bls.SignatureAggregate(preVoteSigs...)
certPreVote := certificate.NewVoteCertificate(1, 0)
certPreVote.SetSignature(preVoteCommitters, []int32{}, preVoteAggSig)

committers := []int32{}
preVoteSigs := []*bls.Signature{}
mainVoteCommitters := []int32{}
mainVoteSigs := []*bls.Signature{}
for i, val := range td.consP.validators {
committers = append(committers, val.Number())
preVoteSigs = append(preVoteSigs, td.valKeys[i].Sign(sbPreVote))
mainVoteJust := &vote.JustMainVoteNoConflict{
QCert: certPreVote,
}
mainVote := vote.NewCPMainVote(hash.UndefHash, 1, 0, cpRound, vote.CPValueYes, mainVoteJust, val.Address())
sbMainVote := mainVote.SignBytes()

mainVoteCommitters = append(mainVoteCommitters, val.Number())
mainVoteSigs = append(mainVoteSigs, td.valKeys[i].Sign(sbMainVote))
}

preVoteAggSig := bls.SignatureAggregate(preVoteSigs...)
mainVoteAggSig := bls.SignatureAggregate(mainVoteSigs...)

certPreVote := certificate.NewCertificate(1, 0, committers, []int32{}, preVoteAggSig)
certMainVote := certificate.NewCertificate(1, 0, committers, []int32{}, mainVoteAggSig)
certMainVote := certificate.NewVoteCertificate(1, 0)
certMainVote.SetSignature(mainVoteCommitters, []int32{}, mainVoteAggSig)
// ====

// round 0
td.addPrepareVote(td.consP, td.RandHash(), 1, 0, tIndexX)
td.addPrepareVote(td.consP, td.RandHash(), 1, 0, tIndexY)
td.addCPPreVote(td.consP, hash.UndefHash, 1, 0, cpRound+1, vote.CPValueOne,
&vote.JustPreVoteHard{QCert: certPreVote}, tIndexY)
td.addCPMainVote(td.consP, hash.UndefHash, 1, 0, cpRound, vote.CPValueOne,
td.addCPPreVote(td.consP, hash.UndefHash, 1, 0, vote.CPValueYes,
&vote.JustInitYes{}, tIndexY)
td.addCPMainVote(td.consP, hash.UndefHash, 1, 0, vote.CPValueYes,
&vote.JustMainVoteNoConflict{QCert: certPreVote}, tIndexY)
td.addCPDecidedVote(td.consP, hash.UndefHash, 1, 0, cpRound, vote.CPValueOne,
td.addCPDecidedVote(td.consP, hash.UndefHash, 1, 0, vote.CPValueYes,
&vote.JustDecided{QCert: certMainVote}, tIndexY)

assert.NotNil(t, td.consP.PickRandomVote(0))
Expand Down Expand Up @@ -881,7 +886,7 @@ func TestByzantine(t *testing.T) {
}

func checkConsensus(td *testData, height uint32, byzVotes []*vote.Vote) (
*certificate.Certificate, error,
*certificate.BlockCertificate, error,
) {
instances := []*consensus{td.consX, td.consY, td.consB, td.consP}

Expand Down
Loading
Loading