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  }