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 */