github.com/cosmos/cosmos-sdk@v0.50.10/types/mempool/sender_nonce.go (about) 1 package mempool 2 3 import ( 4 "context" 5 crand "crypto/rand" // #nosec // crypto/rand is used for seed generation 6 "encoding/binary" 7 "fmt" 8 "math/rand" // #nosec // math/rand is used for random selection and seeded from crypto/rand 9 "sync" 10 11 "github.com/huandu/skiplist" 12 13 sdk "github.com/cosmos/cosmos-sdk/types" 14 "github.com/cosmos/cosmos-sdk/x/auth/signing" 15 ) 16 17 var ( 18 _ ExtMempool = (*SenderNonceMempool)(nil) 19 _ Iterator = (*senderNonceMempoolIterator)(nil) 20 ) 21 22 var DefaultMaxTx = -1 23 24 // SenderNonceMempool is a mempool that prioritizes transactions within a sender 25 // by nonce, the lowest first, but selects a random sender on each iteration. 26 // The mempool is iterated by: 27 // 28 // 1) Maintaining a separate list of nonce ordered txs per sender 29 // 2) For each select iteration, randomly choose a sender and pick the next nonce ordered tx from their list 30 // 3) Repeat 1,2 until the mempool is exhausted 31 // 32 // Note that PrepareProposal could choose to stop iteration before reaching the 33 // end if maxBytes is reached. 34 type SenderNonceMempool struct { 35 mtx sync.Mutex 36 senders map[string]*skiplist.SkipList 37 rnd *rand.Rand 38 maxTx int 39 existingTx map[txKey]bool 40 } 41 42 type SenderNonceOptions func(*SenderNonceMempool) 43 44 type txKey struct { 45 address string 46 nonce uint64 47 } 48 49 // NewSenderNonceMempool creates a new mempool that prioritizes transactions by 50 // nonce, the lowest first, picking a random sender on each iteration. 51 func NewSenderNonceMempool(opts ...SenderNonceOptions) *SenderNonceMempool { 52 senderMap := make(map[string]*skiplist.SkipList) 53 existingTx := make(map[txKey]bool) 54 snp := &SenderNonceMempool{ 55 senders: senderMap, 56 maxTx: DefaultMaxTx, 57 existingTx: existingTx, 58 } 59 60 var seed int64 61 err := binary.Read(crand.Reader, binary.BigEndian, &seed) 62 if err != nil { 63 panic(err) 64 } 65 66 snp.setSeed(seed) 67 68 for _, opt := range opts { 69 opt(snp) 70 } 71 72 return snp 73 } 74 75 // SenderNonceSeedOpt Option To add a Seed for random type when calling the 76 // constructor NewSenderNonceMempool. 77 // 78 // Example: 79 // 80 // random_seed := int64(1000) 81 // NewSenderNonceMempool(SenderNonceSeedTxOpt(random_seed)) 82 func SenderNonceSeedOpt(seed int64) SenderNonceOptions { 83 return func(snp *SenderNonceMempool) { 84 snp.setSeed(seed) 85 } 86 } 87 88 // SenderNonceMaxTxOpt Option To set limit of max tx when calling the constructor 89 // NewSenderNonceMempool. 90 // 91 // Example: 92 // 93 // NewSenderNonceMempool(SenderNonceMaxTxOpt(100)) 94 func SenderNonceMaxTxOpt(maxTx int) SenderNonceOptions { 95 return func(snp *SenderNonceMempool) { 96 snp.maxTx = maxTx 97 } 98 } 99 100 func (snm *SenderNonceMempool) setSeed(seed int64) { 101 s1 := rand.NewSource(seed) 102 snm.rnd = rand.New(s1) //#nosec // math/rand is seeded from crypto/rand by default 103 } 104 105 // NextSenderTx returns the next transaction for a given sender by nonce order, 106 // i.e. the next valid transaction for the sender. If no such transaction exists, 107 // nil will be returned. 108 func (snm *SenderNonceMempool) NextSenderTx(sender string) sdk.Tx { 109 senderIndex, ok := snm.senders[sender] 110 if !ok { 111 return nil 112 } 113 114 cursor := senderIndex.Front() 115 return cursor.Value.(sdk.Tx) 116 } 117 118 // Insert adds a tx to the mempool. It returns an error if the tx does not have 119 // at least one signer. Note, priority is ignored. 120 func (snm *SenderNonceMempool) Insert(_ context.Context, tx sdk.Tx) error { 121 snm.mtx.Lock() 122 defer snm.mtx.Unlock() 123 if snm.maxTx > 0 && len(snm.existingTx) >= snm.maxTx { 124 return ErrMempoolTxMaxCapacity 125 } 126 if snm.maxTx < 0 { 127 return nil 128 } 129 130 sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2() 131 if err != nil { 132 return err 133 } 134 if len(sigs) == 0 { 135 return fmt.Errorf("tx must have at least one signer") 136 } 137 138 sig := sigs[0] 139 sender := sdk.AccAddress(sig.PubKey.Address()).String() 140 nonce := sig.Sequence 141 142 senderTxs, found := snm.senders[sender] 143 if !found { 144 senderTxs = skiplist.New(skiplist.Uint64) 145 snm.senders[sender] = senderTxs 146 } 147 148 senderTxs.Set(nonce, tx) 149 150 key := txKey{nonce: nonce, address: sender} 151 snm.existingTx[key] = true 152 153 return nil 154 } 155 156 // Select returns an iterator ordering transactions the mempool with the lowest 157 // nonce of a random selected sender first. 158 // 159 // NOTE: It is not safe to use this iterator while removing transactions from 160 // the underlying mempool. 161 func (snm *SenderNonceMempool) Select(ctx context.Context, txs [][]byte) Iterator { 162 snm.mtx.Lock() 163 defer snm.mtx.Unlock() 164 return snm.doSelect(ctx, txs) 165 } 166 167 func (snm *SenderNonceMempool) doSelect(_ context.Context, _ [][]byte) Iterator { 168 var senders []string 169 170 senderCursors := make(map[string]*skiplist.Element) 171 orderedSenders := skiplist.New(skiplist.String) 172 173 // #nosec 174 for s := range snm.senders { 175 orderedSenders.Set(s, s) 176 } 177 178 s := orderedSenders.Front() 179 for s != nil { 180 sender := s.Value.(string) 181 senders = append(senders, sender) 182 senderCursors[sender] = snm.senders[sender].Front() 183 s = s.Next() 184 } 185 186 iter := &senderNonceMempoolIterator{ 187 senders: senders, 188 rnd: snm.rnd, 189 senderCursors: senderCursors, 190 } 191 192 return iter.Next() 193 } 194 195 // SelectBy will hold the mutex during the iteration, callback returns if continue. 196 func (snm *SenderNonceMempool) SelectBy(ctx context.Context, txs [][]byte, callback func(sdk.Tx) bool) { 197 snm.mtx.Lock() 198 defer snm.mtx.Unlock() 199 200 iter := snm.doSelect(ctx, txs) 201 for iter != nil && callback(iter.Tx()) { 202 iter = iter.Next() 203 } 204 } 205 206 // CountTx returns the total count of txs in the mempool. 207 func (snm *SenderNonceMempool) CountTx() int { 208 snm.mtx.Lock() 209 defer snm.mtx.Unlock() 210 return len(snm.existingTx) 211 } 212 213 // Remove removes a tx from the mempool. It returns an error if the tx does not 214 // have at least one signer or the tx was not found in the pool. 215 func (snm *SenderNonceMempool) Remove(tx sdk.Tx) error { 216 snm.mtx.Lock() 217 defer snm.mtx.Unlock() 218 sigs, err := tx.(signing.SigVerifiableTx).GetSignaturesV2() 219 if err != nil { 220 return err 221 } 222 if len(sigs) == 0 { 223 return fmt.Errorf("tx must have at least one signer") 224 } 225 226 sig := sigs[0] 227 sender := sdk.AccAddress(sig.PubKey.Address()).String() 228 nonce := sig.Sequence 229 230 senderTxs, found := snm.senders[sender] 231 if !found { 232 return ErrTxNotFound 233 } 234 235 res := senderTxs.Remove(nonce) 236 if res == nil { 237 return ErrTxNotFound 238 } 239 240 if senderTxs.Len() == 0 { 241 delete(snm.senders, sender) 242 } 243 244 key := txKey{nonce: nonce, address: sender} 245 delete(snm.existingTx, key) 246 247 return nil 248 } 249 250 type senderNonceMempoolIterator struct { 251 rnd *rand.Rand 252 currentTx *skiplist.Element 253 senders []string 254 senderCursors map[string]*skiplist.Element 255 } 256 257 // Next returns the next iterator state which will contain a tx with the next 258 // smallest nonce of a randomly selected sender. 259 func (i *senderNonceMempoolIterator) Next() Iterator { 260 for len(i.senders) > 0 { 261 senderIndex := i.rnd.Intn(len(i.senders)) 262 sender := i.senders[senderIndex] 263 senderCursor, found := i.senderCursors[sender] 264 if !found { 265 i.senders = removeAtIndex(i.senders, senderIndex) 266 continue 267 } 268 269 if nextCursor := senderCursor.Next(); nextCursor != nil { 270 i.senderCursors[sender] = nextCursor 271 } else { 272 i.senders = removeAtIndex(i.senders, senderIndex) 273 } 274 275 return &senderNonceMempoolIterator{ 276 senders: i.senders, 277 currentTx: senderCursor, 278 rnd: i.rnd, 279 senderCursors: i.senderCursors, 280 } 281 } 282 283 return nil 284 } 285 286 func (i *senderNonceMempoolIterator) Tx() sdk.Tx { 287 return i.currentTx.Value.(sdk.Tx) 288 } 289 290 func removeAtIndex[T any](slice []T, index int) []T { 291 return append(slice[:index], slice[index+1:]...) 292 }