github.com/iotexproject/iotex-core@v1.14.1-rc1/actpool/queueworker.go (about) 1 package actpool 2 3 import ( 4 "context" 5 "encoding/hex" 6 "errors" 7 "math/big" 8 "sort" 9 "strings" 10 "sync" 11 "sync/atomic" 12 13 "github.com/iotexproject/go-pkgs/cache/ttl" 14 "github.com/iotexproject/iotex-address/address" 15 "go.uber.org/zap" 16 17 "github.com/iotexproject/iotex-core/action" 18 "github.com/iotexproject/iotex-core/action/protocol" 19 accountutil "github.com/iotexproject/iotex-core/action/protocol/account/util" 20 "github.com/iotexproject/iotex-core/pkg/log" 21 "github.com/iotexproject/iotex-core/pkg/tracer" 22 ) 23 24 type ( 25 queueWorker struct { 26 queue chan workerJob 27 ap *actPool 28 mu sync.RWMutex 29 accountActs *accountPool 30 emptyAccounts *ttl.Cache 31 } 32 33 workerJob struct { 34 ctx context.Context 35 act *action.SealedEnvelope 36 rep bool 37 err chan error 38 } 39 40 pendingActions struct { 41 sender string 42 acts []*action.SealedEnvelope 43 } 44 ) 45 46 func newQueueWorker(ap *actPool, jobQueue chan workerJob) *queueWorker { 47 acc, _ := ttl.NewCache() 48 return &queueWorker{ 49 queue: jobQueue, 50 ap: ap, 51 accountActs: newAccountPool(), 52 emptyAccounts: acc, 53 } 54 } 55 56 func (worker *queueWorker) Start() error { 57 if worker.queue == nil || worker.ap == nil { 58 return errors.New("worker is invalid") 59 } 60 go func() { 61 for { 62 job, more := <-worker.queue 63 if !more { // worker chan is closed 64 return 65 } 66 job.err <- worker.Handle(job) 67 } 68 }() 69 return nil 70 } 71 72 func (worker *queueWorker) Stop() error { 73 close(worker.queue) 74 return nil 75 } 76 77 // Hanlde is called sequentially by worker 78 func (worker *queueWorker) Handle(job workerJob) error { 79 ctx := job.ctx 80 // ctx is canceled or timeout 81 if ctx.Err() != nil { 82 return ctx.Err() 83 } 84 85 var ( 86 span = tracer.SpanFromContext(ctx) 87 act = job.act 88 sender = act.SenderAddress().String() 89 actHash, _ = act.Hash() 90 intrinsicGas, _ = act.IntrinsicGas() 91 replace = job.rep 92 ) 93 defer span.End() 94 95 nonce, balance, err := worker.getConfirmedState(ctx, act.SenderAddress()) 96 if err != nil { 97 return err 98 } 99 100 if err := worker.checkSelpWithState(act, nonce, balance); err != nil { 101 return err 102 } 103 if err := worker.putAction(sender, act, nonce, balance); err != nil { 104 return err 105 } 106 107 worker.ap.allActions.Set(actHash, act) 108 109 if desAddress, ok := act.Destination(); ok && !strings.EqualFold(sender, desAddress) { 110 if err := worker.ap.accountDesActs.addAction(act); err != nil { 111 log.L().Debug("fail to add destionation map", zap.Error(err)) 112 } 113 } 114 115 atomic.AddUint64(&worker.ap.gasInPool, intrinsicGas) 116 117 worker.mu.Lock() 118 defer worker.mu.Unlock() 119 if replace { 120 // TODO: early return if sender is the account to pop and nonce is larger than largest in the queue 121 actToReplace := worker.accountActs.PopPeek() 122 if actToReplace == nil { 123 log.L().Warn("UNEXPECTED ERROR: action pool is full, but no action to drop") 124 return nil 125 } 126 worker.ap.removeInvalidActs([]*action.SealedEnvelope{actToReplace}) 127 if actToReplace.SenderAddress().String() == sender && actToReplace.Nonce() == nonce { 128 err = action.ErrTxPoolOverflow 129 _actpoolMtc.WithLabelValues("overMaxNumActsPerPool").Inc() 130 } 131 } 132 133 worker.removeEmptyAccounts() 134 135 return err 136 } 137 138 func (worker *queueWorker) getConfirmedState(ctx context.Context, sender address.Address) (uint64, *big.Int, error) { 139 worker.mu.RLock() 140 queue := worker.accountActs.Account(sender.String()) 141 worker.mu.RUnlock() 142 // account state isn't cached in the actpool 143 if queue == nil { 144 confirmedState, err := accountutil.AccountState(ctx, worker.ap.sf, sender) 145 if err != nil { 146 return 0, nil, err 147 } 148 var nonce uint64 149 if protocol.MustGetFeatureCtx(ctx).UseZeroNonceForFreshAccount { 150 nonce = confirmedState.PendingNonceConsideringFreshAccount() 151 } else { 152 nonce = confirmedState.PendingNonce() 153 } 154 return nonce, confirmedState.Balance, nil 155 } 156 nonce, balance := queue.AccountState() 157 return nonce, balance, nil 158 } 159 160 func (worker *queueWorker) checkSelpWithState(act *action.SealedEnvelope, pendingNonce uint64, balance *big.Int) error { 161 if act.Nonce() < pendingNonce { 162 _actpoolMtc.WithLabelValues("nonceTooSmall").Inc() 163 return action.ErrNonceTooLow 164 } 165 166 // Nonce exceeds current range 167 if act.Nonce()-pendingNonce >= worker.ap.cfg.MaxNumActsPerAcct { 168 hash, _ := act.Hash() 169 log.L().Debug("Rejecting action because nonce is too large.", 170 log.Hex("hash", hash[:]), 171 zap.Uint64("startNonce", pendingNonce), 172 zap.Uint64("actNonce", act.Nonce())) 173 _actpoolMtc.WithLabelValues("nonceTooLarge").Inc() 174 return action.ErrNonceTooHigh 175 } 176 177 if cost, _ := act.Cost(); balance.Cmp(cost) < 0 { 178 _actpoolMtc.WithLabelValues("insufficientBalance").Inc() 179 sender := act.SenderAddress().String() 180 actHash, _ := act.Hash() 181 log.L().Debug("insufficient balance for action", 182 zap.String("actionHash", hex.EncodeToString(actHash[:])), 183 zap.String("cost", cost.String()), 184 zap.String("balance", balance.String()), 185 zap.String("sender", sender), 186 ) 187 return action.ErrInsufficientFunds 188 } 189 return nil 190 } 191 192 func (worker *queueWorker) putAction(sender string, act *action.SealedEnvelope, pendingNonce uint64, confirmedBalance *big.Int) error { 193 worker.mu.Lock() 194 err := worker.accountActs.PutAction( 195 sender, 196 worker.ap, 197 pendingNonce, 198 confirmedBalance, 199 worker.ap.cfg.ActionExpiry, 200 act, 201 ) 202 worker.mu.Unlock() 203 if err != nil { 204 actHash, _ := act.Hash() 205 _actpoolMtc.WithLabelValues("failedPutActQueue").Inc() 206 log.L().Debug("failed put action into ActQueue", 207 zap.String("actionHash", hex.EncodeToString(actHash[:])), 208 zap.Error(err)) 209 return err 210 } 211 212 return nil 213 } 214 215 func (worker *queueWorker) removeEmptyAccounts() { 216 if worker.emptyAccounts.Count() == 0 { 217 return 218 } 219 220 worker.emptyAccounts.Range(func(key, _ interface{}) error { 221 worker.accountActs.DeleteIfEmpty(key.(string)) 222 return nil 223 }) 224 225 worker.emptyAccounts.Reset() 226 } 227 228 func (worker *queueWorker) Reset(ctx context.Context) { 229 worker.mu.RLock() 230 defer worker.mu.RUnlock() 231 232 worker.accountActs.Range(func(from string, queue ActQueue) { 233 addr, _ := address.FromString(from) 234 confirmedState, err := accountutil.AccountState(ctx, worker.ap.sf, addr) 235 if err != nil { 236 log.L().Error("Error when removing confirmed actions", zap.Error(err)) 237 queue.Reset() 238 worker.emptyAccounts.Set(from, struct{}{}) 239 return 240 } 241 var pendingNonce uint64 242 if protocol.MustGetFeatureCtx(ctx).UseZeroNonceForFreshAccount { 243 pendingNonce = confirmedState.PendingNonceConsideringFreshAccount() 244 } else { 245 pendingNonce = confirmedState.PendingNonce() 246 } 247 // Remove all actions that are committed to new block 248 acts := queue.UpdateAccountState(pendingNonce, confirmedState.Balance) 249 acts2 := queue.UpdateQueue() 250 worker.ap.removeInvalidActs(append(acts, acts2...)) 251 // Delete the queue entry if it becomes empty 252 if queue.Empty() { 253 worker.emptyAccounts.Set(from, struct{}{}) 254 } 255 }) 256 } 257 258 // PendingActions returns all accepted actions 259 func (worker *queueWorker) PendingActions(ctx context.Context) []*pendingActions { 260 actionArr := make([]*pendingActions, 0) 261 262 worker.mu.RLock() 263 defer worker.mu.RUnlock() 264 worker.accountActs.Range(func(from string, queue ActQueue) { 265 if queue.Empty() { 266 return 267 } 268 // Remove the actions that are already timeout 269 acts := queue.UpdateQueue() 270 worker.ap.removeInvalidActs(acts) 271 pd := queue.PendingActs(ctx) 272 if len(pd) == 0 { 273 return 274 } 275 actionArr = append(actionArr, &pendingActions{ 276 sender: from, 277 acts: pd, 278 }) 279 }) 280 return actionArr 281 } 282 283 // AllActions returns the all actions of sender 284 func (worker *queueWorker) AllActions(sender address.Address) ([]*action.SealedEnvelope, bool) { 285 worker.mu.RLock() 286 defer worker.mu.RUnlock() 287 if actQueue := worker.accountActs.Account(sender.String()); actQueue != nil { 288 acts := actQueue.AllActs() 289 sort.Slice(acts, func(i, j int) bool { 290 return acts[i].Nonce() < acts[j].Nonce() 291 }) 292 return acts, true 293 } 294 return nil, false 295 } 296 297 // PendingNonce returns the pending nonce of sender 298 func (worker *queueWorker) PendingNonce(sender address.Address) (uint64, bool) { 299 worker.mu.RLock() 300 defer worker.mu.RUnlock() 301 if actQueue := worker.accountActs.Account(sender.String()); actQueue != nil { 302 return actQueue.PendingNonce(), true 303 } 304 return 0, false 305 } 306 307 // ResetAccount resets account in the accountActs of worker 308 func (worker *queueWorker) ResetAccount(sender address.Address) []*action.SealedEnvelope { 309 senderStr := sender.String() 310 worker.mu.RLock() 311 actQueue := worker.accountActs.PopAccount(senderStr) 312 worker.mu.RUnlock() 313 if actQueue != nil { 314 pendingActs := actQueue.AllActs() 315 actQueue.Reset() 316 // the following line is thread safe with worker.mu.RLock 317 worker.emptyAccounts.Set(senderStr, struct{}{}) 318 return pendingActs 319 } 320 return nil 321 }