github.com/aergoio/aergo@v1.3.1/mempool/txlist.go (about)

     1  /**
     2   *  @file
     3   *  @copyright defined in aergo/LICENSE.txt
     4   */
     5  
     6  package mempool
     7  
     8  import (
     9  	"sort"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/aergoio/aergo/types"
    14  )
    15  
    16  // TxList is internal struct for transactions per account
    17  type TxList struct {
    18  	sync.RWMutex
    19  	base     *types.State
    20  	lastTime time.Time
    21  	account  []byte
    22  	ready    int
    23  	list     []types.Transaction // nonce-ordered tx list
    24  }
    25  
    26  // NewTxList creates new TxList with given State
    27  func NewTxList(acc []byte, st *types.State) *TxList {
    28  	return &TxList{
    29  		base:    st,
    30  		account: acc,
    31  	}
    32  }
    33  
    34  func (tl *TxList) GetLastModifiedTime() time.Time {
    35  	return tl.lastTime
    36  }
    37  func (tl *TxList) GetAccount() []byte {
    38  	return tl.account
    39  }
    40  
    41  // Len returns number of transactios which are ready to be processed
    42  func (tl *TxList) Len() int {
    43  	tl.RLock()
    44  	defer tl.RUnlock()
    45  	return tl.ready
    46  }
    47  
    48  // Empty check TxList is empty including orphan
    49  func (tl *TxList) Empty() bool {
    50  	tl.RLock()
    51  	defer tl.RUnlock()
    52  	return len(tl.list) == 0
    53  }
    54  
    55  func (tl *TxList) search(tx types.Transaction) (int, bool) {
    56  	key := tx.GetBody().GetNonce()
    57  	ind := sort.Search(len(tl.list), func(i int) bool {
    58  		comp := tl.list[i].GetBody().GetNonce()
    59  		return comp >= key
    60  	})
    61  	if ind < len(tl.list) && tl.compare(tx, ind) {
    62  		return ind, true
    63  	}
    64  	return ind, false
    65  }
    66  func (tl *TxList) compare(tx types.Transaction, index int) bool {
    67  	if tx.GetBody().GetNonce() == tl.list[index].GetBody().GetNonce() {
    68  		return true
    69  	}
    70  	return false
    71  }
    72  
    73  func (tl *TxList) continuous(index int) bool {
    74  	l := tl.base.Nonce
    75  	r := tl.list[index].GetBody().GetNonce()
    76  	if tl.ready > 0 {
    77  		l = tl.list[tl.ready-1].GetBody().GetNonce()
    78  	}
    79  
    80  	if l+1 == r {
    81  		return true
    82  	}
    83  	return false
    84  }
    85  
    86  // Put inserts transaction into TxList
    87  // if transaction is processible, it is appended to list
    88  // if not, transaction is managed as orphan
    89  func (tl *TxList) Put(tx types.Transaction) (int, error) {
    90  	tl.Lock()
    91  	defer tl.Unlock()
    92  
    93  	nonce := tx.GetBody().GetNonce()
    94  	if nonce <= tl.base.Nonce {
    95  		return 0, types.ErrTxNonceTooLow
    96  	}
    97  
    98  	index, found := tl.search(tx)
    99  	if found == true { // exact match
   100  		return 0, types.ErrSameNonceAlreadyInMempool
   101  	}
   102  
   103  	oldCnt := len(tl.list) - tl.ready
   104  
   105  	if index < len(tl.list) {
   106  		tl.list = append(tl.list[:index], append([]types.Transaction{tx},
   107  			tl.list[index:]...)...)
   108  	} else {
   109  		tl.list = append(tl.list, tx)
   110  	}
   111  
   112  	for ; index < len(tl.list); index++ {
   113  		if !tl.continuous(index) {
   114  			break
   115  		}
   116  		tl.ready++
   117  	}
   118  	newCnt := len(tl.list) - tl.ready
   119  
   120  	tl.lastTime = time.Now()
   121  	return oldCnt - newCnt, nil
   122  }
   123  
   124  // SetMinNonce sets new minimum nonce for TxList
   125  // evict on some transactions is possible due to minimum nonce
   126  func (tl *TxList) FilterByState(st *types.State) (int, []types.Transaction) {
   127  	tl.Lock()
   128  	defer tl.Unlock()
   129  
   130  	var balCheck bool
   131  
   132  	if tl.base.Nonce == st.Nonce {
   133  		tl.base = st
   134  		return 0, nil
   135  	}
   136  
   137  	if tl.base.GetBalanceBigInt().Cmp(st.GetBalanceBigInt()) > 0 {
   138  		balCheck = true
   139  	}
   140  	tl.base = st
   141  
   142  	oldCnt := len(tl.list) - tl.ready
   143  	var left []types.Transaction
   144  	removed := tl.list[:0]
   145  	for i, x := range tl.list {
   146  		err := x.ValidateWithSenderState(st)
   147  		if err == nil || err == types.ErrTxNonceToohigh {
   148  			if err != nil && !balCheck {
   149  				left = append(left, tl.list[i:]...)
   150  				break
   151  			}
   152  			left = append(left, x)
   153  		} else {
   154  			removed = append(removed, x)
   155  		}
   156  	}
   157  
   158  	tl.list = left
   159  	tl.ready = 0
   160  	for i := 0; i < len(tl.list); i++ {
   161  		if !tl.continuous(i) {
   162  			break
   163  		}
   164  		tl.ready++
   165  	}
   166  	newCnt := len(tl.list) - tl.ready
   167  
   168  	tl.lastTime = time.Now()
   169  	return oldCnt - newCnt, removed
   170  }
   171  
   172  // FilterByPrice will evict transactions that needs more amount than balance
   173  /*
   174  func (tl *TxList) FilterByPrice(balance uint64) error {
   175  	tl.Lock()
   176  	defer tl.Unlock()
   177  	return nil
   178  }
   179  */
   180  
   181  // Get returns processible transactions
   182  func (tl *TxList) Get() []types.Transaction {
   183  	tl.RLock()
   184  	defer tl.RUnlock()
   185  	return tl.list[:tl.ready]
   186  }
   187  
   188  // GetAll returns all transactions including orphans
   189  func (tl *TxList) GetAll() []types.Transaction {
   190  	tl.Lock()
   191  	defer tl.Unlock()
   192  	return tl.list
   193  
   194  }
   195  
   196  func (tl *TxList) len() int {
   197  	return len(tl.list)
   198  }
   199  
   200  /*
   201  
   202  func (tl *TxList) printList() {
   203  	fmt.Printf("\t\t")
   204  	for i := 0; i < len(tl.list); i++ {
   205  		cur := tl.list[i].GetBody().GetNonce()
   206  		fmt.Printf("%d, ", cur)
   207  	}
   208  	fmt.Printf("done ready:%d n:%d, b:%d\n", tl.ready, tl.base.Nonce, tl.base.Balance)
   209  
   210  }
   211  
   212  func (tl *TxList) checkSanity() bool {
   213  	prev := uint64(0)
   214  	for _, v := range tl.list {
   215  		x := v.GetBody().GetNonce()
   216  		if prev >= x {
   217  			return false
   218  		}
   219  		prev = x
   220  	}
   221  	return true
   222  }
   223  func (tl *TxList) printList() {
   224  
   225  	var f, l, before uint64
   226  	if tl.list != nil {
   227  		f = tl.list[0].GetBody().GetNonce()
   228  		l = tl.list[len(tl.list)-1].GetBody().GetNonce()
   229  	}
   230  	fmt.Printf("min: %d ready(nr:%d)[%d~%d]", tl.min, len(tl.list), f, l)
   231  
   232  	for i := 0; i < len(tl.list); i++ {
   233  		cur := tl.list[i].GetBody().GetNonce()
   234  		if i != 0 && before+1 != cur {
   235  			fmt.Printf("WARN: List is not sequential")
   236  		}
   237  		before = cur
   238  	}
   239  
   240  	fmt.Println()
   241  	fmt.Printf("deps 1st(%d):", len(tl.deps))
   242  	for k, v := range tl.deps {
   243  		f := v[0].GetBody().GetNonce()
   244  		l := v[len(v)-1].GetBody().GetNonce()
   245  		fmt.Printf("%d=>[%d~%d],", k, f, l)
   246  	}
   247  	fmt.Println()
   248  
   249  	fmt.Printf("dep parent(%d):", len(tl.parent))
   250  	for k, v := range tl.parent {
   251  		fmt.Printf("(%d, %d)", k, v)
   252  	}
   253  	fmt.Println()
   254  }
   255  */