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  }