github.com/letsencrypt/boulder@v0.20251208.0/ratelimits/transaction.go (about)

     1  package ratelimits
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/netip"
     9  	"strconv"
    10  	"time"
    11  
    12  	"google.golang.org/grpc"
    13  	"google.golang.org/protobuf/types/known/emptypb"
    14  
    15  	"github.com/prometheus/client_golang/prometheus"
    16  	"github.com/prometheus/client_golang/prometheus/promauto"
    17  
    18  	"github.com/letsencrypt/boulder/config"
    19  	"github.com/letsencrypt/boulder/core"
    20  	"github.com/letsencrypt/boulder/identifier"
    21  	blog "github.com/letsencrypt/boulder/log"
    22  	sapb "github.com/letsencrypt/boulder/sa/proto"
    23  )
    24  
    25  // ErrInvalidCost indicates that the cost specified was < 0.
    26  var ErrInvalidCost = fmt.Errorf("invalid cost, must be >= 0")
    27  
    28  // ErrInvalidCostOverLimit indicates that the cost specified was > limit.Burst.
    29  var ErrInvalidCostOverLimit = fmt.Errorf("invalid cost, must be <= limit.Burst")
    30  
    31  // newIPAddressBucketKey returns a bucketKey for limits that use
    32  // the 'enum:ipAddress' bucket key format.
    33  func newIPAddressBucketKey(name Name, ip netip.Addr) string {
    34  	return joinWithColon(name.EnumString(), ip.String())
    35  }
    36  
    37  // newIPv6RangeCIDRBucketKey returns a bucketKey for limits that
    38  // use the 'enum:ipv6RangeCIDR' bucket key format.
    39  func newIPv6RangeCIDRBucketKey(name Name, prefix netip.Prefix) string {
    40  	return joinWithColon(name.EnumString(), prefix.String())
    41  }
    42  
    43  // newRegIdBucketKey returns a bucketKey for limits that use the
    44  // 'enum:regId' bucket key format.
    45  func newRegIdBucketKey(name Name, regId int64) string {
    46  	return joinWithColon(name.EnumString(), strconv.FormatInt(regId, 10))
    47  }
    48  
    49  // newDomainOrCIDRBucketKey returns a bucketKey for limits that use
    50  // the 'enum:domainOrCIDR' bucket key formats.
    51  func newDomainOrCIDRBucketKey(name Name, domainOrCIDR string) string {
    52  	return joinWithColon(name.EnumString(), domainOrCIDR)
    53  }
    54  
    55  // newRegIdIdentValueBucketKey returns a bucketKey for limits that use the
    56  // 'enum:regId:identValue' bucket key format.
    57  func newRegIdIdentValueBucketKey(name Name, regId int64, orderIdent string) string {
    58  	return joinWithColon(name.EnumString(), strconv.FormatInt(regId, 10), orderIdent)
    59  }
    60  
    61  // newFQDNSetBucketKey validates and returns a bucketKey for limits that use the
    62  // 'enum:fqdnSet' bucket key format.
    63  func newFQDNSetBucketKey(name Name, orderIdents identifier.ACMEIdentifiers) string {
    64  	return joinWithColon(name.EnumString(), fmt.Sprintf("%x", core.HashIdentifiers(orderIdents)))
    65  }
    66  
    67  // Transaction represents a single rate limit operation. It includes a
    68  // bucketKey, which combines the specific rate limit enum with a unique
    69  // identifier to form the key where the state of the "bucket" can be referenced
    70  // or stored by the Limiter, the rate limit being enforced, a cost which MUST be
    71  // >= 0, and check/spend fields, which indicate how the Transaction should be
    72  // processed. The following are acceptable combinations of check/spend:
    73  //   - check-and-spend: when check and spend are both true, the cost will be
    74  //     checked against the bucket's capacity and spent/refunded, when possible.
    75  //   - check-only: when only check is true, the cost will be checked against the
    76  //     bucket's capacity, but will never be spent/refunded.
    77  //   - spend-only: when only spend is true, spending is best-effort. Regardless
    78  //     of the bucket's capacity, the transaction will be considered "allowed".
    79  //   - reset-only: when reset is true, the bucket will be reset to full capacity.
    80  //   - allow-only: when neither check nor spend are true, the transaction will
    81  //     be considered "allowed" regardless of the bucket's capacity. This is
    82  //     useful for limits that are disabled.
    83  //
    84  // The zero value of Transaction is an allow-only transaction and is valid even if
    85  // it would fail validateTransaction (for instance because cost and burst are zero).
    86  type Transaction struct {
    87  	bucketKey string
    88  	limit     *Limit
    89  	cost      int64
    90  	check     bool
    91  	spend     bool
    92  	reset     bool
    93  }
    94  
    95  func (txn Transaction) checkOnly() bool {
    96  	return txn.check && !txn.spend && !txn.reset
    97  }
    98  
    99  func (txn Transaction) spendOnly() bool {
   100  	return txn.spend && !txn.check && !txn.reset
   101  }
   102  
   103  func (txn Transaction) allowOnly() bool {
   104  	return !txn.check && !txn.spend && !txn.reset
   105  }
   106  
   107  func (txn Transaction) resetOnly() bool {
   108  	return txn.reset && !txn.check && !txn.spend
   109  }
   110  
   111  func validateTransaction(txn Transaction) (Transaction, error) {
   112  	if txn.limit == nil {
   113  		return Transaction{}, fmt.Errorf("invalid limit, must not be nil")
   114  	}
   115  	if txn.reset {
   116  		if txn.check || txn.spend {
   117  			return Transaction{}, fmt.Errorf("invalid reset transaction, check and spend must be false")
   118  		}
   119  		if txn.limit.Burst == 0 {
   120  			return Transaction{}, fmt.Errorf("invalid limit, burst must be > 0")
   121  		}
   122  		return txn, nil
   123  	}
   124  	if txn.cost < 0 {
   125  		return Transaction{}, ErrInvalidCost
   126  	}
   127  	if txn.limit.Burst == 0 {
   128  		// This should never happen. If the limit was loaded from a file,
   129  		// Burst was validated then. If this is a zero-valued Transaction
   130  		// (that is, an allow-only transaction), then validateTransaction
   131  		// shouldn't be called because zero-valued transactions are automatically
   132  		// valid.
   133  		return Transaction{}, fmt.Errorf("invalid limit, burst must be > 0")
   134  	}
   135  	if txn.cost > txn.limit.Burst {
   136  		return Transaction{}, ErrInvalidCostOverLimit
   137  	}
   138  	return txn, nil
   139  }
   140  
   141  func newTransaction(limit *Limit, bucketKey string, cost int64) (Transaction, error) {
   142  	return validateTransaction(Transaction{
   143  		bucketKey: bucketKey,
   144  		limit:     limit,
   145  		cost:      cost,
   146  		check:     true,
   147  		spend:     true,
   148  	})
   149  }
   150  
   151  func newCheckOnlyTransaction(limit *Limit, bucketKey string, cost int64) (Transaction, error) {
   152  	return validateTransaction(Transaction{
   153  		bucketKey: bucketKey,
   154  		limit:     limit,
   155  		cost:      cost,
   156  		check:     true,
   157  	})
   158  }
   159  
   160  func newSpendOnlyTransaction(limit *Limit, bucketKey string, cost int64) (Transaction, error) {
   161  	return validateTransaction(Transaction{
   162  		bucketKey: bucketKey,
   163  		limit:     limit,
   164  		cost:      cost,
   165  		spend:     true,
   166  	})
   167  }
   168  
   169  func newResetTransaction(limit *Limit, bucketKey string) (Transaction, error) {
   170  	return validateTransaction(Transaction{
   171  		bucketKey: bucketKey,
   172  		limit:     limit,
   173  		reset:     true,
   174  	})
   175  }
   176  
   177  func newAllowOnlyTransaction() Transaction {
   178  	// Zero values are sufficient.
   179  	return Transaction{}
   180  }
   181  
   182  // TransactionBuilder is used to build Transactions for various rate limits.
   183  // Each rate limit has a corresponding method that returns a Transaction for
   184  // that limit. Call NewTransactionBuilder to create a new *TransactionBuilder.
   185  type TransactionBuilder struct {
   186  	*limitRegistry
   187  }
   188  
   189  func (builder *TransactionBuilder) Ready() bool {
   190  	return builder.limitRegistry.overridesLoaded
   191  }
   192  
   193  // GetOverridesFunc is used to pass in the sa.GetEnabledRateLimitOverrides
   194  // method to NewTransactionBuilderFromDatabase, rather than storing a full
   195  // sa.SQLStorageAuthority. This makes testing significantly simpler.
   196  type GetOverridesFunc func(context.Context, *emptypb.Empty, ...grpc.CallOption) (grpc.ServerStreamingClient[sapb.RateLimitOverrideResponse], error)
   197  
   198  // NewTransactionBuilderFromDatabase returns a new *TransactionBuilder. The
   199  // provided defaults path is expected to be a path to a YAML file that contains
   200  // the default limits. The provided overrides function is expected to be an SA's
   201  // GetEnabledRateLimitOverrides. Both are required.
   202  func NewTransactionBuilderFromDatabase(defaults string, overrides GetOverridesFunc, stats prometheus.Registerer, logger blog.Logger) (*TransactionBuilder, error) {
   203  	defaultsData, err := loadDefaultsFromFile(defaults)
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	refresher := func(ctx context.Context, errorGauge prometheus.Gauge, logger blog.Logger) (Limits, error) {
   209  		ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
   210  		defer cancel()
   211  
   212  		stream, err := overrides(ctx, &emptypb.Empty{})
   213  		if err != nil {
   214  			return nil, fmt.Errorf("fetching enabled overrides: %w", err)
   215  		}
   216  
   217  		overrides := make(Limits)
   218  		var errorCount float64
   219  		for {
   220  			r, err := stream.Recv()
   221  			if err != nil {
   222  				if err == io.EOF {
   223  					break
   224  				}
   225  				return nil, fmt.Errorf("reading overrides stream: %w", err)
   226  			}
   227  
   228  			limitName := Name(r.Override.LimitEnum)
   229  
   230  			bucketKey, err := hydrateOverrideLimit(r.Override.BucketKey, limitName)
   231  			if err != nil {
   232  				logger.Errf("hydrating %s override with key %q: %v", limitName.String(), r.Override.BucketKey, err)
   233  				errorCount++
   234  				continue
   235  			}
   236  
   237  			newLimit := &Limit{
   238  				Burst:  r.Override.Burst,
   239  				Count:  r.Override.Count,
   240  				Period: config.Duration{Duration: r.Override.Period.AsDuration()},
   241  				Name:   limitName,
   242  				Comment: fmt.Sprintf("Last Updated: %s - %s",
   243  					r.UpdatedAt.AsTime().Format("2006-01-02"),
   244  					r.Override.Comment,
   245  				),
   246  				isOverride: true,
   247  			}
   248  
   249  			err = ValidateLimit(newLimit)
   250  			if err != nil {
   251  				logger.Errf("hydrating %s override with key %q: %v", newLimit.Name.String(), r.Override.BucketKey, err)
   252  				errorCount++
   253  				continue
   254  			}
   255  
   256  			overrides[bucketKey] = newLimit
   257  		}
   258  		errorGauge.Set(errorCount)
   259  		return overrides, nil
   260  	}
   261  
   262  	return NewTransactionBuilder(defaultsData, refresher, stats, logger)
   263  }
   264  
   265  // NewTransactionBuilderFromFiles returns a new *TransactionBuilder. The
   266  // provided defaults and overrides paths are expected to be paths to YAML files
   267  // that contain the default and override limits, respectively. Overrides is
   268  // optional, defaults is required.
   269  func NewTransactionBuilderFromFiles(defaults string, overrides string, stats prometheus.Registerer, logger blog.Logger) (*TransactionBuilder, error) {
   270  	defaultsData, err := loadDefaultsFromFile(defaults)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  
   275  	if overrides == "" {
   276  		return NewTransactionBuilder(defaultsData, nil, stats, logger)
   277  	}
   278  
   279  	refresher := func(ctx context.Context, _ prometheus.Gauge, _ blog.Logger) (Limits, error) {
   280  		overridesData, err := loadOverridesFromFile(overrides)
   281  		if err != nil {
   282  			return nil, err
   283  		}
   284  		return parseOverrideLimits(overridesData)
   285  	}
   286  
   287  	return NewTransactionBuilder(defaultsData, refresher, stats, logger)
   288  }
   289  
   290  // NewTransactionBuilder returns a new *TransactionBuilder. A defaults map is
   291  // required.
   292  func NewTransactionBuilder(defaultConfigs LimitConfigs, refresher OverridesRefresher, stats prometheus.Registerer, logger blog.Logger) (*TransactionBuilder, error) {
   293  	defaults, err := parseDefaultLimits(defaultConfigs)
   294  	if err != nil {
   295  		return nil, err
   296  	}
   297  
   298  	if refresher == nil {
   299  		refresher = func(context.Context, prometheus.Gauge, blog.Logger) (Limits, error) {
   300  			return nil, nil
   301  		}
   302  	}
   303  
   304  	overridesTimestamp := promauto.With(stats).NewGauge(prometheus.GaugeOpts{
   305  		Namespace: "ratelimits",
   306  		Subsystem: "overrides",
   307  		Name:      "timestamp_seconds",
   308  		Help:      "A gauge with the last timestamp when overrides were successfully loaded",
   309  	})
   310  
   311  	overridesErrors := promauto.With(stats).NewGauge(prometheus.GaugeOpts{
   312  		Namespace: "ratelimits",
   313  		Subsystem: "overrides",
   314  		Name:      "errors",
   315  		Help:      "A gauge with the number of errors while last trying to load overrides",
   316  	})
   317  
   318  	overridesPerLimit := promauto.With(stats).NewGaugeVec(prometheus.GaugeOpts{
   319  		Namespace: "ratelimits",
   320  		Subsystem: "overrides",
   321  		Name:      "active",
   322  		Help:      "A gauge with the number of overrides, partitioned by rate limit",
   323  	}, []string{"limit"})
   324  
   325  	registry := &limitRegistry{
   326  		defaults:         defaults,
   327  		refreshOverrides: refresher,
   328  		logger:           logger,
   329  
   330  		overridesTimestamp: overridesTimestamp,
   331  		overridesErrors:    overridesErrors,
   332  		overridesPerLimit:  *overridesPerLimit,
   333  	}
   334  
   335  	return &TransactionBuilder{registry}, nil
   336  }
   337  
   338  // registrationsPerIPAddressTransaction returns a Transaction for the
   339  // NewRegistrationsPerIPAddress limit for the provided IP address.
   340  func (builder *TransactionBuilder) registrationsPerIPAddressTransaction(ip netip.Addr) (Transaction, error) {
   341  	bucketKey := newIPAddressBucketKey(NewRegistrationsPerIPAddress, ip)
   342  	limit, err := builder.getLimit(NewRegistrationsPerIPAddress, bucketKey)
   343  	if err != nil {
   344  		if errors.Is(err, errLimitDisabled) {
   345  			return newAllowOnlyTransaction(), nil
   346  		}
   347  		return Transaction{}, err
   348  	}
   349  	return newTransaction(limit, bucketKey, 1)
   350  }
   351  
   352  // registrationsPerIPv6RangeTransaction returns a Transaction for the
   353  // NewRegistrationsPerIPv6Range limit for the /48 IPv6 range which contains the
   354  // provided IPv6 address.
   355  func (builder *TransactionBuilder) registrationsPerIPv6RangeTransaction(ip netip.Addr) (Transaction, error) {
   356  	prefix, err := coveringIPPrefix(NewRegistrationsPerIPv6Range, ip)
   357  	if err != nil {
   358  		return Transaction{}, fmt.Errorf("computing covering prefix for %q: %w", ip, err)
   359  	}
   360  	bucketKey := newIPv6RangeCIDRBucketKey(NewRegistrationsPerIPv6Range, prefix)
   361  
   362  	limit, err := builder.getLimit(NewRegistrationsPerIPv6Range, bucketKey)
   363  	if err != nil {
   364  		if errors.Is(err, errLimitDisabled) {
   365  			return newAllowOnlyTransaction(), nil
   366  		}
   367  		return Transaction{}, err
   368  	}
   369  	return newTransaction(limit, bucketKey, 1)
   370  }
   371  
   372  // ordersPerAccountTransaction returns a Transaction for the NewOrdersPerAccount
   373  // limit for the provided ACME registration Id.
   374  func (builder *TransactionBuilder) ordersPerAccountTransaction(regId int64) (Transaction, error) {
   375  	bucketKey := newRegIdBucketKey(NewOrdersPerAccount, regId)
   376  	limit, err := builder.getLimit(NewOrdersPerAccount, bucketKey)
   377  	if err != nil {
   378  		if errors.Is(err, errLimitDisabled) {
   379  			return newAllowOnlyTransaction(), nil
   380  		}
   381  		return Transaction{}, err
   382  	}
   383  	return newTransaction(limit, bucketKey, 1)
   384  }
   385  
   386  // FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions returns a slice
   387  // of Transactions for the provided order identifiers. An error is returned if
   388  // any of the order identifiers' values are invalid. This method should be used
   389  // for checking capacity, before allowing more authorizations to be created.
   390  //
   391  // Precondition: len(orderIdents) < maxNames.
   392  func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId int64, orderIdents identifier.ACMEIdentifiers) ([]Transaction, error) {
   393  	// FailedAuthorizationsPerDomainPerAccount limit uses the 'enum:regId'
   394  	// bucket key format for overrides.
   395  	perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsPerDomainPerAccount, regId)
   396  	limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey)
   397  	if err != nil {
   398  		if errors.Is(err, errLimitDisabled) {
   399  			return []Transaction{newAllowOnlyTransaction()}, nil
   400  		}
   401  		return nil, err
   402  	}
   403  
   404  	var txns []Transaction
   405  	for _, ident := range orderIdents {
   406  		// FailedAuthorizationsPerDomainPerAccount limit uses the
   407  		// 'enum:regId:identValue' bucket key format for transactions.
   408  		perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsPerDomainPerAccount, regId, ident.Value)
   409  
   410  		// Add a check-only transaction for each per identValue per account
   411  		// bucket.
   412  		txn, err := newCheckOnlyTransaction(limit, perIdentValuePerAccountBucketKey, 1)
   413  		if err != nil {
   414  			return nil, err
   415  		}
   416  		txns = append(txns, txn)
   417  	}
   418  	return txns, nil
   419  }
   420  
   421  // FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction returns a spend-
   422  // only Transaction for the provided order identifier. An error is returned if
   423  // the order identifier's value is invalid. This method should be used for
   424  // spending capacity, as a result of a failed authorization.
   425  func (builder *TransactionBuilder) FailedAuthorizationsPerDomainPerAccountSpendOnlyTransaction(regId int64, orderIdent identifier.ACMEIdentifier) (Transaction, error) {
   426  	// FailedAuthorizationsPerDomainPerAccount limit uses the 'enum:regId'
   427  	// bucket key format for overrides.
   428  	perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsPerDomainPerAccount, regId)
   429  	limit, err := builder.getLimit(FailedAuthorizationsPerDomainPerAccount, perAccountBucketKey)
   430  	if err != nil {
   431  		if errors.Is(err, errLimitDisabled) {
   432  			return newAllowOnlyTransaction(), nil
   433  		}
   434  		return Transaction{}, err
   435  	}
   436  
   437  	// FailedAuthorizationsPerDomainPerAccount limit uses the
   438  	// 'enum:regId:identValue' bucket key format for transactions.
   439  	perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsPerDomainPerAccount, regId, orderIdent.Value)
   440  	txn, err := newSpendOnlyTransaction(limit, perIdentValuePerAccountBucketKey, 1)
   441  	if err != nil {
   442  		return Transaction{}, err
   443  	}
   444  
   445  	return txn, nil
   446  }
   447  
   448  // FailedAuthorizationsForPausingPerDomainPerAccountTransaction returns a
   449  // Transaction for the provided order identifier. An error is returned if the
   450  // order identifier's value is invalid. This method should be used for spending
   451  // capacity, as a result of a failed authorization.
   452  func (builder *TransactionBuilder) FailedAuthorizationsForPausingPerDomainPerAccountTransaction(regId int64, orderIdent identifier.ACMEIdentifier) (Transaction, error) {
   453  	// FailedAuthorizationsForPausingPerDomainPerAccount limit uses the 'enum:regId'
   454  	// bucket key format for overrides.
   455  	perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId)
   456  	limit, err := builder.getLimit(FailedAuthorizationsForPausingPerDomainPerAccount, perAccountBucketKey)
   457  	if err != nil {
   458  		if errors.Is(err, errLimitDisabled) {
   459  			return newAllowOnlyTransaction(), nil
   460  		}
   461  		return Transaction{}, err
   462  	}
   463  
   464  	// FailedAuthorizationsForPausingPerDomainPerAccount limit uses the
   465  	// 'enum:regId:identValue' bucket key format for transactions.
   466  	perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId, orderIdent.Value)
   467  	txn, err := newTransaction(limit, perIdentValuePerAccountBucketKey, 1)
   468  	if err != nil {
   469  		return Transaction{}, err
   470  	}
   471  
   472  	return txn, nil
   473  }
   474  
   475  // certificatesPerDomainCheckOnlyTransactions returns a slice of Transactions
   476  // for the provided order identifiers. It returns an error if any of the order
   477  // identifiers' values are invalid. This method should be used for checking
   478  // capacity, before allowing more orders to be created. If a
   479  // CertificatesPerDomainPerAccount override is active, a check-only Transaction
   480  // is created for each per account per domainOrCIDR bucket. Otherwise, a
   481  // check-only Transaction is generated for each global per domainOrCIDR bucket.
   482  // This method should be used for checking capacity, before allowing more orders
   483  // to be created.
   484  //
   485  // Precondition: All orderIdents must comply with policy.WellFormedIdentifiers.
   486  func (builder *TransactionBuilder) certificatesPerDomainCheckOnlyTransactions(regId int64, orderIdents identifier.ACMEIdentifiers) ([]Transaction, error) {
   487  	if len(orderIdents) > 100 {
   488  		return nil, fmt.Errorf("unwilling to process more than 100 rate limit transactions, got %d", len(orderIdents))
   489  	}
   490  
   491  	perAccountLimitBucketKey := newRegIdBucketKey(CertificatesPerDomainPerAccount, regId)
   492  	accountOverride := true
   493  	perAccountLimit, err := builder.getLimit(CertificatesPerDomainPerAccount, perAccountLimitBucketKey)
   494  	if err != nil {
   495  		// The CertificatesPerDomainPerAccount limit never has a default. If there is an override for it,
   496  		// the above call will return the override. But if there is none, it will return errLimitDisabled.
   497  		// In that case we want to continue, but make sure we don't reference `perAccountLimit` because it
   498  		// is not a valid limit.
   499  		if errors.Is(err, errLimitDisabled) {
   500  			accountOverride = false
   501  		} else {
   502  			return nil, err
   503  		}
   504  	}
   505  
   506  	coveringIdents, err := coveringIdentifiers(orderIdents)
   507  	if err != nil {
   508  		return nil, err
   509  	}
   510  
   511  	var txns []Transaction
   512  	for _, ident := range coveringIdents {
   513  		perDomainOrCIDRBucketKey := newDomainOrCIDRBucketKey(CertificatesPerDomain, ident)
   514  		if accountOverride {
   515  			if !perAccountLimit.isOverride {
   516  				return nil, fmt.Errorf("shouldn't happen: CertificatesPerDomainPerAccount limit is not an override")
   517  			}
   518  			perAccountPerDomainOrCIDRBucketKey := newRegIdIdentValueBucketKey(CertificatesPerDomainPerAccount, regId, ident)
   519  			// Add a check-only transaction for each per account per identValue
   520  			// bucket.
   521  			txn, err := newCheckOnlyTransaction(perAccountLimit, perAccountPerDomainOrCIDRBucketKey, 1)
   522  			if err != nil {
   523  				if errors.Is(err, errLimitDisabled) {
   524  					continue
   525  				}
   526  				return nil, err
   527  			}
   528  			txns = append(txns, txn)
   529  		} else {
   530  			// Use the per domainOrCIDR bucket key when no per account per
   531  			// domainOrCIDR override is configured.
   532  			perDomainOrCIDRLimit, err := builder.getLimit(CertificatesPerDomain, perDomainOrCIDRBucketKey)
   533  			if err != nil {
   534  				if errors.Is(err, errLimitDisabled) {
   535  					continue
   536  				}
   537  				return nil, err
   538  			}
   539  			// Add a check-only transaction for each per domainOrCIDR bucket.
   540  			txn, err := newCheckOnlyTransaction(perDomainOrCIDRLimit, perDomainOrCIDRBucketKey, 1)
   541  			if err != nil {
   542  				return nil, err
   543  			}
   544  			txns = append(txns, txn)
   545  		}
   546  	}
   547  	return txns, nil
   548  }
   549  
   550  // CertificatesPerDomainSpendOnlyTransactions returns a slice of Transactions
   551  // for the provided order identifiers. It returns an error if any of the order
   552  // identifiers' values are invalid. If a CertificatesPerDomainPerAccount
   553  // override is configured, it generates two types of Transactions:
   554  //   - A spend-only Transaction for each per-account, per-domainOrCIDR bucket,
   555  //     which enforces the limit on certificates issued per domainOrCIDR for
   556  //     each account.
   557  //   - A spend-only Transaction for each per-domainOrCIDR bucket, which
   558  //     enforces the global limit on certificates issued per domainOrCIDR.
   559  //
   560  // If no CertificatesPerDomainPerAccount override is present, it returns a
   561  // spend-only Transaction for each global per-domainOrCIDR bucket. This method
   562  // should be used for spending capacity, when a certificate is issued.
   563  //
   564  // Precondition: orderIdents must all pass policy.WellFormedIdentifiers.
   565  func (builder *TransactionBuilder) CertificatesPerDomainSpendOnlyTransactions(regId int64, orderIdents identifier.ACMEIdentifiers) ([]Transaction, error) {
   566  	if len(orderIdents) > 100 {
   567  		return nil, fmt.Errorf("unwilling to process more than 100 rate limit transactions, got %d", len(orderIdents))
   568  	}
   569  
   570  	perAccountLimitBucketKey := newRegIdBucketKey(CertificatesPerDomainPerAccount, regId)
   571  	accountOverride := true
   572  	perAccountLimit, err := builder.getLimit(CertificatesPerDomainPerAccount, perAccountLimitBucketKey)
   573  	if err != nil {
   574  		// The CertificatesPerDomainPerAccount limit never has a default. If there is an override for it,
   575  		// the above call will return the override. But if there is none, it will return errLimitDisabled.
   576  		// In that case we want to continue, but make sure we don't reference `perAccountLimit` because it
   577  		// is not a valid limit.
   578  		if errors.Is(err, errLimitDisabled) {
   579  			accountOverride = false
   580  		} else {
   581  			return nil, err
   582  		}
   583  	}
   584  
   585  	coveringIdents, err := coveringIdentifiers(orderIdents)
   586  	if err != nil {
   587  		return nil, err
   588  	}
   589  
   590  	var txns []Transaction
   591  	for _, ident := range coveringIdents {
   592  		perDomainOrCIDRBucketKey := newDomainOrCIDRBucketKey(CertificatesPerDomain, ident)
   593  		if accountOverride {
   594  			if !perAccountLimit.isOverride {
   595  				return nil, fmt.Errorf("shouldn't happen: CertificatesPerDomainPerAccount limit is not an override")
   596  			}
   597  			perAccountPerDomainOrCIDRBucketKey := newRegIdIdentValueBucketKey(CertificatesPerDomainPerAccount, regId, ident)
   598  			// Add a spend-only transaction for each per account per
   599  			// domainOrCIDR bucket.
   600  			txn, err := newSpendOnlyTransaction(perAccountLimit, perAccountPerDomainOrCIDRBucketKey, 1)
   601  			if err != nil {
   602  				return nil, err
   603  			}
   604  			txns = append(txns, txn)
   605  
   606  			perDomainOrCIDRLimit, err := builder.getLimit(CertificatesPerDomain, perDomainOrCIDRBucketKey)
   607  			if err != nil {
   608  				if errors.Is(err, errLimitDisabled) {
   609  					continue
   610  				}
   611  				return nil, err
   612  			}
   613  
   614  			// Add a spend-only transaction for each per domainOrCIDR bucket.
   615  			txn, err = newSpendOnlyTransaction(perDomainOrCIDRLimit, perDomainOrCIDRBucketKey, 1)
   616  			if err != nil {
   617  				return nil, err
   618  			}
   619  			txns = append(txns, txn)
   620  		} else {
   621  			// Use the per domainOrCIDR bucket key when no per account per
   622  			// domainOrCIDR override is configured.
   623  			perDomainOrCIDRLimit, err := builder.getLimit(CertificatesPerDomain, perDomainOrCIDRBucketKey)
   624  			if err != nil {
   625  				if errors.Is(err, errLimitDisabled) {
   626  					continue
   627  				}
   628  				return nil, err
   629  			}
   630  			// Add a spend-only transaction for each per domainOrCIDR bucket.
   631  			txn, err := newSpendOnlyTransaction(perDomainOrCIDRLimit, perDomainOrCIDRBucketKey, 1)
   632  			if err != nil {
   633  				return nil, err
   634  			}
   635  			txns = append(txns, txn)
   636  		}
   637  	}
   638  	return txns, nil
   639  }
   640  
   641  // certificatesPerFQDNSetCheckOnlyTransaction returns a check-only Transaction
   642  // for the provided order identifiers. This method should only be used for
   643  // checking capacity, before allowing more orders to be created.
   644  func (builder *TransactionBuilder) certificatesPerFQDNSetCheckOnlyTransaction(orderIdents identifier.ACMEIdentifiers) (Transaction, error) {
   645  	bucketKey := newFQDNSetBucketKey(CertificatesPerFQDNSet, orderIdents)
   646  	limit, err := builder.getLimit(CertificatesPerFQDNSet, bucketKey)
   647  	if err != nil {
   648  		if errors.Is(err, errLimitDisabled) {
   649  			return newAllowOnlyTransaction(), nil
   650  		}
   651  		return Transaction{}, err
   652  	}
   653  	return newCheckOnlyTransaction(limit, bucketKey, 1)
   654  }
   655  
   656  // CertificatesPerFQDNSetSpendOnlyTransaction returns a spend-only Transaction
   657  // for the provided order identifiers. This method should only be used for
   658  // spending capacity, when a certificate is issued.
   659  func (builder *TransactionBuilder) CertificatesPerFQDNSetSpendOnlyTransaction(orderIdents identifier.ACMEIdentifiers) (Transaction, error) {
   660  	bucketKey := newFQDNSetBucketKey(CertificatesPerFQDNSet, orderIdents)
   661  	limit, err := builder.getLimit(CertificatesPerFQDNSet, bucketKey)
   662  	if err != nil {
   663  		if errors.Is(err, errLimitDisabled) {
   664  			return newAllowOnlyTransaction(), nil
   665  		}
   666  		return Transaction{}, err
   667  	}
   668  	return newSpendOnlyTransaction(limit, bucketKey, 1)
   669  }
   670  
   671  // NewOrderLimitTransactions takes in values from a new-order request and
   672  // returns the set of rate limit transactions that should be evaluated before
   673  // allowing the request to proceed.
   674  //
   675  // Precondition: idents must be a list of identifiers that all pass
   676  // policy.WellFormedIdentifiers.
   677  func (builder *TransactionBuilder) NewOrderLimitTransactions(regId int64, idents identifier.ACMEIdentifiers, isRenewal bool) ([]Transaction, error) {
   678  	makeTxnError := func(err error, limit Name) error {
   679  		return fmt.Errorf("error constructing rate limit transaction for %s rate limit: %w", limit, err)
   680  	}
   681  
   682  	var transactions []Transaction
   683  	if !isRenewal {
   684  		txn, err := builder.ordersPerAccountTransaction(regId)
   685  		if err != nil {
   686  			return nil, makeTxnError(err, NewOrdersPerAccount)
   687  		}
   688  		transactions = append(transactions, txn)
   689  	}
   690  
   691  	txns, err := builder.FailedAuthorizationsPerDomainPerAccountCheckOnlyTransactions(regId, idents)
   692  	if err != nil {
   693  		return nil, makeTxnError(err, FailedAuthorizationsPerDomainPerAccount)
   694  	}
   695  	transactions = append(transactions, txns...)
   696  
   697  	if !isRenewal {
   698  		txns, err := builder.certificatesPerDomainCheckOnlyTransactions(regId, idents)
   699  		if err != nil {
   700  			return nil, makeTxnError(err, CertificatesPerDomain)
   701  		}
   702  		transactions = append(transactions, txns...)
   703  	}
   704  
   705  	txn, err := builder.certificatesPerFQDNSetCheckOnlyTransaction(idents)
   706  	if err != nil {
   707  		return nil, makeTxnError(err, CertificatesPerFQDNSet)
   708  	}
   709  	return append(transactions, txn), nil
   710  }
   711  
   712  // NewAccountLimitTransactions takes in an IP address from a new-account request
   713  // and returns the set of rate limit transactions that should be evaluated
   714  // before allowing the request to proceed.
   715  func (builder *TransactionBuilder) NewAccountLimitTransactions(ip netip.Addr) ([]Transaction, error) {
   716  	makeTxnError := func(err error, limit Name) error {
   717  		return fmt.Errorf("error constructing rate limit transaction for %s rate limit: %w", limit, err)
   718  	}
   719  
   720  	var transactions []Transaction
   721  	txn, err := builder.registrationsPerIPAddressTransaction(ip)
   722  	if err != nil {
   723  		return nil, makeTxnError(err, NewRegistrationsPerIPAddress)
   724  	}
   725  	transactions = append(transactions, txn)
   726  
   727  	if ip.Is4() {
   728  		// This request was made from an IPv4 address.
   729  		return transactions, nil
   730  	}
   731  
   732  	txn, err = builder.registrationsPerIPv6RangeTransaction(ip)
   733  	if err != nil {
   734  		return nil, makeTxnError(err, NewRegistrationsPerIPv6Range)
   735  	}
   736  	return append(transactions, txn), nil
   737  }
   738  
   739  func (builder *TransactionBuilder) NewPausingResetTransactions(regId int64, orderIdent identifier.ACMEIdentifier) ([]Transaction, error) {
   740  	perAccountBucketKey := newRegIdBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId)
   741  	limit, err := builder.getLimit(FailedAuthorizationsForPausingPerDomainPerAccount, perAccountBucketKey)
   742  	if err != nil {
   743  		if errors.Is(err, errLimitDisabled) {
   744  			return []Transaction{newAllowOnlyTransaction()}, nil
   745  		}
   746  		return nil, err
   747  	}
   748  
   749  	perIdentValuePerAccountBucketKey := newRegIdIdentValueBucketKey(FailedAuthorizationsForPausingPerDomainPerAccount, regId, orderIdent.Value)
   750  	txn, err := newResetTransaction(limit, perIdentValuePerAccountBucketKey)
   751  	if err != nil {
   752  		return nil, err
   753  	}
   754  
   755  	return []Transaction{txn}, nil
   756  }
   757  
   758  // LimitOverrideRequestsPerIPAddressTransaction returns a Transaction for the
   759  // LimitOverrideRequestsPerIPAddress limit for the provided IP address. This
   760  // limit is used to rate limit requests to the SFE override request endpoint.
   761  func (builder *TransactionBuilder) LimitOverrideRequestsPerIPAddressTransaction(ip netip.Addr) (Transaction, error) {
   762  	bucketKey := newIPAddressBucketKey(LimitOverrideRequestsPerIPAddress, ip)
   763  	limit, err := builder.getLimit(LimitOverrideRequestsPerIPAddress, bucketKey)
   764  	if err != nil {
   765  		if errors.Is(err, errLimitDisabled) {
   766  			return newAllowOnlyTransaction(), nil
   767  		}
   768  		return Transaction{}, err
   769  	}
   770  	return newTransaction(limit, bucketKey, 1)
   771  }