From d63652565a1f010fb10ce1cd4534d7ac58786062 Mon Sep 17 00:00:00 2001 From: Loong Date: Wed, 1 Jul 2020 19:35:36 +1000 Subject: [PATCH] partially define test suite for process --- process/process.go | 34 +++-- process/process_test.go | 231 +++++++++++++++++++++++++++++ process/processutil/processutil.go | 27 ++++ 3 files changed, 279 insertions(+), 13 deletions(-) diff --git a/process/process.go b/process/process.go index f5da8102..a538f886 100644 --- a/process/process.go +++ b/process/process.go @@ -237,7 +237,7 @@ func (p *Process) Start() { // currentRound ← round // currentStep ← propose // if proposer(currentHeight, currentRound) = p then -// if validValue = nil then +// if validValue != nil then // proposal ← validValue // else // proposal ← getValue() @@ -262,23 +262,31 @@ func (p *Process) StartRound(round Round) { p.CurrentStep = Proposing // If we are not the proposer, then we trigger the propose timeout. - proposer := p.scheduler.Schedule(p.CurrentHeight, p.CurrentRound) - if !p.whoami.Equal(&proposer) { - p.timer.TimeoutPropose(p.CurrentHeight, p.CurrentRound) - return + if p.scheduler != nil { + proposer := p.scheduler.Schedule(p.CurrentHeight, p.CurrentRound) + if !p.whoami.Equal(&proposer) { + if p.timer != nil { + p.timer.TimeoutPropose(p.CurrentHeight, p.CurrentRound) + } + return + } } // If we are the proposer, then we emit a propose. proposeValue := p.ValidValue if proposeValue.Equal(&NilValue) { - proposeValue = p.proposer.Propose(p.CurrentHeight, p.CurrentRound) - } - p.broadcaster.BroadcastPropose(Propose{ - Height: p.CurrentHeight, - Round: p.CurrentRound, - ValidRound: p.ValidRound, - Value: proposeValue, - }) + if p.proposer != nil { + proposeValue = p.proposer.Propose(p.CurrentHeight, p.CurrentRound) + } + } + if p.broadcaster != nil { + p.broadcaster.BroadcastPropose(Propose{ + Height: p.CurrentHeight, + Round: p.CurrentRound, + ValidRound: p.ValidRound, + Value: proposeValue, + }) + } } // OnTimeoutPropose is used to notify the Process that a timeout has been diff --git a/process/process_test.go b/process/process_test.go index e1f83e83..7e47069e 100644 --- a/process/process_test.go +++ b/process/process_test.go @@ -15,6 +15,7 @@ import ( ) var _ = Describe("Process", func() { + Context("when unmarshaling fuzz", func() { It("should not panic", func() { f := func(fuzz []byte) bool { @@ -48,4 +49,234 @@ var _ = Describe("Process", func() { Expect(quick.Check(f, nil)).To(Succeed()) }) }) + + // L11: + // Function StartRound(round) + // currentRound ← round + // currentStep ← propose + // if proposer(currentHeight, currentRound) = p then + // if validValue != nil then + // proposal ← validValue + // else + // proposal ← getValue() + // broadcast〈PROPOSAL, currentHeight, currentRound, proposal, validRound〉 + // else + // schedule OnTimeoutPropose(currentHeight, currentRound) to be executed after timeoutPropose(currentRound) + Context("when starting a round", func() { + It("should set the current round to that round and set the current step to proposing", func() { + r := rand.New(rand.NewSource(time.Now().UnixNano())) + f := func() bool { + round := processutil.RandomRound(r) + p := process.New(id.NewPrivKey().Signatory(), 33, nil, nil, nil, nil, nil, nil, nil) + p.StartRound(round) + Expect(p.CurrentRound).To(Equal(round)) + Expect(p.CurrentStep).To(Equal(process.Proposing)) + return true + } + Expect(quick.Check(f, nil)).To(Succeed()) + }) + + Contex("when we are the proposer", func() { + Context("when our valid value is non-nil", func() { + It("should propose the valid value", func() { + panic("unimplemented") + }) + }) + + Context("when our valid value is nil", func() { + It("should propose a new value", func() { + panic("unimplemented") + }) + }) + }) + + Context("when we are not the proposer", func() { + It("should schedule a propose timeout", func() { + panic("unimplemented") + }) + }) + }) + + // L57: + // Function OnTimeoutPropose(height, round) + // if height = currentHeight ∧ round = currentRound ∧ currentStep = propose then + // broadcast〈PREVOTE, currentHeight, currentRound, nil〉 + // currentStep ← prevote + Context("when timing out on a propose", func() { + Context("when the timeout is in the current height", func() { + Context("when the timeout is in the current round", func() { + Context("when we are in the proposing step", func() { + It("should prevote nil and move to the prevoting step", func() { + panic("unimplemented") + }) + }) + + Context("when we are not the proposing step", func() { + It("should do nothing", func() { + panic("unimplemented") + }) + }) + }) + + Context("when the timeout is not in the current round", func() { + It("should do nothing", func() { + panic("unimplemented") + }) + }) + }) + + Context("when the timeout is not in the current height", func() { + It("should do nothing", func() { + panic("unimplemented") + }) + }) + }) + + // L61: + // Function OnTimeoutPrevote(height, round) + // if height = currentHeight ∧ round = currentRound ∧ currentStep = prevote then + // broadcast〈PREVOTE, currentHeight, currentRound, nil + // currentStep ← prevote + Context("when timing out on a prevote", func() { + + }) + + // L65: + // Function OnTimeoutPrecommit(height, round) + // if height = currentHeight ∧ round = currentRound then + // StartRound(currentRound + 1) + Context("when timing out on a precommit", func() { + + }) + + // L22: + // upon〈PROPOSAL, currentHeight, currentRound, v, −1〉from proposer(currentHeight, currentRound) + // while currentStep = propose do + // if valid(v) ∧ (lockedRound = −1 ∨ lockedValue = v) then + // broadcast〈PREVOTE, currentHeight, currentRound, id(v) + // else + // broadcast〈PREVOTE, currentHeight, currentRound, nil + // currentStep ← prevote + Context("when receiving a propose", func() { + }) + + // L28: + // + // upon〈PROPOSAL, currentHeight, currentRound, v, vr〉from proposer(currentHeight, currentRound) AND 2f+ 1〈PREVOTE, currentHeight, vr, id(v)〉 + // while currentStep = propose ∧ (vr ≥ 0 ∧ vr < currentRound) do + // if valid(v) ∧ (lockedRound ≤ vr ∨ lockedValue = v) then + // broadcast〈PREVOTE, currentHeight, currentRound, id(v)〉 + // else + // broadcast〈PREVOTE, currentHeight, currentRound, nil〉 + // currentStep ← prevote + Context("when receiving a propose and 2f+1 prevotes", func() { + Context("when we are in the proposing step", func() { + Context("when the proposed valid round is valid", func() { + Context("when the proposed valid round is less than the current round", func() { + Context("when the proposed value is valid", func() { + Context("when the proposed valid round is greater than our locked round, or the proposed value is our locked value", func() { + It("should prevote for the proposed value", func() { + panic("unimplemented") + }) + }) + + Context("when the proposed valid round is not greater than our locked round, and the proposed value is not our locked value", func() { + It("should prevote nil", func() { + panic("unimplemented") + }) + }) + }) + + Context("when the proposed value is not valid", func() { + It("should prevote nil", func() { + panic("unimplemented") + }) + }) + }) + + Context("when the proposed valid round is not less than the current round", func() { + It("should do nothing", func() { + panic("unimplemented") + }) + }) + }) + + Context("when the proposed valid round is not valid", func() { + It("should do nothing", func() { + panic("unimplemented") + }) + }) + }) + + Context("when we are not in the proposing step", func() { + It("should do nothing", func() { + panic("unimplemented") + }) + }) + }) + + // L34: + // + // upon 2f+ 1〈PREVOTE, currentHeight, currentRound, ∗〉 + // while currentStep = prevote for the first time do + // scheduleOnTimeoutPrevote(currentHeight, currentRound) to be executed after timeoutPrevote(currentRound) + Context("when receiving 2f+1 prevotes", func() { + + }) + + // L36: + // + // upon〈PROPOSAL, currentHeight, currentRound, v, ∗〉from proposer(currentHeight, currentRound) AND 2f+ 1〈PREVOTE, currentHeight, currentRound, id(v)〉 + // while valid(v) ∧ currentStep ≥ prevote for the first time do + // if currentStep = prevote then + // lockedValue ← v + // lockedRound ← currentRound + // broadcast〈PRECOMMIT, currentHeight, currentRound, id(v))〉 + // currentStep ← precommit + // validValue ← v + // validRound ← currentRound + Context("when receiving a propose and 2f+1 prevotes", func() { + Context("when waiting to precommit", func() { + + }) + }) + + // L44: + // + // upon 2f+ 1〈PREVOTE, currentHeight, currentRound, nil〉 + // while currentStep = prevote do + // broadcast〈PRECOMMIT, currentHeight, currentRound, nil〉 + // currentStep ← precommit + Context("when receiving 2f+1 nil prevotes", func() { + + }) + + // L47: + // + // upon 2f+ 1〈PRECOMMIT, currentHeight, currentRound, ∗〉for the first time do + // scheduleOnTimeoutPrecommit(currentHeight, currentRound) to be executed after timeoutPrecommit(currentRound) + Context("when receiving 2f+1 precommits", func() { + + }) + + // L49: + // + // upon〈PROPOSAL, currentHeight, r, v, ∗〉from proposer(currentHeight, r) AND 2f+ 1〈PRECOMMIT, currentHeight, r, id(v)〉 + // while decision[currentHeight] = nil do + // if valid(v) then + // decision[currentHeight] = v + // currentHeight ← currentHeight + 1 + // reset + // StartRound(0) + Context("when receiving a propose and 2f+1 precommits", func() { + + }) + + // L55: + // + // upon f+ 1〈∗, currentHeight, r, ∗, ∗〉with r > currentRound do + // StartRound(r) + Context("when receiving f+1 messages from a future round", func() { + + }) }) diff --git a/process/processutil/processutil.go b/process/processutil/processutil.go index 1e4b13a7..8ce2490b 100644 --- a/process/processutil/processutil.go +++ b/process/processutil/processutil.go @@ -7,6 +7,33 @@ import ( "github.com/renproject/id" ) +type BroadcasterCallbacks struct { + BroadcastProposeCallback func(process.Propose) + BroadcastPrevoteCallback func(process.Prevote) + BroadcastPrecommitCallback func(process.Precommit) +} + +func (broadcaster BroadcasterCallbacks) BroadcastPropose(propose process.Propose) { + if broadcaster.BroadcastProposeCallback == nil { + return + } + broadcaster.BroadcastProposeCallback(propose) +} + +func (broadcaster BroadcasterCallbacks) BroadcastPrevote(prevote process.Prevote) { + if broadcaster.BroadcastPrevoteCallback == nil { + return + } + broadcaster.BroadcastPrevoteCallback(prevote) +} + +func (broadcaster BroadcasterCallbacks) BroadcastPrecommit(precommit process.Precommit) { + if broadcaster.BroadcastPrecommitCallback == nil { + return + } + broadcaster.BroadcastPrecommitCallback(precommit) +} + func RandomHeight(r *rand.Rand) process.Height { switch r.Int() % 10 { case 0: