github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/emitter/control.go (about)

     1  package emitter
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/unicornultrafoundation/go-helios/emitter/ancestor"
     7  	"github.com/unicornultrafoundation/go-helios/native/idx"
     8  	"github.com/unicornultrafoundation/go-helios/native/pos"
     9  	"github.com/unicornultrafoundation/go-helios/utils/piecefunc"
    10  	"github.com/unicornultrafoundation/go-u2u/native"
    11  )
    12  
    13  func scalarUpdMetric(diff idx.Event, weight pos.Weight, totalWeight pos.Weight) ancestor.Metric {
    14  	return ancestor.Metric(scalarUpdMetricF(uint64(diff)*piecefunc.DecimalUnit)) * ancestor.Metric(weight) / ancestor.Metric(totalWeight)
    15  }
    16  
    17  func updMetric(median, cur, upd idx.Event, validatorIdx idx.Validator, validators *pos.Validators) ancestor.Metric {
    18  	if upd <= median || upd <= cur {
    19  		return 0
    20  	}
    21  	weight := validators.GetWeightByIdx(validatorIdx)
    22  	if median < cur {
    23  		return scalarUpdMetric(upd-median, weight, validators.TotalWeight()) - scalarUpdMetric(cur-median, weight, validators.TotalWeight())
    24  	}
    25  	return scalarUpdMetric(upd-median, weight, validators.TotalWeight())
    26  }
    27  
    28  func kickStartMetric(metric ancestor.Metric, seq idx.Event) ancestor.Metric {
    29  	// kickstart metric in a beginning of epoch, when there's nothing to observe yet
    30  	if seq <= 2 && metric < 0.9*piecefunc.DecimalUnit {
    31  		metric += 0.1 * piecefunc.DecimalUnit
    32  	}
    33  	if seq <= 1 && metric <= 0.8*piecefunc.DecimalUnit {
    34  		metric += 0.2 * piecefunc.DecimalUnit
    35  	}
    36  	return metric
    37  }
    38  
    39  func eventMetric(orig ancestor.Metric, seq idx.Event) ancestor.Metric {
    40  	return kickStartMetric(ancestor.Metric(eventMetricF(uint64(orig))), seq)
    41  }
    42  
    43  func (em *Emitter) isAllowedToEmit(e native.EventI, eTxs bool, metric ancestor.Metric, selfParent *native.Event) bool {
    44  	passedTime := e.CreationTime().Time().Sub(em.prevEmittedAtTime)
    45  	if passedTime < 0 {
    46  		passedTime = 0
    47  	}
    48  	passedTimeIdle := e.CreationTime().Time().Sub(em.prevIdleTime)
    49  	if passedTimeIdle < 0 {
    50  		passedTimeIdle = 0
    51  	}
    52  	if em.stakeRatio[e.Creator()] < 0.35*piecefunc.DecimalUnit {
    53  		// top validators emit event right after transaction is originated
    54  		passedTimeIdle = passedTime
    55  	} else if em.stakeRatio[e.Creator()] < 0.7*piecefunc.DecimalUnit {
    56  		// top validators emit event right after transaction is originated
    57  		passedTimeIdle = (passedTimeIdle + passedTime) / 2
    58  	}
    59  	if passedTimeIdle > passedTime {
    60  		passedTimeIdle = passedTime
    61  	}
    62  	// metric is a decimal (0.0, 1.0], being an estimation of how much the event will advance the consensus
    63  	adjustedPassedTime := time.Duration(ancestor.Metric(passedTime/piecefunc.DecimalUnit) * metric)
    64  	adjustedPassedIdleTime := time.Duration(ancestor.Metric(passedTimeIdle/piecefunc.DecimalUnit) * metric)
    65  	passedBlocks := em.world.GetLatestBlockIndex() - em.prevEmittedAtBlock
    66  	// Forbid emitting if not enough power and power is decreasing
    67  	{
    68  		threshold := em.config.EmergencyThreshold
    69  		if e.GasPowerLeft().Min() <= threshold {
    70  			if selfParent != nil && e.GasPowerLeft().Min() < selfParent.GasPowerLeft().Min() {
    71  				em.Periodic.Warn(10*time.Second, "Not enough power to emit event, waiting",
    72  					"power", e.GasPowerLeft().String(),
    73  					"selfParentPower", selfParent.GasPowerLeft().String(),
    74  					"stake%", 100*float64(em.validators.Get(e.Creator()))/float64(em.validators.TotalWeight()))
    75  				return false
    76  			}
    77  		}
    78  	}
    79  	// Enforce emitting if passed too many time/blocks since previous event
    80  	{
    81  		rules := em.world.GetRules()
    82  		maxBlocks := rules.Economy.BlockMissedSlack/2 + 1
    83  		if rules.Economy.BlockMissedSlack > maxBlocks && maxBlocks < rules.Economy.BlockMissedSlack-5 {
    84  			maxBlocks = rules.Economy.BlockMissedSlack - 5
    85  		}
    86  		if passedTime >= em.intervals.Max ||
    87  			passedBlocks >= maxBlocks*4/5 && metric >= piecefunc.DecimalUnit/2 ||
    88  			passedBlocks >= maxBlocks {
    89  			return true
    90  		}
    91  	}
    92  	// Slow down emitting if power is low
    93  	{
    94  		threshold := (em.config.NoTxsThreshold + em.config.EmergencyThreshold) / 2
    95  		if e.GasPowerLeft().Min() <= threshold {
    96  			// it's emitter, so no need in determinism => fine to use float
    97  			minT := float64(em.intervals.Min)
    98  			maxT := float64(em.intervals.Max)
    99  			factor := float64(e.GasPowerLeft().Min()) / float64(threshold)
   100  			adjustedEmitInterval := time.Duration(maxT - (maxT-minT)*factor)
   101  			if passedTime < adjustedEmitInterval {
   102  				return false
   103  			}
   104  		}
   105  	}
   106  	// Slow down emitting if no txs to confirm/originate
   107  	{
   108  		if passedTime < em.intervals.Max &&
   109  			em.idle() &&
   110  			!eTxs {
   111  			return false
   112  		}
   113  	}
   114  	// Emitting is controlled by the efficiency metric
   115  	{
   116  		if passedTime < em.intervals.Min {
   117  			return false
   118  		}
   119  		if adjustedPassedTime < em.intervals.Min &&
   120  			!em.idle() {
   121  			return false
   122  		}
   123  		if adjustedPassedIdleTime < em.intervals.Confirming &&
   124  			!em.idle() &&
   125  			!eTxs {
   126  			return false
   127  		}
   128  	}
   129  
   130  	return true
   131  }
   132  
   133  func (em *Emitter) recheckIdleTime() {
   134  	em.world.Lock()
   135  	defer em.world.Unlock()
   136  	if em.idle() {
   137  		em.prevIdleTime = time.Now()
   138  	}
   139  }