github.com/0xPolygon/supernets2-node@v0.0.0-20230711153321-2fe574524eaa/sequencer/addrqueue.go (about)

     1  package sequencer
     2  
     3  import (
     4  	"math/big"
     5  	"time"
     6  
     7  	"github.com/0xPolygon/supernets2-node/log"
     8  	"github.com/0xPolygon/supernets2-node/state"
     9  	"github.com/0xPolygon/supernets2-node/state/runtime"
    10  	"github.com/ethereum/go-ethereum/common"
    11  )
    12  
    13  // addrQueue is a struct that stores the ready and notReady txs for a specific from address
    14  type addrQueue struct {
    15  	from           common.Address
    16  	fromStr        string
    17  	currentNonce   uint64
    18  	currentBalance *big.Int
    19  	readyTx        *TxTracker
    20  	notReadyTxs    map[uint64]*TxTracker
    21  }
    22  
    23  // newAddrQueue creates and init a addrQueue
    24  func newAddrQueue(addr common.Address, nonce uint64, balance *big.Int) *addrQueue {
    25  	return &addrQueue{
    26  		from:           addr,
    27  		fromStr:        addr.String(),
    28  		currentNonce:   nonce,
    29  		currentBalance: balance,
    30  		readyTx:        nil,
    31  		notReadyTxs:    make(map[uint64]*TxTracker),
    32  	}
    33  }
    34  
    35  // addTx adds a tx to the addrQueue and updates the ready a notReady Txs
    36  func (a *addrQueue) addTx(tx *TxTracker) (newReadyTx, prevReadyTx *TxTracker, dropReason error) {
    37  	if a.currentNonce == tx.Nonce { // Is a possible readyTx
    38  		// We set the tx as readyTx if we do not have one assigned or if the gasPrice is better or equal than the current readyTx
    39  		if a.readyTx == nil || ((a.readyTx != nil) && (tx.GasPrice.Cmp(a.readyTx.GasPrice) >= 0)) {
    40  			oldReadyTx := a.readyTx
    41  			if a.currentBalance.Cmp(tx.Cost) >= 0 { //
    42  				a.readyTx = tx
    43  				return tx, oldReadyTx, nil
    44  			} else { // If there is not enough balance we set the new tx as notReadyTxs
    45  				a.readyTx = nil
    46  				a.notReadyTxs[tx.Nonce] = tx
    47  				return nil, oldReadyTx, nil
    48  			}
    49  		}
    50  	} else if a.currentNonce > tx.Nonce {
    51  		return nil, nil, runtime.ErrIntrinsicInvalidNonce
    52  	}
    53  
    54  	nrTx, found := a.notReadyTxs[tx.Nonce]
    55  	if !found || ((found) && (tx.GasPrice.Cmp(nrTx.GasPrice) >= 0)) {
    56  		a.notReadyTxs[tx.Nonce] = tx
    57  	}
    58  
    59  	return nil, nil, nil
    60  }
    61  
    62  // ExpireTransactions removes the txs that have been in the queue for more than maxTime
    63  func (a *addrQueue) ExpireTransactions(maxTime time.Duration) ([]*TxTracker, *TxTracker) {
    64  	var (
    65  		txs         []*TxTracker
    66  		prevReadyTx *TxTracker
    67  	)
    68  
    69  	for _, txTracker := range a.notReadyTxs {
    70  		if txTracker.ReceivedAt.Add(maxTime).Before(time.Now()) {
    71  			txs = append(txs, txTracker)
    72  			delete(a.notReadyTxs, txTracker.Nonce)
    73  			log.Debugf("Deleting notReadyTx %s from addrQueue %s", txTracker.HashStr, a.fromStr)
    74  		}
    75  	}
    76  
    77  	if a.readyTx != nil && a.readyTx.ReceivedAt.Add(maxTime).Before(time.Now()) {
    78  		prevReadyTx = a.readyTx
    79  		txs = append(txs, a.readyTx)
    80  		a.readyTx = nil
    81  		log.Debugf("Deleting notReadyTx %s from addrQueue %s", prevReadyTx.HashStr, a.fromStr)
    82  	}
    83  
    84  	return txs, prevReadyTx
    85  }
    86  
    87  // IsEmpty returns true if the addrQueue is empty
    88  func (a *addrQueue) IsEmpty() bool {
    89  	return a.readyTx == nil && len(a.notReadyTxs) == 0
    90  }
    91  
    92  // deleteTx deletes the tx from the addrQueue
    93  func (a *addrQueue) deleteTx(txHash common.Hash) (deletedReadyTx *TxTracker) {
    94  	txHashStr := txHash.String()
    95  
    96  	if (a.readyTx != nil) && (a.readyTx.HashStr == txHashStr) {
    97  		log.Infof("Deleting readyTx %s from addrQueue %s", txHashStr, a.fromStr)
    98  		prevReadyTx := a.readyTx
    99  		a.readyTx = nil
   100  		return prevReadyTx
   101  	} else {
   102  		for _, txTracker := range a.notReadyTxs {
   103  			if txTracker.HashStr == txHashStr {
   104  				log.Infof("Deleting notReadyTx %s from addrQueue %s", txHashStr, a.fromStr)
   105  				delete(a.notReadyTxs, txTracker.Nonce)
   106  			}
   107  		}
   108  		return nil
   109  	}
   110  }
   111  
   112  // updateCurrentNonceBalance updates the nonce and balance of the addrQueue and updates the ready and notReady txs
   113  func (a *addrQueue) updateCurrentNonceBalance(nonce *uint64, balance *big.Int) (newReadyTx, prevReadyTx *TxTracker, toDelete []*TxTracker) {
   114  	var oldReadyTx *TxTracker = nil
   115  	txsToDelete := make([]*TxTracker, 0)
   116  
   117  	if balance != nil {
   118  		log.Infof("Updating balance for addrQueue %s from %s to %s", a.fromStr, a.currentBalance.String(), balance.String())
   119  		a.currentBalance = balance
   120  	}
   121  
   122  	if nonce != nil {
   123  		if a.currentNonce != *nonce {
   124  			a.currentNonce = *nonce
   125  			for _, txTracker := range a.notReadyTxs {
   126  				if txTracker.Nonce < a.currentNonce {
   127  					reason := runtime.ErrIntrinsicInvalidNonce.Error()
   128  					txTracker.FailedReason = &reason
   129  					txsToDelete = append(txsToDelete, txTracker)
   130  				}
   131  			}
   132  			for _, txTracker := range txsToDelete {
   133  				log.Infof("Deleting notReadyTx with nonce %d from addrQueue %s", txTracker.Nonce, a.fromStr)
   134  				delete(a.notReadyTxs, txTracker.Nonce)
   135  			}
   136  		}
   137  	}
   138  
   139  	if a.readyTx != nil {
   140  		// If readyTX.nonce is not the currentNonce or currentBalance is less that the readyTx.Cost
   141  		// set readyTx=nil. Later we will move the tx to notReadyTxs
   142  		if (a.readyTx.Nonce != a.currentNonce) || (a.currentBalance.Cmp(a.readyTx.Cost) < 0) {
   143  			oldReadyTx = a.readyTx
   144  			a.readyTx = nil
   145  		}
   146  	}
   147  
   148  	// We check if we have a new readyTx from the notReadyTxs (at this point, to optmize the code,
   149  	// we are not including the oldReadyTx in notReadyTxs, as it can match again if the nonce has not changed)
   150  	if a.readyTx == nil {
   151  		nrTx, found := a.notReadyTxs[a.currentNonce]
   152  		if found {
   153  			if a.currentBalance.Cmp(nrTx.Cost) >= 0 {
   154  				a.readyTx = nrTx
   155  				log.Infof("Moving notReadyTx %s to readyTx for addrQueue %s", nrTx.HashStr, a.fromStr)
   156  				delete(a.notReadyTxs, a.currentNonce)
   157  			}
   158  		}
   159  	}
   160  
   161  	// We add the oldReadyTx to notReadyTxs (if it has a valid nonce) at this point to avoid check it again in the previous if statement
   162  	if oldReadyTx != nil && oldReadyTx.Nonce > a.currentNonce {
   163  		log.Infof("Marking readyTx %s as notReadyTx from addrQueue %s", oldReadyTx.HashStr, a.fromStr)
   164  		a.notReadyTxs[oldReadyTx.Nonce] = oldReadyTx
   165  	}
   166  
   167  	return a.readyTx, oldReadyTx, txsToDelete
   168  }
   169  
   170  // UpdateTxZKCounters updates the ZKCounters for the given tx (txHash)
   171  // If the updated tx is the readyTx it returns a copy of the previous readyTx, nil otherwise
   172  func (a *addrQueue) UpdateTxZKCounters(txHash common.Hash, counters state.ZKCounters, constraints batchConstraintsFloat64, weights batchResourceWeights) (newReadyTx, prevReadyTx *TxTracker) {
   173  	txHashStr := txHash.String()
   174  
   175  	if (a.readyTx != nil) && (a.readyTx.HashStr == txHashStr) {
   176  		// We need to assign the new readyTx as a new TxTracker copy of the previous one with the updated efficiency
   177  		// We need to do in this way because the efficiency value is changed and we use this value as key field to
   178  		// add/delete TxTrackers in the efficiencyList
   179  		prevReadyTx := a.readyTx
   180  		newReadyTx := *a.readyTx
   181  		newReadyTx.updateZKCounters(counters, constraints, weights)
   182  		a.readyTx = &newReadyTx
   183  		log.Debugf("Updating readyTx %s with new ZKCounters from addrQueue %s", txHashStr, a.fromStr)
   184  		return a.readyTx, prevReadyTx
   185  	} else {
   186  		txHashStr := txHash.String()
   187  		for _, txTracker := range a.notReadyTxs {
   188  			if txTracker.HashStr == txHashStr {
   189  				log.Debugf("Updating notReadyTx %s with new ZKCounters from addrQueue %s", txHashStr, a.fromStr)
   190  				txTracker.updateZKCounters(counters, constraints, weights)
   191  				break
   192  			}
   193  		}
   194  		return nil, nil
   195  	}
   196  }