From 1d1683e9342aef248e04dfaf6da2bb515d03d25b Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Mon, 26 Sep 2022 10:42:09 +0200 Subject: [PATCH 01/25] add block to syncInfo --- types.go | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/types.go b/types.go index 51cbfe8c..50ca640a 100644 --- a/types.go +++ b/types.go @@ -172,10 +172,12 @@ func (pc PartialCert) ToBytes() []byte { // Generally, if highQC.View > highTC.View, there is no need to include highTC in the SyncInfo. // However, if highQC.View < highTC.View, we should still include highQC. // This can also hold an AggregateQC for Fast-Hotstuff. +// This can also hola a block. type SyncInfo struct { qc *QuorumCert tc *TimeoutCert aggQC *AggregateQC + block *Block } // NewSyncInfo returns a new SyncInfo struct. @@ -197,6 +199,12 @@ func (si SyncInfo) WithTC(tc TimeoutCert) SyncInfo { return si } +// WithBlock returns a copy of the SyncInfo struct with the given block. +func (si SyncInfo) WithBlock(b *Block) SyncInfo { + si.block = b + return si +} + // WithAggQC returns a copy of the SyncInfo struct with the given AggregateQC. func (si SyncInfo) WithAggQC(aggQC AggregateQC) SyncInfo { si.aggQC = new(AggregateQC) @@ -228,6 +236,14 @@ func (si SyncInfo) AggQC() (_ AggregateQC, _ bool) { return } +// Block returns the block, if present. +func (si SyncInfo) Block() (_ *Block, _ bool) { + if si.block != nil { + return si.block, true + } + return +} + func (si SyncInfo) String() string { var sb strings.Builder sb.WriteString("{ ") @@ -240,6 +256,9 @@ func (si SyncInfo) String() string { if si.aggQC != nil { fmt.Fprintf(&sb, "%s ", si.aggQC) } + if si.block != nil { + fmt.Fprintf(&sb, "%s ", si.block) + } sb.WriteRune('}') return sb.String() } From 3de76bd2c377302aa1cdf51a391eb79ef4087702 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Mon, 26 Sep 2022 10:42:40 +0200 Subject: [PATCH 02/25] simplify updateHighQC --- synchronizer/synchronizer.go | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 040d4f80..d121c7af 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -349,20 +349,9 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { // This method is meant to be used instead of the exported UpdateHighQC internally // in this package when the qc has already been verified. func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { - newBlock, ok := s.blockChain.Get(qc.BlockHash()) - if !ok { - s.logger.Info("updateHighQC: Could not find block referenced by new QC!") - return - } - - oldBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) - if !ok { - s.logger.Panic("Block from the old highQC missing from chain") - } - if newBlock.View() > oldBlock.View() { + if qc.View() > s.highQC.View() { s.highQC = qc - s.leafBlock = newBlock s.logger.Debug("HighQC updated") } } From 854d64ab7f59063a605585dfe3852539b9ca9046 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Mon, 26 Sep 2022 15:39:06 +0200 Subject: [PATCH 03/25] changed updateHighQC --- synchronizer/synchronizer.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index d121c7af..73102c80 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -353,6 +353,18 @@ func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { if qc.View() > s.highQC.View() { s.highQC = qc s.logger.Debug("HighQC updated") + + // I believe the following can be removed, if leafBlock is updated elsewhere. + // However, placed here now for backwards compatibility + highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) + if !ok { + s.logger.Info("updateHighQC: Could not find block referenced by new QC!") + return + } + + if s.leafBlock.View() < s.highQC.View() || !s.blockChain.Extends(s.leafBlock, highQCBlock) { + s.leafBlock = highQCBlock + } } } From 95dcd525dd520e58a4d76350bbe20bd1f8b9afe4 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Sun, 2 Oct 2022 09:35:24 +0200 Subject: [PATCH 04/25] pipelining in synchronizer --- synchronizer/synchronizer.go | 48 +++++++++++++++++++++++++++++------- types.go | 26 ++++++++++++++++--- 2 files changed, 61 insertions(+), 13 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 73102c80..dfda3835 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -36,6 +36,9 @@ type Synchronizer struct { duration ViewDuration timer *time.Timer + // This should be retrieved from a configuration + pipelinedViews uint8 + viewCtx context.Context // a context that is cancelled at the end of the current view cancelCtx context.CancelFunc @@ -91,6 +94,8 @@ func New(viewDuration ViewDuration) modules.Synchronizer { leafBlock: hotstuff.GetGenesis(), currentView: 1, + pipelinedViews: 0, + viewCtx: ctx, cancelCtx: cancel, @@ -192,6 +197,11 @@ func (s *Synchronizer) OnLocalTimeout() { s.consensus.StopVoting(s.currentView) s.configuration.Timeout(timeoutMsg) + + if s.currentView < s.highQC.View()+hotstuff.View(s.pipelinedViews) { + s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView)) + } + s.OnRemoteTimeout(timeoutMsg) } @@ -315,6 +325,26 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { } } + b, ok := syncInfo.Block() + if ok { + highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) + if !ok { + s.logger.Error("AdvanceView: Could not find block referenced by new QC!") + } + if !s.blockChain.Extends(b, highQCBlock) { + b = highQCBlock + } + s.leafBlock = b + if b.View() > v && b.View() < v+hotstuff.View(s.pipelinedViews) { + v = b.View() + } + } + + if timeoutV, ok := syncInfo.TimeoutView(); ok && + timeoutV > v && timeoutV < v+hotstuff.View(s.pipelinedViews) { + v = timeoutV + } + if v < s.currentView { return } @@ -349,21 +379,21 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { // This method is meant to be used instead of the exported UpdateHighQC internally // in this package when the qc has already been verified. func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { - if qc.View() > s.highQC.View() { s.highQC = qc s.logger.Debug("HighQC updated") // I believe the following can be removed, if leafBlock is updated elsewhere. // However, placed here now for backwards compatibility - highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) - if !ok { - s.logger.Info("updateHighQC: Could not find block referenced by new QC!") - return - } - - if s.leafBlock.View() < s.highQC.View() || !s.blockChain.Extends(s.leafBlock, highQCBlock) { - s.leafBlock = highQCBlock + if s.leafBlock.View() < s.highQC.View() { + highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) + if !ok { + s.logger.Info("updateHighQC: Could not find block referenced by new QC!") + return + } + if s.blockChain.Extends(s.leafBlock, highQCBlock) { + s.leafBlock = highQCBlock + } } } } diff --git a/types.go b/types.go index 50ca640a..7e64ea0a 100644 --- a/types.go +++ b/types.go @@ -174,10 +174,11 @@ func (pc PartialCert) ToBytes() []byte { // This can also hold an AggregateQC for Fast-Hotstuff. // This can also hola a block. type SyncInfo struct { - qc *QuorumCert - tc *TimeoutCert - aggQC *AggregateQC - block *Block + qc *QuorumCert + tc *TimeoutCert + aggQC *AggregateQC + block *Block + timoutView View } // NewSyncInfo returns a new SyncInfo struct. @@ -205,6 +206,12 @@ func (si SyncInfo) WithBlock(b *Block) SyncInfo { return si } +// WithTimeoutView returns a copy of the SyncInfo struct with a view marked as local timeout. +func (si SyncInfo) WithTimeoutView(tv View) SyncInfo { + si.timoutView = tv + return si +} + // WithAggQC returns a copy of the SyncInfo struct with the given AggregateQC. func (si SyncInfo) WithAggQC(aggQC AggregateQC) SyncInfo { si.aggQC = new(AggregateQC) @@ -244,6 +251,14 @@ func (si SyncInfo) Block() (_ *Block, _ bool) { return } +// TimeoutView returns the timeout view, if present. +func (si SyncInfo) TimeoutView() (_ View, _ bool) { + if si.timoutView != 0 { + return si.timoutView, true + } + return +} + func (si SyncInfo) String() string { var sb strings.Builder sb.WriteString("{ ") @@ -259,6 +274,9 @@ func (si SyncInfo) String() string { if si.block != nil { fmt.Fprintf(&sb, "%s ", si.block) } + if si.timoutView != 0 { + fmt.Fprintf(&sb, "timeoutView: %d ", si.timoutView) + } sb.WriteRune('}') return sb.String() } From f7519b0430f8a4f278678c014bb9e8d2aa0366a0 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Sun, 2 Oct 2022 13:33:51 +0200 Subject: [PATCH 05/25] pipelined commit rule --- consensus/chainedhotstuff/chainedhotstuff.go | 35 ++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/consensus/chainedhotstuff/chainedhotstuff.go b/consensus/chainedhotstuff/chainedhotstuff.go index d5b34e62..af6983df 100644 --- a/consensus/chainedhotstuff/chainedhotstuff.go +++ b/consensus/chainedhotstuff/chainedhotstuff.go @@ -67,11 +67,46 @@ func (hs *ChainedHotStuff) CommitRule(block *hotstuff.Block) *hotstuff.Block { return nil } + // normal hotstuff if block1.Parent() == block2.Hash() && block2.Parent() == block3.Hash() { hs.logger.Debug("DECIDE: ", block3) return block3 } + // pipelined rule + // find all confirmed blocks + qcs := make(map[hotstuff.Hash]bool, block.View()-block3.View()) + for loopblock := block; loopblock.View() > block1.View(); { + qcs[loopblock.QuorumCert().BlockHash()] = true + parent, ok := hs.blockChain.LocalGet(loopblock.Parent()) + if !ok { + hs.logger.Info("CommitRule: unable to retrieve parent block") + return nil + } + loopblock = parent + } + for loopblock := block1; loopblock.View() > block3.View(); { + if !qcs[loopblock.Hash()] { + hs.logger.Info("CommitRule: unconfirmed parent block") + return nil + } + qcs[loopblock.QuorumCert().BlockHash()] = true + parent, ok := hs.blockChain.LocalGet(loopblock.Parent()) + if !ok { + hs.logger.Info("CommitRule: unable to retrieve parent block") + return nil + } + if parent.View() != loopblock.View()-1 { + hs.logger.Info("CommitRule: found view without block") + return nil + } + if parent.Hash() == block3.Hash() { + hs.logger.Debug("DECIDE: ", block3) + return block3 + } + loopblock = parent + } + return nil } From 0745f444436c25302a7d317c85d1f022975d8682 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Sun, 2 Oct 2022 13:35:35 +0200 Subject: [PATCH 06/25] fix updatehighqc --- synchronizer/synchronizer.go | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index dfda3835..bbcb4c15 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -385,15 +385,14 @@ func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { // I believe the following can be removed, if leafBlock is updated elsewhere. // However, placed here now for backwards compatibility - if s.leafBlock.View() < s.highQC.View() { - highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) - if !ok { - s.logger.Info("updateHighQC: Could not find block referenced by new QC!") - return - } - if s.blockChain.Extends(s.leafBlock, highQCBlock) { - s.leafBlock = highQCBlock - } + highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) + if !ok { + s.logger.Info("updateHighQC: Could not find block referenced by new QC!") + return + } + + if s.leafBlock.View() < s.highQC.View() || !s.blockChain.Extends(s.leafBlock, highQCBlock) { + s.leafBlock = highQCBlock } } } From a1598c0af1a73cb4bf741ab1f7f0052a4497f6a4 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Sun, 2 Oct 2022 13:36:45 +0200 Subject: [PATCH 07/25] pass new leaf block to syncronizer --- consensus/consensus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 51d384fc..1d4828c0 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -215,7 +215,7 @@ func (cs *consensusBase) OnPropose(proposal hotstuff.ProposeMsg) { //nolint:gocy cs.commit(b) } if !didAdvanceView { - cs.synchronizer.AdvanceView(hotstuff.NewSyncInfo().WithQC(block.QuorumCert())) + cs.synchronizer.AdvanceView(hotstuff.NewSyncInfo().WithQC(block.QuorumCert()).WithBlock(block)) } }() From 38d65c349cd245507a14bbbf82b7d4c38596f716 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Sun, 2 Oct 2022 13:37:43 +0200 Subject: [PATCH 08/25] collect certificates for multile blocks --- consensus/votingmachine.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/consensus/votingmachine.go b/consensus/votingmachine.go index bfae8034..7d576d82 100644 --- a/consensus/votingmachine.go +++ b/consensus/votingmachine.go @@ -75,7 +75,7 @@ func (vm *VotingMachine) OnVote(vote hotstuff.VoteMsg) { } } - if block.View() <= vm.synchronizer.LeafBlock().View() { + if block.View() <= vm.synchronizer.HighQC().View() { // too old return } @@ -101,7 +101,7 @@ func (vm *VotingMachine) verifyCert(cert hotstuff.PartialCert, block *hotstuff.B // delete any pending QCs with lower height than bLeaf for k := range vm.verifiedVotes { if block, ok := vm.blockChain.LocalGet(k); ok { - if block.View() <= vm.synchronizer.LeafBlock().View() { + if block.View() <= vm.synchronizer.HighQC().View() { delete(vm.verifiedVotes, k) } } else { From 2e8d5985330e4a7fdb7aba691811940bfabbf186 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Sun, 2 Oct 2022 14:39:16 +0200 Subject: [PATCH 09/25] fix leafblock handling in syncronizer --- consensus/chainedhotstuff/chainedhotstuff.go | 2 +- synchronizer/synchronizer.go | 31 ++++++++++++-------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/consensus/chainedhotstuff/chainedhotstuff.go b/consensus/chainedhotstuff/chainedhotstuff.go index af6983df..df1e5132 100644 --- a/consensus/chainedhotstuff/chainedhotstuff.go +++ b/consensus/chainedhotstuff/chainedhotstuff.go @@ -74,7 +74,7 @@ func (hs *ChainedHotStuff) CommitRule(block *hotstuff.Block) *hotstuff.Block { } // pipelined rule - // find all confirmed blocks + // check if all blocks between b1 and b3 are confirmed qcs := make(map[hotstuff.Hash]bool, block.View()-block3.View()) for loopblock := block; loopblock.View() > block1.View(); { qcs[loopblock.QuorumCert().BlockHash()] = true diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index bbcb4c15..85dadcec 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -94,7 +94,7 @@ func New(viewDuration ViewDuration) modules.Synchronizer { leafBlock: hotstuff.GetGenesis(), currentView: 1, - pipelinedViews: 0, + pipelinedViews: 2, viewCtx: ctx, cancelCtx: cancel, @@ -326,22 +326,27 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { } b, ok := syncInfo.Block() - if ok { - highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) - if !ok { - s.logger.Error("AdvanceView: Could not find block referenced by new QC!") - } - if !s.blockChain.Extends(b, highQCBlock) { - b = highQCBlock - } + if !ok { + b = s.leafBlock + } + + highQCBlock, ok := s.blockChain.LocalGet(s.highQC.BlockHash()) + if !ok { + s.logger.Error("AdvanceView: Could not find block referenced by new QC!") + } + if !s.blockChain.Extends(b, highQCBlock) { + b = highQCBlock + } + if b.View() > s.leafBlock.View() { s.leafBlock = b - if b.View() > v && b.View() < v+hotstuff.View(s.pipelinedViews) { - v = b.View() - } + } + + if b.View() > v && b.View() < s.highQC.View()+hotstuff.View(s.pipelinedViews) { + v = b.View() } if timeoutV, ok := syncInfo.TimeoutView(); ok && - timeoutV > v && timeoutV < v+hotstuff.View(s.pipelinedViews) { + timeoutV > v && timeoutV < s.highQC.View()+hotstuff.View(s.pipelinedViews) { v = timeoutV } From d2508b2aded1310bd3132b21c180b43ac0245a91 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Mon, 3 Oct 2022 09:18:59 +0200 Subject: [PATCH 10/25] send vote to correct leader --- consensus/consensus.go | 2 +- modules/modules.go | 2 ++ synchronizer/synchronizer.go | 12 ++++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 1d4828c0..d7b20de6 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -240,7 +240,7 @@ func (cs *consensusBase) OnPropose(proposal hotstuff.ProposeMsg) { //nolint:gocy return } - leaderID := cs.leaderRotation.GetLeader(cs.lastVote + 1) + leaderID := cs.leaderRotation.GetLeader(block.View() + cs.synchronizer.PipelinedViews()) if leaderID == cs.opts.ID() { cs.eventLoop.AddEvent(hotstuff.VoteMsg{ID: cs.opts.ID(), PartialCert: pc}) return diff --git a/modules/modules.go b/modules/modules.go index f548c38a..53598155 100644 --- a/modules/modules.go +++ b/modules/modules.go @@ -162,6 +162,8 @@ type Synchronizer interface { HighQC() hotstuff.QuorumCert // LeafBlock returns the current leaf block. LeafBlock() *hotstuff.Block + // PipelinedViews returns the number of concurrently executed views. + PipelinedViews() hotstuff.View // Start starts the synchronizer with the given context. Start(context.Context) } diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 85dadcec..b24c48c8 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -36,7 +36,7 @@ type Synchronizer struct { duration ViewDuration timer *time.Timer - // This should be retrieved from a configuration + // number of cuncurrent views, 0 and 1 both give no concurrency. pipelinedViews uint8 viewCtx context.Context // a context that is cancelled at the end of the current view @@ -94,7 +94,7 @@ func New(viewDuration ViewDuration) modules.Synchronizer { leafBlock: hotstuff.GetGenesis(), currentView: 1, - pipelinedViews: 2, + pipelinedViews: 2, // this should be retrieved from a configuration. viewCtx: ctx, cancelCtx: cancel, @@ -125,6 +125,14 @@ func (s *Synchronizer) Start(ctx context.Context) { } } +// PipelinedViews returns 1 or the number of concurrent views in the pipeline +func (s *Synchronizer) PipelinedViews() hotstuff.View { + if s.pipelinedViews < 1 { + return 1 + } + return hotstuff.View(s.pipelinedViews) +} + // HighQC returns the highest known QC. func (s *Synchronizer) HighQC() hotstuff.QuorumCert { return s.highQC From b7cd66bb94c3e35c19f34f4a0460128904f8d879 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Mon, 3 Oct 2022 15:19:18 +0200 Subject: [PATCH 11/25] fix bug on timeout --- synchronizer/synchronizer.go | 2 +- twins/scenario_test.go | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index b24c48c8..a18a4912 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -207,7 +207,7 @@ func (s *Synchronizer) OnLocalTimeout() { s.configuration.Timeout(timeoutMsg) if s.currentView < s.highQC.View()+hotstuff.View(s.pipelinedViews) { - s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView)) + s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView).WithQC(s.highQC)) } s.OnRemoteTimeout(timeoutMsg) diff --git a/twins/scenario_test.go b/twins/scenario_test.go index 1d887d15..15582a05 100644 --- a/twins/scenario_test.go +++ b/twins/scenario_test.go @@ -12,10 +12,23 @@ func TestBasicScenario(t *testing.T) { for i := 1; i <= 4; i++ { allNodesSet.Add(uint32(i)) } + lonely := make(NodeSet) + lonely.Add(3) + + others := make(NodeSet) + for i := 1; i <= 4; i++ { + if !lonely.Contains(uint32(i)) { + others.Add(uint32(i)) + } + } + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) - s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) - s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) - s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{lonely, others}}) + s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) + // s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + // s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) + // s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) result, err := ExecuteScenario(s, 4, 0, 100, "chainedhotstuff") if err != nil { @@ -26,7 +39,11 @@ func TestBasicScenario(t *testing.T) { t.Errorf("Expected no safety violations") } - if result.Commits != 1 { - t.Error("Expected one commit") + if result.Commits < 1 { + t.Error("Expected at least one commit") + } + + if result.Commits > 1 { + t.Error("Expected only one commit") } } From 600a0f7bac31e5f72a2a06f882cfd277f7a6eca6 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Tue, 4 Oct 2022 09:44:11 +0200 Subject: [PATCH 12/25] reorder synchronizer --- synchronizer/synchronizer.go | 50 ++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index a18a4912..fe0d95c0 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -207,7 +207,7 @@ func (s *Synchronizer) OnLocalTimeout() { s.configuration.Timeout(timeoutMsg) if s.currentView < s.highQC.View()+hotstuff.View(s.pipelinedViews) { - s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView).WithQC(s.highQC)) + s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView)) } s.OnRemoteTimeout(timeoutMsg) @@ -334,23 +334,12 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { } b, ok := syncInfo.Block() - if !ok { - b = s.leafBlock - } - - highQCBlock, ok := s.blockChain.LocalGet(s.highQC.BlockHash()) - if !ok { - s.logger.Error("AdvanceView: Could not find block referenced by new QC!") - } - if !s.blockChain.Extends(b, highQCBlock) { - b = highQCBlock - } - if b.View() > s.leafBlock.View() { - s.leafBlock = b + if ok { + s.updateLeafBlock(b) } - if b.View() > v && b.View() < s.highQC.View()+hotstuff.View(s.pipelinedViews) { - v = b.View() + if s.leafBlock.View() < s.highQC.View()+hotstuff.View(s.pipelinedViews) { + v = s.leafBlock.View() } if timeoutV, ok := syncInfo.TimeoutView(); ok && @@ -382,7 +371,7 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { leader := s.leaderRotation.GetLeader(s.currentView) if leader == s.opts.ID() { - s.consensus.Propose(syncInfo) + s.consensus.Propose(syncInfo.WithQC(s.highQC)) } else if replica, ok := s.configuration.Replica(leader); ok { replica.NewView(syncInfo) } @@ -391,25 +380,42 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { // updateHighQC attempts to update the highQC, but does not verify the qc first. // This method is meant to be used instead of the exported UpdateHighQC internally // in this package when the qc has already been verified. +// This method ensures, the block of the highQC is always available locally. func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { if qc.View() > s.highQC.View() { - s.highQC = qc - s.logger.Debug("HighQC updated") - - // I believe the following can be removed, if leafBlock is updated elsewhere. - // However, placed here now for backwards compatibility highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) if !ok { s.logger.Info("updateHighQC: Could not find block referenced by new QC!") return } + s.highQC = qc + s.logger.Debug("HighQC updated") + if s.leafBlock.View() < s.highQC.View() || !s.blockChain.Extends(s.leafBlock, highQCBlock) { s.leafBlock = highQCBlock } } } +// updateLeafBlock attempts to update the leafBlock. +// This method ensures, leafblock extends highQC. +func (s *Synchronizer) updateLeafBlock(b *hotstuff.Block) { + highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) + if !ok { + s.logger.Error("updateLeafBlock: Could not find block referenced by new QC!") + return + } + + if !s.blockChain.Extends(b, highQCBlock) { + s.logger.Info("updateLeafBlock: new block did not extend highQC!") + return + } + if b.View() > s.leafBlock.View() { + s.leafBlock = b + } +} + // updateHighTC attempts to update the highTC, but does not verify the tc first. func (s *Synchronizer) updateHighTC(tc hotstuff.TimeoutCert) { if tc.View() > s.highTC.View() { From 7813a4eb48467f0f2b55986620b6b5e0ee3abe60 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Tue, 4 Oct 2022 09:51:06 +0200 Subject: [PATCH 13/25] fixed typos --- types.go | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/types.go b/types.go index 7e64ea0a..30f021e8 100644 --- a/types.go +++ b/types.go @@ -172,13 +172,13 @@ func (pc PartialCert) ToBytes() []byte { // Generally, if highQC.View > highTC.View, there is no need to include highTC in the SyncInfo. // However, if highQC.View < highTC.View, we should still include highQC. // This can also hold an AggregateQC for Fast-Hotstuff. -// This can also hola a block. +// This can also hold a block and a view with local timeout. type SyncInfo struct { - qc *QuorumCert - tc *TimeoutCert - aggQC *AggregateQC - block *Block - timoutView View + qc *QuorumCert + tc *TimeoutCert + aggQC *AggregateQC + block *Block + timeoutView View } // NewSyncInfo returns a new SyncInfo struct. @@ -208,7 +208,7 @@ func (si SyncInfo) WithBlock(b *Block) SyncInfo { // WithTimeoutView returns a copy of the SyncInfo struct with a view marked as local timeout. func (si SyncInfo) WithTimeoutView(tv View) SyncInfo { - si.timoutView = tv + si.timeoutView = tv return si } @@ -253,8 +253,8 @@ func (si SyncInfo) Block() (_ *Block, _ bool) { // TimeoutView returns the timeout view, if present. func (si SyncInfo) TimeoutView() (_ View, _ bool) { - if si.timoutView != 0 { - return si.timoutView, true + if si.timeoutView != 0 { + return si.timeoutView, true } return } @@ -274,8 +274,8 @@ func (si SyncInfo) String() string { if si.block != nil { fmt.Fprintf(&sb, "%s ", si.block) } - if si.timoutView != 0 { - fmt.Fprintf(&sb, "timeoutView: %d ", si.timoutView) + if si.timeoutView != 0 { + fmt.Fprintf(&sb, "timeoutView: %d ", si.timeoutView) } sb.WriteRune('}') return sb.String() From eb95aa1b566321e76dec2296f05e3403fe58758e Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Tue, 4 Oct 2022 10:34:49 +0200 Subject: [PATCH 14/25] add isInPipelineStretch function --- synchronizer/synchronizer.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index fe0d95c0..9557df76 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -36,8 +36,8 @@ type Synchronizer struct { duration ViewDuration timer *time.Timer - // number of cuncurrent views, 0 and 1 both give no concurrency. - pipelinedViews uint8 + // number of concurrent views, 0 and 1 both give no concurrency. + pipelinedViews hotstuff.View viewCtx context.Context // a context that is cancelled at the end of the current view cancelCtx context.CancelFunc @@ -206,7 +206,7 @@ func (s *Synchronizer) OnLocalTimeout() { s.configuration.Timeout(timeoutMsg) - if s.currentView < s.highQC.View()+hotstuff.View(s.pipelinedViews) { + if s.isInPipelineStretch(s.currentView) { s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView)) } @@ -338,12 +338,12 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { s.updateLeafBlock(b) } - if s.leafBlock.View() < s.highQC.View()+hotstuff.View(s.pipelinedViews) { + if s.isInPipelineStretch(s.leafBlock.View()) { v = s.leafBlock.View() } if timeoutV, ok := syncInfo.TimeoutView(); ok && - timeoutV > v && timeoutV < s.highQC.View()+hotstuff.View(s.pipelinedViews) { + timeoutV > v && s.isInPipelineStretch(timeoutV) { v = timeoutV } @@ -377,6 +377,12 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { } } +// isInPipelineStretch checks wether the given view lies +// less then the pipelinestretch from the highQC +func (s *Synchronizer) isInPipelineStretch(v hotstuff.View) bool { + return v > s.highQC.View() && v < s.highQC.View()+s.pipelinedViews +} + // updateHighQC attempts to update the highQC, but does not verify the qc first. // This method is meant to be used instead of the exported UpdateHighQC internally // in this package when the qc has already been verified. From 9b77e811aa03f609fd6547c4a3e2bc80d4cf2de6 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Tue, 4 Oct 2022 15:17:15 +0200 Subject: [PATCH 15/25] pipelinedViews flag --- consensus/consensus_test.go | 2 +- internal/cli/run.go | 2 + internal/orchestration/worker.go | 2 +- .../proto/orchestrationpb/orchestration.pb.go | 403 +++++++++--------- .../proto/orchestrationpb/orchestration.proto | 10 +- synchronizer/synchronizer.go | 4 +- synchronizer/synchronizer_test.go | 4 +- twins/network.go | 2 +- 8 files changed, 218 insertions(+), 211 deletions(-) diff --git a/consensus/consensus_test.go b/consensus/consensus_test.go index c48076a4..0e3d0ba8 100644 --- a/consensus/consensus_test.go +++ b/consensus/consensus_test.go @@ -19,7 +19,7 @@ func TestVote(t *testing.T) { ctrl := gomock.NewController(t) bl := testutil.CreateBuilders(t, ctrl, n) cs := mocks.NewMockConsensus(ctrl) - bl[0].Add(synchronizer.New(testutil.FixedTimeout(1000)), cs) + bl[0].Add(synchronizer.New(testutil.FixedTimeout(1000), 1), cs) hl := bl.Build() hs := hl[0] diff --git a/internal/cli/run.go b/internal/cli/run.go index 7c10aefc..3d83f6cf 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -62,6 +62,7 @@ func init() { runCmd.Flags().String("leader-rotation", "round-robin", "name of the leader rotation algorithm") runCmd.Flags().Int64("shared-seed", 0, "Shared random number generator seed") runCmd.Flags().StringSlice("modules", nil, "Name additional modules to be loaded.") + runCmd.Flags().Int("pipelined-views", 1, "number of blocks/views proposed concurrently") runCmd.Flags().Bool("worker", false, "run a local worker") runCmd.Flags().StringSlice("hosts", nil, "the remote hosts to run the experiment on via ssh") @@ -116,6 +117,7 @@ func runController() { MaxTimeout: durationpb.New(viper.GetDuration("max-timeout")), SharedSeed: viper.GetInt64("shared-seed"), Modules: viper.GetStringSlice("modules"), + PipelinedViews: viper.GetUint32("pipelined-views"), }, ClientOpts: &orchestrationpb.ClientOpts{ UseTLS: true, diff --git a/internal/orchestration/worker.go b/internal/orchestration/worker.go index 2b35322b..e3d2b229 100644 --- a/internal/orchestration/worker.go +++ b/internal/orchestration/worker.go @@ -193,7 +193,7 @@ func (w *Worker) createReplica(opts *orchestrationpb.ReplicaOpts) (*replica.Repl float64(opts.GetInitialTimeout().AsDuration().Nanoseconds())/float64(time.Millisecond), float64(opts.GetMaxTimeout().AsDuration().Nanoseconds())/float64(time.Millisecond), float64(opts.GetTimeoutMultiplier()), - )) + ), opts.GetPipelinedViews()) builder.Add( eventloop.New(1000), diff --git a/internal/proto/orchestrationpb/orchestration.pb.go b/internal/proto/orchestrationpb/orchestration.pb.go index eab91862..f30d8643 100644 --- a/internal/proto/orchestrationpb/orchestration.pb.go +++ b/internal/proto/orchestrationpb/orchestration.pb.go @@ -1,15 +1,15 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.27.1 -// protoc v3.19.4 +// protoc-gen-go v1.28.0 +// protoc v3.10.0 // source: internal/proto/orchestrationpb/orchestration.proto package orchestrationpb import ( + duration "github.com/golang/protobuf/ptypes/duration" protoreflect "google.golang.org/protobuf/reflect/protoreflect" protoimpl "google.golang.org/protobuf/runtime/protoimpl" - durationpb "google.golang.org/protobuf/types/known/durationpb" reflect "reflect" sync "sync" ) @@ -36,11 +36,11 @@ type ReplicaOpts struct { // Determines whether TLS should be used. UseTLS bool `protobuf:"varint,4,opt,name=UseTLS,proto3" json:"UseTLS,omitempty"` // The replica's TLS certificate. - Certificate []byte `protobuf:"bytes,5,opt,name=Certificate,proto3,oneof" json:"Certificate,omitempty"` + Certificate []byte `protobuf:"bytes,5,opt,name=Certificate,proto3" json:"Certificate,omitempty"` // The private key of the TLS certificate. - CertificateKey []byte `protobuf:"bytes,6,opt,name=CertificateKey,proto3,oneof" json:"CertificateKey,omitempty"` + CertificateKey []byte `protobuf:"bytes,6,opt,name=CertificateKey,proto3" json:"CertificateKey,omitempty"` // The certificate authority that created the TLS certificates. - CertificateAuthority []byte `protobuf:"bytes,7,opt,name=CertificateAuthority,proto3,oneof" json:"CertificateAuthority,omitempty"` + CertificateAuthority []byte `protobuf:"bytes,7,opt,name=CertificateAuthority,proto3" json:"CertificateAuthority,omitempty"` // The name of the crypto implementation to use. Crypto string `protobuf:"bytes,8,opt,name=Crypto,proto3" json:"Crypto,omitempty"` // The name of the consensus implementation to use. @@ -50,11 +50,11 @@ type ReplicaOpts struct { // The number of client commands that should be batched together. BatchSize uint32 `protobuf:"varint,11,opt,name=BatchSize,proto3" json:"BatchSize,omitempty"` // The timeout of the initial connection setup. - ConnectTimeout *durationpb.Duration `protobuf:"bytes,17,opt,name=ConnectTimeout,proto3" json:"ConnectTimeout,omitempty"` + ConnectTimeout *duration.Duration `protobuf:"bytes,17,opt,name=ConnectTimeout,proto3" json:"ConnectTimeout,omitempty"` // The initial view duration. - InitialTimeout *durationpb.Duration `protobuf:"bytes,12,opt,name=InitialTimeout,proto3" json:"InitialTimeout,omitempty"` + InitialTimeout *duration.Duration `protobuf:"bytes,12,opt,name=InitialTimeout,proto3" json:"InitialTimeout,omitempty"` // The maximum view timeout. - MaxTimeout *durationpb.Duration `protobuf:"bytes,19,opt,name=MaxTimeout,proto3" json:"MaxTimeout,omitempty"` + MaxTimeout *duration.Duration `protobuf:"bytes,19,opt,name=MaxTimeout,proto3" json:"MaxTimeout,omitempty"` // The number of samples used to calculate view duration. TimeoutSamples uint32 `protobuf:"varint,13,opt,name=TimeoutSamples,proto3" json:"TimeoutSamples,omitempty"` // The number that the old view duration should be multiplied by when a new @@ -66,6 +66,8 @@ type ReplicaOpts struct { SharedSeed int64 `protobuf:"varint,20,opt,name=SharedSeed,proto3" json:"SharedSeed,omitempty"` // A list of modules to load. Modules []string `protobuf:"bytes,21,rep,name=Modules,proto3" json:"Modules,omitempty"` + // Number of blocks proposed concurrently + PipelinedViews uint32 `protobuf:"varint,22,opt,name=PipelinedViews,proto3" json:"PipelinedViews,omitempty"` } func (x *ReplicaOpts) Reset() { @@ -177,21 +179,21 @@ func (x *ReplicaOpts) GetBatchSize() uint32 { return 0 } -func (x *ReplicaOpts) GetConnectTimeout() *durationpb.Duration { +func (x *ReplicaOpts) GetConnectTimeout() *duration.Duration { if x != nil { return x.ConnectTimeout } return nil } -func (x *ReplicaOpts) GetInitialTimeout() *durationpb.Duration { +func (x *ReplicaOpts) GetInitialTimeout() *duration.Duration { if x != nil { return x.InitialTimeout } return nil } -func (x *ReplicaOpts) GetMaxTimeout() *durationpb.Duration { +func (x *ReplicaOpts) GetMaxTimeout() *duration.Duration { if x != nil { return x.MaxTimeout } @@ -233,6 +235,13 @@ func (x *ReplicaOpts) GetModules() []string { return nil } +func (x *ReplicaOpts) GetPipelinedViews() uint32 { + if x != nil { + return x.PipelinedViews + } + return 0 +} + // ReplicaInfo is the information that the replicas need about each other. type ReplicaInfo struct { state protoimpl.MessageState @@ -332,15 +341,15 @@ type ClientOpts struct { // The size in bytes of each command. PayloadSize uint32 `protobuf:"varint,9,opt,name=PayloadSize,proto3" json:"PayloadSize,omitempty"` // The timeout of the initial connection. - ConnectTimeout *durationpb.Duration `protobuf:"bytes,10,opt,name=ConnectTimeout,proto3" json:"ConnectTimeout,omitempty"` + ConnectTimeout *duration.Duration `protobuf:"bytes,10,opt,name=ConnectTimeout,proto3" json:"ConnectTimeout,omitempty"` // The limit on how many commands can be sent per second. RateLimit float64 `protobuf:"fixed64,11,opt,name=RateLimit,proto3" json:"RateLimit,omitempty"` // The amount of commands per second to increase the rate limit by. RateStep float64 `protobuf:"fixed64,12,opt,name=RateStep,proto3" json:"RateStep,omitempty"` // How often to increase the rate limit. - RateStepInterval *durationpb.Duration `protobuf:"bytes,13,opt,name=RateStepInterval,proto3" json:"RateStepInterval,omitempty"` + RateStepInterval *duration.Duration `protobuf:"bytes,13,opt,name=RateStepInterval,proto3" json:"RateStepInterval,omitempty"` // The timeout for a command. - Timeout *durationpb.Duration `protobuf:"bytes,14,opt,name=Timeout,proto3" json:"Timeout,omitempty"` + Timeout *duration.Duration `protobuf:"bytes,14,opt,name=Timeout,proto3" json:"Timeout,omitempty"` } func (x *ClientOpts) Reset() { @@ -403,7 +412,7 @@ func (x *ClientOpts) GetPayloadSize() uint32 { return 0 } -func (x *ClientOpts) GetConnectTimeout() *durationpb.Duration { +func (x *ClientOpts) GetConnectTimeout() *duration.Duration { if x != nil { return x.ConnectTimeout } @@ -424,14 +433,14 @@ func (x *ClientOpts) GetRateStep() float64 { return 0 } -func (x *ClientOpts) GetRateStepInterval() *durationpb.Duration { +func (x *ClientOpts) GetRateStepInterval() *duration.Duration { if x != nil { return x.RateStepInterval } return nil } -func (x *ClientOpts) GetTimeout() *durationpb.Duration { +func (x *ClientOpts) GetTimeout() *duration.Duration { if x != nil { return x.Timeout } @@ -777,7 +786,7 @@ type StartClientRequest struct { // The clients to create. Clients map[uint32]*ClientOpts `protobuf:"bytes,1,rep,name=Clients,proto3" json:"Clients,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // The certificate authority that created the TLS certificates. - CertificateAuthority []byte `protobuf:"bytes,7,opt,name=CertificateAuthority,proto3,oneof" json:"CertificateAuthority,omitempty"` + CertificateAuthority []byte `protobuf:"bytes,7,opt,name=CertificateAuthority,proto3" json:"CertificateAuthority,omitempty"` // The replicas to connect to. Configuration map[uint32]*ReplicaInfo `protobuf:"bytes,10,rep,name=Configuration,proto3" json:"Configuration,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } @@ -1005,196 +1014,192 @@ var file_internal_proto_orchestrationpb_orchestration_proto_rawDesc = []byte{ 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xb7, 0x06, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x94, 0x06, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x1e, 0x0a, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x12, 0x25, 0x0a, 0x0b, 0x43, + 0x01, 0x28, 0x08, 0x52, 0x06, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x12, 0x20, 0x0a, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, - 0x48, 0x00, 0x52, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x88, - 0x01, 0x01, 0x12, 0x2b, 0x0a, 0x0e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, - 0x65, 0x4b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x0e, 0x43, 0x65, - 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x88, 0x01, 0x01, 0x12, - 0x37, 0x0a, 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, - 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x02, 0x52, - 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x69, 0x74, 0x79, 0x88, 0x01, 0x01, 0x12, 0x16, 0x0a, 0x06, 0x43, 0x72, 0x79, 0x70, - 0x74, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x43, 0x72, 0x79, 0x70, 0x74, 0x6f, - 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x18, 0x09, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x09, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x12, 0x26, - 0x0a, 0x0e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x6f, - 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x61, 0x74, 0x63, 0x68, 0x53, - 0x69, 0x7a, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x42, 0x61, 0x74, 0x63, 0x68, - 0x53, 0x69, 0x7a, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, - 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x41, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, 0x69, - 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x49, 0x6e, 0x69, 0x74, - 0x69, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x4d, 0x61, - 0x78, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x4d, 0x61, 0x78, 0x54, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x54, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x2c, 0x0a, - 0x11, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, - 0x65, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x02, 0x52, 0x11, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x11, 0x42, - 0x79, 0x7a, 0x61, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, - 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x42, 0x79, 0x7a, 0x61, 0x6e, 0x74, 0x69, 0x6e, - 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x68, 0x61, - 0x72, 0x65, 0x64, 0x53, 0x65, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, 0x53, - 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x65, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x73, 0x42, 0x0e, 0x0a, 0x0c, 0x5f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x42, 0x11, 0x0a, 0x0f, 0x5f, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, - 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x42, 0x17, 0x0a, 0x15, 0x5f, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x22, - 0x97, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x12, - 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, - 0x18, 0x0a, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x50, 0x75, - 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x43, - 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xf5, 0x02, 0x0a, 0x0a, 0x43, 0x6c, - 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x54, - 0x4c, 0x53, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, - 0x12, 0x24, 0x0a, 0x0d, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, - 0x74, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, - 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, - 0x64, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x50, 0x61, 0x79, - 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, + 0x52, 0x0b, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x12, 0x26, 0x0a, + 0x0e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0e, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x0c, 0x52, 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x16, 0x0a, 0x06, 0x43, 0x72, 0x79, + 0x70, 0x74, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x43, 0x72, 0x79, 0x70, 0x74, + 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x18, 0x09, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x43, 0x6f, 0x6e, 0x73, 0x65, 0x6e, 0x73, 0x75, 0x73, 0x12, + 0x26, 0x0a, 0x0e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x4c, 0x65, 0x61, 0x64, 0x65, 0x72, 0x52, + 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x42, 0x61, 0x74, 0x63, 0x68, + 0x53, 0x69, 0x7a, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x42, 0x61, 0x74, 0x63, + 0x68, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x41, 0x0a, 0x0e, 0x49, 0x6e, 0x69, 0x74, + 0x69, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x43, 0x6f, 0x6e, - 0x6e, 0x65, 0x63, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x52, - 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, - 0x52, 0x61, 0x74, 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x61, 0x74, - 0x65, 0x53, 0x74, 0x65, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x52, 0x61, 0x74, - 0x65, 0x53, 0x74, 0x65, 0x70, 0x12, 0x45, 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, - 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x49, 0x6e, 0x69, + 0x74, 0x69, 0x61, 0x6c, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x4d, + 0x61, 0x78, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x13, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, - 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x52, 0x61, 0x74, 0x65, - 0x53, 0x74, 0x65, 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x33, 0x0a, 0x07, - 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, + 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0a, 0x4d, 0x61, 0x78, 0x54, + 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, + 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x12, 0x2c, + 0x0a, 0x11, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, + 0x69, 0x65, 0x72, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x02, 0x52, 0x11, 0x54, 0x69, 0x6d, 0x65, 0x6f, + 0x75, 0x74, 0x4d, 0x75, 0x6c, 0x74, 0x69, 0x70, 0x6c, 0x69, 0x65, 0x72, 0x12, 0x2c, 0x0a, 0x11, + 0x42, 0x79, 0x7a, 0x61, 0x6e, 0x74, 0x69, 0x6e, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, + 0x79, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x11, 0x42, 0x79, 0x7a, 0x61, 0x6e, 0x74, 0x69, + 0x6e, 0x65, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x53, 0x68, + 0x61, 0x72, 0x65, 0x64, 0x53, 0x65, 0x65, 0x64, 0x18, 0x14, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0a, + 0x53, 0x68, 0x61, 0x72, 0x65, 0x64, 0x53, 0x65, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x15, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x4d, 0x6f, 0x64, + 0x75, 0x6c, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x50, 0x69, 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, + 0x64, 0x56, 0x69, 0x65, 0x77, 0x73, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x50, 0x69, + 0x70, 0x65, 0x6c, 0x69, 0x6e, 0x65, 0x64, 0x56, 0x69, 0x65, 0x77, 0x73, 0x22, 0x97, 0x01, 0x0a, + 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x0e, 0x0a, 0x02, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x18, 0x0a, 0x07, + 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x41, + 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x1c, 0x0a, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, + 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x09, 0x50, 0x75, 0x62, 0x6c, 0x69, + 0x63, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x50, + 0x6f, 0x72, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, + 0x50, 0x6f, 0x72, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x22, 0xf5, 0x02, 0x0a, 0x0a, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x4f, 0x70, 0x74, 0x73, 0x12, 0x0e, 0x0a, 0x02, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x02, 0x49, 0x44, 0x12, 0x16, 0x0a, 0x06, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x55, 0x73, 0x65, 0x54, 0x4c, 0x53, 0x12, 0x24, 0x0a, + 0x0d, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, 0x65, 0x6e, 0x74, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0d, 0x4d, 0x61, 0x78, 0x43, 0x6f, 0x6e, 0x63, 0x75, 0x72, 0x72, + 0x65, 0x6e, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x53, 0x69, + 0x7a, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0b, 0x50, 0x61, 0x79, 0x6c, 0x6f, 0x61, + 0x64, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, - 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, - 0x74, 0x22, 0xc2, 0x01, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x08, 0x52, 0x65, - 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6f, - 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x52, 0x08, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x1a, 0x59, 0x0a, 0x0d, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, - 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc2, 0x01, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, - 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x4f, 0x0a, 0x08, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, - 0x0b, 0x32, 0x33, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, - 0x1a, 0x59, 0x0a, 0x0d, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x4f, 0x70, 0x74, 0x73, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x01, 0x0a, 0x15, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x50, 0x0a, 0x08, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, - 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, - 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x52, - 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x1a, 0x59, 0x0a, 0x0d, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, - 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x72, 0x63, 0x68, + 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0e, 0x43, 0x6f, 0x6e, 0x6e, 0x65, 0x63, + 0x74, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x52, 0x61, 0x74, 0x65, + 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x01, 0x52, 0x09, 0x52, 0x61, 0x74, + 0x65, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x52, 0x61, 0x74, 0x65, 0x53, 0x74, + 0x65, 0x70, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x01, 0x52, 0x08, 0x52, 0x61, 0x74, 0x65, 0x53, 0x74, + 0x65, 0x70, 0x12, 0x45, 0x0a, 0x10, 0x52, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, 0x70, 0x49, 0x6e, + 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x10, 0x52, 0x61, 0x74, 0x65, 0x53, 0x74, 0x65, + 0x70, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x33, 0x0a, 0x07, 0x54, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xc2, + 0x01, 0x0a, 0x14, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x4f, 0x0a, 0x08, 0x52, 0x65, 0x70, 0x6c, 0x69, + 0x63, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0xe6, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, - 0x69, 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x49, 0x44, - 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x03, 0x49, 0x44, 0x73, 0x12, 0x5d, 0x0a, 0x0d, - 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x37, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x43, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5e, 0x0a, 0x12, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x16, 0x0a, 0x14, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x26, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, - 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x49, 0x44, 0x73, - 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x03, 0x49, 0x44, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x13, - 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x06, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, - 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, - 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x1a, 0x39, 0x0a, - 0x0b, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, - 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, - 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc9, 0x03, 0x0a, 0x12, 0x53, 0x74, 0x61, - 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x4a, 0x0a, 0x07, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, + 0x69, 0x63, 0x61, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x1a, 0x59, 0x0a, 0x0d, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x72, 0x63, + 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0xc2, 0x01, 0x0a, 0x14, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, + 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4f, 0x0a, 0x08, + 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x33, + 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, + 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x08, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x1a, 0x59, 0x0a, + 0x0d, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, + 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x01, 0x0a, 0x15, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x50, 0x0a, 0x08, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x34, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x52, 0x65, 0x70, 0x6c, + 0x69, 0x63, 0x61, 0x73, 0x1a, 0x59, 0x0a, 0x0d, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x73, + 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, + 0xe6, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x49, 0x44, 0x73, 0x18, 0x01, + 0x20, 0x03, 0x28, 0x0d, 0x52, 0x03, 0x49, 0x44, 0x73, 0x12, 0x5d, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x37, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x5e, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1c, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, + 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x16, 0x0a, 0x14, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x26, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0d, 0x52, 0x03, 0x49, 0x44, 0x73, 0x22, 0x9a, 0x01, 0x0a, 0x13, 0x53, 0x74, 0x6f, + 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x48, 0x0a, 0x06, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x52, 0x07, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x37, 0x0a, 0x14, 0x43, - 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, - 0x69, 0x74, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x00, 0x52, 0x14, 0x43, 0x65, 0x72, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, - 0x79, 0x88, 0x01, 0x01, 0x12, 0x5c, 0x0a, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x6f, 0x72, - 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x53, 0x74, - 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x0d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x5e, 0x0a, 0x12, 0x43, - 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, - 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, - 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x17, 0x0a, 0x15, 0x5f, - 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x74, 0x79, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x0a, 0x11, 0x53, - 0x74, 0x6f, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x12, 0x10, 0x0a, 0x03, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x03, 0x49, - 0x44, 0x73, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0d, 0x0a, 0x0b, 0x51, 0x75, 0x69, 0x74, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x62, 0x2f, 0x68, 0x6f, 0x74, 0x73, - 0x74, 0x75, 0x66, 0x66, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2f, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x62, 0x2e, 0x53, 0x74, 0x6f, 0x70, 0x52, 0x65, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, + 0x72, 0x79, 0x52, 0x06, 0x48, 0x61, 0x73, 0x68, 0x65, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x48, 0x61, + 0x73, 0x68, 0x65, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, + 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xab, 0x03, 0x0a, 0x12, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x4a, 0x0a, 0x07, + 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, + 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, + 0x07, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x12, 0x32, 0x0a, 0x14, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, + 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x14, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x5c, 0x0a, 0x0d, + 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, + 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0d, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x1a, 0x57, 0x0a, 0x0c, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x6f, 0x72, + 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x43, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x4f, 0x70, 0x74, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x1a, 0x5e, 0x0a, 0x12, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x32, 0x0a, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x6f, 0x72, 0x63, + 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x70, 0x62, 0x2e, 0x52, 0x65, 0x70, + 0x6c, 0x69, 0x63, 0x61, 0x49, 0x6e, 0x66, 0x6f, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, + 0x02, 0x38, 0x01, 0x22, 0x15, 0x0a, 0x13, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6c, 0x69, 0x65, + 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x0a, 0x11, 0x53, 0x74, + 0x6f, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x10, 0x0a, 0x03, 0x49, 0x44, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0d, 0x52, 0x03, 0x49, 0x44, + 0x73, 0x22, 0x14, 0x0a, 0x12, 0x53, 0x74, 0x6f, 0x70, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x0d, 0x0a, 0x0b, 0x51, 0x75, 0x69, 0x74, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x42, 0x3a, 0x5a, 0x38, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x6c, 0x61, 0x62, 0x2f, 0x68, 0x6f, 0x74, 0x73, 0x74, + 0x75, 0x66, 0x66, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2f, 0x6f, 0x72, 0x63, 0x68, 0x65, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1233,7 +1238,7 @@ var file_internal_proto_orchestrationpb_orchestration_proto_goTypes = []interfac nil, // 19: orchestrationpb.StopReplicaResponse.HashesEntry nil, // 20: orchestrationpb.StartClientRequest.ClientsEntry nil, // 21: orchestrationpb.StartClientRequest.ConfigurationEntry - (*durationpb.Duration)(nil), // 22: google.protobuf.Duration + (*duration.Duration)(nil), // 22: google.protobuf.Duration } var file_internal_proto_orchestrationpb_orchestration_proto_depIdxs = []int32{ 22, // 0: orchestrationpb.ReplicaOpts.ConnectTimeout:type_name -> google.protobuf.Duration @@ -1449,8 +1454,6 @@ func file_internal_proto_orchestrationpb_orchestration_proto_init() { } } } - file_internal_proto_orchestrationpb_orchestration_proto_msgTypes[0].OneofWrappers = []interface{}{} - file_internal_proto_orchestrationpb_orchestration_proto_msgTypes[10].OneofWrappers = []interface{}{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ diff --git a/internal/proto/orchestrationpb/orchestration.proto b/internal/proto/orchestrationpb/orchestration.proto index 16055a32..ee196486 100644 --- a/internal/proto/orchestrationpb/orchestration.proto +++ b/internal/proto/orchestrationpb/orchestration.proto @@ -21,11 +21,11 @@ message ReplicaOpts { // Determines whether TLS should be used. bool UseTLS = 4; // The replica's TLS certificate. - optional bytes Certificate = 5; + bytes Certificate = 5; // The private key of the TLS certificate. - optional bytes CertificateKey = 6; + bytes CertificateKey = 6; // The certificate authority that created the TLS certificates. - optional bytes CertificateAuthority = 7; + bytes CertificateAuthority = 7; // The name of the crypto implementation to use. string Crypto = 8; // The name of the consensus implementation to use. @@ -51,6 +51,8 @@ message ReplicaOpts { int64 SharedSeed = 20; // A list of modules to load. repeated string Modules = 21; + // Number of blocks proposed concurrently + uint32 PipelinedViews = 22; } // ReplicaInfo is the information that the replicas need about each other. @@ -124,7 +126,7 @@ message StartClientRequest { // The clients to create. map Clients = 1; // The certificate authority that created the TLS certificates. - optional bytes CertificateAuthority = 7; + bytes CertificateAuthority = 7; // The replicas to connect to. map Configuration = 10; } diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 9557df76..fa251b7b 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -88,13 +88,13 @@ func (s *Synchronizer) InitModule(mods *modules.Core) { } // New creates a new Synchronizer. -func New(viewDuration ViewDuration) modules.Synchronizer { +func New(viewDuration ViewDuration, pipelinedViews uint32) modules.Synchronizer { ctx, cancel := context.WithCancel(context.Background()) return &Synchronizer{ leafBlock: hotstuff.GetGenesis(), currentView: 1, - pipelinedViews: 2, // this should be retrieved from a configuration. + pipelinedViews: hotstuff.View(pipelinedViews), viewCtx: ctx, cancelCtx: cancel, diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index 6f8b6780..1ae2bbb2 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -16,7 +16,7 @@ func TestAdvanceViewQC(t *testing.T) { const n = 4 ctrl := gomock.NewController(t) builders := testutil.CreateBuilders(t, ctrl, n) - s := New(testutil.FixedTimeout(1000)) + s := New(testutil.FixedTimeout(1000), 1) hs := mocks.NewMockConsensus(ctrl) builders[0].Add(s, hs) @@ -50,7 +50,7 @@ func TestAdvanceViewTC(t *testing.T) { const n = 4 ctrl := gomock.NewController(t) builders := testutil.CreateBuilders(t, ctrl, n) - s := New(testutil.FixedTimeout(100)) + s := New(testutil.FixedTimeout(100), 1) hs := mocks.NewMockConsensus(ctrl) builders[0].Add(s, hs) diff --git a/twins/network.go b/twins/network.go index 30d6ce9f..2328b939 100644 --- a/twins/network.go +++ b/twins/network.go @@ -143,7 +143,7 @@ func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusN consensus.New(consensusModule), consensus.NewVotingMachine(), crypto.NewCache(ecdsa.New(), 100), - synchronizer.New(FixedTimeout(0)), + synchronizer.New(FixedTimeout(0), 1), logging.NewWithDest(&node.log, fmt.Sprintf("r%dn%d", nodeID.ReplicaID, nodeID.NetworkID)), // twins-specific: &configuration{network: n, node: node}, From e13b4616195ddf4804ff73c1783a2e36b05630b6 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Fri, 7 Oct 2022 13:43:36 +0200 Subject: [PATCH 16/25] added some Twins tests and fixes for bugs found. --- consensus/chainedhotstuff/chainedhotstuff.go | 4 +- internal/cli/twins.go | 2 +- synchronizer/synchronizer.go | 6 +- twins/fhsbug_test.go | 2 +- twins/network.go | 11 +- twins/scenario.go | 4 +- twins/scenario_test.go | 121 +++++++++++++++++-- twins/twins_test.go | 2 +- 8 files changed, 132 insertions(+), 20 deletions(-) diff --git a/consensus/chainedhotstuff/chainedhotstuff.go b/consensus/chainedhotstuff/chainedhotstuff.go index df1e5132..0bdd0f0e 100644 --- a/consensus/chainedhotstuff/chainedhotstuff.go +++ b/consensus/chainedhotstuff/chainedhotstuff.go @@ -68,7 +68,9 @@ func (hs *ChainedHotStuff) CommitRule(block *hotstuff.Block) *hotstuff.Block { } // normal hotstuff - if block1.Parent() == block2.Hash() && block2.Parent() == block3.Hash() { + if block1.Parent() == block2.Hash() && + block2.Parent() == block3.Hash() && + block3.View() == block1.View()-2 { hs.logger.Debug("DECIDE: ", block3) return block3 } diff --git a/internal/cli/twins.go b/internal/cli/twins.go index 82e48104..79db0b27 100644 --- a/internal/cli/twins.go +++ b/internal/cli/twins.go @@ -260,7 +260,7 @@ func (ti twinsInstance) generateAndExecuteScenario() (bool, error) { t := time.Now() - result, err := twins.ExecuteScenario(scenario, settings.NumNodes, settings.NumTwins, settings.Ticks, twinsConsensus) + result, err := twins.ExecuteScenario(scenario, settings.NumNodes, settings.NumTwins, settings.Ticks, twinsConsensus, 1) if err != nil { return false, err } diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index fa251b7b..68481ab0 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -389,7 +389,7 @@ func (s *Synchronizer) isInPipelineStretch(v hotstuff.View) bool { // This method ensures, the block of the highQC is always available locally. func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { if qc.View() > s.highQC.View() { - highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) + qcBlock, ok := s.blockChain.Get(qc.BlockHash()) if !ok { s.logger.Info("updateHighQC: Could not find block referenced by new QC!") return @@ -398,8 +398,8 @@ func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { s.highQC = qc s.logger.Debug("HighQC updated") - if s.leafBlock.View() < s.highQC.View() || !s.blockChain.Extends(s.leafBlock, highQCBlock) { - s.leafBlock = highQCBlock + if s.leafBlock.View() < qc.View() || !s.blockChain.Extends(s.leafBlock, qcBlock) { + s.leafBlock = qcBlock } } } diff --git a/twins/fhsbug_test.go b/twins/fhsbug_test.go index 74f68a8f..27adc8a6 100644 --- a/twins/fhsbug_test.go +++ b/twins/fhsbug_test.go @@ -106,7 +106,7 @@ func TestFHSBug(t *testing.T) { settings := src.Settings() - res, err := twins.ExecuteScenario(scenario, settings.NumNodes, settings.NumTwins, 100, vulnerableModule) + res, err := twins.ExecuteScenario(scenario, settings.NumNodes, settings.NumTwins, 100, vulnerableModule, 1) if err != nil { t.Fatalf("failed to execute scenario: %v", err) } diff --git a/twins/network.go b/twins/network.go index 2328b939..605c9133 100644 --- a/twins/network.go +++ b/twins/network.go @@ -120,7 +120,7 @@ func (n *Network) GetNodeBuilder(id NodeID, pk hotstuff.PrivateKey) modules.Buil return builder } -func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusName string) error { +func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusName string, pipelinedViews uint32) error { cg := &commandGenerator{} for _, nodeID := range nodes { @@ -130,6 +130,13 @@ func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusN return err } + if pipelinedViews < 1 || consensusName != "chainedhotstuff" { + if pipelinedViews > 1 && consensusName != "chainedhotstuff" { + fmt.Errorf("pipelining currently only supported for chainedhotstuff") + } + pipelinedViews = 1 + } + builder := n.GetNodeBuilder(nodeID, pk) node := n.nodes[nodeID.NetworkID] @@ -143,7 +150,7 @@ func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusN consensus.New(consensusModule), consensus.NewVotingMachine(), crypto.NewCache(ecdsa.New(), 100), - synchronizer.New(FixedTimeout(0), 1), + synchronizer.New(FixedTimeout(0), pipelinedViews), logging.NewWithDest(&node.log, fmt.Sprintf("r%dn%d", nodeID.ReplicaID, nodeID.NetworkID)), // twins-specific: &configuration{network: n, node: node}, diff --git a/twins/scenario.go b/twins/scenario.go index fa5af748..2d69e809 100644 --- a/twins/scenario.go +++ b/twins/scenario.go @@ -46,7 +46,7 @@ type ScenarioResult struct { } // ExecuteScenario executes a twins scenario. -func ExecuteScenario(scenario Scenario, numNodes, numTwins uint8, numTicks int, consensusName string) (result ScenarioResult, err error) { +func ExecuteScenario(scenario Scenario, numNodes, numTwins uint8, numTicks int, consensusName string, pipelinedViews uint32) (result ScenarioResult, err error) { // Network simulator that blocks proposals, votes, and fetch requests between nodes that are in different partitions. // Timeout and NewView messages are permitted. network := NewPartitionedNetwork(scenario, @@ -60,7 +60,7 @@ func ExecuteScenario(scenario Scenario, numNodes, numTwins uint8, numTicks int, nodes, twins := assignNodeIDs(numNodes, numTwins) nodes = append(nodes, twins...) - err = network.createTwinsNodes(nodes, scenario, consensusName) + err = network.createTwinsNodes(nodes, scenario, consensusName, pipelinedViews) if err != nil { return ScenarioResult{}, err } diff --git a/twins/scenario_test.go b/twins/scenario_test.go index 15582a05..c2235150 100644 --- a/twins/scenario_test.go +++ b/twins/scenario_test.go @@ -12,6 +12,69 @@ func TestBasicScenario(t *testing.T) { for i := 1; i <= 4; i++ { allNodesSet.Add(uint32(i)) } + + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) + + result, err := ExecuteScenario(s, 4, 0, 100, "chainedhotstuff", 1) + if err != nil { + t.Fatal(err) + } + + if !result.Safe { + t.Errorf("Expected no safety violations") + } + + if result.Commits < 1 { + t.Error("Expected at least one commit") + } + + if result.Commits > 1 { + t.Error("Expected only one commit") + } +} + +func TestBasicPipeline(t *testing.T) { + s := Scenario{} + allNodesSet := make(NodeSet) + for i := 1; i <= 4; i++ { + allNodesSet.Add(uint32(i)) + } + + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + + result, err := ExecuteScenario(s, 4, 0, 100, "chainedhotstuff", 2) + if err != nil { + t.Fatal(err) + } + + if !result.Safe { + t.Errorf("Expected no safety violations") + } + + if result.Commits < 1 { + t.Error("Expected at least one commit") + } + + if result.Commits > 1 { + t.Error("Expected only one commit") + } +} + +func TestFaultyPipeline(t *testing.T) { + s := Scenario{} + allNodesSet := make(NodeSet) + for i := 1; i <= 4; i++ { + allNodesSet.Add(uint32(i)) + } lonely := make(NodeSet) lonely.Add(3) @@ -22,15 +85,16 @@ func TestBasicScenario(t *testing.T) { } } + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{lonely, others}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) - s = append(s, View{Leader: 3, Partitions: []NodeSet{lonely, others}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) - // s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) - // s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) - // s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) - result, err := ExecuteScenario(s, 4, 0, 100, "chainedhotstuff") + result, err := ExecuteScenario(s, 4, 0, 100, "chainedhotstuff", 2) if err != nil { t.Fatal(err) } @@ -39,11 +103,50 @@ func TestBasicScenario(t *testing.T) { t.Errorf("Expected no safety violations") } - if result.Commits < 1 { - t.Error("Expected at least one commit") + if result.Commits != 0 { + t.Error("Should not commit") } - if result.Commits > 1 { - t.Error("Expected only one commit") +} + +func TestFaultyPipelineCommit(t *testing.T) { + s := Scenario{} + allNodesSet := make(NodeSet) + for i := 1; i <= 4; i++ { + allNodesSet.Add(uint32(i)) } + lonely := make(NodeSet) + lonely.Add(3) + + others := make(NodeSet) + for i := 1; i <= 4; i++ { + if !lonely.Contains(uint32(i)) { + others.Add(uint32(i)) + } + } + + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{lonely, others}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + + result, err := ExecuteScenario(s, 4, 0, 100, "chainedhotstuff", 2) + if err != nil { + t.Fatal(err) + } + + if !result.Safe { + t.Errorf("Expected no safety violations") + } + + if result.Commits != 3 { + // commits blocks in view 1,2 and 4 + t.Errorf("Should commit 3 blocks, not %d", result.Commits) + } + } diff --git a/twins/twins_test.go b/twins/twins_test.go index 3b563984..47ab1d4e 100644 --- a/twins/twins_test.go +++ b/twins/twins_test.go @@ -31,7 +31,7 @@ func TestTwins(t *testing.T) { if err != nil { break } - result, err := twins.ExecuteScenario(s, numNodes, numTwins, 100, "chainedhotstuff") + result, err := twins.ExecuteScenario(s, numNodes, numTwins, 100, "chainedhotstuff", 1) if err != nil { t.Fatal(err) } From df3fc324538afeeadc39d8adedcddb7d618f4ee8 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Fri, 7 Oct 2022 14:41:31 +0200 Subject: [PATCH 17/25] fixing comments on synchronizer --- synchronizer/synchronizer.go | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 68481ab0..cbfd7482 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -37,7 +37,7 @@ type Synchronizer struct { timer *time.Timer // number of concurrent views, 0 and 1 both give no concurrency. - pipelinedViews hotstuff.View + pipelinedViews uint32 viewCtx context.Context // a context that is cancelled at the end of the current view cancelCtx context.CancelFunc @@ -94,7 +94,7 @@ func New(viewDuration ViewDuration, pipelinedViews uint32) modules.Synchronizer leafBlock: hotstuff.GetGenesis(), currentView: 1, - pipelinedViews: hotstuff.View(pipelinedViews), + pipelinedViews: pipelinedViews, viewCtx: ctx, cancelCtx: cancel, @@ -206,7 +206,7 @@ func (s *Synchronizer) OnLocalTimeout() { s.configuration.Timeout(timeoutMsg) - if s.isInPipelineStretch(s.currentView) { + if s.inPipeline(s.currentView) { s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView)) } @@ -338,12 +338,12 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { s.updateLeafBlock(b) } - if s.isInPipelineStretch(s.leafBlock.View()) { + if s.inPipeline(s.leafBlock.View()) { v = s.leafBlock.View() } if timeoutV, ok := syncInfo.TimeoutView(); ok && - timeoutV > v && s.isInPipelineStretch(timeoutV) { + timeoutV > v && s.inPipeline(timeoutV) { v = timeoutV } @@ -377,10 +377,9 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { } } -// isInPipelineStretch checks wether the given view lies -// less then the pipelinestretch from the highQC -func (s *Synchronizer) isInPipelineStretch(v hotstuff.View) bool { - return v > s.highQC.View() && v < s.highQC.View()+s.pipelinedViews +// inPipeline checks if the given view is in pipeline range from highQC. +func (s *Synchronizer) inPipeline(v hotstuff.View) bool { + return v > s.highQC.View() && v < s.highQC.View()+hotstuff.View(s.pipelinedViews) } // updateHighQC attempts to update the highQC, but does not verify the qc first. @@ -404,8 +403,8 @@ func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { } } -// updateLeafBlock attempts to update the leafBlock. -// This method ensures, leafblock extends highQC. +// updateLeafBlock attempts to update the leaf block. +// This method ensures leafBlock extends highQC. func (s *Synchronizer) updateLeafBlock(b *hotstuff.Block) { highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) if !ok { From 1c0b9a2c543f8f3cc135dfc58acbd916725dfc6f Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Fri, 7 Oct 2022 15:22:47 +0200 Subject: [PATCH 18/25] fix mock synchronizer --- internal/mocks/synchronizer_mock.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/internal/mocks/synchronizer_mock.go b/internal/mocks/synchronizer_mock.go index beb1ff3a..75cd8151 100644 --- a/internal/mocks/synchronizer_mock.go +++ b/internal/mocks/synchronizer_mock.go @@ -114,3 +114,17 @@ func (mr *MockSynchronizerMockRecorder) ViewContext() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ViewContext", reflect.TypeOf((*MockSynchronizer)(nil).ViewContext)) } + +// PipelinedViews mocks base method. +func (m *MockSynchronizer) PipelinedViews() hotstuff.View { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "PipelinedViews") + ret0, _ := ret[0].(hotstuff.View) + return ret0 +} + +// PipelinedViews indicates an expected call of PipelinedViews. +func (mr *MockSynchronizerMockRecorder) PipelinedViews() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PipelinedViews", reflect.TypeOf((*MockSynchronizer)(nil).PipelinedViews)) +} \ No newline at end of file From b3c68c93786e9db850be9ed07f7fd35f9729576a Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Fri, 7 Oct 2022 15:25:15 +0200 Subject: [PATCH 19/25] fix error handling in twins/network.go --- twins/network.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/twins/network.go b/twins/network.go index 605c9133..e952f98e 100644 --- a/twins/network.go +++ b/twins/network.go @@ -132,7 +132,7 @@ func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusN if pipelinedViews < 1 || consensusName != "chainedhotstuff" { if pipelinedViews > 1 && consensusName != "chainedhotstuff" { - fmt.Errorf("pipelining currently only supported for chainedhotstuff") + return fmt.Errorf("pipelining currently only supported for chainedhotstuff") } pipelinedViews = 1 } From aae49db4ba31404475852512adf01459e04e32f4 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Fri, 7 Oct 2022 15:33:58 +0200 Subject: [PATCH 20/25] 0 is an ok value for a view. --- synchronizer/synchronizer.go | 3 +-- types.go | 7 ++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index cbfd7482..be349797 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -342,8 +342,7 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { v = s.leafBlock.View() } - if timeoutV, ok := syncInfo.TimeoutView(); ok && - timeoutV > v && s.inPipeline(timeoutV) { + if timeoutV := syncInfo.TimeoutView(); timeoutV > v && s.inPipeline(timeoutV) { v = timeoutV } diff --git a/types.go b/types.go index 30f021e8..d45415a2 100644 --- a/types.go +++ b/types.go @@ -252,11 +252,8 @@ func (si SyncInfo) Block() (_ *Block, _ bool) { } // TimeoutView returns the timeout view, if present. -func (si SyncInfo) TimeoutView() (_ View, _ bool) { - if si.timeoutView != 0 { - return si.timeoutView, true - } - return +func (si SyncInfo) TimeoutView() (_ View) { + return si.timeoutView } func (si SyncInfo) String() string { From c563b9c50bcd2dae450455e4b9c2c0ef1166b349 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Tue, 11 Oct 2022 12:09:18 +0200 Subject: [PATCH 21/25] check leafblock for view and logging --- consensus/chainedhotstuff/chainedhotstuff.go | 4 ++-- consensus/consensus.go | 7 ++++++- synchronizer/synchronizer.go | 7 +++++-- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/consensus/chainedhotstuff/chainedhotstuff.go b/consensus/chainedhotstuff/chainedhotstuff.go index 0bdd0f0e..64890501 100644 --- a/consensus/chainedhotstuff/chainedhotstuff.go +++ b/consensus/chainedhotstuff/chainedhotstuff.go @@ -82,7 +82,7 @@ func (hs *ChainedHotStuff) CommitRule(block *hotstuff.Block) *hotstuff.Block { qcs[loopblock.QuorumCert().BlockHash()] = true parent, ok := hs.blockChain.LocalGet(loopblock.Parent()) if !ok { - hs.logger.Info("CommitRule: unable to retrieve parent block") + hs.logger.Infof("CommitRule: unable to retrieve block %.6s", loopblock.Parent().String()) return nil } loopblock = parent @@ -95,7 +95,7 @@ func (hs *ChainedHotStuff) CommitRule(block *hotstuff.Block) *hotstuff.Block { qcs[loopblock.QuorumCert().BlockHash()] = true parent, ok := hs.blockChain.LocalGet(loopblock.Parent()) if !ok { - hs.logger.Info("CommitRule: unable to retrieve parent block") + hs.logger.Infof("CommitRule: unable to retrieve block %.6s", loopblock.Parent().String()) return nil } if parent.View() != loopblock.View()-1 { diff --git a/consensus/consensus.go b/consensus/consensus.go index d7b20de6..3bd3e11e 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -128,6 +128,11 @@ func (cs *consensusBase) Propose(cert hotstuff.SyncInfo) { return } + parentHash := qc.BlockHash() + if cs.synchronizer.LeafBlock().View() < cs.synchronizer.View() { + parentHash = cs.synchronizer.LeafBlock().Hash() + } + var proposal hotstuff.ProposeMsg if proposer, ok := cs.impl.(ProposeRuler); ok { proposal, ok = proposer.ProposeRule(cert, cmd) @@ -139,7 +144,7 @@ func (cs *consensusBase) Propose(cert hotstuff.SyncInfo) { proposal = hotstuff.ProposeMsg{ ID: cs.opts.ID(), Block: hotstuff.NewBlock( - cs.synchronizer.LeafBlock().Hash(), + parentHash, qc, cmd, cs.synchronizer.View(), diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index be349797..025e8f16 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -277,6 +277,7 @@ func (s *Synchronizer) OnRemoteTimeout(timeout hotstuff.TimeoutMsg) { // OnNewView handles an incoming consensus.NewViewMsg func (s *Synchronizer) OnNewView(newView hotstuff.NewViewMsg) { + s.logger.Debug("Received NewView from ", newView.ID) s.AdvanceView(newView.SyncInfo) } @@ -371,7 +372,8 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { leader := s.leaderRotation.GetLeader(s.currentView) if leader == s.opts.ID() { s.consensus.Propose(syncInfo.WithQC(s.highQC)) - } else if replica, ok := s.configuration.Replica(leader); ok { + } else if replica, ok := s.configuration.Replica(leader); ok && !s.inPipeline(s.currentView) { + s.logger.Debug("sending NewView msg") replica.NewView(syncInfo) } } @@ -394,7 +396,7 @@ func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { } s.highQC = qc - s.logger.Debug("HighQC updated") + s.logger.Debug("HighQC updated to ", s.highQC) if s.leafBlock.View() < qc.View() || !s.blockChain.Extends(s.leafBlock, qcBlock) { s.leafBlock = qcBlock @@ -404,6 +406,7 @@ func (s *Synchronizer) updateHighQC(qc hotstuff.QuorumCert) { // updateLeafBlock attempts to update the leaf block. // This method ensures leafBlock extends highQC. +// This method does not ensure leafBlock lies within pipeline. func (s *Synchronizer) updateLeafBlock(b *hotstuff.Block) { highQCBlock, ok := s.blockChain.Get(s.highQC.BlockHash()) if !ok { From 5a9203745fcf866e27b9604ca6a8770a56684f21 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Wed, 12 Oct 2022 15:26:05 +0200 Subject: [PATCH 22/25] fixing out of order proposals --- consensus/consensus.go | 14 ++++++++++---- events.go | 1 + synchronizer/synchronizer.go | 6 ++++-- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index 3bd3e11e..d4a2e589 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -127,6 +127,7 @@ func (cs *consensusBase) Propose(cert hotstuff.SyncInfo) { cs.logger.Debug("Propose: No command") return } + cs.logger.Debugf("Propose: cmd %s", cmd) parentHash := qc.BlockHash() if cs.synchronizer.LeafBlock().View() < cs.synchronizer.View() { @@ -199,16 +200,21 @@ func (cs *consensusBase) OnPropose(proposal hotstuff.ProposeMsg) { //nolint:gocy return } - if qcBlock, ok := cs.blockChain.Get(block.QuorumCert().BlockHash()); ok { - cs.acceptor.Proposed(qcBlock.Command()) - } else { - cs.logger.Info("OnPropose: Failed to fetch qcBlock") + if _, ok := cs.blockChain.LocalGet(block.Parent()); !ok { + cs.logger.Info("OnPropose: Out of order block") + if proposal.Deferred { + return + } + proposal.Deferred = true + cs.eventLoop.DelayUntil(hotstuff.ProposeMsg{}, proposal) + return } if !cs.acceptor.Accept(block.Command()) { cs.logger.Info("OnPropose: command not accepted") return } + cs.acceptor.Proposed(block.Command()) // block is safe and was accepted cs.blockChain.Store(block) diff --git a/events.go b/events.go index 31bb9971..647d25ec 100644 --- a/events.go +++ b/events.go @@ -10,6 +10,7 @@ type ProposeMsg struct { ID ID // The ID of the replica who sent the message. Block *Block // The block that is proposed. AggregateQC *AggregateQC // Optional AggregateQC + Deferred bool } func (p ProposeMsg) String() string { diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 025e8f16..33754cae 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -207,6 +207,7 @@ func (s *Synchronizer) OnLocalTimeout() { s.configuration.Timeout(timeoutMsg) if s.inPipeline(s.currentView) { + s.logger.Debug("OnLocalTimeout: advance view") s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView)) } @@ -344,6 +345,7 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { } if timeoutV := syncInfo.TimeoutView(); timeoutV > v && s.inPipeline(timeoutV) { + timeout = true v = timeoutV } @@ -366,13 +368,13 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { s.newCtx(duration) s.timer.Reset(duration) - s.logger.Debugf("advanced to view %d", s.currentView) + s.logger.Debugf("advanced to view %d with timeout %v", s.currentView, duration) s.eventLoop.AddEvent(ViewChangeEvent{View: s.currentView, Timeout: timeout}) leader := s.leaderRotation.GetLeader(s.currentView) if leader == s.opts.ID() { s.consensus.Propose(syncInfo.WithQC(s.highQC)) - } else if replica, ok := s.configuration.Replica(leader); ok && !s.inPipeline(s.currentView) { + } else if replica, ok := s.configuration.Replica(leader); ok && !s.inPipeline(s.currentView) && timeout { s.logger.Debug("sending NewView msg") replica.NewView(syncInfo) } From 7e59d2464ddb7dba515b1b3d6792e726bc67101e Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Thu, 13 Oct 2022 12:05:00 +0200 Subject: [PATCH 23/25] separate nextView and currentView to avoid advancing on local timeout --- consensus/consensus.go | 27 +++++++--- internal/mocks/synchronizer_mock.go | 14 ++++++ modules/modules.go | 2 + synchronizer/synchronizer.go | 57 ++++++++++++--------- twins/network.go | 6 +-- twins/scenario_test.go | 77 +++++++++++++++++++++++++++-- types.go | 26 +++++----- 7 files changed, 157 insertions(+), 52 deletions(-) diff --git a/consensus/consensus.go b/consensus/consensus.go index d4a2e589..f067d05a 100644 --- a/consensus/consensus.go +++ b/consensus/consensus.go @@ -129,8 +129,15 @@ func (cs *consensusBase) Propose(cert hotstuff.SyncInfo) { } cs.logger.Debugf("Propose: cmd %s", cmd) + v := cert.NextView() + if v < cs.synchronizer.View() { + // this should not happen + cs.logger.Errorf("proposeView %v was passed in view %v", v, cs.synchronizer.View()) + v = cs.synchronizer.View() + } + parentHash := qc.BlockHash() - if cs.synchronizer.LeafBlock().View() < cs.synchronizer.View() { + if cs.synchronizer.LeafBlock().View() < v { parentHash = cs.synchronizer.LeafBlock().Hash() } @@ -148,7 +155,7 @@ func (cs *consensusBase) Propose(cert hotstuff.SyncInfo) { parentHash, qc, cmd, - cs.synchronizer.View(), + v, cs.opts.ID(), ), } @@ -200,14 +207,18 @@ func (cs *consensusBase) OnPropose(proposal hotstuff.ProposeMsg) { //nolint:gocy return } - if _, ok := cs.blockChain.LocalGet(block.Parent()); !ok { - cs.logger.Info("OnPropose: Out of order block") - if proposal.Deferred { + if !proposal.Deferred { + if _, ok := cs.blockChain.LocalGet(block.Parent()); !ok { + cs.logger.Info("OnPropose: Out of order block") + proposal.Deferred = true + cs.eventLoop.DelayUntil(hotstuff.ProposeMsg{}, proposal) + return + } + } else { + if _, ok := cs.blockChain.Get(block.Parent()); !ok { + cs.logger.Error("OnPropose: Unable to retrieve parent") return } - proposal.Deferred = true - cs.eventLoop.DelayUntil(hotstuff.ProposeMsg{}, proposal) - return } if !cs.acceptor.Accept(block.Command()) { diff --git a/internal/mocks/synchronizer_mock.go b/internal/mocks/synchronizer_mock.go index 75cd8151..b30d2c98 100644 --- a/internal/mocks/synchronizer_mock.go +++ b/internal/mocks/synchronizer_mock.go @@ -127,4 +127,18 @@ func (m *MockSynchronizer) PipelinedViews() hotstuff.View { func (mr *MockSynchronizerMockRecorder) PipelinedViews() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PipelinedViews", reflect.TypeOf((*MockSynchronizer)(nil).PipelinedViews)) +} + +// NextView mocks base method. +func (m *MockSynchronizer) NextView() hotstuff.View { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "NextView") + ret0, _ := ret[0].(hotstuff.View) + return ret0 +} + +// NextView indicates an expected call of NextView. +func (mr *MockSynchronizerMockRecorder) NextView() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextView", reflect.TypeOf((*MockSynchronizer)(nil).NextView)) } \ No newline at end of file diff --git a/modules/modules.go b/modules/modules.go index 53598155..c8add4f2 100644 --- a/modules/modules.go +++ b/modules/modules.go @@ -156,6 +156,8 @@ type Synchronizer interface { AdvanceView(hotstuff.SyncInfo) // View returns the current view. View() hotstuff.View + // NextView returns the view in which the next proposal is expected. + NextView() hotstuff.View // ViewContext returns a context that is cancelled at the end of the view. ViewContext() context.Context // HighQC returns the highest known QC. diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 33754cae..78389a1b 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -24,6 +24,7 @@ type Synchronizer struct { opts *modules.Options currentView hotstuff.View + nextView hotstuff.View highTC hotstuff.TimeoutCert highQC hotstuff.QuorumCert leafBlock *hotstuff.Block @@ -93,6 +94,7 @@ func New(viewDuration ViewDuration, pipelinedViews uint32) modules.Synchronizer return &Synchronizer{ leafBlock: hotstuff.GetGenesis(), currentView: 1, + nextView: 0, pipelinedViews: pipelinedViews, @@ -121,7 +123,9 @@ func (s *Synchronizer) Start(ctx context.Context) { // start the initial proposal if s.currentView == 1 && s.leaderRotation.GetLeader(s.currentView) == s.opts.ID() { - s.consensus.Propose(s.SyncInfo()) + s.nextView = 1 + s.consensus.Propose(s.SyncInfo().WithNextView(s.nextView)) + } } @@ -148,6 +152,11 @@ func (s *Synchronizer) View() hotstuff.View { return s.currentView } +// NextView returns the view in which the next proposal is expected. +func (s *Synchronizer) NextView() hotstuff.View { + return s.nextView +} + // ViewContext returns a context that is cancelled at the end of the view. func (s *Synchronizer) ViewContext() context.Context { return s.viewCtx @@ -206,11 +215,6 @@ func (s *Synchronizer) OnLocalTimeout() { s.configuration.Timeout(timeoutMsg) - if s.inPipeline(s.currentView) { - s.logger.Debug("OnLocalTimeout: advance view") - s.AdvanceView(hotstuff.NewSyncInfo().WithTimeoutView(s.currentView)) - } - s.OnRemoteTimeout(timeoutMsg) } @@ -286,6 +290,7 @@ func (s *Synchronizer) OnNewView(newView hotstuff.NewViewMsg) { // qc must be either a regular quorum certificate, or a timeout certificate. func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { v := hotstuff.View(0) + nextPropose := hotstuff.View(0) timeout := false // check for a TC @@ -335,31 +340,44 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { } } + if v >= s.currentView { + s.increaseView(v+1, timeout) + nextPropose = v + 1 + } + b, ok := syncInfo.Block() if ok { s.updateLeafBlock(b) } - if s.inPipeline(s.leafBlock.View()) { - v = s.leafBlock.View() + if s.inPipeline(s.leafBlock.View() + 1) { + nextPropose = s.leafBlock.View() + 1 } - if timeoutV := syncInfo.TimeoutView(); timeoutV > v && s.inPipeline(timeoutV) { - timeout = true - v = timeoutV + if nextPropose <= s.nextView { + return } - if v < s.currentView { - return + s.nextView = nextPropose + + leader := s.leaderRotation.GetLeader(s.nextView) + if leader == s.opts.ID() { + s.consensus.Propose(syncInfo.WithQC(s.highQC).WithNextView(s.nextView)) + } else if replica, ok := s.configuration.Replica(leader); ok && !s.inPipeline(s.nextView) { + s.logger.Debug("sending NewView msg") + replica.NewView(syncInfo) } +} +// increaseView resets the currentView and updates context and timer +func (s *Synchronizer) increaseView(newView hotstuff.View, timeout bool) { s.timer.Stop() if !timeout { s.duration.ViewSucceeded() } - s.currentView = v + 1 + s.currentView = newView s.lastTimeout = nil s.duration.ViewStarted() @@ -370,19 +388,12 @@ func (s *Synchronizer) AdvanceView(syncInfo hotstuff.SyncInfo) { s.logger.Debugf("advanced to view %d with timeout %v", s.currentView, duration) s.eventLoop.AddEvent(ViewChangeEvent{View: s.currentView, Timeout: timeout}) - - leader := s.leaderRotation.GetLeader(s.currentView) - if leader == s.opts.ID() { - s.consensus.Propose(syncInfo.WithQC(s.highQC)) - } else if replica, ok := s.configuration.Replica(leader); ok && !s.inPipeline(s.currentView) && timeout { - s.logger.Debug("sending NewView msg") - replica.NewView(syncInfo) - } } // inPipeline checks if the given view is in pipeline range from highQC. func (s *Synchronizer) inPipeline(v hotstuff.View) bool { - return v > s.highQC.View() && v < s.highQC.View()+hotstuff.View(s.pipelinedViews) + // current view is always the first view in the pipeline + return v >= s.currentView && v < s.currentView+hotstuff.View(s.pipelinedViews) } // updateHighQC attempts to update the highQC, but does not verify the qc first. diff --git a/twins/network.go b/twins/network.go index e952f98e..88c35e7d 100644 --- a/twins/network.go +++ b/twins/network.go @@ -168,7 +168,7 @@ func (n *Network) run(ticks int) { // kick off the initial proposal(s) for _, node := range n.nodes { if node.leaderRotation.GetLeader(1) == node.id.ReplicaID { - node.consensus.Propose(node.synchronizer.(*synchronizer.Synchronizer).SyncInfo()) + node.consensus.Propose(node.synchronizer.(*synchronizer.Synchronizer).SyncInfo().WithNextView(1)) } } @@ -202,10 +202,10 @@ func (n *Network) shouldDrop(sender, receiver uint32, message any) bool { // Index into viewPartitions. i := -1 - if node.effectiveView > node.synchronizer.View() { + if node.effectiveView > node.synchronizer.NextView() { i += int(node.effectiveView) } else { - i += int(node.synchronizer.View()) + i += int(node.synchronizer.NextView()) } if i < 0 { diff --git a/twins/scenario_test.go b/twins/scenario_test.go index c2235150..4cd6938d 100644 --- a/twins/scenario_test.go +++ b/twins/scenario_test.go @@ -36,6 +36,72 @@ func TestBasicScenario(t *testing.T) { } } +func TestFastBasic(t *testing.T) { + s := Scenario{} + allNodesSet := make(NodeSet) + for i := 1; i <= 4; i++ { + allNodesSet.Add(uint32(i)) + } + + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + + result, err := ExecuteScenario(s, 4, 0, 100, "fasthotstuff", 1) + if err != nil { + t.Fatal(err) + } + + if !result.Safe { + t.Errorf("Expected no safety violations") + } + + if result.Commits < 1 { + t.Error("Expected at least one commit") + } + + if result.Commits > 1 { + t.Error("Expected only one commit") + } +} + +func TestFastFailover(t *testing.T) { + s := Scenario{} + allNodesSet := make(NodeSet) + for i := 1; i <= 4; i++ { + allNodesSet.Add(uint32(i)) + } + lonely := make(NodeSet) + lonely.Add(2) + + others := make(NodeSet) + for i := 1; i <= 4; i++ { + if !lonely.Contains(uint32(i)) { + others.Add(uint32(i)) + } + } + + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 2, Partitions: []NodeSet{lonely, others}}) + s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) + s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + + result, err := ExecuteScenario(s, 4, 0, 100, "fasthotstuff", 1) + if err != nil { + t.Fatal(err) + } + + if !result.Safe { + t.Errorf("Expected no safety violations") + } + + if result.Commits != 2 { + // block in view 3 committed (including 1) + t.Errorf("Should commit 2 blocks, not %d", result.Commits) + } +} + func TestBasicPipeline(t *testing.T) { s := Scenario{} allNodesSet := make(NodeSet) @@ -76,7 +142,7 @@ func TestFaultyPipeline(t *testing.T) { allNodesSet.Add(uint32(i)) } lonely := make(NodeSet) - lonely.Add(3) + lonely.Add(2) others := make(NodeSet) for i := 1; i <= 4; i++ { @@ -116,7 +182,7 @@ func TestFaultyPipelineCommit(t *testing.T) { allNodesSet.Add(uint32(i)) } lonely := make(NodeSet) - lonely.Add(3) + lonely.Add(2) others := make(NodeSet) for i := 1; i <= 4; i++ { @@ -134,6 +200,7 @@ func TestFaultyPipelineCommit(t *testing.T) { s = append(s, View{Leader: 3, Partitions: []NodeSet{allNodesSet}}) s = append(s, View{Leader: 4, Partitions: []NodeSet{allNodesSet}}) s = append(s, View{Leader: 1, Partitions: []NodeSet{allNodesSet}}) + // s = append(s, View{Leader: 2, Partitions: []NodeSet{allNodesSet}}) result, err := ExecuteScenario(s, 4, 0, 100, "chainedhotstuff", 2) if err != nil { @@ -144,9 +211,9 @@ func TestFaultyPipelineCommit(t *testing.T) { t.Errorf("Expected no safety violations") } - if result.Commits != 3 { - // commits blocks in view 1,2 and 4 - t.Errorf("Should commit 3 blocks, not %d", result.Commits) + if result.Commits != 2 { + // block in view 3 committed (including 1) + t.Errorf("Should commit 2 blocks, not %d", result.Commits) } } diff --git a/types.go b/types.go index d45415a2..5cc299f6 100644 --- a/types.go +++ b/types.go @@ -174,11 +174,11 @@ func (pc PartialCert) ToBytes() []byte { // This can also hold an AggregateQC for Fast-Hotstuff. // This can also hold a block and a view with local timeout. type SyncInfo struct { - qc *QuorumCert - tc *TimeoutCert - aggQC *AggregateQC - block *Block - timeoutView View + qc *QuorumCert + tc *TimeoutCert + aggQC *AggregateQC + block *Block + nextView View } // NewSyncInfo returns a new SyncInfo struct. @@ -206,9 +206,9 @@ func (si SyncInfo) WithBlock(b *Block) SyncInfo { return si } -// WithTimeoutView returns a copy of the SyncInfo struct with a view marked as local timeout. -func (si SyncInfo) WithTimeoutView(tv View) SyncInfo { - si.timeoutView = tv +// WithNextView returns a copy of the SyncInfo struct with a next view to propose in. +func (si SyncInfo) WithNextView(nv View) SyncInfo { + si.nextView = nv return si } @@ -251,9 +251,9 @@ func (si SyncInfo) Block() (_ *Block, _ bool) { return } -// TimeoutView returns the timeout view, if present. -func (si SyncInfo) TimeoutView() (_ View) { - return si.timeoutView +// NextView returns the next view, if present. +func (si SyncInfo) NextView() (_ View) { + return si.nextView } func (si SyncInfo) String() string { @@ -271,8 +271,8 @@ func (si SyncInfo) String() string { if si.block != nil { fmt.Fprintf(&sb, "%s ", si.block) } - if si.timeoutView != 0 { - fmt.Fprintf(&sb, "timeoutView: %d ", si.timeoutView) + if si.nextView != 0 { + fmt.Fprintf(&sb, "nextView: %d ", si.nextView) } sb.WriteRune('}') return sb.String() From fb04815f86cc71243e680d698634e0b055489b3b Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Thu, 13 Oct 2022 12:20:54 +0200 Subject: [PATCH 24/25] fix pr comments --- internal/cli/run.go | 13 +++++++++++++ internal/mocks/synchronizer_mock.go | 2 +- twins/network.go | 15 ++++++++------- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/internal/cli/run.go b/internal/cli/run.go index 3d83f6cf..9c325d8f 100644 --- a/internal/cli/run.go +++ b/internal/cli/run.go @@ -2,6 +2,7 @@ package cli import ( "bufio" + "errors" "fmt" "io" "log" @@ -98,6 +99,9 @@ func runController() { checkf("failed to create output directory: %v", err) } + err = checkFlags() + checkf("invalid flag combination: %v", err) + experiment := orchestration.Experiment{ Logger: logging.New("ctrl"), NumReplicas: viper.GetInt("replicas"), @@ -326,3 +330,12 @@ func startLocalProfiling(output string) (stop func() error, err error) { stop, err = profiling.StartProfilers(cpuProfile, memProfile, trace, fgprofProfile) return } + +func checkFlags() error { + consensus := viper.GetString("consensus") + pipelinedViews := viper.GetInt("pipelined-views") + if pipelinedViews > 1 && consensus != "chainedhotstuff" { + return errors.New("piplining only supported for chainedhotstuff consensus") + } + return nil +} diff --git a/internal/mocks/synchronizer_mock.go b/internal/mocks/synchronizer_mock.go index b30d2c98..266c510d 100644 --- a/internal/mocks/synchronizer_mock.go +++ b/internal/mocks/synchronizer_mock.go @@ -141,4 +141,4 @@ func (m *MockSynchronizer) NextView() hotstuff.View { func (mr *MockSynchronizerMockRecorder) NextView() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NextView", reflect.TypeOf((*MockSynchronizer)(nil).NextView)) -} \ No newline at end of file +} diff --git a/twins/network.go b/twins/network.go index 88c35e7d..acde0ae6 100644 --- a/twins/network.go +++ b/twins/network.go @@ -121,6 +121,14 @@ func (n *Network) GetNodeBuilder(id NodeID, pk hotstuff.PrivateKey) modules.Buil } func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusName string, pipelinedViews uint32) error { + + if pipelinedViews > 1 && consensusName != "chainedhotstuff" { + return fmt.Errorf("pipelining currently only supported for chainedhotstuff") + } + if pipelinedViews < 1 { + pipelinedViews = 1 + } + cg := &commandGenerator{} for _, nodeID := range nodes { @@ -130,13 +138,6 @@ func (n *Network) createTwinsNodes(nodes []NodeID, scenario Scenario, consensusN return err } - if pipelinedViews < 1 || consensusName != "chainedhotstuff" { - if pipelinedViews > 1 && consensusName != "chainedhotstuff" { - return fmt.Errorf("pipelining currently only supported for chainedhotstuff") - } - pipelinedViews = 1 - } - builder := n.GetNodeBuilder(nodeID, pk) node := n.nodes[nodeID.NetworkID] From 842075629b2ff31e3464a8df8c31f7d24056f4f6 Mon Sep 17 00:00:00 2001 From: Leander Jehl Date: Thu, 13 Oct 2022 17:46:59 +0200 Subject: [PATCH 25/25] fix fhsbug test --- twins/fhsbug_test.go | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/twins/fhsbug_test.go b/twins/fhsbug_test.go index 27adc8a6..001600cb 100644 --- a/twins/fhsbug_test.go +++ b/twins/fhsbug_test.go @@ -137,6 +137,7 @@ func TestFHSBug(t *testing.T) { type vulnerableFHS struct { logger logging.Logger blockChain modules.BlockChain + opts *modules.Options inner fasthotstuff.FastHotStuff } @@ -144,6 +145,7 @@ func (fhs *vulnerableFHS) InitModule(mods *modules.Core) { mods.Get( &fhs.logger, &fhs.blockChain, + &fhs.opts, ) fhs.inner.InitModule(mods) @@ -181,6 +183,30 @@ func (fhs *vulnerableFHS) CommitRule(block *hotstuff.Block) *hotstuff.Block { return nil } +func (fhs *vulnerableFHS) ProposeRule(cert hotstuff.SyncInfo, cmd hotstuff.Command) (proposal hotstuff.ProposeMsg, ok bool) { + qc, ok := cert.QC() + if !ok { + return hotstuff.ProposeMsg{}, false + } + + proposal = hotstuff.ProposeMsg{ + ID: fhs.opts.ID(), + Block: hotstuff.NewBlock( + qc.BlockHash(), + qc, + cmd, + cert.NextView(), + fhs.opts.ID(), + ), + } + + if aggQC, ok := cert.AggQC(); ok { + proposal.AggregateQC = &aggQC + } + + return proposal, true +} + // ChainLength returns the number of blocks that need to be chained together in order to commit. func (fhs *vulnerableFHS) ChainLength() int { return fhs.inner.ChainLength()