aboutsummaryrefslogblamecommitdiffstats
path: root/core/negative-ack.go
blob: 417862912a8bfaddb4244875ce2f6a2bbbf1e9b6 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                                    
  
                                                                               



                                                                              
                                                                                 




                                                                           
                                                      






                                  
                                                                



                                                                         

                                          
 
                      




                                                         
                                                                               
                      
                                              
 


                                                                                     
                               

                                                              
 


                                                                                   


                                                     
                                                    
                          





                                                                                                
         
                      


                



                                                                      
                                     


                         
 
                                   
                                                

                                                                 



                                                    
                                   

                                     

                                                                     







                                                                           
                                                
                                        


                                      


                               
                                   
                                    
                                                        

                                                     
                                                                                       


                                               


                                                                



                                                                    
                                                           


                                                     
                                                                
                         
                                                   





                                             
                                   










                                                    





                                                                



                 
                                                                      
                               
                                          













                                                      



                                                            
 
                                                                        
                                    
                                                                




                                                      
                                      
                                    
                                                                      


         

                                                    
 
                                

                                    
                                           
         

                                  

                                    
                                           
                                                
                                                       


                 
                                  
 
// Copyright 2018 The dexon-consensus Authors
// This file is part of the dexon-consensus library.
//
// The dexon-consensus 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 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 library. If not, see
// <http://www.gnu.org/licenses/>.

package core

import (
    "time"

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

type negativeAck struct {
    // owner is the ID of proposer itself, this is used when deciding
    // a node to be restricted or not.
    owner types.NodeID

    numOfNodes int

    // timeDelay and timeExpire are for nack timeout.
    timeDelay  time.Duration
    timeExpire time.Duration

    // restricteds stores nodes which has been restricted and the time it's
    // restricted.
    restricteds map[types.NodeID]time.Time

    // lastVotes and lockedVotes store the votes for nack. lastVotes[nid1][nid2]
    // and lockedVotes[nid1][nid2] both mean that nid2 votes nid1. The difference
    // is lockedVotes works only when nid1 is restricted, so that the votes are
    // needed to be locked.
    lastVotes   map[types.NodeID]map[types.NodeID]struct{}
    lockedVotes map[types.NodeID]map[types.NodeID]struct{}

    // timeDiffs is the cache for last time stamps. timeDiffs[nid1][nid2] means
    // the last updated timestamps nid1 sees nid2.
    timeDiffs map[types.NodeID]map[types.NodeID]map[types.NodeID]time.Time
}

// newNegativeAck creates a new negaticeAck instance.
func newNegativeAck(nid types.NodeID) *negativeAck {
    n := &negativeAck{
        owner:       nid,
        numOfNodes:  0,
        restricteds: make(map[types.NodeID]time.Time),
        lastVotes:   make(map[types.NodeID]map[types.NodeID]struct{}),
        lockedVotes: make(map[types.NodeID]map[types.NodeID]struct{}),
        timeDiffs:   make(map[types.NodeID]map[types.NodeID]map[types.NodeID]time.Time),
    }
    n.addNode(nid)
    return n
}

// processNewVote is called when a new "vote" occurs, that is, a node
// sees that other 2f + 1 nodes think a node is slow. "nid" is the
// node which propesed the block which the timestamps votes and "h" is
// the node been voted to be nacked.
func (n *negativeAck) processNewVote(
    nid types.NodeID,
    h types.NodeID,
) []types.NodeID {

    nackeds := []types.NodeID{}
    if _, exist := n.restricteds[h]; exist {
        n.lockedVotes[h][nid] = struct{}{}
        if len(n.lockedVotes[h]) > 2*(n.numOfNodes-1)/3 {
            nackeds = append(nackeds, h)
            delete(n.restricteds, h)
        }
    } else {
        if n.owner == nid {
            n.restrict(h)
        } else {
            n.lastVotes[h][nid] = struct{}{}
            if len(n.lastVotes[h]) > (n.numOfNodes-1)/3 {
                n.restrict(h)
            }
        }
    }
    return nackeds
}

// processTimestamps process new timestamps of a block which is proposed by
// node nid, and returns the nodes being nacked.
func (n *negativeAck) processTimestamps(
    nid types.NodeID,
    ts map[types.NodeID]time.Time,
) []types.NodeID {

    n.checkRestrictExpire()

    nackeds := []types.NodeID{}
    for h := range n.timeDiffs {
        if n.timeDiffs[nid][h][h].Equal(ts[h]) {
            votes := 0
            for hh := range n.timeDiffs {
                if ts[hh].Sub(n.timeDiffs[nid][h][hh]) >= n.timeDelay {
                    votes++
                }
            }
            if votes > 2*((n.numOfNodes-1)/3) {
                n.lastVotes[h][nid] = struct{}{}
                nack := n.processNewVote(nid, h)
                for _, i := range nack {
                    nackeds = append(nackeds, i)
                }
            } else {
                delete(n.lastVotes[h], nid)
            }
        } else {
            for hh := range n.timeDiffs {
                n.timeDiffs[nid][h][hh] = ts[hh]
            }
            delete(n.lastVotes[h], nid)
        }
    }
    return nackeds
}

func (n *negativeAck) checkRestrictExpire() {
    expired := []types.NodeID{}
    now := time.Now()
    for h, t := range n.restricteds {
        if now.Sub(t) >= n.timeExpire {
            expired = append(expired, h)
        }
    }
    for _, h := range expired {
        delete(n.restricteds, h)
    }
}

func (n *negativeAck) restrict(nid types.NodeID) {
    if _, exist := n.restricteds[nid]; !exist {
        n.restricteds[nid] = time.Now().UTC()
        n.lockedVotes[nid] = map[types.NodeID]struct{}{}
        for h := range n.lastVotes[nid] {
            n.lockedVotes[nid][h] = struct{}{}
        }
    }
}

func (n *negativeAck) getRestrictedNodes() map[types.NodeID]struct{} {
    n.checkRestrictExpire()
    ret := map[types.NodeID]struct{}{}
    for h := range n.restricteds {
        ret[h] = struct{}{}
    }
    return ret
}

func (n *negativeAck) setTimeDelay(t time.Duration) {
    n.timeDelay = t
}

func (n *negativeAck) setTimeExpire(t time.Duration) {
    n.timeExpire = t
}

func (n *negativeAck) addNode(nid types.NodeID) {
    n.numOfNodes++
    n.lastVotes[nid] = make(map[types.NodeID]struct{})
    n.lockedVotes[nid] = make(map[types.NodeID]struct{})

    newTimeDiff := make(map[types.NodeID]map[types.NodeID]time.Time)
    for h := range n.timeDiffs {
        newTimeDiff2 := make(map[types.NodeID]time.Time)
        for hh := range n.timeDiffs {
            newTimeDiff2[hh] = time.Time{}
        }
        newTimeDiff[h] = newTimeDiff2
    }
    n.timeDiffs[nid] = newTimeDiff
    for h := range n.timeDiffs {
        n.timeDiffs[h][nid] = make(map[types.NodeID]time.Time)
    }
}

func (n *negativeAck) deleteNode(nid types.NodeID) {
    n.numOfNodes--

    delete(n.timeDiffs, nid)

    for h := range n.lastVotes {
        delete(n.lastVotes[h], nid)
    }
    delete(n.lastVotes, nid)
    delete(n.lockedVotes, nid)

    for h := range n.timeDiffs {
        delete(n.timeDiffs[h], nid)
        for hh := range n.timeDiffs[h] {
            delete(n.timeDiffs[h][hh], nid)
        }
    }

    delete(n.restricteds, nid)
}