diff --git a/core/txpool/errors.go b/core/txpool/errors.go index c679a00134..32e49db87c 100644 --- a/core/txpool/errors.go +++ b/core/txpool/errors.go @@ -65,4 +65,8 @@ var ( // signed by an address which already has in-flight transactions known to the // pool. ErrAuthorityReserved = errors.New("authority already reserved") + + // ErrAuthorityNonce is returned if a transaction has an authorization with + // a nonce that is not currently valid for the authority. + ErrAuthorityNonceTooLow = errors.New("authority nonce too low") ) diff --git a/core/txpool/validation.go b/core/txpool/validation.go index e997865bf2..a3e1d2d01a 100644 --- a/core/txpool/validation.go +++ b/core/txpool/validation.go @@ -284,6 +284,15 @@ func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, op return fmt.Errorf("%w: authorization conflicts with other known tx", ErrAuthorityReserved) } } + // Verify the every authorization's nonce is not stale. This check is expensive. + for _, auth := range tx.SetCodeAuthorizations() { + if addr, err := auth.Authority(); err == nil { + next := opts.State.GetNonce(addr) + if auth.Nonce < next { + return fmt.Errorf("%w: next nonce %d, auth nonce %d", ErrAuthorityNonceTooLow, next, auth.Nonce) + } + } + } } return nil }