github.com/iotexproject/iotex-core@v1.14.1-rc1/actpool/actqueue.go (about) 1 // Copyright (c) 2019 IoTeX Foundation 2 // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability 3 // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed. 4 // This source code is governed by Apache License 2.0 that can be found in the LICENSE file. 5 6 package actpool 7 8 import ( 9 "container/heap" 10 "context" 11 "math/big" 12 "sync" 13 "time" 14 15 "github.com/facebookgo/clock" 16 "go.uber.org/zap" 17 18 "github.com/iotexproject/iotex-address/address" 19 20 "github.com/iotexproject/iotex-core/action" 21 "github.com/iotexproject/iotex-core/action/protocol" 22 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 23 "github.com/iotexproject/iotex-core/pkg/log" 24 ) 25 26 // ActQueue is the interface of actQueue 27 type ActQueue interface { 28 Put(*action.SealedEnvelope) error 29 UpdateQueue() []*action.SealedEnvelope 30 UpdateAccountState(uint64, *big.Int) []*action.SealedEnvelope 31 AccountState() (uint64, *big.Int) 32 PendingNonce() uint64 33 NextAction() (bool, *big.Int) 34 Len() int 35 Empty() bool 36 PendingActs(context.Context) []*action.SealedEnvelope 37 AllActs() []*action.SealedEnvelope 38 PopActionWithLargestNonce() *action.SealedEnvelope 39 Reset() 40 } 41 42 // actQueue is a queue of actions from an account 43 type actQueue struct { 44 ap *actPool 45 address string 46 // Map that stores all the actions belonging to an account associated with nonces 47 items map[uint64]*action.SealedEnvelope 48 // Priority Queue that stores all the nonces belonging to an account. Nonces are used as indices for action map 49 ascQueue ascNoncePriorityQueue 50 descQueue descNoncePriorityQueue 51 // Current pending nonce tracking previous actions that can be committed to the next block for the account 52 pendingNonce uint64 53 // Pending balance map 54 pendingBalance map[uint64]*big.Int 55 // Current account nonce 56 accountNonce uint64 57 // Current account balance 58 accountBalance *big.Int 59 clock clock.Clock 60 ttl time.Duration 61 mu sync.RWMutex 62 } 63 64 // NewActQueue create a new action queue 65 func NewActQueue(ap *actPool, address string, pendingNonce uint64, balance *big.Int, ops ...ActQueueOption) ActQueue { 66 aq := &actQueue{ 67 ap: ap, 68 address: address, 69 items: make(map[uint64]*action.SealedEnvelope), 70 ascQueue: ascNoncePriorityQueue{}, 71 descQueue: descNoncePriorityQueue{}, 72 pendingNonce: pendingNonce, 73 pendingBalance: make(map[uint64]*big.Int), 74 accountNonce: pendingNonce, 75 accountBalance: new(big.Int).Set(balance), 76 clock: clock.New(), 77 ttl: 0, 78 } 79 for _, op := range ops { 80 op.SetActQueueOption(aq) 81 } 82 return aq 83 } 84 85 func (q *actQueue) NextAction() (bool, *big.Int) { 86 q.mu.RLock() 87 defer q.mu.RUnlock() 88 if len(q.ascQueue) == 0 { 89 return false, nil 90 } 91 return q.pendingNonce > q.accountNonce, q.items[q.ascQueue[0].nonce].GasPrice() 92 } 93 94 // Put inserts a new action into the map, also updating the queue's nonce index 95 func (q *actQueue) Put(act *action.SealedEnvelope) error { 96 q.mu.Lock() 97 defer q.mu.Unlock() 98 nonce := act.Nonce() 99 100 if cost, _ := act.Cost(); q.getPendingBalanceAtNonce(nonce).Cmp(cost) < 0 { 101 return action.ErrInsufficientFunds 102 } 103 104 if actInPool, exist := q.items[nonce]; exist { 105 // act of higher gas price can cut in line 106 if nonce < q.pendingNonce && act.GasPrice().Cmp(actInPool.GasPrice()) != 1 { 107 return action.ErrReplaceUnderpriced 108 } 109 // update action in q.items and q.index 110 q.items[nonce] = act 111 for i := range q.ascQueue { 112 if q.ascQueue[i].nonce == nonce { 113 q.ascQueue[i].deadline = q.clock.Now().Add(q.ttl) 114 break 115 } 116 } 117 q.updateFromNonce(nonce) 118 return nil 119 } 120 nttl := &nonceWithTTL{nonce: nonce, deadline: q.clock.Now().Add(q.ttl)} 121 heap.Push(&q.ascQueue, nttl) 122 heap.Push(&q.descQueue, nttl) 123 q.items[nonce] = act 124 if nonce == q.pendingNonce { 125 q.updateFromNonce(q.pendingNonce) 126 } 127 return nil 128 } 129 130 func (q *actQueue) getPendingBalanceAtNonce(nonce uint64) *big.Int { 131 if nonce > q.pendingNonce { 132 return q.getPendingBalanceAtNonce(q.pendingNonce) 133 } 134 if _, exist := q.pendingBalance[nonce]; !exist { 135 return new(big.Int).Set(q.accountBalance) 136 } 137 return new(big.Int).Set(q.pendingBalance[nonce]) 138 } 139 140 func (q *actQueue) updateFromNonce(start uint64) { 141 if start > q.pendingNonce { 142 return 143 } 144 145 for balance := q.getPendingBalanceAtNonce(start); ; start++ { 146 act, exist := q.items[start] 147 if !exist { 148 break 149 } 150 151 cost, _ := act.Cost() 152 if balance.Cmp(cost) < 0 { 153 break 154 } 155 156 balance = new(big.Int).Sub(balance, cost) 157 q.pendingBalance[start+1] = new(big.Int).Set(balance) 158 } 159 160 q.pendingNonce = start 161 } 162 163 // UpdateQueue updates the pending nonce and balance of the queue 164 func (q *actQueue) UpdateQueue() []*action.SealedEnvelope { 165 q.mu.Lock() 166 defer q.mu.Unlock() 167 // First remove all timed out actions 168 removedFromQueue := q.cleanTimeout() 169 // Now, starting from the current pending nonce, incrementally find the next pending nonce 170 q.updateFromNonce(q.pendingNonce) 171 return removedFromQueue 172 } 173 174 func (q *actQueue) cleanTimeout() []*action.SealedEnvelope { 175 if q.ttl == 0 { 176 return []*action.SealedEnvelope{} 177 } 178 var ( 179 removedFromQueue = make([]*action.SealedEnvelope, 0) 180 timeNow = q.clock.Now() 181 size = len(q.ascQueue) 182 ) 183 for i := 0; i < size; { 184 nonce := q.ascQueue[i].nonce 185 if timeNow.After(q.ascQueue[i].deadline) && nonce > q.pendingNonce { 186 removedFromQueue = append(removedFromQueue, q.items[nonce]) 187 delete(q.items, nonce) 188 delete(q.pendingBalance, nonce) 189 q.ascQueue[i] = q.ascQueue[size-1] 190 size-- 191 continue 192 } 193 i++ 194 } 195 for i := 0; i < size; i++ { 196 q.descQueue[i] = q.ascQueue[i] 197 q.descQueue[i].ascIdx = i 198 q.descQueue[i].descIdx = i 199 } 200 q.ascQueue = q.ascQueue[:size] 201 q.descQueue = q.descQueue[:size] 202 // using heap.Init is better here, more detail to see BenchmarkHeapInitAndRemove 203 heap.Init(&q.ascQueue) 204 heap.Init(&q.descQueue) 205 return removedFromQueue 206 } 207 208 // UpdateAccountState updates the account's nonce and balance and cleans confirmed actions 209 func (q *actQueue) UpdateAccountState(nonce uint64, balance *big.Int) []*action.SealedEnvelope { 210 q.mu.Lock() 211 defer q.mu.Unlock() 212 q.pendingNonce = nonce 213 q.pendingBalance = make(map[uint64]*big.Int) 214 q.accountNonce = nonce 215 q.accountBalance.Set(balance) 216 var removed []*action.SealedEnvelope 217 // Pop off priority queue and delete corresponding entries from map 218 for q.ascQueue.Len() > 0 && (q.ascQueue)[0].nonce < q.accountNonce { 219 nttl := heap.Pop(&q.ascQueue).(*nonceWithTTL) 220 heap.Remove(&q.descQueue, nttl.descIdx) 221 nonce := nttl.nonce 222 removed = append(removed, q.items[nonce]) 223 delete(q.items, nonce) 224 } 225 return removed 226 } 227 228 // AccountState returns the current account's nonce and balance 229 func (q *actQueue) AccountState() (uint64, *big.Int) { 230 q.mu.RLock() 231 defer q.mu.RUnlock() 232 return q.accountNonce, new(big.Int).Set(q.accountBalance) 233 } 234 235 // PendingNonce returns the current pending nonce of the queue 236 func (q *actQueue) PendingNonce() uint64 { 237 q.mu.RLock() 238 defer q.mu.RUnlock() 239 return q.pendingNonce 240 } 241 242 // Len returns the length of the action map 243 func (q *actQueue) Len() int { 244 q.mu.RLock() 245 defer q.mu.RUnlock() 246 return len(q.items) 247 } 248 249 // Empty returns whether the queue of actions is empty or not 250 func (q *actQueue) Empty() bool { 251 q.mu.RLock() 252 defer q.mu.RUnlock() 253 return len(q.items) == 0 254 } 255 256 // Reset makes the queue into a dummy queue 257 func (q *actQueue) Reset() { 258 q.mu.Lock() 259 defer q.mu.Unlock() 260 q.items = make(map[uint64]*action.SealedEnvelope) 261 q.ascQueue = ascNoncePriorityQueue{} 262 q.descQueue = descNoncePriorityQueue{} 263 q.pendingNonce = 0 264 q.pendingBalance = make(map[uint64]*big.Int) 265 q.accountNonce = 0 266 q.accountBalance = big.NewInt(0) 267 } 268 269 // PendingActs creates a consecutive nonce-sorted slice of actions 270 func (q *actQueue) PendingActs(ctx context.Context) []*action.SealedEnvelope { 271 if q.Len() == 0 { 272 return nil 273 } 274 addr, err := address.FromString(q.address) 275 if err != nil { 276 log.L().Error("Error when getting the address", zap.String("address", q.address), zap.Error(err)) 277 return nil 278 } 279 // TODO: no need to refetch confirmed state, leave it to block builder to validate 280 confirmedState, err := accountutil.AccountState(ctx, q.ap.sf, addr) 281 if err != nil { 282 log.L().Error("Error when getting the nonce", zap.String("address", q.address), zap.Error(err)) 283 return nil 284 } 285 286 var ( 287 nonce uint64 288 balance = new(big.Int).Set(confirmedState.Balance) 289 acts = make([]*action.SealedEnvelope, 0, len(q.items)) 290 ) 291 if protocol.MustGetFeatureCtx(ctx).UseZeroNonceForFreshAccount { 292 nonce = confirmedState.PendingNonceConsideringFreshAccount() 293 } else { 294 nonce = confirmedState.PendingNonce() 295 } 296 q.mu.RLock() 297 defer q.mu.RUnlock() 298 for ; ; nonce++ { 299 act, exist := q.items[nonce] 300 if !exist { 301 break 302 } 303 304 cost, _ := act.Cost() 305 if balance.Cmp(cost) < 0 { 306 break 307 } 308 309 balance = new(big.Int).Sub(balance, cost) 310 acts = append(acts, act) 311 } 312 return acts 313 } 314 315 // AllActs returns all the actions currently in queue 316 func (q *actQueue) AllActs() []*action.SealedEnvelope { 317 q.mu.RLock() 318 defer q.mu.RUnlock() 319 acts := make([]*action.SealedEnvelope, 0, len(q.items)) 320 if len(q.items) == 0 { 321 return acts 322 } 323 for _, nonce := range q.ascQueue { 324 acts = append(acts, q.items[nonce.nonce]) 325 } 326 return acts 327 } 328 329 func (q *actQueue) PopActionWithLargestNonce() *action.SealedEnvelope { 330 q.mu.Lock() 331 defer q.mu.Unlock() 332 if len(q.items) == 0 { 333 return nil 334 } 335 itemMeta := heap.Pop(&q.descQueue).(*nonceWithTTL) 336 heap.Remove(&q.ascQueue, itemMeta.ascIdx) 337 item := q.items[itemMeta.nonce] 338 delete(q.items, itemMeta.nonce) 339 q.updateFromNonce(itemMeta.nonce) 340 341 return item 342 }