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

     1  package emitter
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/unicornultrafoundation/go-helios/common/bigendian"
     7  	"github.com/unicornultrafoundation/go-helios/hash"
     8  	"github.com/unicornultrafoundation/go-helios/native/idx"
     9  	"github.com/unicornultrafoundation/go-helios/native/pos"
    10  	"github.com/unicornultrafoundation/go-u2u/common"
    11  	"github.com/unicornultrafoundation/go-u2u/core/types"
    12  	"github.com/unicornultrafoundation/go-u2u/params"
    13  
    14  	"github.com/unicornultrafoundation/go-u2u/eventcheck/epochcheck"
    15  	"github.com/unicornultrafoundation/go-u2u/eventcheck/gaspowercheck"
    16  	"github.com/unicornultrafoundation/go-u2u/native"
    17  	"github.com/unicornultrafoundation/go-u2u/utils"
    18  	"github.com/unicornultrafoundation/go-u2u/utils/txtime"
    19  )
    20  
    21  const (
    22  	TxTurnPeriod        = 8 * time.Second
    23  	TxTurnPeriodLatency = 1 * time.Second
    24  	TxTurnNonces        = 32
    25  )
    26  
    27  func max64(a, b uint64) uint64 {
    28  	if a > b {
    29  		return a
    30  	}
    31  	return b
    32  }
    33  
    34  func (em *Emitter) maxGasPowerToUse(e *native.MutableEventPayload) uint64 {
    35  	rules := em.world.GetRules()
    36  	maxGasToUse := rules.Economy.Gas.MaxEventGas
    37  	if maxGasToUse > e.GasPowerLeft().Min() {
    38  		maxGasToUse = e.GasPowerLeft().Min()
    39  	}
    40  	// Smooth TPS if power isn't big
    41  	if em.config.LimitedTpsThreshold > em.config.NoTxsThreshold {
    42  		upperThreshold := em.config.LimitedTpsThreshold
    43  		downThreshold := em.config.NoTxsThreshold
    44  
    45  		estimatedAlloc := gaspowercheck.CalcValidatorGasPower(e, e.CreationTime(), e.MedianTime(), 0, em.validators, gaspowercheck.Config{
    46  			Idx:                native.LongTermGas,
    47  			AllocPerSec:        rules.Economy.LongGasPower.AllocPerSec * 4 / 5,
    48  			MaxAllocPeriod:     native.Timestamp(time.Minute),
    49  			MinEnsuredAlloc:    0,
    50  			StartupAllocPeriod: 0,
    51  			MinStartupGas:      0,
    52  		})
    53  
    54  		gasPowerLeft := e.GasPowerLeft().Min() + estimatedAlloc
    55  		if gasPowerLeft < downThreshold {
    56  			return 0
    57  		}
    58  		newGasPowerLeft := uint64(0)
    59  		if gasPowerLeft > maxGasToUse {
    60  			newGasPowerLeft = gasPowerLeft - maxGasToUse
    61  		}
    62  
    63  		var x1, x2 = newGasPowerLeft, gasPowerLeft
    64  		if x1 < downThreshold {
    65  			x1 = downThreshold
    66  		}
    67  		if x2 > upperThreshold {
    68  			x2 = upperThreshold
    69  		}
    70  		trespassingPart := uint64(0)
    71  		if x2 > x1 {
    72  			trespassingPart = x2 - x1
    73  		}
    74  		healthyPart := uint64(0)
    75  		if gasPowerLeft > x2 {
    76  			healthyPart = gasPowerLeft - x2
    77  		}
    78  
    79  		smoothGasToUse := healthyPart + trespassingPart/2
    80  		if maxGasToUse > smoothGasToUse {
    81  			maxGasToUse = smoothGasToUse
    82  		}
    83  	}
    84  	// pendingGas should be below MaxBlockGas
    85  	{
    86  		maxPendingGas := max64(max64(rules.Blocks.MaxBlockGas/3, rules.Economy.Gas.MaxEventGas), 15000000)
    87  		if maxPendingGas <= em.pendingGas {
    88  			return 0
    89  		}
    90  		if maxPendingGas < em.pendingGas+maxGasToUse {
    91  			maxGasToUse = maxPendingGas - em.pendingGas
    92  		}
    93  	}
    94  	// No txs if power is low
    95  	{
    96  		threshold := em.config.NoTxsThreshold
    97  		if e.GasPowerLeft().Min() <= threshold {
    98  			return 0
    99  		} else if e.GasPowerLeft().Min() < threshold+maxGasToUse {
   100  			maxGasToUse = e.GasPowerLeft().Min() - threshold
   101  		}
   102  	}
   103  	return maxGasToUse
   104  }
   105  
   106  func getTxRoundIndex(now, txTime time.Time, validatorsNum idx.Validator) int {
   107  	passed := now.Sub(txTime)
   108  	if passed < 0 {
   109  		passed = 0
   110  	}
   111  	return int((passed / TxTurnPeriod) % time.Duration(validatorsNum))
   112  }
   113  
   114  // safe for concurrent use
   115  func (em *Emitter) isMyTxTurn(txHash common.Hash, sender common.Address, accountNonce uint64, now time.Time, validators *pos.Validators, me idx.ValidatorID, epoch idx.Epoch) bool {
   116  	txTime := txtime.Of(txHash)
   117  
   118  	roundIndex := getTxRoundIndex(now, txTime, validators.Len())
   119  	if roundIndex != getTxRoundIndex(now.Add(TxTurnPeriodLatency), txTime, validators.Len()) {
   120  		// round is about to change, avoid originating the transaction to avoid racing with another validator
   121  		return false
   122  	}
   123  
   124  	roundsHash := hash.Of(sender.Bytes(), bigendian.Uint64ToBytes(accountNonce/TxTurnNonces), epoch.Bytes())
   125  	rounds := utils.WeightedPermutation(roundIndex+1, validators.SortedWeights(), roundsHash)
   126  	return validators.GetID(idx.Validator(rounds[roundIndex])) == me
   127  }
   128  
   129  func (em *Emitter) addTxs(e *native.MutableEventPayload, sorted *types.TransactionsByPriceAndNonce) {
   130  	maxGasUsed := em.maxGasPowerToUse(e)
   131  	if maxGasUsed <= e.GasPowerUsed() {
   132  		return
   133  	}
   134  
   135  	// sort transactions by price and nonce
   136  	rules := em.world.GetRules()
   137  	for tx := sorted.Peek(); tx != nil; tx = sorted.Peek() {
   138  		sender, _ := types.Sender(em.world.TxSigner, tx)
   139  		// check transaction epoch rules
   140  		if epochcheck.CheckTxs(types.Transactions{tx}, rules) != nil {
   141  			sorted.Pop()
   142  			continue
   143  		}
   144  		// check there's enough gas power to originate the transaction
   145  		if tx.Gas() >= e.GasPowerLeft().Min() || e.GasPowerUsed()+tx.Gas() >= maxGasUsed {
   146  			if params.TxGas >= e.GasPowerLeft().Min() || e.GasPowerUsed()+params.TxGas >= maxGasUsed {
   147  				// stop if cannot originate even an empty transaction
   148  				break
   149  			}
   150  			sorted.Pop()
   151  			continue
   152  		}
   153  		// check not conflicted with already originated txs (in any connected event)
   154  		if em.originatedTxs.TotalOf(sender) != 0 {
   155  			sorted.Pop()
   156  			continue
   157  		}
   158  		// my turn, i.e. try to not include the same tx simultaneously by different validators
   159  		if !em.isMyTxTurn(tx.Hash(), sender, tx.Nonce(), time.Now(), em.validators, e.Creator(), em.epoch) {
   160  			sorted.Pop()
   161  			continue
   162  		}
   163  		// check transaction is not outdated
   164  		if !em.world.TxPool.Has(tx.Hash()) {
   165  			sorted.Pop()
   166  			continue
   167  		}
   168  		// add
   169  		e.SetGasPowerUsed(e.GasPowerUsed() + tx.Gas())
   170  		e.SetGasPowerLeft(e.GasPowerLeft().Sub(tx.Gas()))
   171  		e.SetTxs(append(e.Txs(), tx))
   172  		sorted.Shift()
   173  	}
   174  }