hermez-node/coordinator/txmanager.go
coordinator/txmanager.goRaw
1package coordinator
2
3import (
4 "context"
5 "fmt"
6 "math/big"
7 "strings"
8 "time"
9
10 "github.com/ethereum/go-ethereum"
11 "github.com/ethereum/go-ethereum/accounts"
12 "github.com/ethereum/go-ethereum/accounts/abi/bind"
13 "github.com/ethereum/go-ethereum/core"
14 "github.com/ethereum/go-ethereum/core/types"
15 "github.com/hermeznetwork/hermez-node/common"
16 "github.com/hermeznetwork/hermez-node/db/l2db"
17 "github.com/hermeznetwork/hermez-node/eth"
18 "github.com/hermeznetwork/hermez-node/log"
19 "github.com/hermeznetwork/hermez-node/synchronizer"
20 "github.com/hermeznetwork/tracerr"
21)
22
23// TxManager handles everything related to ethereum transactions: It makes the
24// call to forge, waits for transaction confirmation, and keeps checking them
25// until a number of confirmed blocks have passed.
26type TxManager struct {
27 cfg Config
28 ethClient eth.ClientInterface
29 l2DB *l2db.L2DB // Used only to mark forged txs as forged in the L2DB
30 coord *Coordinator // Used only to send messages to stop the pipeline
31 batchCh chan *BatchInfo
32 chainID *big.Int
33 account accounts.Account
34 consts synchronizer.SCConsts
35
36 stats synchronizer.Stats
37 vars synchronizer.SCVariables
38 statsVarsCh chan statsVars
39
40 discardPipelineCh chan int // int refers to the pipelineNum
41
42 minPipelineNum int
43 queue Queue
44 // lastSuccessBatch stores the last BatchNum that who's forge call was confirmed
45 lastSuccessBatch common.BatchNum
46 // lastPendingBatch common.BatchNum
47 // accNonce is the account nonce in the last mined block (due to mined txs)
48 accNonce uint64
49 // accNextNonce is the nonce that we should use to send the next tx.
50 // In some cases this will be a reused nonce of an already pending tx.
51 accNextNonce uint64
52
53 lastSentL1BatchBlockNum int64
54}
55
56// NewTxManager creates a new TxManager
57func NewTxManager(ctx context.Context, cfg *Config, ethClient eth.ClientInterface, l2DB *l2db.L2DB,
58 coord *Coordinator, scConsts *synchronizer.SCConsts, initSCVars *synchronizer.SCVariables) (
59 *TxManager, error) {
60 chainID, err := ethClient.EthChainID()
61 if err != nil {
62 return nil, tracerr.Wrap(err)
63 }
64 address, err := ethClient.EthAddress()
65 if err != nil {
66 return nil, tracerr.Wrap(err)
67 }
68 accNonce, err := ethClient.EthNonceAt(ctx, *address, nil)
69 if err != nil {
70 return nil, err
71 }
72 log.Infow("TxManager started", "nonce", accNonce)
73 return &TxManager{
74 cfg: *cfg,
75 ethClient: ethClient,
76 l2DB: l2DB,
77 coord: coord,
78 batchCh: make(chan *BatchInfo, queueLen),
79 statsVarsCh: make(chan statsVars, queueLen),
80 discardPipelineCh: make(chan int, queueLen),
81 account: accounts.Account{
82 Address: *address,
83 },
84 chainID: chainID,
85 consts: *scConsts,
86
87 vars: *initSCVars,
88
89 minPipelineNum: 0,
90 queue: NewQueue(),
91 accNonce: accNonce,
92 accNextNonce: accNonce,
93 }, nil
94}
95
96// AddBatch is a thread safe method to pass a new batch TxManager to be sent to
97// the smart contract via the forge call
98func (t *TxManager) AddBatch(ctx context.Context, batchInfo *BatchInfo) {
99 select {
100 case t.batchCh <- batchInfo:
101 case <-ctx.Done():
102 }
103}
104
105// SetSyncStatsVars is a thread safe method to sets the synchronizer Stats
106func (t *TxManager) SetSyncStatsVars(ctx context.Context, stats *synchronizer.Stats,
107 vars *synchronizer.SCVariablesPtr) {
108 select {
109 case t.statsVarsCh <- statsVars{Stats: *stats, Vars: *vars}:
110 case <-ctx.Done():
111 }
112}
113
114// DiscardPipeline is a thread safe method to notify about a discarded pipeline
115// due to a reorg
116func (t *TxManager) DiscardPipeline(ctx context.Context, pipelineNum int) {
117 select {
118 case t.discardPipelineCh <- pipelineNum:
119 case <-ctx.Done():
120 }
121}
122
123func (t *TxManager) syncSCVars(vars synchronizer.SCVariablesPtr) {
124 updateSCVars(&t.vars, vars)
125}
126
127// NewAuth generates a new auth object for an ethereum transaction
128func (t *TxManager) NewAuth(ctx context.Context, batchInfo *BatchInfo) (*bind.TransactOpts, error) {
129 gasPrice, err := t.ethClient.EthSuggestGasPrice(ctx)
130 if err != nil {
131 return nil, tracerr.Wrap(err)
132 }
133 if t.cfg.GasPriceIncPerc != 0 {
134 inc := new(big.Int).Set(gasPrice)
135 inc.Mul(inc, new(big.Int).SetInt64(t.cfg.GasPriceIncPerc))
136 // nolint reason: to calculate percentages we use 100
137 inc.Div(inc, new(big.Int).SetUint64(100)) //nolint:gomnd
138 gasPrice.Add(gasPrice, inc)
139 }
140
141 // log.Debugw("TxManager: transaction metadata", "gasPrice", gasPrice)
142
143 auth, err := bind.NewKeyStoreTransactorWithChainID(t.ethClient.EthKeyStore(), t.account, t.chainID)
144 if err != nil {
145 return nil, tracerr.Wrap(err)
146 }
147 auth.Value = big.NewInt(0) // in wei
148
149 gasLimit := t.cfg.ForgeBatchGasCost.Fixed +
150 uint64(len(batchInfo.L1UserTxsExtra))*t.cfg.ForgeBatchGasCost.L1UserTx +
151 uint64(len(batchInfo.L1CoordTxs))*t.cfg.ForgeBatchGasCost.L1CoordTx +
152 uint64(len(batchInfo.L2Txs))*t.cfg.ForgeBatchGasCost.L2Tx
153 auth.GasLimit = gasLimit
154 auth.GasPrice = gasPrice
155 auth.Nonce = nil
156
157 return auth, nil
158}
159
160func (t *TxManager) shouldSendRollupForgeBatch(batchInfo *BatchInfo) error {
161 nextBlock := t.stats.Eth.LastBlock.Num + 1
162 if !t.canForgeAt(nextBlock) {
163 return tracerr.Wrap(fmt.Errorf("can't forge in the next block: %v", nextBlock))
164 }
165 if t.mustL1L2Batch(nextBlock) && !batchInfo.L1Batch {
166 return tracerr.Wrap(fmt.Errorf("can't forge non-L1Batch in the next block: %v", nextBlock))
167 }
168 margin := t.cfg.SendBatchBlocksMarginCheck
169 if margin != 0 {
170 if !t.canForgeAt(nextBlock + margin) {
171 return tracerr.Wrap(fmt.Errorf("can't forge after %v blocks: %v",
172 margin, nextBlock))
173 }
174 if t.mustL1L2Batch(nextBlock+margin) && !batchInfo.L1Batch {
175 return tracerr.Wrap(fmt.Errorf("can't forge non-L1Batch after %v blocks: %v",
176 margin, nextBlock))
177 }
178 }
179 return nil
180}
181
182func addPerc(v *big.Int, p int64) *big.Int {
183 r := new(big.Int).Set(v)
184 r.Mul(r, big.NewInt(p))
185 // nolint reason: to calculate percentages we divide by 100
186 r.Div(r, big.NewInt(100)) //nolit:gomnd
187 return r.Add(v, r)
188}
189
190func (t *TxManager) sendRollupForgeBatch(ctx context.Context, batchInfo *BatchInfo,
191 resend bool) error {
192 var ethTx *types.Transaction
193 var err error
194 auth, err := t.NewAuth(ctx, batchInfo)
195 if err != nil {
196 return tracerr.Wrap(err)
197 }
198 auth.Nonce = big.NewInt(int64(t.accNextNonce))
199 if resend {
200 auth.Nonce = big.NewInt(int64(batchInfo.EthTx.Nonce()))
201 }
202 for attempt := 0; attempt < t.cfg.EthClientAttempts; attempt++ {
203 if auth.GasPrice.Cmp(t.cfg.MaxGasPrice) > 0 {
204 return tracerr.Wrap(fmt.Errorf("calculated gasPrice (%v) > maxGasPrice (%v)",
205 auth.GasPrice, t.cfg.MaxGasPrice))
206 }
207 // RollupForgeBatch() calls ethclient.SendTransaction()
208 ethTx, err = t.ethClient.RollupForgeBatch(batchInfo.ForgeBatchArgs, auth)
209 // We check the errors via strings because we match the
210 // definition of the error from geth, with the string returned
211 // via RPC obtained by the client.
212 if err == nil {
213 break
214 } else if strings.Contains(err.Error(), core.ErrNonceTooLow.Error()) {
215 log.Warnw("TxManager ethClient.RollupForgeBatch incrementing nonce",
216 "err", err, "nonce", auth.Nonce, "batchNum", batchInfo.BatchNum)
217 auth.Nonce.Add(auth.Nonce, big.NewInt(1))
218 attempt--
219 } else if strings.Contains(err.Error(), core.ErrNonceTooHigh.Error()) {
220 log.Warnw("TxManager ethClient.RollupForgeBatch decrementing nonce",
221 "err", err, "nonce", auth.Nonce, "batchNum", batchInfo.BatchNum)
222 auth.Nonce.Sub(auth.Nonce, big.NewInt(1))
223 attempt--
224 } else if strings.Contains(err.Error(), core.ErrReplaceUnderpriced.Error()) {
225 log.Warnw("TxManager ethClient.RollupForgeBatch incrementing gasPrice",
226 "err", err, "gasPrice", auth.GasPrice, "batchNum", batchInfo.BatchNum)
227 auth.GasPrice = addPerc(auth.GasPrice, 10)
228 attempt--
229 } else if strings.Contains(err.Error(), core.ErrUnderpriced.Error()) {
230 log.Warnw("TxManager ethClient.RollupForgeBatch incrementing gasPrice",
231 "err", err, "gasPrice", auth.GasPrice, "batchNum", batchInfo.BatchNum)
232 auth.GasPrice = addPerc(auth.GasPrice, 10)
233 attempt--
234 } else {
235 log.Errorw("TxManager ethClient.RollupForgeBatch",
236 "attempt", attempt, "err", err, "block", t.stats.Eth.LastBlock.Num+1,
237 "batchNum", batchInfo.BatchNum)
238 }
239 select {
240 case <-ctx.Done():
241 return tracerr.Wrap(common.ErrDone)
242 case <-time.After(t.cfg.EthClientAttemptsDelay):
243 }
244 }
245 if err != nil {
246 return tracerr.Wrap(fmt.Errorf("reached max attempts for ethClient.RollupForgeBatch: %w", err))
247 }
248 if !resend {
249 t.accNextNonce = auth.Nonce.Uint64() + 1
250 }
251 batchInfo.EthTx = ethTx
252 log.Infow("TxManager ethClient.RollupForgeBatch", "batch", batchInfo.BatchNum, "tx", ethTx.Hash())
253 now := time.Now()
254 batchInfo.SendTimestamp = now
255
256 if resend {
257 batchInfo.Debug.ResendNum++
258 }
259 batchInfo.Debug.Status = StatusSent
260 batchInfo.Debug.SendBlockNum = t.stats.Eth.LastBlock.Num + 1
261 batchInfo.Debug.SendTimestamp = batchInfo.SendTimestamp
262 batchInfo.Debug.StartToSendDelay = batchInfo.Debug.SendTimestamp.Sub(
263 batchInfo.Debug.StartTimestamp).Seconds()
264 t.cfg.debugBatchStore(batchInfo)
265
266 if !resend {
267 if batchInfo.L1Batch {
268 t.lastSentL1BatchBlockNum = t.stats.Eth.LastBlock.Num + 1
269 }
270 }
271 if err := t.l2DB.DoneForging(common.TxIDsFromL2Txs(batchInfo.L2Txs),
272 batchInfo.BatchNum); err != nil {
273 return tracerr.Wrap(err)
274 }
275 return nil
276}
277
278// checkEthTransactionReceipt takes the txHash from the BatchInfo and stores
279// the corresponding receipt if found
280func (t *TxManager) checkEthTransactionReceipt(ctx context.Context, batchInfo *BatchInfo) error {
281 txHash := batchInfo.EthTx.Hash()
282 var receipt *types.Receipt
283 var err error
284 for attempt := 0; attempt < t.cfg.EthClientAttempts; attempt++ {
285 receipt, err = t.ethClient.EthTransactionReceipt(ctx, txHash)
286 if ctx.Err() != nil {
287 continue
288 } else if tracerr.Unwrap(err) == ethereum.NotFound {
289 err = nil
290 break
291 } else if err != nil {
292 log.Errorw("TxManager ethClient.EthTransactionReceipt",
293 "attempt", attempt, "err", err)
294 } else {
295 break
296 }
297 select {
298 case <-ctx.Done():
299 return tracerr.Wrap(common.ErrDone)
300 case <-time.After(t.cfg.EthClientAttemptsDelay):
301 }
302 }
303 if err != nil {
304 return tracerr.Wrap(
305 fmt.Errorf("reached max attempts for ethClient.EthTransactionReceipt: %w",
306 err))
307 }
308 batchInfo.Receipt = receipt
309 t.cfg.debugBatchStore(batchInfo)
310 return nil
311}
312
313func (t *TxManager) handleReceipt(ctx context.Context, batchInfo *BatchInfo) (*int64, error) {
314 receipt := batchInfo.Receipt
315 if receipt != nil {
316 if batchInfo.EthTx.Nonce()+1 > t.accNonce {
317 t.accNonce = batchInfo.EthTx.Nonce() + 1
318 }
319 if receipt.Status == types.ReceiptStatusFailed {
320 batchInfo.Debug.Status = StatusFailed
321 _, err := t.ethClient.EthCall(ctx, batchInfo.EthTx, receipt.BlockNumber)
322 log.Warnw("TxManager receipt status is failed", "tx", receipt.TxHash,
323 "batch", batchInfo.BatchNum, "block", receipt.BlockNumber.Int64(),
324 "err", err)
325 batchInfo.EthTxErr = err
326 if batchInfo.BatchNum <= t.lastSuccessBatch {
327 t.lastSuccessBatch = batchInfo.BatchNum - 1
328 }
329 t.cfg.debugBatchStore(batchInfo)
330 return nil, tracerr.Wrap(fmt.Errorf(
331 "ethereum transaction receipt status is failed: %w", err))
332 } else if receipt.Status == types.ReceiptStatusSuccessful {
333 batchInfo.Debug.Status = StatusMined
334 batchInfo.Debug.MineBlockNum = receipt.BlockNumber.Int64()
335 batchInfo.Debug.StartToMineBlocksDelay = batchInfo.Debug.MineBlockNum -
336 batchInfo.Debug.StartBlockNum
337 if batchInfo.Debug.StartToMineDelay == 0 {
338 if block, err := t.ethClient.EthBlockByNumber(ctx,
339 receipt.BlockNumber.Int64()); err != nil {
340 log.Warnw("TxManager: ethClient.EthBlockByNumber", "err", err)
341 } else {
342 batchInfo.Debug.SendToMineDelay = block.Timestamp.Sub(
343 batchInfo.Debug.SendTimestamp).Seconds()
344 batchInfo.Debug.StartToMineDelay = block.Timestamp.Sub(
345 batchInfo.Debug.StartTimestamp).Seconds()
346 }
347 }
348 t.cfg.debugBatchStore(batchInfo)
349 if batchInfo.BatchNum > t.lastSuccessBatch {
350 t.lastSuccessBatch = batchInfo.BatchNum
351 }
352 confirm := t.stats.Eth.LastBlock.Num - receipt.BlockNumber.Int64()
353 return &confirm, nil
354 }
355 }
356 return nil, nil
357}
358
359// TODO:
360// - After sending a message: CancelPipeline, stop all consecutive pending Batches (transactions)
361
362// Queue of BatchInfos
363type Queue struct {
364 list []*BatchInfo
365 // nonceByBatchNum map[common.BatchNum]uint64
366 next int
367}
368
369// NewQueue returns a new queue
370func NewQueue() Queue {
371 return Queue{
372 list: make([]*BatchInfo, 0),
373 // nonceByBatchNum: make(map[common.BatchNum]uint64),
374 next: 0,
375 }
376}
377
378// Len is the length of the queue
379func (q *Queue) Len() int {
380 return len(q.list)
381}
382
383// At returns the BatchInfo at position (or nil if position is out of bounds)
384func (q *Queue) At(position int) *BatchInfo {
385 if position >= len(q.list) {
386 return nil
387 }
388 return q.list[position]
389}
390
391// Next returns the next BatchInfo (or nil if queue is empty)
392func (q *Queue) Next() (int, *BatchInfo) {
393 if len(q.list) == 0 {
394 return 0, nil
395 }
396 defer func() { q.next = (q.next + 1) % len(q.list) }()
397 return q.next, q.list[q.next]
398}
399
400// Remove removes the BatchInfo at position
401func (q *Queue) Remove(position int) {
402 // batchInfo := q.list[position]
403 // delete(q.nonceByBatchNum, batchInfo.BatchNum)
404 q.list = append(q.list[:position], q.list[position+1:]...)
405 if len(q.list) == 0 {
406 q.next = 0
407 } else {
408 q.next = position % len(q.list)
409 }
410}
411
412// Push adds a new BatchInfo
413func (q *Queue) Push(batchInfo *BatchInfo) {
414 q.list = append(q.list, batchInfo)
415 // q.nonceByBatchNum[batchInfo.BatchNum] = batchInfo.EthTx.Nonce()
416}
417
418// func (q *Queue) NonceByBatchNum(batchNum common.BatchNum) (uint64, bool) {
419// nonce, ok := q.nonceByBatchNum[batchNum]
420// return nonce, ok
421// }
422
423// Run the TxManager
424func (t *TxManager) Run(ctx context.Context) {
425 var statsVars statsVars
426 select {
427 case statsVars = <-t.statsVarsCh:
428 case <-ctx.Done():
429 }
430 t.stats = statsVars.Stats
431 t.syncSCVars(statsVars.Vars)
432 log.Infow("TxManager: received initial statsVars",
433 "block", t.stats.Eth.LastBlock.Num, "batch", t.stats.Eth.LastBatchNum)
434
435 timer := time.NewTimer(longWaitDuration)
436 for {
437 select {
438 case <-ctx.Done():
439 log.Info("TxManager done")
440 return
441 case statsVars := <-t.statsVarsCh:
442 t.stats = statsVars.Stats
443 t.syncSCVars(statsVars.Vars)
444 case pipelineNum := <-t.discardPipelineCh:
445 t.minPipelineNum = pipelineNum + 1
446 if err := t.removeBadBatchInfos(ctx); ctx.Err() != nil {
447 continue
448 } else if err != nil {
449 log.Errorw("TxManager: removeBadBatchInfos", "err", err)
450 continue
451 }
452 case batchInfo := <-t.batchCh:
453 if batchInfo.PipelineNum < t.minPipelineNum {
454 log.Warnw("TxManager: batchInfo received pipelineNum < minPipelineNum",
455 "num", batchInfo.PipelineNum, "minNum", t.minPipelineNum)
456 }
457 if err := t.shouldSendRollupForgeBatch(batchInfo); err != nil {
458 log.Warnw("TxManager: shouldSend", "err", err,
459 "batch", batchInfo.BatchNum)
460 t.coord.SendMsg(ctx, MsgStopPipeline{
461 Reason: fmt.Sprintf("forgeBatch shouldSend: %v", err)})
462 continue
463 }
464 if err := t.sendRollupForgeBatch(ctx, batchInfo, false); ctx.Err() != nil {
465 continue
466 } else if err != nil {
467 // If we reach here it's because our ethNode has
468 // been unable to send the transaction to
469 // ethereum. This could be due to the ethNode
470 // failure, or an invalid transaction (that
471 // can't be mined)
472 log.Warnw("TxManager: forgeBatch send failed", "err", err,
473 "batch", batchInfo.BatchNum)
474 t.coord.SendMsg(ctx, MsgStopPipeline{
475 Reason: fmt.Sprintf("forgeBatch send: %v", err)})
476 continue
477 }
478 t.queue.Push(batchInfo)
479 if !timer.Stop() {
480 <-timer.C
481 }
482 timer.Reset(t.cfg.TxManagerCheckInterval)
483 case <-timer.C:
484 queuePosition, batchInfo := t.queue.Next()
485 if batchInfo == nil {
486 timer.Reset(longWaitDuration)
487 continue
488 }
489 timer.Reset(t.cfg.TxManagerCheckInterval)
490 if err := t.checkEthTransactionReceipt(ctx, batchInfo); ctx.Err() != nil {
491 continue
492 } else if err != nil { //nolint:staticcheck
493 // Our ethNode is giving an error different
494 // than "not found" when getting the receipt
495 // for the transaction, so we can't figure out
496 // if it was not mined, mined and succesfull or
497 // mined and failed. This could be due to the
498 // ethNode failure.
499 t.coord.SendMsg(ctx, MsgStopPipeline{
500 Reason: fmt.Sprintf("forgeBatch receipt: %v", err)})
501 }
502
503 confirm, err := t.handleReceipt(ctx, batchInfo)
504 if ctx.Err() != nil {
505 continue
506 } else if err != nil { //nolint:staticcheck
507 // Transaction was rejected
508 if err := t.removeBadBatchInfos(ctx); ctx.Err() != nil {
509 continue
510 } else if err != nil {
511 log.Errorw("TxManager: removeBadBatchInfos", "err", err)
512 continue
513 }
514 t.coord.SendMsg(ctx, MsgStopPipeline{
515 Reason: fmt.Sprintf("forgeBatch reject: %v", err)})
516 continue
517 }
518 now := time.Now()
519 if !t.cfg.EthNoReuseNonce && confirm == nil &&
520 now.Sub(batchInfo.SendTimestamp) > t.cfg.EthTxResendTimeout {
521 log.Infow("TxManager: forgeBatch tx not been mined timeout, resending",
522 "tx", batchInfo.EthTx.Hash(), "batch", batchInfo.BatchNum)
523 if err := t.sendRollupForgeBatch(ctx, batchInfo, true); ctx.Err() != nil {
524 continue
525 } else if err != nil {
526 // If we reach here it's because our ethNode has
527 // been unable to send the transaction to
528 // ethereum. This could be due to the ethNode
529 // failure, or an invalid transaction (that
530 // can't be mined)
531 log.Warnw("TxManager: forgeBatch resend failed", "err", err,
532 "batch", batchInfo.BatchNum)
533 t.coord.SendMsg(ctx, MsgStopPipeline{
534 Reason: fmt.Sprintf("forgeBatch resend: %v", err)})
535 continue
536 }
537 }
538
539 if confirm != nil && *confirm >= t.cfg.ConfirmBlocks {
540 log.Debugw("TxManager: forgeBatch tx confirmed",
541 "tx", batchInfo.EthTx.Hash(), "batch", batchInfo.BatchNum)
542 t.queue.Remove(queuePosition)
543 }
544 }
545 }
546}
547
548func (t *TxManager) removeBadBatchInfos(ctx context.Context) error {
549 next := 0
550 for {
551 batchInfo := t.queue.At(next)
552 if batchInfo == nil {
553 break
554 }
555 if err := t.checkEthTransactionReceipt(ctx, batchInfo); ctx.Err() != nil {
556 return nil
557 } else if err != nil {
558 // Our ethNode is giving an error different
559 // than "not found" when getting the receipt
560 // for the transaction, so we can't figure out
561 // if it was not mined, mined and succesfull or
562 // mined and failed. This could be due to the
563 // ethNode failure.
564 next++
565 continue
566 }
567 confirm, err := t.handleReceipt(ctx, batchInfo)
568 if ctx.Err() != nil {
569 return nil
570 } else if err != nil {
571 // Transaction was rejected
572 if t.minPipelineNum <= batchInfo.PipelineNum {
573 t.minPipelineNum = batchInfo.PipelineNum + 1
574 }
575 t.queue.Remove(next)
576 continue
577 }
578 // If tx is pending but is from a cancelled pipeline, remove it
579 // from the queue
580 if confirm == nil {
581 if batchInfo.PipelineNum < t.minPipelineNum {
582 t.queue.Remove(next)
583 continue
584 }
585 }
586 next++
587 }
588 accNonce, err := t.ethClient.EthNonceAt(ctx, t.account.Address, nil)
589 if err != nil {
590 return err
591 }
592 if !t.cfg.EthNoReuseNonce {
593 t.accNextNonce = accNonce
594 }
595 return nil
596}
597
598func (t *TxManager) canForgeAt(blockNum int64) bool {
599 return canForge(&t.consts.Auction, &t.vars.Auction,
600 &t.stats.Sync.Auction.CurrentSlot, &t.stats.Sync.Auction.NextSlot,
601 t.cfg.ForgerAddress, blockNum)
602}
603
604func (t *TxManager) mustL1L2Batch(blockNum int64) bool {
605 lastL1BatchBlockNum := t.lastSentL1BatchBlockNum
606 if t.stats.Sync.LastL1BatchBlock > lastL1BatchBlockNum {
607 lastL1BatchBlockNum = t.stats.Sync.LastL1BatchBlock
608 }
609 return blockNum-lastL1BatchBlockNum >= t.vars.Rollup.ForgeL1L2BatchTimeout-1
610}