aboutsummaryrefslogblamecommitdiffstats
path: root/core/agreement-state.go
blob: fbee21a5cd0ceebd98bfee89821afabf978b6a49 (plain) (tree)




















                                                                               
              
















































                                                                                     
                                           

                                 
                                                                         


                                                                 
         





























                                                                         
                                         


                                         
          






                                                     

                        


                                                      
                             
                     









                                                                         


                             






                                                            
                                                 


                                                     
                  
                              
         
















                                                                     





                                                                        
                                                 


                                                  
                  





                                                                       
                                                                 


                                                                  
                                  







                                                  
                                                 


                                                    
                  







                                                       

                                 




                                                  

                                  
                                                          


















                                                                     
                                                           





                                          


                             

                          

                                                             
                              

                                        

                  
// Copyright 2018 The dexon-consensus-core Authors
// This file is part of the dexon-consensus-core library.
//
// The dexon-consensus-core library is free software: you can redistribute it
// and/or modify it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation, either version 3 of the License,
// or (at your option) any later version.
//
// The dexon-consensus-core library is distributed in the hope that it will be
// useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the dexon-consensus-core library. If not, see
// <http://www.gnu.org/licenses/>.

package core

import (
    "fmt"
    "sync"

    "github.com/dexon-foundation/dexon-consensus-core/common"
    "github.com/dexon-foundation/dexon-consensus-core/core/types"
)

// Errors for agreement state module.
var (
    ErrNoEnoughVoteInPrepareState = fmt.Errorf("no enough vote in prepare state")
    ErrNoEnoughVoteInAckState     = fmt.Errorf("no enough vote in ack state")
)

// agreementStateType is the state of agreement
type agreementStateType int

// agreementStateType enum.
const (
    statePrepare agreementStateType = iota
    stateAck
    stateConfirm
    statePass1
    statePass2
)

var nullBlockHash = common.Hash{}

type agreementState interface {
    state() agreementStateType
    nextState() (agreementState, error)
    receiveVote() error
    clocks() int
    terminate()
}

//----- PrepareState -----
type prepareState struct {
    a *agreementData
}

func newPrepareState(a *agreementData) *prepareState {
    return &prepareState{a: a}
}

func (s *prepareState) state() agreementStateType { return statePrepare }
func (s *prepareState) clocks() int               { return 0 }
func (s *prepareState) terminate()                {}
func (s *prepareState) nextState() (agreementState, error) {
    hash := common.Hash{}
    if s.a.period == 1 {
        hash = s.a.blockProposer().Hash
        s.a.recv.ProposeBlock(hash)
    } else {
        var proposed bool
        _, proposed = s.a.countVote(s.a.period-1, types.VotePass)
        if !proposed {
            return nil, ErrNoEnoughVoteInPrepareState
        }
    }
    return newAckState(s.a), nil
}
func (s *prepareState) receiveVote() error { return nil }

// ----- AckState -----
type ackState struct {
    a *agreementData
}

func newAckState(a *agreementData) *ackState {
    return &ackState{a: a}
}

func (s *ackState) state() agreementStateType { return stateAck }
func (s *ackState) clocks() int               { return 2 }
func (s *ackState) terminate()                {}
func (s *ackState) nextState() (agreementState, error) {
    acked := false
    hash := common.Hash{}
    if s.a.period == 1 {
        acked = true
    } else {
        hash, acked = s.a.countVote(s.a.period-1, types.VotePass)
    }
    if !acked {
        return nil, ErrNoEnoughVoteInAckState
    }
    if hash == nullBlockHash {
        hash = s.a.leader.leaderBlockHash()
    }
    s.a.recv.ProposeVote(&types.Vote{
        Type:      types.VoteAck,
        BlockHash: hash,
        Period:    s.a.period,
    })
    return newConfirmState(s.a), nil
}
func (s *ackState) receiveVote() error { return nil }

// ----- ConfirmState -----
type confirmState struct {
    a     *agreementData
    lock  sync.Mutex
    voted bool
}

func newConfirmState(a *agreementData) *confirmState {
    return &confirmState{
        a: a,
    }
}

func (s *confirmState) state() agreementStateType { return stateConfirm }
func (s *confirmState) clocks() int               { return 2 }
func (s *confirmState) terminate()                {}
func (s *confirmState) nextState() (agreementState, error) {
    return newPass1State(s.a), nil
}
func (s *confirmState) receiveVote() error {
    s.lock.Lock()
    defer s.lock.Unlock()
    if s.voted {
        return nil
    }
    hash, ok := s.a.countVote(s.a.period, types.VoteAck)
    if !ok {
        return nil
    }
    if hash != nullBlockHash {
        s.a.recv.ProposeVote(&types.Vote{
            Type:      types.VoteConfirm,
            BlockHash: hash,
            Period:    s.a.period,
        })
        s.voted = true
    }
    return nil
}

// ----- Pass1State -----
type pass1State struct {
    a *agreementData
}

func newPass1State(a *agreementData) *pass1State {
    return &pass1State{a: a}
}

func (s *pass1State) state() agreementStateType { return statePass1 }
func (s *pass1State) clocks() int               { return 0 }
func (s *pass1State) terminate()                {}
func (s *pass1State) nextState() (agreementState, error) {
    voteDefault := false
    if vote, exist := func() (*types.Vote, bool) {
        s.a.votesLock.RLock()
        defer s.a.votesLock.RUnlock()
        v, e := s.a.votes[s.a.period][types.VoteConfirm][s.a.ID]
        return v, e
    }(); exist {
        s.a.recv.ProposeVote(&types.Vote{
            Type:      types.VotePass,
            BlockHash: vote.BlockHash,
            Period:    s.a.period,
        })
    } else if s.a.period == 1 {
        voteDefault = true
    } else {
        hash, ok := s.a.countVote(s.a.period-1, types.VotePass)
        if ok {
            if hash == nullBlockHash {
                s.a.recv.ProposeVote(&types.Vote{
                    Type:      types.VotePass,
                    BlockHash: hash,
                    Period:    s.a.period,
                })
            } else {
                voteDefault = true
            }
        } else {
            voteDefault = true
        }
    }
    if voteDefault {
        s.a.recv.ProposeVote(&types.Vote{
            Type:      types.VotePass,
            BlockHash: s.a.defaultBlock,
            Period:    s.a.period,
        })
    }
    return newPass2State(s.a), nil
}
func (s *pass1State) receiveVote() error { return nil }

// ----- Pass2State -----
type pass2State struct {
    a              *agreementData
    lock           sync.Mutex
    voted          bool
    enoughPassVote chan common.Hash
    terminateChan  chan struct{}
}

func newPass2State(a *agreementData) *pass2State {
    return &pass2State{
        a:              a,
        enoughPassVote: make(chan common.Hash, 1),
        terminateChan:  make(chan struct{}),
    }
}

func (s *pass2State) state() agreementStateType { return statePass2 }
func (s *pass2State) clocks() int               { return 0 }
func (s *pass2State) terminate() {
    s.terminateChan <- struct{}{}
}
func (s *pass2State) nextState() (agreementState, error) {
    select {
    case <-s.terminateChan:
        break
    case hash := <-s.enoughPassVote:
        s.a.votesLock.RLock()
        defer s.a.votesLock.RUnlock()
        s.a.defaultBlock = hash
        s.a.period++
        oldBlock := s.a.blocks[s.a.ID]
        s.a.blocks = map[types.NodeID]*types.Block{
            s.a.ID: oldBlock,
        }
    }
    return newPrepareState(s.a), nil
}
func (s *pass2State) receiveVote() error {
    s.lock.Lock()
    defer s.lock.Unlock()
    if s.voted {
        return nil
    }
    hash, ok := s.a.countVote(s.a.period, types.VotePass)
    if ok {
        s.voted = true
        s.enoughPassVote <- hash
    }
    return nil
}