github.com/piotrnar/gocoin@v0.0.0-20240512203912-faa0448c5e96/client/network/txpool_sort.go (about)

     1  package network
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"time"
     7  
     8  	"github.com/piotrnar/gocoin/client/common"
     9  	"github.com/piotrnar/gocoin/lib/btc"
    10  )
    11  
    12  var (
    13  	expireTxsNow  bool = true
    14  	lastTxsExpire time.Time
    15  )
    16  
    17  // GetSortedMempool returns txs sorted by SPB, but with parents first.
    18  func GetSortedMempool() (result []*OneTxToSend) {
    19  	all_txs := make([]BIDX, len(TransactionsToSend))
    20  	var idx int
    21  	const MIN_PKB = 200
    22  	for k := range TransactionsToSend {
    23  		all_txs[idx] = k
    24  		idx++
    25  	}
    26  	sort.Slice(all_txs, func(i, j int) bool {
    27  		rec_i := TransactionsToSend[all_txs[i]]
    28  		rec_j := TransactionsToSend[all_txs[j]]
    29  		rate_i := rec_i.Fee * uint64(rec_j.Weight())
    30  		rate_j := rec_j.Fee * uint64(rec_i.Weight())
    31  		if rate_i != rate_j {
    32  			return rate_i > rate_j
    33  		}
    34  		if rec_i.MemInputCnt != rec_j.MemInputCnt {
    35  			return rec_i.MemInputCnt < rec_j.MemInputCnt
    36  		}
    37  		for x := 0; x < 32; x++ {
    38  			if rec_i.Hash.Hash[x] != rec_i.Hash.Hash[x] {
    39  				return rec_i.Hash.Hash[x] < rec_i.Hash.Hash[x]
    40  			}
    41  		}
    42  		return false
    43  	})
    44  
    45  	// now put the childrer after the parents
    46  	result = make([]*OneTxToSend, len(all_txs))
    47  	already_in := make(map[BIDX]bool, len(all_txs))
    48  	parent_of := make(map[BIDX][]BIDX)
    49  
    50  	idx = 0
    51  
    52  	var missing_parents = func(txkey BIDX, is_any bool) (res []BIDX, yes bool) {
    53  		tx := TransactionsToSend[txkey]
    54  		if tx.MemInputs == nil {
    55  			return
    56  		}
    57  		var cnt_ok int
    58  		for idx, inp := range tx.TxIn {
    59  			if tx.MemInputs[idx] {
    60  				txk := btc.BIdx(inp.Input.Hash[:])
    61  				if _, ok := already_in[txk]; ok {
    62  				} else {
    63  					yes = true
    64  					if is_any {
    65  						return
    66  					}
    67  					res = append(res, txk)
    68  				}
    69  
    70  				cnt_ok++
    71  				if cnt_ok == tx.MemInputCnt {
    72  					return
    73  				}
    74  			}
    75  		}
    76  		return
    77  	}
    78  
    79  	var append_txs func(txkey BIDX)
    80  	append_txs = func(txkey BIDX) {
    81  		result[idx] = TransactionsToSend[txkey]
    82  		idx++
    83  		already_in[txkey] = true
    84  
    85  		if toretry, ok := parent_of[txkey]; ok {
    86  			for _, kv := range toretry {
    87  				if _, in := already_in[kv]; in {
    88  					continue
    89  				}
    90  				if _, yes := missing_parents(kv, true); !yes {
    91  					append_txs(kv)
    92  				}
    93  			}
    94  			delete(parent_of, txkey)
    95  		}
    96  	}
    97  
    98  	for _, txkey := range all_txs {
    99  		if missing, yes := missing_parents(txkey, false); yes {
   100  			for _, kv := range missing {
   101  				parent_of[kv] = append(parent_of[kv], txkey)
   102  			}
   103  			continue
   104  		}
   105  		append_txs(txkey)
   106  	}
   107  
   108  	if idx != len(result) || idx != len(already_in) || len(parent_of) != 0 {
   109  		fmt.Println("Get sorted mempool idx:", idx, " result:", len(result), " alreadyin:", len(already_in), " parents:", len(parent_of))
   110  		fmt.Println("DUPA!!!!!!!!!!")
   111  		result = result[:idx]
   112  	}
   113  
   114  	return
   115  }
   116  
   117  // LimitPoolSize must be called with TxMutex locked.
   118  func LimitPoolSize(maxlen uint64) {
   119  	ticklen := maxlen >> 5 // 1/32th of the max size = X
   120  
   121  	if TransactionsToSendSize < maxlen {
   122  		if TransactionsToSendSize < maxlen-2*ticklen {
   123  			if common.SetMinFeePerKB(0) {
   124  				var cnt uint64
   125  				for k, v := range TransactionsRejected {
   126  					if v.Reason == TX_REJECTED_LOW_FEE {
   127  						deleteRejected(k)
   128  						cnt++
   129  					}
   130  				}
   131  				common.CounterMutex.Lock()
   132  				common.Count("TxPoolSizeLow")
   133  				common.CountAdd("TxRejectedFeeUndone", cnt)
   134  				common.CounterMutex.Unlock()
   135  				//fmt.Println("Mempool size low:", TransactionsToSendSize, maxlen, maxlen-2*ticklen, "-", cnt, "rejected purged")
   136  			}
   137  		} else {
   138  			common.CountSafe("TxPoolSizeOK")
   139  			//fmt.Println("Mempool size OK:", TransactionsToSendSize, maxlen, maxlen-2*ticklen)
   140  		}
   141  		return
   142  	}
   143  
   144  	//sta := time.Now()
   145  
   146  	sorted := GetSortedMempoolNew()
   147  	idx := len(sorted)
   148  
   149  	old_size := TransactionsToSendSize
   150  
   151  	maxlen -= ticklen
   152  
   153  	for idx > 0 && TransactionsToSendSize > maxlen {
   154  		idx--
   155  		tx := sorted[idx]
   156  		if _, ok := TransactionsToSend[tx.Hash.BIdx()]; !ok {
   157  			// this has already been rmoved
   158  			continue
   159  		}
   160  		tx.Delete(true, TX_REJECTED_LOW_FEE)
   161  	}
   162  
   163  	if cnt := len(sorted) - idx; cnt > 0 {
   164  		newspkb := uint64(float64(1000*sorted[idx].Fee) / float64(sorted[idx].VSize()))
   165  		common.SetMinFeePerKB(newspkb)
   166  
   167  		/*fmt.Println("Mempool purged in", time.Now().Sub(sta).String(), "-",
   168  		old_size-TransactionsToSendSize, "/", old_size, "bytes and", cnt, "/", len(sorted), "txs removed. SPKB:", newspkb)*/
   169  		common.CounterMutex.Lock()
   170  		common.Count("TxPoolSizeHigh")
   171  		common.CountAdd("TxPurgedSizCnt", uint64(cnt))
   172  		common.CountAdd("TxPurgedSizBts", old_size-TransactionsToSendSize)
   173  		common.CounterMutex.Unlock()
   174  	}
   175  }
   176  
   177  func GetSortedRejected() (sorted []*OneTxRejected) {
   178  	var idx int
   179  	sorted = make([]*OneTxRejected, len(TransactionsRejected))
   180  	for _, t := range TransactionsRejected {
   181  		sorted[idx] = t
   182  		idx++
   183  	}
   184  	var now = time.Now()
   185  	sort.Slice(sorted, func(i, j int) bool {
   186  		return int64(sorted[i].Size)*int64(now.Sub(sorted[i].Time)) < int64(sorted[j].Size)*int64(now.Sub(sorted[j].Time))
   187  	})
   188  	return
   189  }
   190  
   191  // LimitRejectedSize must be called with TxMutex locked.
   192  func LimitRejectedSize() {
   193  	//ticklen := maxlen >> 5 // 1/32th of the max size = X
   194  	var idx int
   195  	var sorted []*OneTxRejected
   196  
   197  	old_cnt := len(TransactionsRejected)
   198  	old_size := TransactionsRejectedSize
   199  
   200  	maxlen, maxcnt := common.RejectedTxsLimits()
   201  
   202  	if maxcnt > 0 && len(TransactionsRejected) > maxcnt {
   203  		common.CountSafe("TxRejectedCntHigh")
   204  		sorted = GetSortedRejected()
   205  		maxcnt -= maxcnt >> 5
   206  		for idx = maxcnt; idx < len(sorted); idx++ {
   207  			deleteRejected(sorted[idx].Id.BIdx())
   208  		}
   209  		sorted = sorted[:maxcnt]
   210  	}
   211  
   212  	if maxlen > 0 && TransactionsRejectedSize > maxlen {
   213  		common.CountSafe("TxRejectedBtsHigh")
   214  		if sorted == nil {
   215  			sorted = GetSortedRejected()
   216  		}
   217  		maxlen -= maxlen >> 5
   218  		for idx = len(sorted) - 1; idx >= 0; idx-- {
   219  			deleteRejected(sorted[idx].Hash.BIdx())
   220  			if TransactionsRejectedSize <= maxlen {
   221  				break
   222  			}
   223  		}
   224  	}
   225  
   226  	if old_cnt > len(TransactionsRejected) {
   227  		common.CounterMutex.Lock()
   228  		common.CountAdd("TxRejectedSizCnt", uint64(old_cnt-len(TransactionsRejected)))
   229  		common.CountAdd("TxRejectedSizBts", old_size-TransactionsRejectedSize)
   230  		if common.GetBool(&common.CFG.TXPool.Debug) {
   231  			println("Removed", uint64(old_cnt-len(TransactionsRejected)), "txs and", old_size-TransactionsRejectedSize,
   232  				"bytes from the rejected poool")
   233  		}
   234  		common.CounterMutex.Unlock()
   235  	}
   236  }
   237  
   238  /* --== Let's keep it here for now as it sometimes comes handy for debuging
   239  
   240  var first_ = true
   241  
   242  // call this one when TxMutex is locked
   243  func MPC_locked() bool {
   244  	if first_ && MempoolCheck() {
   245  		first_ = false
   246  		_, file, line, _ := runtime.Caller(1)
   247  		println("=====================================================")
   248  		println("Mempool first iime seen broken from", file, line)
   249  		return true
   250  	}
   251  	return false
   252  }
   253  
   254  func MPC() (res bool) {
   255  	TxMutex.Lock()
   256  	res = MPC_locked()
   257  	TxMutex.Unlock()
   258  	return
   259  }
   260  */
   261  
   262  // MempoolCheck verifies the Mempool for consistency.
   263  // Make sure to call it with TxMutex Locked.
   264  func MempoolCheck() (dupa bool) {
   265  	var spent_cnt int
   266  	var totsize uint64
   267  
   268  	// First check if t2s.MemInputs fields are properly set
   269  	for _, t2s := range TransactionsToSend {
   270  		var micnt int
   271  
   272  		totsize += uint64(len(t2s.Raw))
   273  
   274  		for i, inp := range t2s.TxIn {
   275  			spent_cnt++
   276  
   277  			outk, ok := SpentOutputs[inp.Input.UIdx()]
   278  			if ok {
   279  				if outk != t2s.Hash.BIdx() {
   280  					fmt.Println("Tx", t2s.Hash.String(), "input", i, "has a mismatch in SpentOutputs record", outk)
   281  					dupa = true
   282  				}
   283  			} else {
   284  				fmt.Println("Tx", t2s.Hash.String(), "input", i, "is not in SpentOutputs")
   285  				dupa = true
   286  			}
   287  
   288  			_, ok = TransactionsToSend[btc.BIdx(inp.Input.Hash[:])]
   289  
   290  			if t2s.MemInputs == nil {
   291  				if ok {
   292  					fmt.Println("Tx", t2s.Hash.String(), "MemInputs==nil but input", i, "is in mempool", inp.Input.String())
   293  					dupa = true
   294  				}
   295  			} else {
   296  				if t2s.MemInputs[i] {
   297  					micnt++
   298  					if !ok {
   299  						fmt.Println("Tx", t2s.Hash.String(), "MemInput set but input", i, "NOT in mempool", inp.Input.String())
   300  						dupa = true
   301  					}
   302  				} else {
   303  					if ok {
   304  						fmt.Println("Tx", t2s.Hash.String(), "MemInput NOT set but input", i, "IS in mempool", inp.Input.String())
   305  						dupa = true
   306  					}
   307  				}
   308  			}
   309  
   310  			if _, ok := TransactionsToSend[btc.BIdx(inp.Input.Hash[:])]; !ok {
   311  				if unsp := common.BlockChain.Unspent.UnspentGet(&inp.Input); unsp == nil {
   312  					fmt.Println("Mempool tx", t2s.Hash.String(), "has no input", i)
   313  					dupa = true
   314  				}
   315  			}
   316  		}
   317  		if t2s.MemInputs != nil && micnt == 0 {
   318  			fmt.Println("Tx", t2s.Hash.String(), "has MemInputs array with all false values")
   319  			dupa = true
   320  		}
   321  		if t2s.MemInputCnt != micnt {
   322  			fmt.Println("Tx", t2s.Hash.String(), "has incorrect MemInputCnt", t2s.MemInputCnt, micnt)
   323  			dupa = true
   324  		}
   325  	}
   326  
   327  	if spent_cnt != len(SpentOutputs) {
   328  		fmt.Println("SpentOutputs length mismatch", spent_cnt, len(SpentOutputs))
   329  		dupa = true
   330  	}
   331  
   332  	if totsize != TransactionsToSendSize {
   333  		fmt.Println("TransactionsToSendSize mismatch", totsize, TransactionsToSendSize)
   334  		dupa = true
   335  	}
   336  
   337  	totsize = 0
   338  	for _, tr := range TransactionsRejected {
   339  		totsize += uint64(tr.Size)
   340  	}
   341  	if totsize != TransactionsRejectedSize {
   342  		fmt.Println("TransactionsRejectedSize mismatch", totsize, TransactionsRejectedSize)
   343  		dupa = true
   344  	}
   345  
   346  	return
   347  }
   348  
   349  // GetChildren gets all first level children of the tx.
   350  func (tx *OneTxToSend) GetChildren() (result []*OneTxToSend) {
   351  	var po btc.TxPrevOut
   352  	po.Hash = tx.Hash.Hash
   353  
   354  	res := make(map[*OneTxToSend]bool)
   355  
   356  	for po.Vout = 0; po.Vout < uint32(len(tx.TxOut)); po.Vout++ {
   357  		uidx := po.UIdx()
   358  		if val, ok := SpentOutputs[uidx]; ok {
   359  			res[TransactionsToSend[val]] = true
   360  		}
   361  	}
   362  
   363  	result = make([]*OneTxToSend, len(res))
   364  	var idx int
   365  	for ttx := range res {
   366  		result[idx] = ttx
   367  		idx++
   368  	}
   369  	return
   370  }
   371  
   372  // GetItWithAllChildren gets all the children (and all of their children...) of the tx.
   373  // If any of the children has other unconfirmed parents, they are also included in the result.
   374  // The result is sorted with the input parent first and always with parents before their children.
   375  func (tx *OneTxToSend) GetItWithAllChildren() (result []*OneTxToSend) {
   376  	already_included := make(map[*OneTxToSend]bool)
   377  
   378  	result = []*OneTxToSend{tx} // out starting (parent) tx shall be the first element of the result
   379  	already_included[tx] = true
   380  
   381  	for idx := 0; idx < len(result); idx++ {
   382  		par := result[idx]
   383  		for _, ch := range par.GetChildren() {
   384  			// do it for each returned child,
   385  
   386  			// but only if it has not been included yet ...
   387  			if _, ok := already_included[ch]; !ok {
   388  
   389  				// first make sure we have all of its parents...
   390  				for _, prnt := range ch.GetAllParentsExcept(par) {
   391  					if _, ok := already_included[prnt]; !ok {
   392  						// if we dont have a parent, just insert it here into the result
   393  						result = append(result, prnt)
   394  						// ... and mark it as included, for later
   395  						already_included[prnt] = true
   396  					}
   397  				}
   398  
   399  				// now we can safely insert the child, as all its parent shall be already included
   400  				result = append(result, ch)
   401  				// ... and mark it as included, for later
   402  				already_included[ch] = true
   403  			}
   404  		}
   405  	}
   406  	return
   407  }
   408  
   409  // GetAllChildren gets all the children (and all of their children...) of the tx.
   410  // The result is sorted by the oldest parent.
   411  func (tx *OneTxToSend) GetAllChildren() (result []*OneTxToSend) {
   412  	already_included := make(map[*OneTxToSend]bool)
   413  	var idx int
   414  	par := tx
   415  	for {
   416  		chlds := par.GetChildren()
   417  		for _, ch := range chlds {
   418  			if _, ok := already_included[ch]; !ok {
   419  				already_included[ch] = true
   420  				result = append(result, ch)
   421  			}
   422  		}
   423  		if idx == len(result) {
   424  			break
   425  		}
   426  
   427  		par = result[idx]
   428  		already_included[par] = true
   429  		idx++
   430  	}
   431  	return
   432  }
   433  
   434  // GetAllParents gets all the unconfirmed parents of the given tx.
   435  // The result is sorted by the oldest parent.
   436  func (tx *OneTxToSend) GetAllParents() (result []*OneTxToSend) {
   437  	already_in := make(map[*OneTxToSend]bool)
   438  	already_in[tx] = true
   439  	var do_one func(*OneTxToSend)
   440  	do_one = func(tx *OneTxToSend) {
   441  		if tx.MemInputCnt > 0 {
   442  			for idx := range tx.TxIn {
   443  				if tx.MemInputs[idx] {
   444  					par_tx := TransactionsToSend[btc.BIdx(tx.TxIn[idx].Input.Hash[:])]
   445  					if _, ok := already_in[par_tx]; !ok {
   446  						do_one(par_tx)
   447  					}
   448  				}
   449  			}
   450  		}
   451  		if _, ok := already_in[tx]; !ok {
   452  			result = append(result, tx)
   453  			already_in[tx] = true
   454  		}
   455  	}
   456  	do_one(tx)
   457  	return
   458  }
   459  
   460  // GetAllParents gets all the unconfirmed parents of the given tx, except for the input tx (and its parents).
   461  // The result is sorted by the oldest parent.
   462  func (tx *OneTxToSend) GetAllParentsExcept(except *OneTxToSend) (result []*OneTxToSend) {
   463  	already_in := make(map[*OneTxToSend]bool)
   464  	already_in[tx] = true
   465  	var do_one func(*OneTxToSend)
   466  	do_one = func(tx *OneTxToSend) {
   467  		if tx.MemInputCnt > 0 {
   468  			for idx := range tx.TxIn {
   469  				if tx.MemInputs[idx] {
   470  					if par_tx := TransactionsToSend[btc.BIdx(tx.TxIn[idx].Input.Hash[:])]; par_tx != except {
   471  						if _, ok := already_in[par_tx]; !ok {
   472  							do_one(par_tx)
   473  						}
   474  					}
   475  				}
   476  			}
   477  		}
   478  		if _, ok := already_in[tx]; !ok {
   479  			result = append(result, tx)
   480  			already_in[tx] = true
   481  		}
   482  	}
   483  	do_one(tx)
   484  	return
   485  }
   486  
   487  func (tx *OneTxToSend) SPW() float64 {
   488  	return float64(tx.Fee) / float64(tx.Weight())
   489  }
   490  
   491  func (tx *OneTxToSend) SPB() float64 {
   492  	return tx.SPW() * 4.0
   493  }
   494  
   495  type OneTxsPackage struct {
   496  	Txs    []*OneTxToSend
   497  	Weight int
   498  	Fee    uint64
   499  }
   500  
   501  func (pk *OneTxsPackage) AnyIn(list map[*OneTxToSend]bool) (ok bool) {
   502  	for _, par := range pk.Txs {
   503  		if _, ok = list[par]; ok {
   504  			return
   505  		}
   506  	}
   507  	return
   508  }
   509  
   510  func LookForPackages(txs []*OneTxToSend) (result []*OneTxsPackage) {
   511  	for _, tx := range txs {
   512  		if tx.MemInputCnt > 0 {
   513  			continue
   514  		}
   515  		var pkg OneTxsPackage
   516  		pandch := tx.GetItWithAllChildren()
   517  		if len(pandch) > 1 {
   518  			pkg.Txs = pandch
   519  			for _, t := range pkg.Txs {
   520  				pkg.Weight += t.Weight()
   521  				pkg.Fee += t.Fee
   522  			}
   523  			result = append(result, &pkg)
   524  		}
   525  	}
   526  	sort.Slice(result, func(i, j int) bool {
   527  		return result[i].Fee*uint64(result[j].Weight) > result[j].Fee*uint64(result[i].Weight)
   528  	})
   529  	return
   530  }
   531  
   532  /* This one uses the old method, which turned out to be very slow sometimes
   533  func LookForPackages(txs []*OneTxToSend) (result []*OneTxsPackage) {
   534  	for _, tx := range txs {
   535  		if tx.MemInputCnt == 0 {
   536  			continue
   537  		}
   538  		var pkg OneTxsPackage
   539  		childs := tx.GetAllParents()
   540  		if len(childs) > 0 {
   541  			pkg.Txs = append(childs, tx)
   542  			for _, t := range pkg.Txs {
   543  				pkg.Weight += t.Weight()
   544  				pkg.Fee += t.Fee
   545  			}
   546  			result = append(result, &pkg)
   547  		}
   548  	}
   549  	sort.Slice(result, func(i, j int) bool {
   550  		return result[i].Fee*uint64(result[j].Weight) > result[j].Fee*uint64(result[i].Weight)
   551  	})
   552  	return
   553  }
   554  */
   555  
   556  // GetSortedMempoolNew is like GetSortedMempool(), but one uses Child-Pays-For-Parent algo.
   557  func GetSortedMempoolNew() (result []*OneTxToSend) {
   558  	txs := GetSortedMempool()
   559  	pkgs := LookForPackages(txs)
   560  	//println(len(pkgs), "pkgs from", len(txs), "txs")
   561  
   562  	result = make([]*OneTxToSend, len(txs))
   563  	var txs_idx, pks_idx, res_idx int
   564  	already_in := make(map[*OneTxToSend]bool, len(txs))
   565  	for txs_idx < len(txs) {
   566  		tx := txs[txs_idx]
   567  
   568  		if pks_idx < len(pkgs) {
   569  			pk := pkgs[pks_idx]
   570  			if pk.Fee*uint64(tx.Weight()) > tx.Fee*uint64(pk.Weight) {
   571  				pks_idx++
   572  				if pk.AnyIn(already_in) {
   573  					continue
   574  				}
   575  				// all package's txs new: incude them all
   576  				copy(result[res_idx:], pk.Txs)
   577  				res_idx += len(pk.Txs)
   578  				for _, _t := range pk.Txs {
   579  					already_in[_t] = true
   580  				}
   581  				continue
   582  			}
   583  		}
   584  
   585  		txs_idx++
   586  		if _, ok := already_in[tx]; ok {
   587  			continue
   588  		}
   589  		result[res_idx] = tx
   590  		already_in[tx] = true
   591  		res_idx++
   592  	}
   593  	//println("All sorted.  res_idx:", res_idx, "  txs:", len(txs))
   594  	return
   595  }
   596  
   597  // GetMempoolFees only takes tx/package weight and the fee.
   598  func GetMempoolFees(maxweight uint64) (result [][2]uint64) {
   599  	txs := GetSortedMempool()
   600  	pkgs := LookForPackages(txs)
   601  
   602  	var txs_idx, pks_idx, res_idx int
   603  	var weightsofar uint64
   604  	result = make([][2]uint64, len(txs))
   605  	already_in := make(map[*OneTxToSend]bool, len(txs))
   606  	for txs_idx < len(txs) && weightsofar < maxweight {
   607  		tx := txs[txs_idx]
   608  
   609  		if pks_idx < len(pkgs) {
   610  			pk := pkgs[pks_idx]
   611  			if pk.Fee*uint64(tx.Weight()) > tx.Fee*uint64(pk.Weight) {
   612  				pks_idx++
   613  				if pk.AnyIn(already_in) {
   614  					continue
   615  				}
   616  
   617  				result[res_idx] = [2]uint64{uint64(pk.Weight), pk.Fee}
   618  				res_idx++
   619  				weightsofar += uint64(pk.Weight)
   620  
   621  				for _, _t := range pk.Txs {
   622  					already_in[_t] = true
   623  				}
   624  				continue
   625  			}
   626  		}
   627  
   628  		txs_idx++
   629  		if _, ok := already_in[tx]; ok {
   630  			continue
   631  		}
   632  		result[res_idx] = [2]uint64{uint64(tx.Weight()), tx.Fee}
   633  		res_idx++
   634  		weightsofar += uint64(tx.Weight())
   635  
   636  		already_in[tx] = true
   637  	}
   638  	result = result[:res_idx]
   639  	return
   640  }
   641  
   642  func ExpireTxs() {
   643  	lastTxsExpire = time.Now()
   644  	expireTxsNow = false
   645  
   646  	TxMutex.Lock()
   647  
   648  	if maxpoolsize := common.MaxMempoolSize(); maxpoolsize != 0 {
   649  		LimitPoolSize(maxpoolsize)
   650  	}
   651  
   652  	LimitRejectedSize()
   653  
   654  	TxMutex.Unlock()
   655  
   656  	common.CountSafe("TxPurgedTicks")
   657  }