git.gammaspectra.live/P2Pool/consensus@v0.0.0-20240403173234-a039820b20c9/p2pool/mempool/mempool.go (about) 1 package mempool 2 3 import ( 4 "git.gammaspectra.live/P2Pool/consensus/types" 5 "git.gammaspectra.live/P2Pool/consensus/utils" 6 "git.gammaspectra.live/P2Pool/go-monero/pkg/rpc/daemon" 7 "lukechampine.com/uint128" 8 "slices" 9 ) 10 11 type MempoolEntry struct { 12 Id types.Hash 13 BlobSize uint64 14 Weight uint64 15 Fee uint64 16 } 17 18 type Mempool []*MempoolEntry 19 20 func (m Mempool) Sort() { 21 // Sort all transactions by fee per byte (highest to lowest) 22 23 slices.SortFunc(m, func(a, b *MempoolEntry) int { 24 return a.Compare(b) 25 }) 26 } 27 28 func (m Mempool) WeightAndFees() (weight, fees uint64) { 29 for _, e := range m { 30 weight += e.Weight 31 fees += e.Fee 32 } 33 return 34 } 35 36 func (m Mempool) Fees() (r uint64) { 37 for _, e := range m { 38 r += e.Fee 39 } 40 return r 41 } 42 43 func (m Mempool) Weight() (r uint64) { 44 for _, e := range m { 45 r += e.Weight 46 } 47 return r 48 } 49 50 func (m Mempool) Pick(baseReward, minerTxWeight, medianWeight uint64) Mempool { 51 // Sort all transactions by fee per byte (highest to lowest) 52 m.Sort() 53 54 finalReward := baseReward 55 finalFees := uint64(0) 56 finalWeight := minerTxWeight 57 58 m2 := make(Mempool, 0, len(m)) 59 60 for i, tx := range m { 61 k := -1 62 63 reward := GetBlockReward(baseReward, medianWeight, finalFees+tx.Fee, finalWeight+tx.Weight) 64 if reward > finalReward { 65 // If simply adding this transaction increases the reward, remember it 66 finalReward = reward 67 k = 1 68 } 69 70 // Try replacing other transactions when we are above the limit 71 if finalWeight+tx.Weight > medianWeight { 72 // Don't check more than 100 transactions deep because they have higher and higher fee/byte 73 n := len(m2) 74 for j, j1 := n-1, max(0, n-100); j >= j1; j-- { 75 prevTx := m2[j] 76 reward2 := GetBlockReward(baseReward, medianWeight, finalFees+prevTx.Fee, finalWeight+prevTx.Weight) 77 if reward2 > finalReward { 78 // If replacing some other transaction increases the reward even more, remember it 79 // And keep trying to replace other transactions 80 finalReward = reward2 81 k = j 82 } 83 } 84 } 85 86 if k == i { 87 // Simply adding this tx improves the reward 88 m2 = append(m2, tx) 89 finalFees += tx.Fee 90 finalWeight += tx.Weight 91 } else if k >= 0 { 92 // Replacing another tx with this tx improves the reward 93 prevTx := m2[k] 94 m2[k] = tx 95 finalFees += tx.Fee - prevTx.Fee 96 finalWeight += tx.Weight - prevTx.Weight 97 } 98 } 99 100 return m2 101 } 102 103 func (m Mempool) perfectSumRecursion(c chan Mempool, targetFee uint64, i int, currentSum uint64, top *int, m2 Mempool) { 104 if currentSum == targetFee { 105 c <- slices.Clone(m2) 106 return 107 } 108 109 if currentSum < targetFee && i < len(m) { 110 if top != nil && *top < i { 111 *top = i 112 utils.Logf("Mempool", "index %d/%d", i, len(m)) 113 } 114 m3 := append(m2, m[i]) 115 m.perfectSumRecursion(c, targetFee, i+1, currentSum+m[i].Fee, nil, m3) 116 m.perfectSumRecursion(c, targetFee, i+1, currentSum, top, m2) 117 } 118 } 119 120 func (m Mempool) PerfectSum(targetFee uint64) chan Mempool { 121 m2 := make(Mempool, 0, len(m)) 122 c := make(chan Mempool) 123 go func() { 124 defer close(c) 125 var i int 126 m.perfectSumRecursion(c, targetFee, 0, 0, &i, m2) 127 }() 128 return c 129 } 130 131 // Compare returns -1 if self is preferred over o, 0 if equal, 1 if o is preferred over self 132 func (t *MempoolEntry) Compare(o *MempoolEntry) int { 133 a := t.Fee * o.Weight 134 b := o.Fee * t.Weight 135 136 // Prefer transactions with higher fee/byte 137 if a > b { 138 return -1 139 } 140 if a < b { 141 return 1 142 } 143 144 // If fee/byte is the same, prefer smaller transactions (they give smaller penalty when going above the median block size limit) 145 if t.Weight < o.Weight { 146 return -1 147 } 148 if t.Weight > o.Weight { 149 return 1 150 } 151 152 // If two transactions have exactly the same fee and weight, just order them by id 153 return t.Id.Compare(o.Id) 154 } 155 156 func GetBlockReward(baseReward, medianWeight, fees, weight uint64) uint64 { 157 if weight <= medianWeight { 158 return baseReward + fees 159 } 160 if weight > medianWeight*2 { 161 return 0 162 } 163 164 reward := uint128.From64(baseReward).Mul64(medianWeight*2 - weight).Div64(medianWeight).Div64(medianWeight) 165 return reward.Lo + fees 166 } 167 168 func isRctBulletproof(t int) bool { 169 switch t { 170 case 3, 4, 5: // RCTTypeBulletproof, RCTTypeBulletproof2, RCTTypeCLSAG: 171 return true 172 default: 173 return false 174 } 175 } 176 177 func isRctBulletproofPlus(t int) bool { 178 switch t { 179 case 6: // RCTTypeBulletproofPlus: 180 return true 181 default: 182 return false 183 } 184 } 185 186 func NewEntryFromRPCData(id types.Hash, buf []byte, json *daemon.TransactionJSON) *MempoolEntry { 187 isBulletproof := isRctBulletproof(json.RctSignatures.Type) 188 isBulletproofPlus := isRctBulletproofPlus(json.RctSignatures.Type) 189 190 var weight, paddedOutputs, bpBase, bpSize, bpClawback uint64 191 if !isBulletproof && !isBulletproofPlus { 192 weight = uint64(len(buf)) 193 } else if isBulletproofPlus { 194 for _, proof := range json.RctsigPrunable.Bpp { 195 LSize := len(proof.L) / 2 196 n2 := uint64(1 << (LSize - 6)) 197 if n2 == 0 { 198 paddedOutputs = 0 199 break 200 } 201 paddedOutputs += n2 202 } 203 { 204 205 bpBase = uint64(32*6+7*2) / 2 206 207 //get_transaction_weight_clawback 208 if len(json.RctSignatures.Outpk) <= 2 { 209 bpClawback = 0 210 } else { 211 nlr := 0 212 for (1 << nlr) < paddedOutputs { 213 nlr++ 214 } 215 nlr += 6 216 217 bpSize = uint64(32*6 + 2*nlr) 218 219 bpClawback = (bpBase*paddedOutputs - bpSize) * 4 / 5 220 } 221 } 222 223 weight = uint64(len(buf)) + bpClawback 224 } else { 225 for _, proof := range json.RctsigPrunable.Bp { 226 LSize := len(proof.L) / 2 227 n2 := uint64(1 << (LSize - 6)) 228 if n2 == 0 { 229 paddedOutputs = 0 230 break 231 } 232 paddedOutputs += n2 233 } 234 { 235 236 bpBase = uint64(32*9+7*2) / 2 237 238 //get_transaction_weight_clawback 239 if len(json.RctSignatures.Outpk) <= 2 { 240 bpClawback = 0 241 } else { 242 nlr := 0 243 for (1 << nlr) < paddedOutputs { 244 nlr++ 245 } 246 nlr += 6 247 248 bpSize = uint64(32*9 + 2*nlr) 249 250 bpClawback = (bpBase*paddedOutputs - bpSize) * 4 / 5 251 } 252 } 253 254 weight = uint64(len(buf)) + bpClawback 255 } 256 257 return &MempoolEntry{ 258 Id: id, 259 BlobSize: uint64(len(buf)), 260 Weight: weight, 261 Fee: json.RctSignatures.Txnfee, 262 } 263 }