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 }