|
67 | 67 | // transactions is reached for specific accounts.
|
68 | 68 | ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
|
69 | 69 |
|
| 70 | + // ErrOutOfOrderTxFromDelegated is returned when the transaction with gapped |
| 71 | + // nonce received from the accounts with delegation or pending delegation. |
| 72 | + ErrOutOfOrderTxFromDelegated = errors.New("gapped-nonce tx from delegated accounts") |
| 73 | + |
70 | 74 | // ErrAuthorityReserved is returned if a transaction has an authorization
|
71 | 75 | // signed by an address which already has in-flight transactions known to the
|
72 | 76 | // pool.
|
@@ -606,33 +610,39 @@ func (pool *LegacyPool) validateTx(tx *types.Transaction) error {
|
606 | 610 | return pool.validateAuth(tx)
|
607 | 611 | }
|
608 | 612 |
|
| 613 | +// checkDelegationLimit determines if the tx sender is delegated or has a |
| 614 | +// pending delegation, and if so, ensures they have at most one in-flight |
| 615 | +// **executable** transaction, e.g. disallow stacked and gapped transactions |
| 616 | +// from the account. |
| 617 | +func (pool *LegacyPool) checkDelegationLimit(tx *types.Transaction) error { |
| 618 | + from, _ := types.Sender(pool.signer, tx) // validated |
| 619 | + |
| 620 | + // Short circuit if the sender has neither delegation nor pending delegation. |
| 621 | + if pool.currentState.GetCodeHash(from) == types.EmptyCodeHash && len(pool.all.auths[from]) == 0 { |
| 622 | + return nil |
| 623 | + } |
| 624 | + pending := pool.pending[from] |
| 625 | + if pending == nil { |
| 626 | + // Transaction with gapped nonce is not supported for delegated accounts |
| 627 | + if pool.pendingNonces.get(from) != tx.Nonce() { |
| 628 | + return ErrOutOfOrderTxFromDelegated |
| 629 | + } |
| 630 | + return nil |
| 631 | + } |
| 632 | + // Transaction replacement is supported |
| 633 | + if pending.Contains(tx.Nonce()) { |
| 634 | + return nil |
| 635 | + } |
| 636 | + return ErrInflightTxLimitReached |
| 637 | +} |
| 638 | + |
609 | 639 | // validateAuth verifies that the transaction complies with code authorization
|
610 | 640 | // restrictions brought by SetCode transaction type.
|
611 | 641 | func (pool *LegacyPool) validateAuth(tx *types.Transaction) error {
|
612 |
| - from, _ := types.Sender(pool.signer, tx) // validated |
613 |
| - |
614 | 642 | // Allow at most one in-flight tx for delegated accounts or those with a
|
615 | 643 | // pending authorization.
|
616 |
| - if pool.currentState.GetCodeHash(from) != types.EmptyCodeHash || len(pool.all.auths[from]) != 0 { |
617 |
| - var ( |
618 |
| - count int |
619 |
| - exists bool |
620 |
| - ) |
621 |
| - pending := pool.pending[from] |
622 |
| - if pending != nil { |
623 |
| - count += pending.Len() |
624 |
| - exists = pending.Contains(tx.Nonce()) |
625 |
| - } |
626 |
| - queue := pool.queue[from] |
627 |
| - if queue != nil { |
628 |
| - count += queue.Len() |
629 |
| - exists = exists || queue.Contains(tx.Nonce()) |
630 |
| - } |
631 |
| - // Replace the existing in-flight transaction for delegated accounts |
632 |
| - // are still supported |
633 |
| - if count >= 1 && !exists { |
634 |
| - return ErrInflightTxLimitReached |
635 |
| - } |
| 644 | + if err := pool.checkDelegationLimit(tx); err != nil { |
| 645 | + return err |
636 | 646 | }
|
637 | 647 | // Authorities cannot conflict with any pending or queued transactions.
|
638 | 648 | if auths := tx.SetCodeAuthorities(); len(auths) > 0 {
|
|
0 commit comments