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 }