github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/eventcheck/gaspowercheck/gas_power_check.go (about) 1 package gaspowercheck 2 3 import ( 4 "errors" 5 "math/big" 6 "time" 7 8 "github.com/unicornultrafoundation/go-helios/eventcheck/epochcheck" 9 "github.com/unicornultrafoundation/go-helios/hash" 10 "github.com/unicornultrafoundation/go-helios/native/idx" 11 "github.com/unicornultrafoundation/go-helios/native/pos" 12 13 "github.com/unicornultrafoundation/go-u2u/native" 14 "github.com/unicornultrafoundation/go-u2u/native/iblockproc" 15 ) 16 17 var ( 18 // ErrWrongGasPowerLeft indicates that event's GasPowerLeft is miscalculated. 19 ErrWrongGasPowerLeft = errors.New("event has wrong GasPowerLeft") 20 ) 21 22 type ValidatorState struct { 23 PrevEpochEvent iblockproc.EventInfo 24 GasRefund uint64 25 } 26 27 // ValidationContext for gaspower checking 28 type ValidationContext struct { 29 Epoch idx.Epoch 30 Configs [native.GasPowerConfigs]Config 31 EpochStart native.Timestamp 32 Validators *pos.Validators 33 ValidatorStates []ValidatorState 34 } 35 36 // Reader is accessed by the validator to get the current state. 37 type Reader interface { 38 GetValidationContext() *ValidationContext 39 } 40 41 // Config for gaspower checking. There'll be 2 different configs for short-term and long-term gas power checks. 42 type Config struct { 43 Idx int 44 AllocPerSec uint64 45 MaxAllocPeriod native.Timestamp 46 MinEnsuredAlloc uint64 47 StartupAllocPeriod native.Timestamp 48 MinStartupGas uint64 49 } 50 51 // Checker which checks gas power 52 type Checker struct { 53 reader Reader 54 } 55 56 // New Checker for gas power 57 func New(reader Reader) *Checker { 58 return &Checker{ 59 reader: reader, 60 } 61 } 62 63 func mul(a *big.Int, b uint64) { 64 a.Mul(a, new(big.Int).SetUint64(b)) 65 } 66 67 func div(a *big.Int, b uint64) { 68 a.Div(a, new(big.Int).SetUint64(b)) 69 } 70 71 // CalcGasPower calculates available gas power for the event, i.e. how many gas its content may consume 72 func (v *Checker) CalcGasPower(e native.EventI, selfParent native.EventI) (native.GasPowerLeft, error) { 73 ctx := v.reader.GetValidationContext() 74 // check that all the data is for the same epoch 75 if ctx.Epoch != e.Epoch() { 76 return native.GasPowerLeft{}, epochcheck.ErrNotRelevant 77 } 78 79 var res native.GasPowerLeft 80 for i := range ctx.Configs { 81 res.Gas[i] = calcGasPower(e, selfParent, ctx, ctx.Configs[i]) 82 } 83 84 return res, nil 85 } 86 87 func calcGasPower(e native.EventI, selfParent native.EventI, ctx *ValidationContext, config Config) uint64 { 88 var prevGasPowerLeft uint64 89 var prevTime native.Timestamp 90 91 if e.SelfParent() != nil { 92 prevGasPowerLeft = selfParent.GasPowerLeft().Gas[config.Idx] 93 prevTime = selfParent.MedianTime() 94 } else { 95 validatorState := ctx.ValidatorStates[ctx.Validators.GetIdx(e.Creator())] 96 if validatorState.PrevEpochEvent.ID != hash.ZeroEvent { 97 prevGasPowerLeft = validatorState.PrevEpochEvent.GasPowerLeft.Gas[config.Idx] 98 prevTime = validatorState.PrevEpochEvent.Time 99 } else { 100 prevGasPowerLeft = 0 101 prevTime = ctx.EpochStart 102 } 103 prevGasPowerLeft += validatorState.GasRefund 104 } 105 106 return CalcValidatorGasPower(e, e.MedianTime(), prevTime, prevGasPowerLeft, ctx.Validators, config) 107 } 108 109 func CalcValidatorGasPower(e native.EventI, eTime, prevTime native.Timestamp, prevGasPowerLeft uint64, validators *pos.Validators, config Config) uint64 { 110 gasPowerPerSec, maxGasPower, startup := CalcValidatorGasPowerPerSec(e.Creator(), validators, config) 111 112 if e.SelfParent() == nil { 113 if prevGasPowerLeft < startup { 114 prevGasPowerLeft = startup 115 } 116 } 117 118 if prevTime > eTime { 119 prevTime = eTime 120 } 121 122 gasPowerAllocatedBn := new(big.Int).SetUint64(uint64(eTime - prevTime)) 123 mul(gasPowerAllocatedBn, gasPowerPerSec) 124 div(gasPowerAllocatedBn, uint64(time.Second)) 125 126 gasPower := gasPowerAllocatedBn.Uint64() + prevGasPowerLeft 127 if gasPower > maxGasPower { 128 gasPower = maxGasPower 129 } 130 131 return gasPower 132 } 133 134 func CalcValidatorGasPowerPerSec( 135 validator idx.ValidatorID, 136 validators *pos.Validators, 137 config Config, 138 ) ( 139 perSec uint64, 140 maxGasPower uint64, 141 startup uint64, 142 ) { 143 stake := validators.Get(validator) 144 if stake == 0 { 145 return 0, 0, 0 146 } 147 148 gas := config 149 150 validatorGasPowerPerSecBn := new(big.Int).SetUint64(gas.AllocPerSec) 151 mul(validatorGasPowerPerSecBn, uint64(stake)) 152 div(validatorGasPowerPerSecBn, uint64(validators.TotalWeight())) 153 perSec = validatorGasPowerPerSecBn.Uint64() 154 155 maxGasPower = perSec * (uint64(gas.MaxAllocPeriod) / uint64(time.Second)) 156 if maxGasPower < gas.MinEnsuredAlloc { 157 maxGasPower = gas.MinEnsuredAlloc 158 } 159 160 startup = perSec * (uint64(gas.StartupAllocPeriod) / uint64(time.Second)) 161 if startup < gas.MinStartupGas { 162 startup = gas.MinStartupGas 163 } 164 165 return 166 } 167 168 // Validate event 169 func (v *Checker) Validate(e native.EventI, selfParent native.EventI) error { 170 gasPowers, err := v.CalcGasPower(e, selfParent) 171 if err != nil { 172 return err 173 } 174 for i := range gasPowers.Gas { 175 if e.GasPowerLeft().Gas[i]+e.GasPowerUsed() != gasPowers.Gas[i] { // GasPowerUsed is checked in basic_check 176 return ErrWrongGasPowerLeft 177 } 178 } 179 return nil 180 }