mirror of
https://github.com/fluencelabs/tendermint
synced 2025-06-12 21:01:21 +00:00
[WIP] Fill in consensus core details in ADR 030 (#2696)
* Initial work towards making ConsensusCore spec complete * Initial version of executor and complete consensus
This commit is contained in:
committed by
Ethan Buchman
parent
be00cd1add
commit
1895cde590
@ -126,6 +126,312 @@ func TestConsensusXXX(t *testing.T) {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Consensus Executor
|
||||||
|
|
||||||
|
## Consensus Core
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Event interface{}
|
||||||
|
|
||||||
|
type EventNewHeight struct {
|
||||||
|
Height int64
|
||||||
|
ValidatorId int
|
||||||
|
}
|
||||||
|
|
||||||
|
type EventNewRound HeightAndRound
|
||||||
|
|
||||||
|
type EventProposal struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
Timestamp Time
|
||||||
|
BlockID BlockID
|
||||||
|
POLRound int
|
||||||
|
Sender int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Majority23PrevotesBlock struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
}
|
||||||
|
|
||||||
|
type Majority23PrecommitBlock struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeightAndRound struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Majority23PrevotesAny HeightAndRound
|
||||||
|
type Majority23PrecommitAny HeightAndRound
|
||||||
|
type TimeoutPropose HeightAndRound
|
||||||
|
type TimeoutPrevotes HeightAndRound
|
||||||
|
type TimeoutPrecommit HeightAndRound
|
||||||
|
|
||||||
|
|
||||||
|
type Message interface{}
|
||||||
|
|
||||||
|
type MessageProposal struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
POLRound int
|
||||||
|
}
|
||||||
|
|
||||||
|
type VoteType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
VoteTypeUnknown VoteType = iota
|
||||||
|
Prevote
|
||||||
|
Precommit
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
type MessageVote struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
Type VoteType
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type MessageDecision struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
BlockID BlockID
|
||||||
|
}
|
||||||
|
|
||||||
|
type TriggerTimeout struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
Duration Duration
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
type RoundStep int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoundStepUnknown RoundStep = iota
|
||||||
|
RoundStepPropose
|
||||||
|
RoundStepPrevote
|
||||||
|
RoundStepPrecommit
|
||||||
|
RoundStepCommit
|
||||||
|
)
|
||||||
|
|
||||||
|
type State struct {
|
||||||
|
Height int64
|
||||||
|
Round int
|
||||||
|
Step RoundStep
|
||||||
|
LockedValue BlockID
|
||||||
|
LockedRound int
|
||||||
|
ValidValue BlockID
|
||||||
|
ValidRound int
|
||||||
|
ValidatorId int
|
||||||
|
ValidatorSetSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func proposer(height int64, round int) int {}
|
||||||
|
func getValue() BlockID {}
|
||||||
|
|
||||||
|
func Consensus(event Event, state State) (State, Message, TriggerTimeout) {
|
||||||
|
msg = nil
|
||||||
|
timeout = nil
|
||||||
|
switch event := event.(type) {
|
||||||
|
case EventNewHeight:
|
||||||
|
if event.Height > state.Height {
|
||||||
|
state.Height = event.Height
|
||||||
|
state.Round = -1
|
||||||
|
state.Step = RoundStepPropose
|
||||||
|
state.LockedValue = nil
|
||||||
|
state.LockedRound = -1
|
||||||
|
state.ValidValue = nil
|
||||||
|
state.ValidRound = -1
|
||||||
|
state.ValidatorId = event.ValidatorId
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case EventNewRound:
|
||||||
|
if event.Height == state.Height and event.Round > state.Round {
|
||||||
|
state.Round = eventRound
|
||||||
|
state.Step = RoundStepPropose
|
||||||
|
if proposer(state.Height, state.Round) == state.ValidatorId {
|
||||||
|
proposal = state.ValidValue
|
||||||
|
if proposal == nil {
|
||||||
|
proposal = getValue()
|
||||||
|
}
|
||||||
|
msg = MessageProposal { state.Height, state.Round, proposal, state.ValidRound }
|
||||||
|
}
|
||||||
|
timeout = TriggerTimeout { state.Height, state.Round, timeoutPropose(state.Round) }
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case EventProposal:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and
|
||||||
|
event.Sender == proposal(state.Height, state.Round) and state.Step == RoundStepPropose {
|
||||||
|
if event.POLRound >= state.LockedRound or event.BlockID == state.BlockID or state.LockedRound == -1 {
|
||||||
|
msg = MessageVote { state.Height, state.Round, event.BlockID, Prevote }
|
||||||
|
}
|
||||||
|
state.Step = RoundStepPrevote
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case TimeoutPropose:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPropose {
|
||||||
|
msg = MessageVote { state.Height, state.Round, nil, Prevote }
|
||||||
|
state.Step = RoundStepPrevote
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrevotesBlock:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step >= RoundStepPrevote and event.Round > state.ValidRound {
|
||||||
|
state.ValidRound = event.Round
|
||||||
|
state.ValidValue = event.BlockID
|
||||||
|
if state.Step == RoundStepPrevote {
|
||||||
|
state.LockedRound = event.Round
|
||||||
|
state.LockedValue = event.BlockID
|
||||||
|
msg = MessageVote { state.Height, state.Round, event.BlockID, Precommit }
|
||||||
|
state.Step = RoundStepPrecommit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrevotesAny:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPrevote {
|
||||||
|
timeout = TriggerTimeout { state.Height, state.Round, timeoutPrevote(state.Round) }
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case TimeoutPrevote:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round and state.Step == RoundStepPrevote {
|
||||||
|
msg = MessageVote { state.Height, state.Round, nil, Precommit }
|
||||||
|
state.Step = RoundStepPrecommit
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrecommitBlock:
|
||||||
|
if event.Height == state.Height {
|
||||||
|
state.Step = RoundStepCommit
|
||||||
|
state.LockedValue = event.BlockID
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case Majority23PrecommitAny:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round {
|
||||||
|
timeout = TriggerTimeout { state.Height, state.Round, timeoutPrecommit(state.Round) }
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
|
||||||
|
case TimeoutPrecommit:
|
||||||
|
if event.Height == state.Height and event.Round == state.Round {
|
||||||
|
state.Round = state.Round + 1
|
||||||
|
}
|
||||||
|
return state, msg, timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConsensusExecutor() {
|
||||||
|
proposal = nil
|
||||||
|
votes = HeightVoteSet { Height: 1 }
|
||||||
|
state = State {
|
||||||
|
Height: 1
|
||||||
|
Round: 0
|
||||||
|
Step: RoundStepPropose
|
||||||
|
LockedValue: nil
|
||||||
|
LockedRound: -1
|
||||||
|
ValidValue: nil
|
||||||
|
ValidRound: -1
|
||||||
|
}
|
||||||
|
|
||||||
|
event = EventNewHeight {1, id}
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
|
||||||
|
event = EventNewRound {state.Height, 0}
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
|
||||||
|
if msg != nil {
|
||||||
|
send msg
|
||||||
|
}
|
||||||
|
|
||||||
|
if timeout != nil {
|
||||||
|
trigger timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case message := <- msgCh:
|
||||||
|
switch msg := message.(type) {
|
||||||
|
case MessageProposal:
|
||||||
|
|
||||||
|
case MessageVote:
|
||||||
|
if msg.Height == state.Height {
|
||||||
|
newVote = votes.AddVote(msg)
|
||||||
|
if newVote {
|
||||||
|
switch msg.Type {
|
||||||
|
case Prevote:
|
||||||
|
prevotes = votes.Prevotes(msg.Round)
|
||||||
|
if prevotes.WeakCertificate() and msg.Round > state.Round {
|
||||||
|
event = EventNewRound { msg.Height, msg.Round }
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
if blockID, ok = prevotes.TwoThirdsMajority(); ok and blockID != nil {
|
||||||
|
if msg.Round == state.Round and hasBlock(blockID) {
|
||||||
|
event = Majority23PrevotesBlock { msg.Height, msg.Round, blockID }
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
if proposal != nil and proposal.POLRound == msg.Round and hasBlock(blockID) {
|
||||||
|
event = EventProposal {
|
||||||
|
Height: state.Height
|
||||||
|
Round: state.Round
|
||||||
|
BlockID: blockID
|
||||||
|
POLRound: proposal.POLRound
|
||||||
|
Sender: message.Sender
|
||||||
|
}
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if prevotes.HasTwoThirdsAny() and msg.Round == state.Round {
|
||||||
|
event = Majority23PrevotesAny { msg.Height, msg.Round, blockID }
|
||||||
|
state, msg, timeout = Consensus(event, state)
|
||||||
|
state = handleStateChange(state, msg, timeout)
|
||||||
|
}
|
||||||
|
|
||||||
|
case Precommit:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case timeout := <- timeoutCh:
|
||||||
|
|
||||||
|
case block := <- blockCh:
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleStateChange(state, msg, timeout) State {
|
||||||
|
if state.Step == Commit {
|
||||||
|
state = ExecuteBlock(state.LockedValue)
|
||||||
|
}
|
||||||
|
if msg != nil {
|
||||||
|
send msg
|
||||||
|
}
|
||||||
|
if timeout != nil {
|
||||||
|
trigger timeout
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
### Implementation roadmap
|
### Implementation roadmap
|
||||||
|
|
||||||
* implement proposed implementation
|
* implement proposed implementation
|
||||||
|
@ -62,7 +62,7 @@ func TestEvidence(t *testing.T) {
|
|||||||
{vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round
|
{vote1, makeVote(val, chainID, 0, 10, 3, 1, blockID2), false}, // wrong round
|
||||||
{vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step
|
{vote1, makeVote(val, chainID, 0, 10, 2, 2, blockID2), false}, // wrong step
|
||||||
{vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator
|
{vote1, makeVote(val2, chainID, 0, 10, 2, 1, blockID), false}, // wrong validator
|
||||||
{vote1, badVote, false}, // signed by wrong key
|
{vote1, badVote, false}, // signed by wrong key
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKey := val.GetPubKey()
|
pubKey := val.GetPubKey()
|
||||||
|
Reference in New Issue
Block a user