github.com/elastos/Elastos.ELA.SideChain.ETH@v0.2.2/dpos/consensusview.go (about)

     1  // Copyright (c) 2017-2019 The Elastos Foundation
     2  // Use of this source code is governed by an MIT
     3  // license that can be found in the LICENSE file.
     4  //
     5  
     6  package dpos
     7  
     8  import (
     9  	"bytes"
    10  	"sort"
    11  	"time"
    12  
    13  	"github.com/elastos/Elastos.ELA/common"
    14  	"github.com/elastos/Elastos.ELA/common/log"
    15  	"github.com/elastos/Elastos.ELA/dpos/p2p/peer"
    16  )
    17  
    18  type ViewListener interface {
    19  	OnViewChanged(isOnDuty bool, force bool)
    20  }
    21  
    22  const (
    23  	ConsensusReady = iota
    24  	ConsensusRunning
    25  )
    26  
    27  type ConsensusView struct {
    28  	consensusStatus uint32
    29  	viewOffset      uint32
    30  	publicKey       []byte
    31  	signTolerance   time.Duration
    32  	viewStartTime   time.Time
    33  	viewChangeTime  time.Time
    34  	isDposOnDuty    bool
    35  	producers       *Producers
    36  
    37  	listener ViewListener
    38  }
    39  
    40  func (v *ConsensusView) resetViewOffset() {
    41  	v.viewOffset = 0
    42  }
    43  
    44  func (v *ConsensusView) SetRunning() {
    45  	v.consensusStatus = ConsensusRunning
    46  }
    47  
    48  func (v *ConsensusView) SetReady() {
    49  	v.consensusStatus = ConsensusReady
    50  }
    51  
    52  func (v *ConsensusView) IsRunning() bool {
    53  	return v.consensusStatus == ConsensusRunning
    54  }
    55  
    56  func (v *ConsensusView) IsReady() bool {
    57  	return v.consensusStatus == ConsensusReady
    58  }
    59  
    60  func (v *ConsensusView) TryChangeView(now time.Time) {
    61  	if v.IsRunning() && now.After(v.viewChangeTime) {
    62  		Info("[TryChangeView] succeed", "now", now.String(), "changeTime", v.viewChangeTime.String())
    63  		parentTime := float64(v.viewChangeTime.Unix()) - v.signTolerance.Seconds()
    64  		v.ChangeView(now, false, uint64(parentTime))
    65  	}
    66  }
    67  
    68  func (v *ConsensusView) GetProducers() [][]byte {
    69  	return v.producers.GetProducers()
    70  }
    71  
    72  func (v *ConsensusView) GetTotalArbiterCount() int {
    73  	return v.producers.totalProducers
    74  }
    75  
    76  func (v *ConsensusView) GetSpvHeight() uint64 {
    77  	return v.producers.spvHeight
    78  }
    79  
    80  func (v *ConsensusView) GetTotalProducersCount() int {
    81  	return v.producers.totalProducers
    82  }
    83  
    84  func (v *ConsensusView) UpdateProducers(producers [][]byte, totalCount int, spvHeight uint64) {
    85  	sort.Slice(producers, func(i, j int) bool {
    86  		return bytes.Compare(producers[i], producers[j]) < 0
    87  	})
    88  	v.producers.UpdateProducers(producers, totalCount, spvHeight)
    89  }
    90  
    91  func (v *ConsensusView) ChangeCurrentProducers(changeHeight uint64, spvHeight uint64) {
    92  	v.producers.ChangeCurrentProducers(changeHeight, spvHeight)
    93  }
    94  
    95  func (v *ConsensusView) ProducerIndex(signer []byte) int {
    96  	if len(signer) <= 0 {
    97  		return -1
    98  	}
    99  	return v.producers.ProducerIndex(signer)
   100  }
   101  
   102  func (v *ConsensusView) SetWorkingHeight(workingHeight uint64) {
   103  	v.producers.SetWorkingHeight(workingHeight)
   104  }
   105  
   106  func (v *ConsensusView) getCurrentNeedConnectArbiters() []peer.PID {
   107  	return v.producers.getCurrentNeedConnectArbiters()
   108  }
   109  
   110  func (v *ConsensusView) GetNextNeedConnectArbiters() []peer.PID {
   111  	return v.producers.GetNextNeedConnectArbiters()
   112  }
   113  
   114  func (v *ConsensusView) UpdateNextProducers(producers []peer.PID, totalCount int) {
   115  	v.producers.UpdateNextProducers(producers, totalCount)
   116  }
   117  
   118  func (v *ConsensusView) IsSameProducers(curProducers [][]byte) bool {
   119  	v.producers.mtx.Lock()
   120  	defer v.producers.mtx.Unlock()
   121  	nextProducers := v.producers.nextProducers
   122  	if len(curProducers) != len(nextProducers) {
   123  		return false
   124  	}
   125  	sort.Slice(nextProducers, func(i, j int) bool {
   126  		return bytes.Compare(nextProducers[i][:], nextProducers[j][:]) < 0
   127  	})
   128  
   129  	sort.Slice(curProducers, func(i, j int) bool {
   130  		return bytes.Compare(curProducers[i], curProducers[j]) < 0
   131  	})
   132  
   133  	for index, v := range curProducers {
   134  		if !bytes.Equal(v, nextProducers[index][:]) {
   135  			return false
   136  		}
   137  	}
   138  	return true
   139  }
   140  
   141  func (v *ConsensusView) IsCurrentProducers(curProducers [][]byte) bool {
   142  	v.producers.mtx.Lock()
   143  	defer v.producers.mtx.Unlock()
   144  	producers := v.producers.producers
   145  	if len(producers) <= 0 {
   146  		return false
   147  	}
   148  	if len(curProducers) != len(producers) {
   149  		return false
   150  	}
   151  	sort.Slice(producers, func(i, j int) bool {
   152  		return bytes.Compare(producers[i][:], producers[j][:]) < 0
   153  	})
   154  
   155  	sort.Slice(curProducers, func(i, j int) bool {
   156  		return bytes.Compare(curProducers[i], curProducers[j]) < 0
   157  	})
   158  
   159  	for index, v := range curProducers {
   160  		if !bytes.Equal(v, producers[index][:]) {
   161  			return false
   162  		}
   163  	}
   164  	return true
   165  }
   166  
   167  func (v *ConsensusView) calculateOffsetTime(startTime time.Time,
   168  	now time.Time) (uint32, time.Duration) {
   169  	duration := now.Sub(startTime)
   170  	offset := duration / v.signTolerance
   171  	offsetTime := duration % v.signTolerance
   172  
   173  	return uint32(offset), offsetTime
   174  }
   175  
   176  func (v *ConsensusView) UpdateDutyIndex(height uint64) {
   177  	v.producers.UpdateDutyIndex(height)
   178  
   179  	currentProducer := v.producers.GetNextOnDutyProducer(v.viewOffset)
   180  	v.isDposOnDuty = bytes.Equal(currentProducer, v.publicKey)
   181  }
   182  
   183  func (v *ConsensusView) ChangeView(now time.Time, force bool, parentTime uint64) {
   184  	offset, offsetTime := v.calculateOffsetTime(v.viewStartTime, now)
   185  	if offset > 0 {
   186  		v.viewStartTime = now.Add(-offsetTime)
   187  		v.ResetView(uint64(v.viewStartTime.Unix()))
   188  	}
   189  	v.viewOffset += offset
   190  	if force {
   191  		offset = 1
   192  		v.resetViewOffset()
   193  		v.ResetView(parentTime)
   194  	}
   195  
   196  	if offset > 0 {
   197  		Info("\n\n\n--------------------Change View---------------------")
   198  		Info("viewStartTime:", v.viewStartTime, "changeViewTime", v.viewChangeTime, "nowTime:", now, "offset:", offset, "offsetTime:", offsetTime, "force:", force,
   199  			"viewOffset", v.viewOffset, "dutyIndex", v.producers.dutyIndex)
   200  		currentProducer := v.producers.GetNextOnDutyProducer(v.viewOffset)
   201  		v.isDposOnDuty = bytes.Equal(currentProducer, v.publicKey)
   202  		v.DumpInfo()
   203  		Info("\n\n\n")
   204  		if v.listener != nil {
   205  			v.listener.OnViewChanged(v.isDposOnDuty, force)
   206  		}
   207  	}
   208  }
   209  
   210  func (v *ConsensusView) DumpInfo() {
   211  	str := "\n"
   212  	for _, signer := range v.producers.producers {
   213  		if v.ProducerIsOnDuty(signer) {
   214  			duty := log.Color(log.Green, common.BytesToHexString(signer)+" onDuty \n")
   215  			str = str + duty
   216  		} else {
   217  			str = str + common.BytesToHexString(signer) + " not onDuty \n"
   218  		}
   219  	}
   220  	Info(str)
   221  }
   222  
   223  func (v *ConsensusView) GetViewInterval() time.Duration {
   224  	return v.signTolerance
   225  }
   226  
   227  func (v *ConsensusView) GetViewStartTime() time.Time {
   228  	return v.viewStartTime
   229  }
   230  
   231  func (v *ConsensusView) GetChangeViewTime() time.Time {
   232  	return v.viewChangeTime
   233  }
   234  
   235  func (v *ConsensusView) GetDutyIndex() uint32 {
   236  	return v.producers.dutyIndex
   237  }
   238  
   239  func (v *ConsensusView) GetViewOffset() uint32 {
   240  	return v.viewOffset
   241  }
   242  
   243  func (v *ConsensusView) ResetView(parentTime uint64) {
   244  	v.SetChangViewTime(parentTime)
   245  }
   246  
   247  func (v *ConsensusView) SetChangViewTime(parentTime uint64) {
   248  	headerTime := time.Unix(int64(parentTime), 0)
   249  	v.viewChangeTime = headerTime.Add(v.signTolerance)
   250  	v.viewStartTime = headerTime
   251  }
   252  
   253  func (v *ConsensusView) IsProducers(account []byte) bool {
   254  	return v.producers.IsProducers(account)
   255  }
   256  
   257  func (v *ConsensusView) IsOnduty() bool {
   258  	return v.isDposOnDuty
   259  }
   260  
   261  func (v *ConsensusView) ProducerIsOnDuty(account []byte) bool {
   262  	producer := v.producers.GetNextOnDutyProducer(v.viewOffset)
   263  	return bytes.Equal(producer, account)
   264  }
   265  
   266  func (v *ConsensusView) IsMajorityAgree(count int) bool {
   267  	return v.producers.IsMajorityAgree(count)
   268  }
   269  
   270  func (v *ConsensusView) IsMajorityRejected(count int) bool {
   271  	return v.producers.IsMajorityRejected(count)
   272  }
   273  
   274  func (v *ConsensusView) HasArbitersMinorityCount(count int) bool {
   275  	return v.producers.HasArbitersMinorityCount(count)
   276  }
   277  
   278  func (v *ConsensusView) HasProducerMajorityCount(count int) bool {
   279  	return v.producers.HasProducerMajorityCount(count)
   280  }
   281  
   282  func (v *ConsensusView) GetMajorityCount() int {
   283  	return v.producers.GetMajorityCount()
   284  }
   285  
   286  func (v *ConsensusView) GetCRMajorityCount() int {
   287  	return v.producers.GetCRMajorityCount()
   288  }
   289  
   290  func (v *ConsensusView) GetMajorityCountByTotalSigners(totalSigner int) int {
   291  	return v.producers.GetMajorityCountByTotalSigners(totalSigner)
   292  }
   293  
   294  func NewConsensusView(tolerance time.Duration, account []byte,
   295  	producers *Producers, viewListener ViewListener) *ConsensusView {
   296  	c := &ConsensusView{
   297  		consensusStatus: ConsensusReady,
   298  		viewStartTime:   time.Unix(0, 0),
   299  		viewOffset:      0,
   300  		publicKey:       account,
   301  		signTolerance:   tolerance,
   302  		producers:       producers,
   303  		listener:        viewListener,
   304  		isDposOnDuty:    false,
   305  	}
   306  	return c
   307  }