Repository: prebid/prebid-server
Pull request #3063 (2023-08-30 19:14:55)

Adjust imp.BidFloor using BidAdjustment
This pr contains changes to adjust the  imp.BidFloor for each bidder request  using the BidAdjustmentFactors

PRD:
https://docs.google.com/document/d/1YAt00RfQaU2Ugciqw1LnirLgjyUz9ibfyjfxWUHR_P0/edit#heading=h.6lovavdxmitt


Patch:
diff --git a/account/account.go b/account/account.go
index 052d0199..156ad5e6 100644
--- a/account/account.go
+++ b/account/account.go
@@ -4,7 +4,6 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-
 	"github.com/buger/jsonparser"
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/config"
@@ -12,6 +11,7 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/prebid/prebid-server/util/iputil"
 	jsonpatch "gopkg.in/evanphx/json-patch.v4"
 )
 
@@ -103,6 +103,14 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r
 		return nil, errs
 	}
 
+	if ipV6Err := account.Privacy.IPv6Config.Validate(nil); len(ipV6Err) > 0 {
+		account.Privacy.IPv6Config.AnonKeepBits = iputil.IPv6DefaultMaskingBitSize
+	}
+
+	if ipV4Err := account.Privacy.IPv4Config.Validate(nil); len(ipV4Err) > 0 {
+		account.Privacy.IPv4Config.AnonKeepBits = iputil.IPv4DefaultMaskingBitSize
+	}
+
 	// set the value of events.enabled field based on deprecated events_enabled field and ensure backward compatibility
 	deprecateEventsEnabledField(account)
 
diff --git a/adapters/datablocks/datablocks.go b/adapters/datablocks/datablocks.go
index 8929c848..a39721a4 100644
--- a/adapters/datablocks/datablocks.go
+++ b/adapters/datablocks/datablocks.go
@@ -27,7 +27,7 @@ func (a *DatablocksAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *
 		"Accept":       {"application/json"},
 	}
 
-	// Pull the host and source ID info from the bidder params.
+	// Pull the source ID info from the bidder params.
 	reqImps, err := splitImpressions(request.Imp)
 
 	if err != nil {
@@ -45,7 +45,7 @@ func (a *DatablocksAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *
 			continue
 		}
 
-		urlParams := macros.EndpointTemplateParams{Host: reqExt.Host, SourceId: strconv.Itoa(reqExt.SourceId)}
+		urlParams := macros.EndpointTemplateParams{SourceId: strconv.Itoa(reqExt.SourceId)}
 		url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams)
 
 		if err != nil {
@@ -137,7 +137,7 @@ func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDatablocks, error) {
 	var datablocksExt openrtb_ext.ExtImpDatablocks
 	if err := json.Unmarshal(bidderExt.Bidder, &datablocksExt); err != nil {
 		return nil, &errortypes.BadInput{
-			Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()),
+			Message: fmt.Sprintf("Cannot Resolve sourceId: %s", err.Error()),
 		}
 	}
 
@@ -147,12 +147,6 @@ func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDatablocks, error) {
 		}
 	}
 
-	if len(datablocksExt.Host) < 1 {
-		return nil, &errortypes.BadInput{
-			Message: "Invalid/Missing Host",
-		}
-	}
-
 	return &datablocksExt, nil
 }
 
diff --git a/adapters/huaweiads/huaweiads.go b/adapters/huaweiads/huaweiads.go
index 071f0761..2b7c3c25 100644
--- a/adapters/huaweiads/huaweiads.go
+++ b/adapters/huaweiads/huaweiads.go
@@ -185,6 +185,7 @@ type metaData struct {
 	ApkInfo     apkInfo     `json:"apkInfo"`
 	Duration    int64       `json:"duration"`
 	MediaFile   mediaFile   `json:"mediaFile"`
+	Cta         string      `json:"cta"`
 }
 
 type imageInfo struct {
@@ -535,8 +536,7 @@ func getNativeFormat(adslot30 *adslot30, openRTBImp *openrtb2.Imp) error {
 	// only compute the main image number, type = native1.ImageAssetTypeMain
 	var numMainImage = 0
 	var numVideo = 0
-	var width int64
-	var height int64
+
 	for _, asset := range nativePayload.Assets {
 		// Only one of the {title,img,video,data} objects should be present in each object.
 		if asset.Video != nil {
@@ -547,19 +547,10 @@ func getNativeFormat(adslot30 *adslot30, openRTBImp *openrtb2.Imp) error {
 		if asset.Img != nil {
 			if asset.Img.Type == native1.ImageAssetTypeMain {
 				numMainImage++
-				if asset.Img.H != 0 && asset.Img.W != 0 {
-					width = asset.Img.W
-					height = asset.Img.H
-				} else if asset.Img.WMin != 0 && asset.Img.HMin != 0 {
-					width = asset.Img.WMin
-					height = asset.Img.HMin
-				}
 			}
 			continue
 		}
 	}
-	adslot30.W = width
-	adslot30.H = height
 
 	var detailedCreativeTypeList = make([]string, 0, 2)
 	if numVideo >= 1 {
@@ -1169,6 +1160,11 @@ func (a *adapter) extractAdmNative(adType int32, content *content, bidType openr
 				dataObject.Label = "desc"
 				dataObject.Value = getDecodeValue(content.MetaData.Description)
 			}
+
+			if asset.Data.Type == native1.DataAssetTypeCTAText {
+				dataObject.Type = native1.DataAssetTypeCTAText
+				dataObject.Value = getDecodeValue(content.MetaData.Cta)
+			}
 			responseAsset.Data = &dataObject
 		}
 		var id = asset.ID
diff --git a/adapters/response.go b/adapters/response.go
index 080f4627..8b7d59bd 100644
--- a/adapters/response.go
+++ b/adapters/response.go
@@ -15,7 +15,9 @@ func CheckResponseStatusCodeForErrors(response *ResponseData) error {
 	}
 
 	if response.StatusCode != http.StatusOK {
-		return fmt.Errorf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)
+		return &errortypes.BadServerResponse{
+			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+		}
 	}
 
 	return nil
diff --git a/adapters/sovrn/sovrn.go b/adapters/sovrn/sovrn.go
index 24022490..405e5ebf 100644
--- a/adapters/sovrn/sovrn.go
+++ b/adapters/sovrn/sovrn.go
@@ -20,6 +20,11 @@ type SovrnAdapter struct {
 	URI string
 }
 
+type sovrnImpExt struct {
+	Bidder     openrtb_ext.ExtImpSovrn `json:"bidder"`
+	AdUnitCode string                  `json:"adunitcode,omitempty"`
+}
+
 func (s *SovrnAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
 	headers := http.Header{}
 	headers.Add("Content-Type", "application/json")
@@ -74,6 +79,20 @@ func (s *SovrnAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapt
 			imp.BidFloor = sovrnExt.BidFloor
 		}
 
+		var impExtBuffer []byte
+		impExtBuffer, err = json.Marshal(&sovrnImpExt{
+			Bidder:     sovrnExt,
+			AdUnitCode: sovrnExt.AdUnitCode,
+		})
+		if err != nil {
+			errs = append(errs, &errortypes.BadInput{
+				Message: err.Error(),
+			})
+			continue
+		}
+
+		imp.Ext = impExtBuffer
+
 		// Validate video params if appropriate
 		video := imp.Video
 		if video != nil {
diff --git a/adapters/undertone/undertone.go b/adapters/undertone/undertone.go
index 62934fa5..7f8dde35 100644
--- a/adapters/undertone/undertone.go
+++ b/adapters/undertone/undertone.go
@@ -26,6 +26,11 @@ type undertoneParams struct {
 	Version string `json:"version"`
 }
 
+type impExt struct {
+	Bidder *openrtb_ext.ExtImpUndertone `json:"bidder,omitempty"`
+	Gpid   string                       `json:"gpid,omitempty"`
+}
+
 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
 	bidder := &adapter{
 		endpoint: config.Endpoint,
@@ -158,24 +163,29 @@ func getImpsAndPublisherId(bidRequest *openrtb2.BidRequest) ([]openrtb2.Imp, int
 	var validImps []openrtb2.Imp
 
 	for _, imp := range bidRequest.Imp {
-		var extImpBidder adapters.ExtImpBidder
-		if err := json.Unmarshal(imp.Ext, &extImpBidder); err != nil {
-			errs = append(errs, getInvalidImpErr(imp.ID, err))
-			continue
-		}
-
-		var extImpUndertone openrtb_ext.ExtImpUndertone
-		if err := json.Unmarshal(extImpBidder.Bidder, &extImpUndertone); err != nil {
+		var ext impExt
+		if err := json.Unmarshal(imp.Ext, &ext); err != nil {
 			errs = append(errs, getInvalidImpErr(imp.ID, err))
 			continue
 		}
 
 		if publisherId == 0 {
-			publisherId = extImpUndertone.PublisherID
+			publisherId = ext.Bidder.PublisherID
 		}
 
-		imp.TagID = strconv.Itoa(extImpUndertone.PlacementID)
+		imp.TagID = strconv.Itoa(ext.Bidder.PlacementID)
 		imp.Ext = nil
+
+		if ext.Gpid != "" {
+			ext.Bidder = nil
+			impExtJson, err := json.Marshal(&ext)
+			if err != nil {
+				errs = append(errs, getInvalidImpErr(imp.ID, err))
+				continue
+			}
+			imp.Ext = impExtJson
+		}
+
 		validImps = append(validImps, imp)
 	}
 
diff --git a/config/account.go b/config/account.go
index 02040211..e5265072 100644
--- a/config/account.go
+++ b/config/account.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/prebid/go-gdpr/consentconstants"
 	"github.com/prebid/prebid-server/openrtb_ext"
+	"github.com/prebid/prebid-server/util/iputil"
 )
 
 // ChannelType enumerates the values of integrations Prebid Server can configure for an account
@@ -40,7 +41,7 @@ type Account struct {
 	Validations             Validations                                 `mapstructure:"validations" json:"validations"`
 	DefaultBidLimit         int                                         `mapstructure:"default_bid_limit" json:"default_bid_limit"`
 	BidAdjustments          *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"`
-	Privacy                 *AccountPrivacy                             `mapstructure:"privacy" json:"privacy"`
+	Privacy                 AccountPrivacy                              `mapstructure:"privacy" json:"privacy"`
 }
 
 // CookieSync represents the account-level defaults for the cookie sync endpoint.
@@ -84,6 +85,10 @@ func (pf *AccountPriceFloors) validate(errs []error) []error {
 	return errs
 }
 
+func (pf *AccountPriceFloors) IsAdjustForBidAdjustmentEnabled() bool {
+	return pf.AdjustForBidAdjustment
+}
+
 // EnabledForChannelType indicates whether CCPA is turned on at the account level for the specified channel type
 // by using the channel type setting if defined or the general CCPA setting if defined; otherwise it returns nil
 func (a *AccountCCPA) EnabledForChannelType(channelType ChannelType) *bool {
@@ -295,5 +300,31 @@ func (a *AccountChannel) IsSet() bool {
 }
 
 type AccountPrivacy struct {
-	AllowActivities AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
+	AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"`
+	IPv6Config      IPv6             `mapstructure:"ipv6" json:"ipv6"`
+	IPv4Config      IPv4             `mapstructure:"ipv4" json:"ipv4"`
+}
+
+type IPv6 struct {
+	AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
+}
+
+type IPv4 struct {
+	AnonKeepBits int `mapstructure:"anon_keep_bits" json:"anon_keep_bits"`
+}
+
+func (ip *IPv6) Validate(errs []error) []error {
+	if ip.AnonKeepBits > iputil.IPv6BitSize || ip.AnonKeepBits < 0 {
+		err := fmt.Errorf("bits cannot exceed %d in ipv6 address, or be less than 0", iputil.IPv6BitSize)
+		errs = append(errs, err)
+	}
+	return errs
+}
+
+func (ip *IPv4) Validate(errs []error) []error {
+	if ip.AnonKeepBits > iputil.IPv4BitSize || ip.AnonKeepBits < 0 {
+		err := fmt.Errorf("bits cannot exceed %d in ipv4 address, or be less than 0", iputil.IPv4BitSize)
+		errs = append(errs, err)
+	}
+	return errs
 }
diff --git a/config/bidderinfo.go b/config/bidderinfo.go
index df9e9d5a..96d6fd15 100644
--- a/config/bidderinfo.go
+++ b/config/bidderinfo.go
@@ -241,17 +241,11 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 	for fileName, data := range bidderConfigs {
 		bidderName := strings.Split(fileName, ".")
 		if len(bidderName) == 2 && bidderName[1] == "yaml" {
-			normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
-			if !bidderNameExists {
-				return nil, fmt.Errorf("error parsing config for bidder %s: unknown bidder", fileName)
-			}
 			info := BidderInfo{}
 			if err := yaml.Unmarshal(data, &info); err != nil {
 				return nil, fmt.Errorf("error parsing config for bidder %s: %v", fileName, err)
 			}
 
-			bidderInfos[string(normalizedBidderName)] = info
-
 			//need to maintain nullable fields from BidderInfo struct into bidderInfoNullableFields
 			//to handle the default values in aliases yaml
 			if len(info.AliasOf) > 0 {
@@ -260,7 +254,25 @@ func processBidderInfos(reader InfoReader, normalizeBidderName func(string) (ope
 					return nil, fmt.Errorf("error parsing config for aliased bidder %s: %v", fileName, err)
 				}
 
+				//required for CoreBidderNames function to also return aliasBiddernames
+				if err := openrtb_ext.SetAliasBidderName(bidderName[0], openrtb_ext.BidderName(info.AliasOf)); err != nil {
+					return nil, err
+				}
+
+				normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
+				if !bidderNameExists {
+					return nil, fmt.Errorf("error parsing config for an alias %s: unknown bidder", fileName)
+				}
+
 				aliasNillableFieldsByBidder[string(normalizedBidderName)] = aliasFields
+				bidderInfos[string(normalizedBidderName)] = info
+			} else {
+				normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName[0])
+				if !bidderNameExists {
+					return nil, fmt.Errorf("error parsing config for bidder %s: unknown bidder", fileName)
+				}
+
+				bidderInfos[string(normalizedBidderName)] = info
 			}
 		}
 	}
@@ -276,8 +288,7 @@ func processBidderAliases(aliasNillableFieldsByBidder map[string]aliasNillableFi
 		if err := validateAliases(aliasBidderInfo, bidderInfos, bidderName); err != nil {
 			return nil, err
 		}
-		//required for CoreBidderNames function to also return aliasBiddernames
-		openrtb_ext.SetAliasBidderName(openrtb_ext.BidderName(string(bidderName)))
+
 		parentBidderInfo := bidderInfos[aliasBidderInfo.AliasOf]
 		if aliasBidderInfo.AppSecret == "" {
 			aliasBidderInfo.AppSecret = parentBidderInfo.AppSecret
diff --git a/config/config.go b/config/config.go
index 2b539c57..e564c34a 100644
--- a/config/config.go
+++ b/config/config.go
@@ -149,12 +149,11 @@ func (cfg *Configuration) validate(v *viper.Viper) []error {
 		errs = append(errs, errors.New("account_defaults.Events.VASTEvents has no effect as the feature is under development."))
 	}
 
-	if cfg.AccountDefaults.Privacy != nil {
-		glog.Warning("account_defaults.Privacy has no effect as the feature is under development.")
-	}
-
 	errs = cfg.Experiment.validate(errs)
 	errs = cfg.BidderInfos.validate(errs)
+	errs = cfg.AccountDefaults.Privacy.IPv6Config.Validate(errs)
+	errs = cfg.AccountDefaults.Privacy.IPv4Config.Validate(errs)
+
 	return errs
 }
 
@@ -1019,6 +1018,8 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) {
 	v.SetDefault("account_defaults.price_floors.max_rules", 100)
 	v.SetDefault("account_defaults.price_floors.max_schema_dims", 3)
 	v.SetDefault("account_defaults.events_enabled", false)
+	v.SetDefault("account_defaults.privacy.ipv6.anon_keep_bits", 56)
+	v.SetDefault("account_defaults.privacy.ipv4.anon_keep_bits", 24)
 
 	v.SetDefault("compression.response.enable_gzip", false)
 	v.SetDefault("compression.request.enable_gzip", false)
diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go
index 5caa543c..0321fea3 100644
--- a/endpoints/cookie_sync.go
+++ b/endpoints/cookie_sync.go
@@ -149,12 +149,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, pr
 		}
 	}
 
-	activityControl, activitiesErr := privacy.NewActivityControl(account.Privacy)
-	if activitiesErr != nil {
-		if errortypes.ContainsFatalError([]error{activitiesErr}) {
-			activityControl = privacy.ActivityControl{}
-		}
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	syncTypeFilter, err := parseTypeFilter(request.FilterSettings)
 	if err != nil {
diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go
index fb4b1928..2879610d 100644
--- a/endpoints/openrtb2/amp_auction.go
+++ b/endpoints/openrtb2/amp_auction.go
@@ -6,12 +6,13 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/privacy"
 	"net/http"
 	"net/url"
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/privacy"
+
 	"github.com/buger/jsonparser"
 	"github.com/golang/glog"
 	"github.com/julienschmidt/httprouter"
@@ -229,12 +230,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
-	if activitiesErr != nil {
-		errL = append(errL, activitiesErr)
-		writeError(errL, w, &labels)
-		return
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	secGPC := r.Header.Get("Sec-GPC")
 
@@ -253,7 +249,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h
 		HookExecutor:               hookExecutor,
 		QueryParams:                r.URL.Query(),
 		TCF2Config:                 tcf2Config,
-		Activities:                 activities,
+		Activities:                 activityControl,
 		TmaxAdjustments:            deps.tmaxAdjustments,
 	}
 
diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go
index 6ce2c323..aeb5c20e 100644
--- a/endpoints/openrtb2/auction.go
+++ b/endpoints/openrtb2/auction.go
@@ -6,7 +6,6 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"github.com/prebid/prebid-server/privacy"
 	"io"
 	"net/http"
 	"net/url"
@@ -15,6 +14,8 @@ import (
 	"strings"
 	"time"
 
+	"github.com/prebid/prebid-server/privacy"
+
 	"github.com/buger/jsonparser"
 	"github.com/gofrs/uuid"
 	"github.com/golang/glog"
@@ -193,14 +194,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 
 	tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR)
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
-	if activitiesErr != nil {
-		errL = append(errL, activitiesErr)
-		if errortypes.ContainsFatalError(errL) {
-			writeError(errL, w, &labels)
-			return
-		}
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	ctx := context.Background()
 
@@ -251,7 +245,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http
 		PubID:                      labels.PubID,
 		HookExecutor:               hookExecutor,
 		TCF2Config:                 tcf2Config,
-		Activities:                 activities,
+		Activities:                 activityControl,
 		TmaxAdjustments:            deps.tmaxAdjustments,
 	}
 	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil)
@@ -1966,8 +1960,12 @@ func setAuctionTypeImplicitly(r *openrtb_ext.RequestWrapper) {
 }
 
 func setSiteImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
+	if r.Site == nil {
+		r.Site = &openrtb2.Site{}
+	}
+
 	referrerCandidate := httpReq.Referer()
-	if referrerCandidate == "" && r.Site != nil && r.Site.Page != "" {
+	if referrerCandidate == "" && r.Site.Page != "" {
 		referrerCandidate = r.Site.Page // If http referer is disabled and thus has empty value - use site.page instead
 	}
 
@@ -1981,17 +1979,13 @@ func setSiteImplicitly(httpReq *http.Request, r *openrtb_ext.RequestWrapper) {
 		}
 	}
 
-	if r.Site != nil {
-		if siteExt, err := r.GetSiteExt(); err == nil && siteExt.GetAmp() == nil {
-			siteExt.SetAmp(&notAmp)
-		}
+	if siteExt, err := r.GetSiteExt(); err == nil && siteExt.GetAmp() == nil {
+		siteExt.SetAmp(&notAmp)
 	}
+
 }
 
 func setSitePageIfEmpty(site *openrtb2.Site, sitePage string) {
-	if site == nil {
-		site = &openrtb2.Site{}
-	}
 	if site.Page == "" {
 		site.Page = sitePage
 	}
diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
index d21ca4a9..8ec2273e 100644
--- a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
+++ b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json
@@ -19,5 +19,5 @@
     ]
   },
   "expectedReturnCode": 400,
-  "expectedErrorMessage": "Invalid request: request.site or request.app must be defined, but not both.\n"
+  "expectedErrorMessage": "Invalid request: request.site should include at least one of request.site.id or request.site.page.\n"
 }
diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go
index 8fc2f844..a2a519fa 100644
--- a/endpoints/openrtb2/video_auction.go
+++ b/endpoints/openrtb2/video_auction.go
@@ -303,14 +303,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		return
 	}
 
-	activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
-	if activitiesErr != nil {
-		errL = append(errL, activitiesErr)
-		if errortypes.ContainsFatalError(errL) {
-			handleError(&labels, w, errL, &vo, &debugLog)
-			return
-		}
-	}
+	activityControl := privacy.NewActivityControl(&account.Privacy)
 
 	secGPC := r.Header.Get("Sec-GPC")
 	auctionRequest := &exchange.AuctionRequest{
@@ -324,7 +317,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re
 		PubID:                      labels.PubID,
 		HookExecutor:               hookexecution.EmptyHookExecutor{},
 		TmaxAdjustments:            deps.tmaxAdjustments,
-		Activities:                 activities,
+		Activities:                 activityControl,
 	}
 
 	auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, &debugLog)
diff --git a/endpoints/setuid.go b/endpoints/setuid.go
index 561989e0..b9023cfc 100644
--- a/endpoints/setuid.go
+++ b/endpoints/setuid.go
@@ -104,14 +104,9 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use
 			return
 		}
 
-		activities, activitiesErr := privacy.NewActivityControl(account.Privacy)
-		if activitiesErr != nil {
-			if errortypes.ContainsFatalError([]error{activitiesErr}) {
-				activities = privacy.ActivityControl{}
-			}
-		}
+		activityControl := privacy.NewActivityControl(&account.Privacy)
 
-		userSyncActivityAllowed := activities.Allow(privacy.ActivitySyncUser,
+		userSyncActivityAllowed := activityControl.Allow(privacy.ActivitySyncUser,
 			privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderName})
 		if !userSyncActivityAllowed {
 			w.WriteHeader(http.StatusUnavailableForLegalReasons)
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 84b1f1af..c5e9dc49 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -230,6 +230,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderApacdex:           apacdex.Builder,
 		openrtb_ext.BidderApplogy:           applogy.Builder,
 		openrtb_ext.BidderAppnexus:          appnexus.Builder,
+		openrtb_ext.BidderAppstock:          limelightDigital.Builder,
 		openrtb_ext.BidderAppush:            appush.Builder,
 		openrtb_ext.BidderAudienceNetwork:   audienceNetwork.Builder,
 		openrtb_ext.BidderAutomatad:         automatad.Builder,
@@ -286,6 +287,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
 		openrtb_ext.BidderGamoshi:           gamoshi.Builder,
 		openrtb_ext.BidderGlobalsun:         globalsun.Builder,
 		openrtb_ext.BidderGothamads:         gothamads.Builder,
+		openrtb_ext.BidderGreedygame:        limelightDigital.Builder,
 		openrtb_ext.BidderGrid:              grid.Builder,
 		openrtb_ext.BidderGumGum:            gumgum.Builder,
 		openrtb_ext.BidderHuaweiAds:         huaweiads.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 70d92c08..136f1040 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -319,7 +319,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
 		Prebid: *requestExtPrebid,
 		SChain: requestExt.GetSChain(),
 	}
-	bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprDefaultValue)
+	bidderRequests, privacyLabels, errs := e.requestSplitter.cleanOpenRTBRequests(ctx, *r, requestExtLegacy, gdprDefaultValue, bidAdjustmentFactors)
 	errs = append(errs, floorErrs...)
 
 	mergedBidAdj, err := bidadjustment.Merge(r.BidRequestWrapper, r.Account.BidAdjustments)
diff --git a/exchange/utils.go b/exchange/utils.go
index baedcf78..5731c9e4 100644
--- a/exchange/utils.go
+++ b/exchange/utils.go
@@ -54,7 +54,7 @@ type requestSplitter struct {
 func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 	auctionReq AuctionRequest,
 	requestExt *openrtb_ext.ExtRequest,
-	gdprDefaultValue gdpr.Signal,
+	gdprDefaultValue gdpr.Signal, bidAdjustmentFactors map[string]float64,
 ) (allowedBidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) {
 
 	req := auctionReq.BidRequestWrapper
@@ -93,6 +93,11 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 		}
 	}
 
+	if auctionReq.Account.PriceFloors.IsAdjustForBidAdjustmentEnabled() {
+		//Apply BidAdjustmentFactor to imp.BidFloor
+		applyBidAdjustmentToFloor(allBidderRequests, bidAdjustmentFactors)
+	}
+
 	gdprSignal, err := getGDPR(req)
 	if err != nil {
 		errs = append(errs, err)
@@ -214,7 +219,7 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context,
 
 		privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTids, scopedName)
 
-		privacyEnforcement.Apply(bidderRequest.BidRequest)
+		privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy)
 		allowedBidderRequests = append(allowedBidderRequests, bidderRequest)
 
 		// GPP downgrade: always downgrade unless we can confirm GPP is supported
@@ -1071,3 +1076,25 @@ func getPrebidMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
 		Message: errMsg,
 	}
 }
+
+func applyBidAdjustmentToFloor(allBidderRequests []BidderRequest, bidAdjustmentFactors map[string]float64) {
+
+	if len(bidAdjustmentFactors) == 0 {
+		return
+	}
+
+	for _, bidderRequest := range allBidderRequests {
+		bidAdjustment := 1.0
+
+		if bidAdjustemntValue, ok := bidAdjustmentFactors[string(bidderRequest.BidderName)]; ok {
+			bidAdjustment = bidAdjustemntValue
+		}
+
+		if bidAdjustment != 1.0 {
+			for index, imp := range bidderRequest.BidRequest.Imp {
+				imp.BidFloor = imp.BidFloor / bidAdjustment
+				bidderRequest.BidRequest.Imp[index] = imp
+			}
+		}
+	}
+}
diff --git a/floors/enforce.go b/floors/enforce.go
index 1f50dd2b..9318c9d2 100644
--- a/floors/enforce.go
+++ b/floors/enforce.go
@@ -137,7 +137,7 @@ func enforceFloorToBids(bidRequestWrapper *openrtb_ext.RequestWrapper, seatBids
 				}
 
 				bidPrice := rate * bid.Bid.Price
-				if reqImp.BidFloor > bidPrice {
+				if (bidPrice + floorPrecision) < reqImp.BidFloor {
 					rejectedBid := &entities.PbsOrtbSeatBid{
 						Currency: seatBid.Currency,
 						Seat:     seatBid.Seat,
diff --git a/floors/floors.go b/floors/floors.go
index f9e13ec0..52591915 100644
--- a/floors/floors.go
+++ b/floors/floors.go
@@ -16,15 +16,16 @@ type Price struct {
 }
 
 const (
-	defaultCurrency  string = "USD"
-	defaultDelimiter string = "|"
-	catchAll         string = "*"
-	skipRateMin      int    = 0
-	skipRateMax      int    = 100
-	modelWeightMax   int    = 100
-	modelWeightMin   int    = 1
-	enforceRateMin   int    = 0
-	enforceRateMax   int    = 100
+	defaultCurrency  string  = "USD"
+	defaultDelimiter string  = "|"
+	catchAll         string  = "*"
+	skipRateMin      int     = 0
+	skipRateMax      int     = 100
+	modelWeightMax   int     = 100
+	modelWeightMin   int     = 1
+	enforceRateMin   int     = 0
+	enforceRateMax   int     = 100
+	floorPrecision   float64 = 0.01
 )
 
 // EnrichWithPriceFloors checks for floors enabled in account and request and selects floors data from dynamic fetched if present
diff --git a/hooks/hookexecution/executor.go b/hooks/hookexecution/executor.go
index ce516630..5074d4b9 100644
--- a/hooks/hookexecution/executor.go
+++ b/hooks/hookexecution/executor.go
@@ -160,7 +160,7 @@ func (e *hookExecutor) ExecuteProcessedAuctionStage(request *openrtb_ext.Request
 
 	stageName := hooks.StageProcessedAuctionRequest.String()
 	executionCtx := e.newContext(stageName)
-	payload := hookstage.ProcessedAuctionRequestPayload{BidRequest: request.BidRequest}
+	payload := hookstage.ProcessedAuctionRequestPayload{RequestWrapper: request}
 
 	outcome, _, contexts, reject := executeStage(executionCtx, plan, payload, handler, e.metricEngine)
 	outcome.Entity = entityAuctionRequest
diff --git a/hooks/hookstage/processedauctionrequest.go b/hooks/hookstage/processedauctionrequest.go
index 30cb0dab..fe06bc6f 100644
--- a/hooks/hookstage/processedauctionrequest.go
+++ b/hooks/hookstage/processedauctionrequest.go
@@ -3,7 +3,7 @@ package hookstage
 import (
 	"context"
 
-	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/openrtb_ext"
 )
 
 // ProcessedAuctionRequest hooks are invoked after the request is parsed
@@ -23,8 +23,8 @@ type ProcessedAuctionRequest interface {
 	) (HookResult[ProcessedAuctionRequestPayload], error)
 }
 
-// ProcessedAuctionRequestPayload consists of the openrtb2.BidRequest object.
-// Hooks are allowed to modify openrtb2.BidRequest using mutations.
+// ProcessedAuctionRequestPayload consists of the openrtb_ext.RequestWrapper object.
+// Hooks are allowed to modify openrtb_ext.RequestWrapper using mutations.
 type ProcessedAuctionRequestPayload struct {
-	BidRequest *openrtb2.BidRequest
+	RequestWrapper *openrtb_ext.RequestWrapper
 }
diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go
index 7dc620d4..2e190389 100644
--- a/openrtb_ext/bid.go
+++ b/openrtb_ext/bid.go
@@ -66,6 +66,8 @@ type ExtBidPrebidMeta struct {
 	PrimaryCategoryID    string          `json:"primaryCatId,omitempty"`
 	RendererName         string          `json:"rendererName,omitempty"`
 	RendererVersion      string          `json:"rendererVersion,omitempty"`
+	RendererData         json.RawMessage `json:"rendererData,omitempty"`
+	RendererUrl          string          `json:"rendererUrl,omitempty"`
 	SecondaryCategoryIDs []string        `json:"secondaryCatIds,omitempty"`
 }
 
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index c60b9dca..11f01988 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -15,7 +15,7 @@ import (
 // BidderName refers to a core bidder id or an alias id.
 type BidderName string
 
-var aliasBidderNames []BidderName
+var aliasBidderToParent map[BidderName]BidderName = map[BidderName]BidderName{}
 
 var coreBidderNames []BidderName = []BidderName{
 	Bidder33Across,
@@ -54,6 +54,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderApacdex,
 	BidderApplogy,
 	BidderAppnexus,
+	BidderAppstock,
 	BidderAppush,
 	BidderAudienceNetwork,
 	BidderAutomatad,
@@ -110,6 +111,7 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderGamoshi,
 	BidderGlobalsun,
 	BidderGothamads,
+	BidderGreedygame,
 	BidderGrid,
 	BidderGumGum,
 	BidderHuaweiAds,
@@ -224,17 +226,18 @@ var coreBidderNames []BidderName = []BidderName{
 	BidderZetaGlobalSsp,
 }
 
-func SetAliasBidderName(name BidderName) {
-	aliasBidderNames = append(aliasBidderNames, name)
-	coreBidderNames = append(coreBidderNames, name)
+func GetAliasBidderToParent() map[BidderName]BidderName {
+	return aliasBidderToParent
 }
 
-func GetAliasBidderNamesSet() map[BidderName]struct{} {
-	set := map[BidderName]struct{}{}
-	for _, name := range aliasBidderNames {
-		set[name] = struct{}{}
+func SetAliasBidderName(aliasBidderName string, parentBidderName BidderName) error {
+	if IsBidderNameReserved(aliasBidderName) {
+		return fmt.Errorf("alias %s is a reserved bidder name and cannot be used", aliasBidderName)
 	}
-	return set
+	aliasBidder := BidderName(aliasBidderName)
+	coreBidderNames = append(coreBidderNames, aliasBidder)
+	aliasBidderToParent[aliasBidder] = parentBidderName
+	return nil
 }
 
 func (name BidderName) MarshalJSON() ([]byte, error) {
@@ -344,6 +347,7 @@ const (
 	BidderApacdex           BidderName = "apacdex"
 	BidderApplogy           BidderName = "applogy"
 	BidderAppnexus          BidderName = "appnexus"
+	BidderAppstock          BidderName = "appstock"
 	BidderAppush            BidderName = "appush"
 	BidderAudienceNetwork   BidderName = "audienceNetwork"
 	BidderAutomatad         BidderName = "automatad"
@@ -400,6 +404,7 @@ const (
 	BidderGamoshi           BidderName = "gamoshi"
 	BidderGlobalsun         BidderName = "globalsun"
 	BidderGothamads         BidderName = "gothamads"
+	BidderGreedygame        BidderName = "greedygame"
 	BidderGrid              BidderName = "grid"
 	BidderGumGum            BidderName = "gumgum"
 	BidderHuaweiAds         BidderName = "huaweiads"
@@ -555,11 +560,11 @@ var bidderNameLookup = func() map[string]BidderName {
 		lookup[bidderNameLower] = name
 	}
 	return lookup
-}()
+}
 
 func NormalizeBidderName(name string) (BidderName, bool) {
 	nameLower := strings.ToLower(name)
-	bidderName, exists := bidderNameLookup[nameLower]
+	bidderName, exists := bidderNameLookup()[nameLower]
 	return bidderName, exists
 }
 
@@ -572,10 +577,42 @@ type BidderParamValidator interface {
 	Schema(name BidderName) string
 }
 
+type bidderParamsFileSystem interface {
+	readDir(name string) ([]os.DirEntry, error)
+	readFile(name string) ([]byte, error)
+	newReferenceLoader(source string) gojsonschema.JSONLoader
+	newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error)
+	abs(path string) (string, error)
+}
+
+type standardBidderParamsFileSystem struct{}
+
+func (standardBidderParamsFileSystem) readDir(name string) ([]os.DirEntry, error) {
+	return os.ReadDir(name)
+}
+
+func (standardBidderParamsFileSystem) readFile(name string) ([]byte, error) {
+	return os.ReadFile(name)
+}
+
+func (standardBidderParamsFileSystem) newReferenceLoader(source string) gojsonschema.JSONLoader {
+	return gojsonschema.NewReferenceLoader(source)
+}
+
+func (standardBidderParamsFileSystem) newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error) {
+	return gojsonschema.NewSchema(l)
+}
+
+func (standardBidderParamsFileSystem) abs(path string) (string, error) {
+	return filepath.Abs(path)
+}
+
+var paramsValidator bidderParamsFileSystem = standardBidderParamsFileSystem{}
+
 // NewBidderParamsValidator makes a BidderParamValidator, assuming all the necessary files exist in the filesystem.
 // This will error if, for example, a Bidder gets added but no JSON schema is written for them.
 func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, error) {
-	fileInfos, err := os.ReadDir(schemaDirectory)
+	fileInfos, err := paramsValidator.readDir(schemaDirectory)
 	if err != nil {
 		return nil, fmt.Errorf("Failed to read JSON schemas from directory %s. %v", schemaDirectory, err)
 	}
@@ -589,17 +626,17 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err
 		if _, ok := bidderMap[bidderName]; !ok {
 			return nil, fmt.Errorf("File %s/%s does not match a valid BidderName.", schemaDirectory, fileInfo.Name())
 		}
-		toOpen, err := filepath.Abs(filepath.Join(schemaDirectory, fileInfo.Name()))
+		toOpen, err := paramsValidator.abs(filepath.Join(schemaDirectory, fileInfo.Name()))
 		if err != nil {
 			return nil, fmt.Errorf("Failed to get an absolute representation of the path: %s, %v", toOpen, err)
 		}
-		schemaLoader := gojsonschema.NewReferenceLoader("file:///" + filepath.ToSlash(toOpen))
-		loadedSchema, err := gojsonschema.NewSchema(schemaLoader)
+		schemaLoader := paramsValidator.newReferenceLoader("file:///" + filepath.ToSlash(toOpen))
+		loadedSchema, err := paramsValidator.newSchema(schemaLoader)
 		if err != nil {
 			return nil, fmt.Errorf("Failed to load json schema at %s: %v", toOpen, err)
 		}
 
-		fileBytes, err := os.ReadFile(fmt.Sprintf("%s/%s", schemaDirectory, fileInfo.Name()))
+		fileBytes, err := paramsValidator.readFile(fmt.Sprintf("%s/%s", schemaDirectory, fileInfo.Name()))
 		if err != nil {
 			return nil, fmt.Errorf("Failed to read file %s/%s: %v", schemaDirectory, fileInfo.Name(), err)
 		}
@@ -608,6 +645,15 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err
 		schemaContents[BidderName(bidderName)] = string(fileBytes)
 	}
 
+	//set alias bidder params schema to its parent
+	for alias, parent := range aliasBidderToParent {
+		parentSchema := schemas[parent]
+		schemas[alias] = parentSchema
+
+		parentSchemaContents := schemaContents[parent]
+		schemaContents[alias] = parentSchemaContents
+	}
+
 	return &bidderParamValidator{
 		schemaContents: schemaContents,
 		parsedSchemas:  schemas,
diff --git a/openrtb_ext/imp_datablocks.go b/openrtb_ext/imp_datablocks.go
index c8659d3f..dc3bae3f 100644
--- a/openrtb_ext/imp_datablocks.go
+++ b/openrtb_ext/imp_datablocks.go
@@ -2,6 +2,5 @@ package openrtb_ext
 
 // ExtImpDatablocks defines the contract for bidrequest.imp[i].ext.prebid.bidder.datablocks
 type ExtImpDatablocks struct {
-	SourceId int    `json:"sourceId"`
-	Host     string `json:"host"`
+	SourceId int `json:"sourceId"`
 }
diff --git a/openrtb_ext/imp_sovrn.go b/openrtb_ext/imp_sovrn.go
index 38d2f785..362463f9 100644
--- a/openrtb_ext/imp_sovrn.go
+++ b/openrtb_ext/imp_sovrn.go
@@ -1,7 +1,8 @@
 package openrtb_ext
 
 type ExtImpSovrn struct {
-	TagId    string  `json:"tagId,omitempty"`
-	Tagid    string  `json:"tagid,omitempty"`
-	BidFloor float64 `json:"bidfloor"`
+	TagId      string  `json:"tagId,omitempty"`
+	Tagid      string  `json:"tagid,omitempty"`
+	BidFloor   float64 `json:"bidfloor,omitempty"`
+	AdUnitCode string  `json:"adunitcode,omitempty"`
 }
diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go
index 6f39f0be..f5418ba4 100644
--- a/openrtb_ext/request.go
+++ b/openrtb_ext/request.go
@@ -352,8 +352,9 @@ type ExtRequestSdk struct {
 }
 
 type ExtRequestSdkRenderer struct {
-	Name    string `json:"name,omitempty"`
-	Version string `json:"version,omitempty"`
+	Name    string          `json:"name,omitempty"`
+	Version string          `json:"version,omitempty"`
+	Data    json.RawMessage `json:"data,omitempty"`
 }
 
 type ExtMultiBid struct {
diff --git a/privacy/activitycontrol.go b/privacy/activitycontrol.go
index fee5c650..33c138f7 100644
--- a/privacy/activitycontrol.go
+++ b/privacy/activitycontrol.go
@@ -18,66 +18,35 @@ type ActivityControl struct {
 	plans map[Activity]ActivityPlan
 }
 
-func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, error) {
+func NewActivityControl(cfg *config.AccountPrivacy) ActivityControl {
 	ac := ActivityControl{}
-	var err error
 
-	if privacyConf == nil {
-		return ac, nil
-	}
-
-	plans := make(map[Activity]ActivityPlan)
-
-	plans[ActivitySyncUser], err = buildEnforcementPlan(privacyConf.AllowActivities.SyncUser)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityFetchBids], err = buildEnforcementPlan(privacyConf.AllowActivities.FetchBids)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityEnrichUserFPD], err = buildEnforcementPlan(privacyConf.AllowActivities.EnrichUserFPD)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityReportAnalytics], err = buildEnforcementPlan(privacyConf.AllowActivities.ReportAnalytics)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitUserFPD], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitUserFPD)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitPreciseGeo], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitPreciseGeo)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitUniqueRequestIds], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitUniqueRequestIds)
-	if err != nil {
-		return ac, err
-	}
-	plans[ActivityTransmitTids], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitTids)
-	if err != nil {
-		return ac, err
+	if cfg == nil || cfg.AllowActivities == nil {
+		return ac
 	}
 
+	plans := make(map[Activity]ActivityPlan, 8)
+	plans[ActivitySyncUser] = buildPlan(cfg.AllowActivities.SyncUser)
+	plans[ActivityFetchBids] = buildPlan(cfg.AllowActivities.FetchBids)
+	plans[ActivityEnrichUserFPD] = buildPlan(cfg.AllowActivities.EnrichUserFPD)
+	plans[ActivityReportAnalytics] = buildPlan(cfg.AllowActivities.ReportAnalytics)
+	plans[ActivityTransmitUserFPD] = buildPlan(cfg.AllowActivities.TransmitUserFPD)
+	plans[ActivityTransmitPreciseGeo] = buildPlan(cfg.AllowActivities.TransmitPreciseGeo)
+	plans[ActivityTransmitUniqueRequestIds] = buildPlan(cfg.AllowActivities.TransmitUniqueRequestIds)
+	plans[ActivityTransmitTids] = buildPlan(cfg.AllowActivities.TransmitTids)
 	ac.plans = plans
 
-	return ac, nil
+	return ac
 }
 
-func buildEnforcementPlan(activity config.Activity) (ActivityPlan, error) {
-	ef := ActivityPlan{}
-	rules, err := activityRulesToEnforcementRules(activity.Rules)
-	if err != nil {
-		return ef, err
+func buildPlan(activity config.Activity) ActivityPlan {
+	return ActivityPlan{
+		rules:         cfgToRules(activity.Rules),
+		defaultResult: cfgToDefaultResult(activity.Default),
 	}
-	ef.defaultResult = activityDefaultToDefaultResult(activity.Default)
-	ef.rules = rules
-	return ef, nil
 }
 
-func activityRulesToEnforcementRules(rules []config.ActivityRule) ([]Rule, error) {
+func cfgToRules(rules []config.ActivityRule) []Rule {
 	var enfRules []Rule
 
 	for _, r := range rules {
@@ -88,34 +57,17 @@ func activityRulesToEnforcementRules(rules []config.ActivityRule) ([]Rule, error
 			result = ActivityDeny
 		}
 
-		componentName, err := conditionToRuleComponentNames(r.Condition.ComponentName)
-		if err != nil {
-			return nil, err
-		}
-
-		er := ComponentEnforcementRule{
+		er := ConditionRule{
 			result:        result,
-			componentName: componentName,
+			componentName: r.Condition.ComponentName,
 			componentType: r.Condition.ComponentType,
 		}
 		enfRules = append(enfRules, er)
 	}
-	return enfRules, nil
-}
-
-func conditionToRuleComponentNames(conditions []string) ([]Component, error) {
-	sn := make([]Component, 0)
-	for _, condition := range conditions {
-		scope, err := ParseComponent(condition)
-		if err != nil {
-			return nil, err
-		}
-		sn = append(sn, scope)
-	}
-	return sn, nil
+	return enfRules
 }
 
-func activityDefaultToDefaultResult(activityDefault *bool) bool {
+func cfgToDefaultResult(activityDefault *bool) bool {
 	if activityDefault == nil {
 		return defaultActivityResult
 	}
diff --git a/privacy/component.go b/privacy/component.go
index a203c6bd..33767938 100644
--- a/privacy/component.go
+++ b/privacy/component.go
@@ -1,8 +1,6 @@
 package privacy
 
 import (
-	"errors"
-	"fmt"
 	"strings"
 )
 
@@ -18,48 +16,10 @@ type Component struct {
 	Name string
 }
 
-func (c Component) Matches(target Component) bool {
-	return strings.EqualFold(c.Type, target.Type) && (c.Name == "*" || strings.EqualFold(c.Name, target.Name))
+func (c Component) MatchesName(v string) bool {
+	return strings.EqualFold(c.Name, v)
 }
 
-var ErrComponentEmpty = errors.New("unable to parse empty component")
-
-func ParseComponent(v string) (Component, error) {
-	if len(v) == 0 {
-		return Component{}, ErrComponentEmpty
-	}
-
-	split := strings.Split(v, ".")
-
-	if len(split) == 2 {
-		if !validComponentType(split[0]) {
-			return Component{}, fmt.Errorf("unable to parse component (invalid type): %s", v)
-		}
-		return Component{
-			Type: split[0],
-			Name: split[1],
-		}, nil
-	}
-
-	if len(split) == 1 {
-		return Component{
-			Type: ComponentTypeBidder,
-			Name: split[0],
-		}, nil
-	}
-
-	return Component{}, fmt.Errorf("unable to parse component: %s", v)
-}
-
-func validComponentType(t string) bool {
-	t = strings.ToLower(t)
-
-	if t == ComponentTypeBidder ||
-		t == ComponentTypeAnalytics ||
-		t == ComponentTypeRealTimeData ||
-		t == ComponentTypeGeneral {
-		return true
-	}
-
-	return false
+func (c Component) MatchesType(v string) bool {
+	return strings.EqualFold(c.Type, v)
 }
diff --git a/privacy/enforcement.go b/privacy/enforcement.go
index 1a66cfab..8074d96a 100644
--- a/privacy/enforcement.go
+++ b/privacy/enforcement.go
@@ -1,6 +1,9 @@
 package privacy
 
-import "github.com/prebid/openrtb/v19/openrtb2"
+import (
+	"github.com/prebid/openrtb/v19/openrtb2"
+	"github.com/prebid/prebid-server/config"
+)
 
 // Enforcement represents the privacy policies to enforce for an OpenRTB bid request.
 type Enforcement struct {
@@ -27,8 +30,8 @@ func (e Enforcement) AnyActivities() bool {
 }
 
 // Apply cleans personally identifiable information from an OpenRTB bid request.
-func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest) {
-	e.apply(bidRequest, NewScrubber())
+func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest, privacy config.AccountPrivacy) {
+	e.apply(bidRequest, NewScrubber(privacy.IPv6Config, privacy.IPv4Config))
 }
 
 func (e Enforcement) apply(bidRequest *openrtb2.BidRequest, scrubber Scrubber) {
@@ -55,21 +58,16 @@ func (e Enforcement) getDeviceIDScrubStrategy() ScrubStrategyDeviceID {
 
 func (e Enforcement) getIPv4ScrubStrategy() ScrubStrategyIPV4 {
 	if e.COPPA || e.GDPRGeo || e.CCPA || e.LMT {
-		return ScrubStrategyIPV4Lowest8
+		return ScrubStrategyIPV4Subnet
 	}
 
 	return ScrubStrategyIPV4None
 }
 
 func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 {
-	if e.COPPA {
-		return ScrubStrategyIPV6Lowest32
+	if e.GDPRGeo || e.CCPA || e.LMT || e.COPPA {
+		return ScrubStrategyIPV6Subnet
 	}
-
-	if e.GDPRGeo || e.CCPA || e.LMT {
-		return ScrubStrategyIPV6Lowest16
-	}
-
 	return ScrubStrategyIPV6None
 }
 
diff --git a/privacy/rule.go b/privacy/rule.go
new file mode 100644
index 00000000..47d0913c
--- /dev/null
+++ b/privacy/rule.go
@@ -0,0 +1,5 @@
+package privacy
+
+type Rule interface {
+	Evaluate(target Component) ActivityResult
+}
diff --git a/privacy/rules.go b/privacy/rule_condition.go
similarity index 64%
rename from privacy/rules.go
rename to privacy/rule_condition.go
index 502e10ef..705543e5 100644
--- a/privacy/rules.go
+++ b/privacy/rule_condition.go
@@ -1,20 +1,12 @@
 package privacy
 
-import (
-	"strings"
-)
-
-type Rule interface {
-	Evaluate(target Component) ActivityResult
-}
-
-type ComponentEnforcementRule struct {
+type ConditionRule struct {
 	result        ActivityResult
-	componentName []Component
+	componentName []string
 	componentType []string
 }
 
-func (r ComponentEnforcementRule) Evaluate(target Component) ActivityResult {
+func (r ConditionRule) Evaluate(target Component) ActivityResult {
 	if matched := evaluateComponentName(target, r.componentName); !matched {
 		return ActivityAbstain
 	}
@@ -26,7 +18,7 @@ func (r ComponentEnforcementRule) Evaluate(target Component) ActivityResult {
 	return r.result
 }
 
-func evaluateComponentName(target Component, componentNames []Component) bool {
+func evaluateComponentName(target Component, componentNames []string) bool {
 	// no clauses are considered a match
 	if len(componentNames) == 0 {
 		return true
@@ -34,7 +26,7 @@ func evaluateComponentName(target Component, componentNames []Component) bool {
 
 	// if there are clauses, at least one needs to match
 	for _, n := range componentNames {
-		if n.Matches(target) {
+		if target.MatchesName(n) {
 			return true
 		}
 	}
@@ -49,8 +41,8 @@ func evaluateComponentType(target Component, componentTypes []string) bool {
 	}
 
 	// if there are clauses, at least one needs to match
-	for _, s := range componentTypes {
-		if strings.EqualFold(s, target.Type) {
+	for _, t := range componentTypes {
+		if target.MatchesType(t) {
 			return true
 		}
 	}
diff --git a/privacy/scrubber.go b/privacy/scrubber.go
index f2fb790d..4cc90c82 100644
--- a/privacy/scrubber.go
+++ b/privacy/scrubber.go
@@ -2,8 +2,10 @@ package privacy
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/config"
+	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
-	"strings"
+	"net"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
 )
@@ -15,8 +17,8 @@ const (
 	// ScrubStrategyIPV4None does not remove any part of an IPV4 address.
 	ScrubStrategyIPV4None ScrubStrategyIPV4 = iota
 
-	// ScrubStrategyIPV4Lowest8 zeroes out the last 8 bits of an IPV4 address.
-	ScrubStrategyIPV4Lowest8
+	// ScrubStrategyIPV4Subnet zeroes out the last 8 bits of an IPV4 address.
+	ScrubStrategyIPV4Subnet
 )
 
 // ScrubStrategyIPV6 defines the approach to scrub PII from an IPV6 address.
@@ -26,11 +28,8 @@ const (
 	// ScrubStrategyIPV6None does not remove any part of an IPV6 address.
 	ScrubStrategyIPV6None ScrubStrategyIPV6 = iota
 
-	// ScrubStrategyIPV6Lowest16 zeroes out the last 16 bits of an IPV6 address.
-	ScrubStrategyIPV6Lowest16
-
-	// ScrubStrategyIPV6Lowest32 zeroes out the last 32 bits of an IPV6 address.
-	ScrubStrategyIPV6Lowest32
+	// ScrubStrategyIPV6Subnet zeroes out the last 16 bits of an IPV6 sub net address.
+	ScrubStrategyIPV6Subnet
 )
 
 // ScrubStrategyGeo defines the approach to scrub PII from geographical data.
@@ -76,14 +75,20 @@ type Scrubber interface {
 	ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User
 }
 
-type scrubber struct{}
+type scrubber struct {
+	ipV6 config.IPv6
+	ipV4 config.IPv4
+}
 
 // NewScrubber returns an OpenRTB scrubber.
-func NewScrubber() Scrubber {
-	return scrubber{}
+func NewScrubber(ipV6 config.IPv6, ipV4 config.IPv4) Scrubber {
+	return scrubber{
+		ipV6: ipV6,
+		ipV4: ipV4,
+	}
 }
 
-func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
+func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest {
 	var userExtParsed map[string]json.RawMessage
 	userExtModified := false
 
@@ -169,8 +174,8 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc
 			if deviceCopy.Geo != nil {
 				deviceCopy.Geo = scrubGeoPrecision(deviceCopy.Geo)
 			}
-			deviceCopy.IP = scrubIPV4Lowest8(deviceCopy.IP)
-			deviceCopy.IPv6 = scrubIPV6Lowest32Bits(deviceCopy.IPv6)
+			deviceCopy.IP = scrubIP(deviceCopy.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize)
+			deviceCopy.IPv6 = scrubIP(deviceCopy.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize)
 		}
 	}
 
@@ -179,7 +184,7 @@ func (scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforc
 	return bidRequest
 }
 
-func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
+func (s scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device {
 	if device == nil {
 		return nil
 	}
@@ -198,15 +203,13 @@ func (scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, i
 	}
 
 	switch ipv4 {
-	case ScrubStrategyIPV4Lowest8:
-		deviceCopy.IP = scrubIPV4Lowest8(device.IP)
+	case ScrubStrategyIPV4Subnet:
+		deviceCopy.IP = scrubIP(device.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize)
 	}
 
 	switch ipv6 {
-	case ScrubStrategyIPV6Lowest16:
-		deviceCopy.IPv6 = scrubIPV6Lowest16Bits(device.IPv6)
-	case ScrubStrategyIPV6Lowest32:
-		deviceCopy.IPv6 = scrubIPV6Lowest32Bits(device.IPv6)
+	case ScrubStrategyIPV6Subnet:
+		deviceCopy.IPv6 = scrubIP(device.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize)
 	}
 
 	switch geo {
@@ -244,44 +247,13 @@ func (scrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo S
 	return &userCopy
 }
 
-func scrubIPV4Lowest8(ip string) string {
-	i := strings.LastIndex(ip, ".")
-	if i == -1 {
-		return ""
-	}
-
-	return ip[0:i] + ".0"
-}
-
-func scrubIPV6Lowest16Bits(ip string) string {
-	ip = removeLowestIPV6Segment(ip)
-
-	if ip != "" {
-		ip += ":0"
-	}
-
-	return ip
-}
-
-func scrubIPV6Lowest32Bits(ip string) string {
-	ip = removeLowestIPV6Segment(ip)
-	ip = removeLowestIPV6Segment(ip)
-
-	if ip != "" {
-		ip += ":0:0"
-	}
-
-	return ip
-}
-
-func removeLowestIPV6Segment(ip string) string {
-	i := strings.LastIndex(ip, ":")
-
-	if i == -1 {
+func scrubIP(ip string, ones, bits int) string {
+	if ip == "" {
 		return ""
 	}
-
-	return ip[0:i]
+	ipMask := net.CIDRMask(ones, bits)
+	ipMasked := net.ParseIP(ip).Mask(ipMask)
+	return ipMasked.String()
 }
 
 func scrubGeoFull(geo *openrtb2.Geo) *openrtb2.Geo {
diff --git a/router/router.go b/router/router.go
index fcf674b4..f7270445 100644
--- a/router/router.go
+++ b/router/router.go
@@ -55,6 +55,10 @@ import (
 // This function stores the file contents in memory, and should not be used on large directories.
 // If the root directory, or any of the files in it, cannot be read, then the program will exit.
 func NewJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.BidderParamValidator, aliases map[string]string) httprouter.Handle {
+	return newJsonDirectoryServer(schemaDirectory, validator, aliases, openrtb_ext.GetAliasBidderToParent())
+}
+
+func newJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.BidderParamValidator, aliases map[string]string, yamlAliases map[openrtb_ext.BidderName]openrtb_ext.BidderName) httprouter.Handle {
 	// Slurp the files into memory first, since they're small and it minimizes request latency.
 	files, err := os.ReadDir(schemaDirectory)
 	if err != nil {
@@ -73,6 +77,11 @@ func NewJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.Bidder
 		data[bidder] = json.RawMessage(validator.Schema(bidderName))
 	}
 
+	// Add in any aliases
+	for aliasName, parentBidder := range yamlAliases {
+		data[string(aliasName)] = json.RawMessage(validator.Schema(parentBidder))
+	}
+
 	// Add in any default aliases
 	for aliasName, bidderName := range aliases {
 		bidderData, ok := data[bidderName]
diff --git a/static/bidder-info/appstock.yaml b/static/bidder-info/appstock.yaml
new file mode 100644
index 00000000..28f420e9
--- /dev/null
+++ b/static/bidder-info/appstock.yaml
@@ -0,0 +1,16 @@
+endpoint: "http://ads-pbs.pre.vr-tb.com/openrtb/{{.PublisherID}}?host={{.Host}}"
+maintainer:
+  email: "engineering@project-limelight.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
diff --git a/static/bidder-info/datablocks.yaml b/static/bidder-info/datablocks.yaml
index 418b0cab..1f8d6e8d 100644
--- a/static/bidder-info/datablocks.yaml
+++ b/static/bidder-info/datablocks.yaml
@@ -1,4 +1,4 @@
-endpoint: "http://{{.Host}}/openrtb2?sid={{.SourceId}}"
+endpoint: "http://pbserver.dblks.net/openrtb2?sid={{.SourceId}}"
 maintainer:
   email: "prebid@datablocks.net"
 capabilities:
diff --git a/static/bidder-info/greedygame.yaml b/static/bidder-info/greedygame.yaml
new file mode 100644
index 00000000..5e73e1fe
--- /dev/null
+++ b/static/bidder-info/greedygame.yaml
@@ -0,0 +1,16 @@
+endpoint: "http://ads-pbs.rtb-greedygame.com/openrtb/{{.PublisherID}}?host={{.Host}}"
+maintainer:
+  email: "engineering@project-limelight.com"
+capabilities:
+  app:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
+  site:
+    mediaTypes:
+      - banner
+      - video
+      - audio
+      - native
diff --git a/static/bidder-info/huaweiads.yaml b/static/bidder-info/huaweiads.yaml
index 8fe35b4e..fc36ff95 100644
--- a/static/bidder-info/huaweiads.yaml
+++ b/static/bidder-info/huaweiads.yaml
@@ -1,7 +1,7 @@
 endpoint: "https://acd.op.hicloud.com/ppsadx/getResult"
 disabled: true
 maintainer:
-  email: hwads@huawei.com
+  email: prebid@huawei.com
 gvlVendorID: 856
 modifyingVastXmlAllowed: true
 capabilities:
diff --git a/static/bidder-info/sovrn.yaml b/static/bidder-info/sovrn.yaml
index 06dadcf9..62d74152 100644
--- a/static/bidder-info/sovrn.yaml
+++ b/static/bidder-info/sovrn.yaml
@@ -17,6 +17,5 @@ userSync:
     url: "https://ap.lijit.com/pixel?redir={{.RedirectURL}}"
     userMacro: "$UID"
   iframe:
-    url: "https://ap.lijit.com/beacon/prebid-server/?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url={{.RedirectURL}}"
+    url: "https://ap.lijit.com/beacon/prebid-server/?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&url={{.RedirectURL}}"
     userMacro: "$UID"
-    
\ No newline at end of file
diff --git a/static/bidder-info/taboola.yaml b/static/bidder-info/taboola.yaml
index 348165a9..436f7469 100644
--- a/static/bidder-info/taboola.yaml
+++ b/static/bidder-info/taboola.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://{{.MediaType}}.bidder.taboola.com/OpenRTB/PS/auction/{{.GvlID}}/{{.PublisherID}}"
+endpoint: "https://{{.MediaType}}.bidder.taboola.com/OpenRTB/PS/auction?exchange={{.GvlID}}&publisher={{.PublisherID}}"
 maintainer:
   email:  ps-team@taboola.com
 gvlVendorID: 42
diff --git a/static/bidder-params/appstock.json b/static/bidder-params/appstock.json
new file mode 100644
index 00000000..eb2251e4
--- /dev/null
+++ b/static/bidder-params/appstock.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "Appstock Adapter Params",
+  "description": "A schema which validates params accepted by the Appstock adapter",
+  "type": "object",
+
+  "properties": {
+    "host": {
+      "type": "string",
+      "description": "Ad network's RTB host",
+      "format": "hostname",
+      "pattern": "^.+\\..+$"
+    },
+    "publisherId": {
+      "type": ["integer", "string"],
+      "description": "Publisher ID",
+      "minimum": 1,
+      "pattern": "^[1-9][0-9]*$"
+    }
+  },
+
+  "required": ["host", "publisherId"]
+}
diff --git a/static/bidder-params/datablocks.json b/static/bidder-params/datablocks.json
index 2abb9867..59b7f947 100644
--- a/static/bidder-params/datablocks.json
+++ b/static/bidder-params/datablocks.json
@@ -9,11 +9,7 @@
         "type": "integer",
         "minimum": 1,
         "description": "Website Source Id"
-      },
-      "host": {
-        "type": "string",
-        "description": "Network Host to request from"
       }
   },
-  "required": ["host", "sourceId"]
+  "required": ["sourceId"]
 }
diff --git a/static/bidder-params/greedygame.json b/static/bidder-params/greedygame.json
new file mode 100644
index 00000000..2057c147
--- /dev/null
+++ b/static/bidder-params/greedygame.json
@@ -0,0 +1,23 @@
+{
+  "$schema": "http://json-schema.org/draft-04/schema#",
+  "title": "GreedyGame Adapter Params",
+  "description": "A schema which validates params accepted by the GreedyGame adapter",
+  "type": "object",
+
+  "properties": {
+    "host": {
+      "type": "string",
+      "description": "Ad network's RTB host",
+      "format": "hostname",
+      "pattern": "^.+\\..+$"
+    },
+    "publisherId": {
+      "type": ["integer", "string"],
+      "description": "Publisher ID",
+      "minimum": 1,
+      "pattern": "^[1-9][0-9]*$"
+    }
+  },
+
+  "required": ["host", "publisherId"]
+}
diff --git a/static/bidder-params/sovrn.json b/static/bidder-params/sovrn.json
index 6b345b55..d821a808 100644
--- a/static/bidder-params/sovrn.json
+++ b/static/bidder-params/sovrn.json
@@ -16,6 +16,10 @@
     "bidfloor": {
       "type": "number",
       "description": "The minimium acceptable bid, in CPM, using US Dollars"
+    },
+    "adunitcode": {
+      "type": "string",
+      "description": "The string which identifies Ad Unit"
     }
   },
   "oneOf": [
diff --git a/util/iputil/parse.go b/util/iputil/parse.go
index bcb00760..c226ece8 100644
--- a/util/iputil/parse.go
+++ b/util/iputil/parse.go
@@ -15,6 +15,14 @@ const (
 	IPv6       IPVersion = 6
 )
 
+const (
+	IPv4BitSize = 32
+	IPv6BitSize = 128
+
+	IPv4DefaultMaskingBitSize = 24
+	IPv6DefaultMaskingBitSize = 56
+)
+
 // ParseIP parses v as an ip address returning the result and version, or nil and unknown if invalid.
 func ParseIP(v string) (net.IP, IPVersion) {
 	if ip := net.ParseIP(v); ip != nil {


Test patch:
diff --git a/account/account_test.go b/account/account_test.go
index 4bac0894..d2694abc 100644
--- a/account/account_test.go
+++ b/account/account_test.go
@@ -12,17 +12,19 @@ import (
 	"github.com/prebid/prebid-server/metrics"
 	"github.com/prebid/prebid-server/openrtb_ext"
 	"github.com/prebid/prebid-server/stored_requests"
+	"github.com/prebid/prebid-server/util/iputil"
 	"github.com/prebid/prebid-server/util/ptrutil"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 )
 
 var mockAccountData = map[string]json.RawMessage{
-	"valid_acct":                json.RawMessage(`{"disabled":false}`),
-	"disabled_acct":             json.RawMessage(`{"disabled":true}`),
-	"malformed_acct":            json.RawMessage(`{"disabled":"invalid type"}`),
-	"gdpr_channel_enabled_acct": json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`),
-	"ccpa_channel_enabled_acct": json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`),
+	"valid_acct":                                   json.RawMessage(`{"disabled":false}`),
+	"invalid_acct_ipv6_ipv4":                       json.RawMessage(`{"disabled":false, "privacy": {"ipv6": {"anon_keep_bits": -32}, "ipv4": {"anon_keep_bits": -16}}}`),
+	"disabled_acct":                                json.RawMessage(`{"disabled":true}`),
+	"malformed_acct":                               json.RawMessage(`{"disabled":"invalid type"}`),
+	"gdpr_channel_enabled_acct":                    json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`),
+	"ccpa_channel_enabled_acct":                    json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`),
 	"gdpr_channel_enabled_deprecated_purpose_acct": json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}, "channel_enabled":{"amp":true}}}`),
 	"gdpr_deprecated_purpose1":                     json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`),
 	"gdpr_deprecated_purpose2":                     json.RawMessage(`{"disabled":false,"gdpr":{"purpose2":{"enforce_purpose":"full"}}}`),
@@ -54,6 +56,8 @@ func TestGetAccount(t *testing.T) {
 		required bool
 		// account_defaults.disabled
 		disabled bool
+		// checkDefaultIP indicates IPv6 and IPv6 should be set to default values
+		checkDefaultIP bool
 		// expected error, or nil if account should be found
 		err error
 	}{
@@ -78,6 +82,8 @@ func TestGetAccount(t *testing.T) {
 		{accountID: "valid_acct", required: false, disabled: true, err: nil},
 		{accountID: "valid_acct", required: true, disabled: true, err: nil},
 
+		{accountID: "invalid_acct_ipv6_ipv4", required: true, disabled: false, err: nil, checkDefaultIP: true},
+
 		// pubID given and matches a host account explicitly disabled (Disabled: true on account json)
 		{accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}},
 		{accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}},
@@ -129,6 +135,10 @@ func TestGetAccount(t *testing.T) {
 				assert.Nil(t, account, "return account must be nil on error")
 				assert.IsType(t, test.err, errors[0], "error is of unexpected type")
 			}
+			if test.checkDefaultIP {
+				assert.Equal(t, account.Privacy.IPv6Config.AnonKeepBits, iputil.IPv6DefaultMaskingBitSize, "ipv6 should be set to default value")
+				assert.Equal(t, account.Privacy.IPv4Config.AnonKeepBits, iputil.IPv4DefaultMaskingBitSize, "ipv4 should be set to default value")
+			}
 		})
 	}
 }
diff --git a/adapters/datablocks/datablocks_test.go b/adapters/datablocks/datablocks_test.go
index 9a5fb9a2..3d4d746d 100644
--- a/adapters/datablocks/datablocks_test.go
+++ b/adapters/datablocks/datablocks_test.go
@@ -11,7 +11,7 @@ import (
 
 func TestJsonSamples(t *testing.T) {
 	bidder, buildErr := Builder(openrtb_ext.BidderDatablocks, config.Adapter{
-		Endpoint: "http://{{.Host}}/openrtb2?sid={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+		Endpoint: "http://pbserver.dblks.net/openrtb2?sid={{.SourceId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
 
 	if buildErr != nil {
 		t.Fatalf("Builder returned unexpected error %v", buildErr)
diff --git a/adapters/datablocks/datablockstest/exemplary/multi-request.json b/adapters/datablocks/datablockstest/exemplary/multi-request.json
index 286e058a..eafd8092 100644
--- a/adapters/datablocks/datablockstest/exemplary/multi-request.json
+++ b/adapters/datablocks/datablockstest/exemplary/multi-request.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -34,7 +34,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -54,7 +54,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -73,7 +73,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
@@ -91,7 +91,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/native.json b/adapters/datablocks/datablockstest/exemplary/native.json
index 9487079c..c4cf189e 100644
--- a/adapters/datablocks/datablockstest/exemplary/native.json
+++ b/adapters/datablocks/datablockstest/exemplary/native.json
@@ -14,7 +14,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -38,7 +38,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -54,7 +54,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/simple-banner.json b/adapters/datablocks/datablockstest/exemplary/simple-banner.json
index 289549ab..5c01a29a 100644
--- a/adapters/datablocks/datablockstest/exemplary/simple-banner.json
+++ b/adapters/datablocks/datablockstest/exemplary/simple-banner.json
@@ -21,7 +21,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -41,7 +41,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -64,7 +64,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/exemplary/simple-video.json b/adapters/datablocks/datablockstest/exemplary/simple-video.json
index 6f807310..fcb2defa 100644
--- a/adapters/datablocks/datablockstest/exemplary/simple-video.json
+++ b/adapters/datablocks/datablockstest/exemplary/simple-video.json
@@ -18,7 +18,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 906295
                 }
             }
@@ -39,7 +39,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=906295",
             "body":
             {
                 "id": "some-request-id",
@@ -59,7 +59,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 906295
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-host.json b/adapters/datablocks/datablockstest/supplemental/bad-host.json
deleted file mode 100644
index cee5efbe..00000000
--- a/adapters/datablocks/datablockstest/supplemental/bad-host.json
+++ /dev/null
@@ -1,33 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "bad-host-test",
-    "imp": [
-      {
-        "id": "bad-host-test-imp",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": 
-        {
-          "bidder":
-          {
-              "host": "",
-              "sourceId": 123
-          }
-        }
-      }
-    ]
-  },
-
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "Invalid/Missing Host",
-      "comparison": "literal"
-    } 
-  ]
-}
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-response-body.json b/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
index 7c4801e1..e60c6aab 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-response-body.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-server-response.json b/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
index da2924f6..cdcc0c37 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-server-response.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json b/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
index 08f2fddf..3182e71d 100644
--- a/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
+++ b/adapters/datablocks/datablockstest/supplemental/bad-sourceId.json
@@ -16,7 +16,7 @@
         {
           "bidder":
           {
-              "host": "search.nutella.datablocks.net",
+              "host": "pbserver.dblks.net",
               "sourceId": 0
           }
         }
diff --git a/adapters/datablocks/datablockstest/supplemental/missing-extparam.json b/adapters/datablocks/datablockstest/supplemental/missing-extparam.json
deleted file mode 100644
index d272cd53..00000000
--- a/adapters/datablocks/datablockstest/supplemental/missing-extparam.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  "mockBidRequest": {
-    "id": "missing-extbid-test",
-    "imp": [
-      {
-        "id": "missing-extbid-test",
-        "banner": {
-          "format": [
-            {
-              "w": 300,
-              "h": 250
-            }
-          ]
-        },
-        "ext": {
-          "sourceId":54326
-        }
-      }
-    ]
-  },
-
-  "expectedMakeRequestsErrors": [
-    {
-      "value": "Cannot Resolve host or sourceId: unexpected end of JSON input",
-      "comparison": "literal"
-    } 
-  ]
-
-  
-}
diff --git a/adapters/datablocks/datablockstest/supplemental/no-content-response.json b/adapters/datablocks/datablockstest/supplemental/no-content-response.json
index 7fc1b75c..0e53f472 100644
--- a/adapters/datablocks/datablockstest/supplemental/no-content-response.json
+++ b/adapters/datablocks/datablockstest/supplemental/no-content-response.json
@@ -17,7 +17,7 @@
             {
                 "bidder":
                 {
-                    "host": "search.nutella.datablocks.net",
+                    "host": "pbserver.dblks.net",
                     "sourceId": 123
                 }
             }
@@ -37,7 +37,7 @@
     {
         "expectedRequest":
         {
-            "uri": "http://search.nutella.datablocks.net/openrtb2?sid=123",
+            "uri": "http://pbserver.dblks.net/openrtb2?sid=123",
             "body":
             {
                 "id": "some-request-id",
@@ -56,7 +56,7 @@
                     {
                         "bidder":
                         {
-                            "host": "search.nutella.datablocks.net",
+                            "host": "pbserver.dblks.net",
                             "sourceId": 123
                         }
                     }
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json
index 8a7ca1b0..24aad066 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeIncludeVideo.json
@@ -95,9 +95,7 @@
               "detailedCreativeTypeList": [
                 "903"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json
index 240098e0..8e86675b 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeSingleImage.json
@@ -5,7 +5,7 @@
       {
         "id": "test-imp-id",
         "native": {
-          "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"aurlsupport\":0,\"durlsupport\":0,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"privacy\":0,\"assets\":[{\"id\":100,\"title\":{\"len\":90},\"required\":1},{\"id\":103,\"img\":{\"type\":3,\"wmin\":200,\"hmin\":200},\"required\":1},{\"id\":105,\"data\":{\"type\":2,\"len\":90},\"required\":1}],\"ver\":\"1.2\"}",
+          "request": "{\"context\":2,\"contextsubtype\":20,\"plcmttype\":1,\"plcmtcnt\":1,\"seq\":0,\"aurlsupport\":0,\"durlsupport\":0,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]}],\"privacy\":0,\"assets\":[{\"id\":101,\"title\":{\"len\":90},\"required\":1},{\"id\":102,\"img\":{\"type\":3,\"wmin\":200,\"hmin\":200},\"required\":1},{\"id\":103,\"data\":{\"type\":2,\"len\":90},\"required\":1},{\"id\":105,\"data\":{\"type\":12,\"len\":90}}],\"ver\":\"1.2\"}",
           "ver": "1.2"
         },
         "ext": {
@@ -93,9 +93,7 @@
               "detailedCreativeTypeList": [
                 "901"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
@@ -166,6 +164,7 @@
                     "appId": "101219405",
                     "appPromotionChannel": "401721412",
                     "clickUrl": "https://ads.huawei.com/usermgtportal/home/index.html#/",
+                    "cta": "install",
                     "duration": 6038,
                     "description": "",
                     "icon": [
@@ -300,7 +299,7 @@
               "huaweiads"
             ],
             "crid": "58022259",
-            "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":100,\"title\":{\"text\":\"/test/\",\"len\":6}},{\"id\":103,\"img\":{\"type\":3,\"url\":\"http://image.jpg\",\"w\":720,\"h\":1280}},{\"id\":105,\"data\":{\"label\":\"desc\",\"value\":\"\"}}],\"link\":{\"url\":\"https://ads.huawei.com/usermgtportal/home/index.html#/\",\"clicktrackers\":[\"http://test/click\"]},\"eventtrackers\":[{\"event\":1,\"method\":1,\"url\":\"http://test/imp\"}]}",
+            "adm": "{\"ver\":\"1.2\",\"assets\":[{\"id\":101,\"title\":{\"text\":\"/test/\",\"len\":6}},{\"id\":102,\"img\":{\"type\":3,\"url\":\"http://image.jpg\",\"w\":720,\"h\":1280}},{\"id\":103,\"data\":{\"label\":\"desc\",\"value\":\"\"}},{\"id\":105,\"data\":{\"type\":12,\"value\":\"install\"}}],\"link\":{\"url\":\"https://ads.huawei.com/usermgtportal/home/index.html#/\",\"clicktrackers\":[\"http://test/click\"]},\"eventtrackers\":[{\"event\":1,\"method\":1,\"url\":\"http://test/imp\"}]}",
             "id": "test-imp-id",
             "impid": "test-imp-id",
             "price": 2.8,
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json
index 9b46072d..c29b2352 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImage.json
@@ -92,9 +92,7 @@
               "detailedCreativeTypeList": [
                 "904"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
diff --git a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json
index f78f50e2..5d3af506 100644
--- a/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json
+++ b/adapters/huaweiads/huaweiadstest/exemplary/nativeThreeImageIncludeIcon.json
@@ -93,9 +93,7 @@
               "detailedCreativeTypeList": [
                 "904"
               ],
-              "h": 200,
-              "test": 1,
-              "w": 200
+              "test": 1
             }
           ],
           "device": {
diff --git a/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json b/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json
index c38bc9f6..14667370 100644
--- a/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json
+++ b/adapters/huaweiads/huaweiadstest/supplemental/bad_response_not_native.json
@@ -93,9 +93,7 @@
               "test": 1,
               "detailedCreativeTypeList": [
                 "903"
-              ],
-              "h": 200,
-              "w": 200
+              ]
             }
           ],
           "device": {
diff --git a/adapters/response_test.go b/adapters/response_test.go
index 824a295e..0651ff26 100644
--- a/adapters/response_test.go
+++ b/adapters/response_test.go
@@ -8,17 +8,33 @@ import (
 )
 
 func TestCheckResponseStatusCodeForErrors(t *testing.T) {
-	t.Run("bad_input", func(t *testing.T) {
-		err := CheckResponseStatusCodeForErrors(&ResponseData{StatusCode: 400})
-		expectedErr := &errortypes.BadInput{Message: "Unexpected status code: 400. Run with request.debug = 1 for more info"}
-		assert.Equal(t, expectedErr.Error(), err.Error())
-	})
+	testCases := []struct {
+		name           string
+		responseStatus int
+		expectedErr    error
+	}{
+		{
+			name:           "bad_input",
+			responseStatus: 400,
+			expectedErr: &errortypes.BadInput{
+				Message: "Unexpected status code: 400. Run with request.debug = 1 for more info",
+			},
+		},
+		{
+			name:           "internal_server_error",
+			responseStatus: 500,
+			expectedErr: &errortypes.BadServerResponse{
+				Message: "Unexpected status code: 500. Run with request.debug = 1 for more info",
+			},
+		},
+	}
 
-	t.Run("internal_server_error", func(t *testing.T) {
-		err := CheckResponseStatusCodeForErrors(&ResponseData{StatusCode: 500})
-		expectedErrMessage := "Unexpected status code: 500. Run with request.debug = 1 for more info"
-		assert.Equal(t, expectedErrMessage, err.Error())
-	})
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			err := CheckResponseStatusCodeForErrors(&ResponseData{StatusCode: tc.responseStatus})
+			assert.Equal(t, tc.expectedErr, err)
+		})
+	}
 }
 
 func TestIsResponseStatusCodeNoContent(t *testing.T) {
diff --git a/adapters/sovrn/sovrntest/supplemental/adunitcode.json b/adapters/sovrn/sovrntest/supplemental/adunitcode.json
new file mode 100644
index 00000000..47197520
--- /dev/null
+++ b/adapters/sovrn/sovrntest/supplemental/adunitcode.json
@@ -0,0 +1,125 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "bidder": {
+            "tagid": "123456",
+            "adunitcode": "sovrn_auc"
+          }
+        }
+      }
+    ],
+    "device": { },
+    "site": {
+      "domain": "www.publisher.com",
+      "page": "http://www.publisher.com/awesome/site"
+    },
+    "user": {
+      "buyeruid": "test_reader_id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": ["application/json"],
+          "Cookie": ["ljt_reader=test_reader_id"]
+        },
+        "uri": "http://sovrn.com/test/endpoint",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "123456",
+              "ext": {
+                "adunitcode": "sovrn_auc",
+                "bidder": {
+                  "tagid": "123456",
+                  "adunitcode": "sovrn_auc"
+                }
+              }
+            }
+          ],
+          "site": {
+            "domain": "www.publisher.com",
+            "page": "http://www.publisher.com/awesome/site"
+          },
+          "user": {
+            "buyeruid": "test_reader_id"
+          },
+          "device": { }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "impid": "test-imp-id",
+                  "price": 3.5,
+                  "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "adm": "some-test-ad",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "impid": "test-imp-id",
+            "price": 3.5,
+            "adm": "some-test-ad",
+            "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/sovrn/sovrntest/supplemental/fpd.json b/adapters/sovrn/sovrntest/supplemental/fpd.json
index 005d159d..e4368c13 100644
--- a/adapters/sovrn/sovrntest/supplemental/fpd.json
+++ b/adapters/sovrn/sovrntest/supplemental/fpd.json
@@ -86,8 +86,7 @@
               "tagid": "123456",
               "ext": {
                 "bidder": {
-                  "tagid": "123456",
-                  "anotherid": "654321"
+                  "tagid": "123456"
                 }
               }
             }
diff --git a/adapters/sovrn/sovrntest/supplemental/invalid-adunitcode.json b/adapters/sovrn/sovrntest/supplemental/invalid-adunitcode.json
new file mode 100644
index 00000000..fdb14371
--- /dev/null
+++ b/adapters/sovrn/sovrntest/supplemental/invalid-adunitcode.json
@@ -0,0 +1,123 @@
+{
+  "mockBidRequest": {
+    "id": "test-request-id",
+    "imp": [
+      {
+        "id": "test-imp-id",
+        "banner": {
+          "format": [
+            {
+              "w": 300,
+              "h": 250
+            },
+            {
+              "w": 300,
+              "h": 600
+            }
+          ]
+        },
+        "ext": {
+          "AdUnitCode": "sovrn_auc",
+          "bidder": {
+            "tagid": "123456"
+          }
+        }
+      }
+    ],
+    "device": { },
+    "site": {
+      "domain": "www.publisher.com",
+      "page": "http://www.publisher.com/awesome/site"
+    },
+    "user": {
+      "buyeruid": "test_reader_id"
+    }
+  },
+
+  "httpCalls": [
+    {
+      "expectedRequest": {
+        "headers": {
+          "Content-Type": ["application/json"],
+          "Cookie": ["ljt_reader=test_reader_id"]
+        },
+        "uri": "http://sovrn.com/test/endpoint",
+        "body": {
+          "id": "test-request-id",
+          "imp": [
+            {
+              "id": "test-imp-id",
+              "banner": {
+                "format": [
+                  {
+                    "w": 300,
+                    "h": 250
+                  },
+                  {
+                    "w": 300,
+                    "h": 600
+                  }
+                ]
+              },
+              "tagid": "123456",
+              "ext": {
+                "bidder": {
+                  "tagid": "123456"
+                }
+              }
+            }
+          ],
+          "site": {
+            "domain": "www.publisher.com",
+            "page": "http://www.publisher.com/awesome/site"
+          },
+          "user": {
+            "buyeruid": "test_reader_id"
+          },
+          "device": { }
+        }
+      },
+      "mockResponse": {
+        "status": 200,
+        "body": {
+          "id": "test-request-id",
+          "seatbid": [
+            {
+              "bid": [
+                {
+                  "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "impid": "test-imp-id",
+                  "price": 3.5,
+                  "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+                  "adm": "some-test-ad",
+                  "w": 300,
+                  "h": 250
+                }
+              ]
+            }
+          ]
+        }
+      }
+    }
+  ],
+
+  "expectedBidResponses": [
+    {
+      "currency": "USD",
+      "bids": [
+        {
+          "bid": {
+            "id": "a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "impid": "test-imp-id",
+            "price": 3.5,
+            "adm": "some-test-ad",
+            "nurl": "http://sovrn.com/rtb/impression?bannerid=138743&campaignid=3699&zoneid=449642&cb=69493397&tid=a_449642_554a13d3b9f348fba707cf83f0f63800",
+            "w": 300,
+            "h": 250
+          },
+          "type": "banner"
+        }
+      ]
+    }
+  ]
+}
diff --git a/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json b/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json
index e6b4a83a..71ad2ae1 100644
--- a/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json
+++ b/adapters/undertone/undertonetest/exemplary/multi-imp-app-request.json
@@ -54,7 +54,8 @@
           "bidder": {
             "publisherId": 1234,
             "placementId": 123456
-          }
+          },
+          "gpid": "gpid-value"
         }
       }
     ],
@@ -116,7 +117,10 @@
                   4
                 ]
               },
-              "tagid": "123456"
+              "tagid": "123456",
+              "ext": {
+                "gpid": "gpid-value"
+              }
             }
           ],
           "app": {
diff --git a/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json b/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json
index 0de38caa..9e59ffbc 100644
--- a/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json
+++ b/adapters/undertone/undertonetest/exemplary/multi-imp-site-request.json
@@ -20,7 +20,8 @@
           "bidder": {
             "publisherId": 1234,
             "placementId": 12345
-          }
+          },
+          "gpid": "gpid-value"
         }
       },
       {
@@ -87,7 +88,10 @@
                   }
                 ]
               },
-              "tagid": "12345"
+              "tagid": "12345",
+              "ext": {
+                "gpid": "gpid-value"
+              }
             },
             {
               "id": "test-imp-video-id",
diff --git a/config/account_test.go b/config/account_test.go
index 20c60532..31d9796a 100644
--- a/config/account_test.go
+++ b/config/account_test.go
@@ -910,3 +910,49 @@ func TestAccountPriceFloorsValidate(t *testing.T) {
 		})
 	}
 }
+
+func TestIPMaskingValidate(t *testing.T) {
+	tests := []struct {
+		name    string
+		privacy AccountPrivacy
+		want    []error
+	}{
+		{
+			name: "valid",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: 1},
+				IPv6Config: IPv6{AnonKeepBits: 0},
+			},
+		},
+		{
+			name: "invalid",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: -100},
+				IPv6Config: IPv6{AnonKeepBits: -200},
+			},
+			want: []error{
+				errors.New("bits cannot exceed 32 in ipv4 address, or be less than 0"),
+				errors.New("bits cannot exceed 128 in ipv6 address, or be less than 0"),
+			},
+		},
+		{
+			name: "mixed",
+			privacy: AccountPrivacy{
+				IPv4Config: IPv4{AnonKeepBits: 10},
+				IPv6Config: IPv6{AnonKeepBits: -10},
+			},
+			want: []error{
+				errors.New("bits cannot exceed 128 in ipv6 address, or be less than 0"),
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			var errs []error
+			errs = tt.privacy.IPv4Config.Validate(errs)
+			errs = tt.privacy.IPv6Config.Validate(errs)
+			assert.ElementsMatch(t, errs, tt.want)
+		})
+	}
+}
diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go
index e505d2f8..77625c0c 100644
--- a/config/bidderinfo_test.go
+++ b/config/bidderinfo_test.go
@@ -152,6 +152,14 @@ func TestProcessBidderInfo(t *testing.T) {
 			expectedBidderInfos: nil,
 			expectError:         "error parsing config for bidder bidderA.yaml",
 		},
+		{
+			description: "Invalid alias name",
+			bidderInfos: map[string][]byte{
+				"all.yaml": []byte(testSimpleAliasYAML),
+			},
+			expectedBidderInfos: nil,
+			expectError:         "alias all is a reserved bidder name and cannot be used",
+		},
 		{
 			description: "Valid aliases",
 			bidderInfos: map[string][]byte{
@@ -442,7 +450,6 @@ func TestProcessAliasBidderInfo(t *testing.T) {
 			assert.Equal(t, test.expectedErr, err)
 		} else {
 			assert.Equal(t, test.expectedBidderInfos, bidderInfos)
-			assert.Contains(t, openrtb_ext.CoreBidderNames(), openrtb_ext.BidderName("bidderB"))
 		}
 	}
 }
diff --git a/config/config_test.go b/config/config_test.go
index a35891ba..057ed06e 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -206,6 +206,9 @@ func TestDefaults(t *testing.T) {
 	cmpUnsignedInts(t, "tmax_adjustments.bidder_network_latency_buffer_ms", 0, cfg.TmaxAdjustments.BidderNetworkLatencyBuffer)
 	cmpUnsignedInts(t, "tmax_adjustments.pbs_response_preparation_duration_ms", 0, cfg.TmaxAdjustments.PBSResponsePreparationDuration)
 
+	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 56, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
+	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 24, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
+
 	//Assert purpose VendorExceptionMap hash tables were built correctly
 	expectedTCF2 := TCF2{
 		Enabled: true,
@@ -477,6 +480,11 @@ account_defaults:
         use_dynamic_data: true
         max_rules: 120
         max_schema_dims: 5
+    privacy:
+        ipv6:
+            anon_keep_bits: 50
+        ipv4:
+            anon_keep_bits: 20
 tmax_adjustments:
   enabled: true
   bidder_response_duration_min_ms: 700
@@ -583,6 +591,9 @@ func TestFullConfig(t *testing.T) {
 	cmpBools(t, "account_defaults.events_enabled", *cfg.AccountDefaults.EventsEnabled, true)
 	cmpNils(t, "account_defaults.events.enabled", cfg.AccountDefaults.Events.Enabled)
 
+	cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 50, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits)
+	cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 20, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits)
+
 	// Assert compression related defaults
 	cmpBools(t, "enable_gzip", false, cfg.EnableGzip)
 	cmpBools(t, "compression.request.enable_gzip", true, cfg.Compression.Request.GZIP)
diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go
index 22ecc1fa..a674e9db 100644
--- a/endpoints/cookie_sync_test.go
+++ b/endpoints/cookie_sync_test.go
@@ -974,38 +974,6 @@ func TestCookieSyncParseRequest(t *testing.T) {
 			expectedError:        errCookieSyncAccountBlocked.Error(),
 			givenAccountRequired: true,
 		},
-
-		{
-			description: "Account Defaults - Invalid Activities",
-			givenBody: strings.NewReader(`{` +
-				`"bidders":["a", "b"],` +
-				`"account":"ValidAccountInvalidActivities"` +
-				`}`),
-			givenGDPRConfig:  config.GDPR{Enabled: true, DefaultValue: "0"},
-			givenCCPAEnabled: true,
-			givenConfig: config.UserSync{
-				Cooperative: config.UserSyncCooperative{
-					EnabledByDefault: false,
-					PriorityGroups:   [][]string{{"a", "b", "c"}},
-				},
-			},
-			expectedPrivacy: privacy.Policies{},
-			expectedRequest: usersync.Request{
-				Bidders: []string{"a", "b"},
-				Cooperative: usersync.Cooperative{
-					Enabled:        false,
-					PriorityGroups: [][]string{{"a", "b", "c"}},
-				},
-				Limit: 0,
-				Privacy: usersyncPrivacy{
-					gdprPermissions: &fakePermissions{},
-				},
-				SyncTypeFilter: usersync.SyncTypeFilter{
-					IFrame:   usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
-					Redirect: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude),
-				},
-			},
-		},
 	}
 
 	for _, test := range testCases {
@@ -1934,8 +1902,7 @@ func TestCookieSyncActivityControlIntegration(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			activities, err := privacy.NewActivityControl(test.accountPrivacy)
-			assert.NoError(t, err)
+			activities := privacy.NewActivityControl(test.accountPrivacy)
 			up := usersyncPrivacy{
 				activityControl: activities,
 			}
@@ -2108,7 +2075,7 @@ func (p *fakePermissions) AuctionActivitiesAllowed(ctx context.Context, bidderCo
 
 func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
 	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+		AllowActivities: &config.AllowActivities{
 			SyncUser: config.Activity{
 				Default: ptrutil.ToPtr(true),
 				Rules: []config.ActivityRule{
diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go
index c42209e0..84e5cf4b 100644
--- a/endpoints/openrtb2/auction_test.go
+++ b/endpoints/openrtb2/auction_test.go
@@ -965,19 +965,22 @@ func TestImplicitDNTEndToEnd(t *testing.T) {
 func TestReferer(t *testing.T) {
 	testCases := []struct {
 		description             string
-		givenSitePage           string
-		givenSiteDomain         string
-		givenPublisherDomain    string
 		givenReferer            string
 		expectedDomain          string
 		expectedPage            string
 		expectedPublisherDomain string
+		bidReq                  *openrtb_ext.RequestWrapper
 	}{
 		{
 			description:             "site.page/domain are unchanged when site.page/domain and http referer are not set",
 			expectedDomain:          "",
 			expectedPage:            "",
 			expectedPublisherDomain: "",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.page/domain are derived from referer when neither is set and http referer is set",
@@ -985,39 +988,73 @@ func TestReferer(t *testing.T) {
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "somepage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.domain is derived from site.page when site.page is set and http referer is not set",
-			givenSitePage:           "https://test.somepage.com",
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "somepage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.domain is derived from http referer when site.page and http referer are set",
-			givenSitePage:           "https://test.somepage.com",
 			givenReferer:            "http://test.com",
 			expectedDomain:          "test.com",
 			expectedPublisherDomain: "test.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "site.page/domain are unchanged when site.page/domain and http referer are set",
-			givenSitePage:           "https://test.somepage.com",
-			givenSiteDomain:         "some.domain.com",
 			givenReferer:            "http://test.com",
 			expectedDomain:          "some.domain.com",
 			expectedPublisherDomain: "test.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Domain:    "some.domain.com",
+					Page:      "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{},
+				},
+			}},
 		},
 		{
 			description:             "Publisher domain shouldn't be overrwriten if already set",
-			givenSitePage:           "https://test.somepage.com",
-			givenSiteDomain:         "",
-			givenPublisherDomain:    "differentpage.com",
 			expectedDomain:          "test.somepage.com",
 			expectedPublisherDomain: "differentpage.com",
 			expectedPage:            "https://test.somepage.com",
+			bidReq: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
+				Site: &openrtb2.Site{
+					Domain: "",
+					Page:   "https://test.somepage.com",
+					Publisher: &openrtb2.Publisher{
+						Domain: "differentpage.com",
+					},
+				},
+			}},
+		},
+		{
+			description:             "request.site is nil",
+			givenReferer:            "http://test.com",
+			expectedDomain:          "test.com",
+			expectedPublisherDomain: "test.com",
+			expectedPage:            "http://test.com",
+			bidReq:                  &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{}},
 		},
 	}
 
@@ -1025,20 +1062,12 @@ func TestReferer(t *testing.T) {
 		httpReq := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(validRequest(t, "site.json")))
 		httpReq.Header.Set("Referer", test.givenReferer)
 
-		bidReq := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{
-			Site: &openrtb2.Site{
-				Domain:    test.givenSiteDomain,
-				Page:      test.givenSitePage,
-				Publisher: &openrtb2.Publisher{Domain: test.givenPublisherDomain},
-			},
-		}}
-
-		setSiteImplicitly(httpReq, bidReq)
+		setSiteImplicitly(httpReq, test.bidReq)
 
-		assert.NotNil(t, bidReq.Site, test.description)
-		assert.Equal(t, test.expectedDomain, bidReq.Site.Domain, test.description)
-		assert.Equal(t, test.expectedPage, bidReq.Site.Page, test.description)
-		assert.Equal(t, test.expectedPublisherDomain, bidReq.Site.Publisher.Domain, test.description)
+		assert.NotNil(t, test.bidReq.Site, test.description)
+		assert.Equal(t, test.expectedDomain, test.bidReq.Site.Domain, test.description)
+		assert.Equal(t, test.expectedPage, test.bidReq.Site.Page, test.description)
+		assert.Equal(t, test.expectedPublisherDomain, test.bidReq.Site.Publisher.Domain, test.description)
 	}
 }
 
diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go
index a32afd24..da4522eb 100644
--- a/endpoints/openrtb2/video_auction_test.go
+++ b/endpoints/openrtb2/video_auction_test.go
@@ -65,20 +65,6 @@ func TestVideoEndpointImpressionsNumber(t *testing.T) {
 	assert.Equal(t, resp.AdPods[0].Targeting[0].HbDeal, "ABC_123", "If DealID exists in bid response, hb_deal targeting needs to be added to resp")
 }
 
-func TestVideoEndpointInvalidPrivacyConfig(t *testing.T) {
-	ex := &mockExchangeVideo{}
-	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample.json")
-	req := httptest.NewRequest("POST", "/openrtb2/video", strings.NewReader(reqBody))
-	recorder := httptest.NewRecorder()
-
-	deps := mockDepsInvalidPrivacy(t, ex)
-	deps.VideoAuctionEndpoint(recorder, req, nil)
-
-	respBytes := recorder.Body.Bytes()
-	expectedErrorMessage := "Critical error while running the video endpoint:  unable to parse component: bidderA.BidderB.bidderC"
-	assert.Equal(t, expectedErrorMessage, string(respBytes), "error message is incorrect")
-}
-
 func TestVideoEndpointImpressionsDuration(t *testing.T) {
 	ex := &mockExchangeVideo{}
 	reqBody := readVideoTestFile(t, "sample-requests/video/video_valid_sample_different_durations.json")
@@ -1299,8 +1285,8 @@ func mockDepsInvalidPrivacy(t *testing.T, ex *mockExchangeVideo) *endpointDeps {
 		&mockAccountFetcher{data: mockVideoAccountData},
 		&config.Configuration{MaxRequestSize: maxSize,
 			AccountDefaults: config.Account{
-				Privacy: &config.AccountPrivacy{
-					AllowActivities: config.AllowActivities{
+				Privacy: config.AccountPrivacy{
+					AllowActivities: &config.AllowActivities{
 						TransmitPreciseGeo: config.Activity{Rules: []config.ActivityRule{
 							{Condition: config.ActivityCondition{ComponentName: []string{"bidderA.BidderB.bidderC"}}},
 						}},
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 919f8dce..8ca96880 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -2352,10 +2352,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
 		impExtInfoMap[impID] = ImpExtInfo{}
 	}
 
-	activityControl, err := privacy.NewActivityControl(spec.AccountPrivacy)
-	if err != nil {
-		t.Errorf("%s: Exchange returned an unexpected error. Got %s", filename, err.Error())
-	}
+	activityControl := privacy.NewActivityControl(spec.AccountPrivacy)
 
 	auctionRequest := &AuctionRequest{
 		BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &spec.IncomingRequest.OrtbRequest},
diff --git a/exchange/exchangetest/request-ext-prebid-filtering.json b/exchange/exchangetest/request-ext-prebid-filtering.json
index 4355f517..8e85d8ed 100644
--- a/exchange/exchangetest/request-ext-prebid-filtering.json
+++ b/exchange/exchangetest/request-ext-prebid-filtering.json
@@ -65,7 +65,10 @@
                         "renderers": [
                             {
                                 "name": "test-name",
-                                "version": "test-version"
+                                "version": "test-version",
+                                "data" : {
+                                    "complex": "data"
+                                }
                             }
                         ]
                     }
@@ -121,7 +124,10 @@
                                 "renderers": [
                                     {
                                         "name": "test-name",
-                                        "version": "test-version"
+                                        "version": "test-version",
+                                        "data": {
+                                            "complex": "data"
+                                        }
                                     }
                                 ]
                             }
diff --git a/exchange/utils_test.go b/exchange/utils_test.go
index 63ca54c2..ee6dad1a 100644
--- a/exchange/utils_test.go
+++ b/exchange/utils_test.go
@@ -486,7 +486,7 @@ func TestCleanOpenRTBRequests(t *testing.T) {
 			hostSChainNode:    nil,
 			bidderInfo:        config.BidderInfos{},
 		}
-		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 		if test.hasError {
 			assert.NotNil(t, err, "Error shouldn't be nil")
 		} else {
@@ -552,7 +552,7 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+		bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 		assert.Empty(t, err, "No errors should be returned")
 		for _, bidderRequest := range bidderRequests {
 			bidderName := bidderRequest.BidderName
@@ -867,7 +867,7 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		actualBidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		assert.Empty(t, err, "No errors should be returned")
 		assert.Len(t, actualBidderRequests, len(test.expectedBidderRequests), "result len doesn't match for testCase %s", test.description)
 		for _, actualBidderRequest := range actualBidderRequests {
@@ -1036,7 +1036,7 @@ func TestCleanOpenRTBRequestsCCPA(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1113,7 +1113,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo)
+		_, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, &reqExtStruct, gdpr.SignalNo, map[string]float64{})
 
 		assert.ElementsMatch(t, []error{test.expectError}, errs, test.description)
 	}
@@ -1172,7 +1172,7 @@ func TestCleanOpenRTBRequestsCOPPA(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		bidderRequests, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := bidderRequests[0]
 
 		assert.Nil(t, errs)
@@ -1265,7 +1265,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1336,7 +1336,7 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		if test.hasError == true {
 			assert.NotNil(t, errs)
 			assert.Len(t, bidderRequests, 0)
@@ -1923,7 +1923,7 @@ func TestCleanOpenRTBRequestsLMT(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 		result := results[0]
 
 		assert.Nil(t, errs)
@@ -2156,7 +2156,7 @@ func TestCleanOpenRTBRequestsGDPR(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue)
+		results, privacyLabels, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdprDefaultValue, map[string]float64{})
 		result := results[0]
 
 		if test.expectError {
@@ -2257,7 +2257,7 @@ func TestCleanOpenRTBRequestsGDPRBlockBidRequest(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 
 		// extract bidder name from each request in the results
 		bidders := []openrtb_ext.BidderName{}
@@ -2344,7 +2344,7 @@ func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) {
 				hostSChainNode:    nil,
 				bidderInfo:        test.bidderInfos,
 			}
-			bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo)
+			bidderRequests, _, err := reqSplitter.cleanOpenRTBRequests(context.Background(), test.req, nil, gdpr.SignalNo, map[string]float64{})
 			assert.Nil(t, err, "Err should be nil")
 			bidRequest := bidderRequests[0]
 			assert.Equal(t, test.expectRegs, bidRequest.BidRequest.Regs)
@@ -2605,7 +2605,8 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest {
 			Geo:      &openrtb2.Geo{Lat: 123.456},
 		},
 		Imp: []openrtb2.Imp{{
-			ID: "some-imp-id",
+			BidFloor: 100,
+			ID:       "some-imp-id",
 			Banner: &openrtb2.Banner{
 				Format: []openrtb2.Format{{
 					W: 300,
@@ -3100,7 +3101,7 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 		hostSChainNode:    nil,
 		bidderInfo:        config.BidderInfos{},
 	}
-	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+	bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 
 	assert.Nil(t, errs)
 	assert.Len(t, bidderRequests, 2, "Bid request count is not 2")
@@ -3116,6 +3117,117 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) {
 	assert.Equal(t, axonixPrebidSchainsSchain, bidRequestSourceExts["axonix"], "Incorrect axonix bid request schain in source.ext")
 }
 
+func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) {
+	tcf2Consent := "COzTVhaOzTVhaGvAAAENAiCIAP_AAH_AAAAAAEEUACCKAAA"
+	falseValue := false
+	testCases := []struct {
+		description         string
+		gdprAccountEnabled  *bool
+		gdprHostEnabled     bool
+		gdpr                string
+		gdprConsent         string
+		gdprScrub           bool
+		permissionsError    error
+		gdprDefaultValue    string
+		expectPrivacyLabels metrics.PrivacyLabels
+		expectError         bool
+		bidAdjustmentFactor map[string]float64
+		expectedImp         []openrtb2.Imp
+	}{
+		{
+			description:        "BidFloor Adjustment Done for Appnexus",
+			gdprAccountEnabled: &falseValue,
+			gdprHostEnabled:    true,
+			gdpr:               "1",
+			gdprConsent:        tcf2Consent,
+			gdprScrub:          false,
+			gdprDefaultValue:   "1",
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   false,
+				GDPRTCFVersion: "",
+			},
+			bidAdjustmentFactor: map[string]float64{"appnexus": 0.50},
+			expectedImp: []openrtb2.Imp{{
+				BidFloor: 200,
+				ID:       "some-imp-id",
+				Banner: &openrtb2.Banner{
+					Format: []openrtb2.Format{{
+						W: 300,
+						H: 250,
+					}, {
+						W: 300,
+						H: 600,
+					}},
+				},
+				Ext: json.RawMessage(`{"bidder":{"placementId":1}}`),
+			}},
+		},
+		{
+			description:        "bidAjustement Not provided",
+			gdprAccountEnabled: &falseValue,
+			gdprHostEnabled:    true,
+			gdpr:               "1",
+			gdprConsent:        tcf2Consent,
+			gdprScrub:          false,
+			gdprDefaultValue:   "1",
+			expectPrivacyLabels: metrics.PrivacyLabels{
+				GDPREnforced:   false,
+				GDPRTCFVersion: "",
+			},
+			bidAdjustmentFactor: map[string]float64{},
+			expectedImp: []openrtb2.Imp{{
+				BidFloor: 100,
+				ID:       "some-imp-id",
+				Banner: &openrtb2.Banner{
+					Format: []openrtb2.Format{{
+						W: 300,
+						H: 250,
+					}, {
+						W: 300,
+						H: 600,
+					}},
+				},
+				Ext: json.RawMessage(`{"bidder":{"placementId":1}}`),
+			}},
+		},
+	}
+	for _, test := range testCases {
+		req := newBidRequest(t)
+		accountConfig := config.Account{
+			GDPR: config.AccountGDPR{
+				Enabled: &falseValue,
+			},
+			PriceFloors: config.AccountPriceFloors{
+				AdjustForBidAdjustment: true,
+			},
+		}
+		auctionReq := AuctionRequest{
+			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: req},
+			UserSyncs:         &emptyUsersync{},
+			Account:           accountConfig,
+		}
+		gdprPermissionsBuilder := fakePermissionsBuilder{
+			permissions: &permissionsMock{
+				allowAllBidders: true,
+				passGeo:         !test.gdprScrub,
+				passID:          !test.gdprScrub,
+				activitiesError: test.permissionsError,
+			},
+		}.Builder
+		reqSplitter := &requestSplitter{
+			bidderToSyncerKey: map[string]string{},
+			me:                &metrics.MetricsEngineMock{},
+			gdprPermsBuilder:  gdprPermissionsBuilder,
+			hostSChainNode:    nil,
+			bidderInfo:        config.BidderInfos{},
+		}
+		results, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, test.bidAdjustmentFactor)
+		result := results[0]
+		assert.Nil(t, errs)
+		assert.Equal(t, test.expectedImp, result.BidRequest.Imp, test.description)
+	}
+}
+
 func TestApplyFPD(t *testing.T) {
 
 	testCases := []struct {
@@ -3450,7 +3562,7 @@ func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) {
 			bidderInfo:        config.BidderInfos{},
 		}
 
-		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo)
+		bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, extRequest, gdpr.SignalNo, map[string]float64{})
 		assert.Equal(t, test.wantError, len(errs) != 0, test.desc)
 		sort.Slice(bidderRequests, func(i, j int) bool {
 			return bidderRequests[i].BidderCoreName < bidderRequests[j].BidderCoreName
@@ -4272,7 +4384,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 	testCases := []struct {
 		name                 string
 		req                  *openrtb2.BidRequest
-		privacyConfig        *config.AccountPrivacy
+		privacyConfig        config.AccountPrivacy
 		componentName        string
 		allow                bool
 		expectedReqNumber    int
@@ -4364,8 +4476,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 	}
 
 	for _, test := range testCases {
-		activities, err := privacy.NewActivityControl(test.privacyConfig)
-		assert.NoError(t, err, "")
+		activities := privacy.NewActivityControl(&test.privacyConfig)
 		auctionReq := AuctionRequest{
 			BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req},
 			UserSyncs:         &emptyUsersync{},
@@ -4381,7 +4492,7 @@ func TestCleanOpenRTBRequestsActivities(t *testing.T) {
 		}
 
 		t.Run(test.name, func(t *testing.T) {
-			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo)
+			bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{})
 			assert.Empty(t, errs)
 			assert.Len(t, bidderRequests, test.expectedReqNumber)
 
@@ -4410,34 +4521,153 @@ func buildDefaultActivityConfig(componentName string, allow bool) config.Activit
 	}
 }
 
-func getFetchBidsActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getFetchBidsActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			FetchBids: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitUFPDActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitUFPDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitUserFPD: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitPreciseGeo: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
 
-func getTransmitTIDActivityConfig(componentName string, allow bool) *config.AccountPrivacy {
-	return &config.AccountPrivacy{
-		AllowActivities: config.AllowActivities{
+func getTransmitTIDActivityConfig(componentName string, allow bool) config.AccountPrivacy {
+	return config.AccountPrivacy{
+		AllowActivities: &config.AllowActivities{
 			TransmitTids: buildDefaultActivityConfig(componentName, allow),
 		},
 	}
 }
+
+func TestApplyBidAdjustmentToFloor(t *testing.T) {
+	type args struct {
+		allBidderRequests    []BidderRequest
+		bidAdjustmentFactors map[string]float64
+	}
+	tests := []struct {
+		name                      string
+		args                      args
+		expectedAllBidderRequests []BidderRequest
+	}{
+		{
+			name: " bidAdjustmentFactor is empty",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor not present for request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor present for request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"pubmatic": 1.0, "appnexus": 0.75},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+			},
+		},
+		{
+			name: "bidAdjustmentFactor present only for appnexus request bidder",
+			args: args{
+				allBidderRequests: []BidderRequest{
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("appnexus"),
+					},
+					{
+						BidRequest: &openrtb2.BidRequest{
+							Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+						},
+						BidderName: openrtb_ext.BidderName("pubmatic"),
+					},
+				},
+				bidAdjustmentFactors: map[string]float64{"appnexus": 0.75},
+			},
+			expectedAllBidderRequests: []BidderRequest{
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 133.33333333333334}, {BidFloor: 200}},
+					},
+					BidderName: openrtb_ext.BidderName("appnexus"),
+				},
+				{
+					BidRequest: &openrtb2.BidRequest{
+						Imp: []openrtb2.Imp{{BidFloor: 100}, {BidFloor: 150}},
+					},
+					BidderName: openrtb_ext.BidderName("pubmatic"),
+				},
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			applyBidAdjustmentToFloor(tt.args.allBidderRequests, tt.args.bidAdjustmentFactors)
+			assert.Equal(t, tt.expectedAllBidderRequests, tt.args.allBidderRequests, tt.name)
+		})
+	}
+}
diff --git a/floors/enforce_test.go b/floors/enforce_test.go
index bb5d4434..725ac22b 100644
--- a/floors/enforce_test.go
+++ b/floors/enforce_test.go
@@ -196,6 +196,71 @@ func TestEnforceFloorToBids(t *testing.T) {
 			},
 			expErrs: []error{},
 		},
+		{
+			name: "Bids with price less than bidfloor with floorsPrecision",
+			args: args{
+				bidRequestWrapper: func() *openrtb_ext.RequestWrapper {
+					bw := openrtb_ext.RequestWrapper{
+						BidRequest: &openrtb2.BidRequest{
+							ID: "some-request-id",
+							Imp: []openrtb2.Imp{
+								{ID: "some-impression-id-1", BidFloor: 1, BidFloorCur: "USD"},
+								{ID: "some-impression-id-2", BidFloor: 2, BidFloorCur: "USD"},
+							},
+						},
+					}
+					bw.RebuildRequest()
+					return &bw
+				}(),
+				seatBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+					"pubmatic": {
+						Bids: []*entities.PbsOrtbBid{
+							{Bid: &openrtb2.Bid{ID: "some-bid-1", Price: 0.998, ImpID: "some-impression-id-1"}},
+							{Bid: &openrtb2.Bid{ID: "some-bid-2", Price: 1.5, DealID: "deal_Id", ImpID: "some-impression-id-2"}},
+						},
+						Seat:     "pubmatic",
+						Currency: "USD",
+					},
+					"appnexus": {
+						Bids: []*entities.PbsOrtbBid{
+							{Bid: &openrtb2.Bid{ID: "some-bid-11", Price: 0.8, ImpID: "some-impression-id-1"}},
+							{Bid: &openrtb2.Bid{ID: "some-bid-12", Price: 2.2, ImpID: "some-impression-id-2"}},
+						},
+						Seat:     "appnexus",
+						Currency: "USD",
+					},
+				},
+				conversions:       currency.Conversions(convert{}),
+				enforceDealFloors: false,
+			},
+			expEligibleBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{
+				"pubmatic": {
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-1", Price: 0.998, ImpID: "some-impression-id-1"}},
+						{Bid: &openrtb2.Bid{ID: "some-bid-2", Price: 1.5, DealID: "deal_Id", ImpID: "some-impression-id-2"}},
+					},
+					Seat:     "pubmatic",
+					Currency: "USD",
+				},
+				"appnexus": {
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-12", Price: 2.2, ImpID: "some-impression-id-2"}},
+					},
+					Seat:     "appnexus",
+					Currency: "USD",
+				},
+			},
+			expRejectedBids: []*entities.PbsOrtbSeatBid{
+				{
+					Seat:     "appnexus",
+					Currency: "USD",
+					Bids: []*entities.PbsOrtbBid{
+						{Bid: &openrtb2.Bid{ID: "some-bid-11", Price: 0.8, ImpID: "some-impression-id-1"}},
+					},
+				},
+			},
+			expErrs: []error{},
+		},
 		{
 			name: "Bids with different currency with enforceDealFloor true",
 			args: args{
@@ -1154,7 +1219,6 @@ func TestUpdateEnforcePBS(t *testing.T) {
 		t.Run(tt.name, func(t *testing.T) {
 			got := updateEnforcePBS(tt.args.enforceFloors, tt.args.reqExt)
 			assert.Equal(t, tt.want, got, tt.name)
-
 		})
 	}
 }
diff --git a/hooks/hookexecution/mocks_test.go b/hooks/hookexecution/mocks_test.go
index e8670ff8..ff543c38 100644
--- a/hooks/hookexecution/mocks_test.go
+++ b/hooks/hookexecution/mocks_test.go
@@ -131,7 +131,7 @@ func (e mockTimeoutHook) HandleProcessedAuctionHook(_ context.Context, _ hooksta
 	time.Sleep(20 * time.Millisecond)
 	c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
 	c.AddMutation(func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
-		payload.BidRequest.User.CustomData = "some-custom-data"
+		payload.RequestWrapper.User.CustomData = "some-custom-data"
 		return payload, nil
 	}, hookstage.MutationUpdate, "bidRequest", "user.customData")
 
@@ -305,12 +305,20 @@ func (e mockUpdateBidRequestHook) HandleProcessedAuctionHook(_ context.Context,
 	c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{}
 	c.AddMutation(
 		func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
-			payload.BidRequest.User.Yob = 2000
+			payload.RequestWrapper.User.Yob = 2000
+			userExt, err := payload.RequestWrapper.GetUserExt()
+			if err != nil {
+				return payload, err
+			}
+			newPrebidExt := &openrtb_ext.ExtUserPrebid{
+				BuyerUIDs: map[string]string{"some": "id"},
+			}
+			userExt.SetPrebid(newPrebidExt)
 			return payload, nil
 		}, hookstage.MutationUpdate, "bidRequest", "user.yob",
 	).AddMutation(
 		func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) {
-			payload.BidRequest.User.Consent = "true"
+			payload.RequestWrapper.User.Consent = "true"
 			return payload, nil
 		}, hookstage.MutationUpdate, "bidRequest", "user.consent",
 	)
diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go
index eb60f452..2176c28e 100644
--- a/openrtb_ext/bidders_test.go
+++ b/openrtb_ext/bidders_test.go
@@ -2,7 +2,10 @@ package openrtb_ext
 
 import (
 	"encoding/json"
+	"errors"
+	"os"
 	"testing"
+	"testing/fstest"
 
 	"github.com/stretchr/testify/assert"
 	"github.com/xeipuuv/gojsonschema"
@@ -126,8 +129,154 @@ func TestIsBidderNameReserved(t *testing.T) {
 	}
 }
 
-func TestCoreBidderNames(t *testing.T) {
-	bidderA := "BidderA"
-	SetAliasBidderName(BidderName(bidderA))
-	assert.Contains(t, CoreBidderNames(), BidderName(bidderA))
+func TestSetAliasBidderName(t *testing.T) {
+	parentBidder := BidderName("pBidder")
+	existingCoreBidderNames := coreBidderNames
+
+	testCases := []struct {
+		aliasBidderName string
+		err             error
+	}{
+		{"aBidder", nil},
+		{"all", errors.New("alias all is a reserved bidder name and cannot be used")},
+	}
+
+	for _, test := range testCases {
+		err := SetAliasBidderName(test.aliasBidderName, parentBidder)
+		if err != nil {
+			assert.Equal(t, test.err, err)
+		} else {
+			assert.Contains(t, CoreBidderNames(), BidderName(test.aliasBidderName))
+			assert.Contains(t, aliasBidderToParent, BidderName(test.aliasBidderName))
+		}
+	}
+
+	//reset package variables to not interfere with other test cases. Example - TestBidderParamSchemas
+	coreBidderNames = existingCoreBidderNames
+	aliasBidderToParent = map[BidderName]BidderName{}
+}
+
+type mockParamsHelper struct {
+	fs              fstest.MapFS
+	absFilePath     string
+	absPathErr      error
+	schemaLoaderErr error
+	readFileErr     error
+}
+
+func (m *mockParamsHelper) readDir(name string) ([]os.DirEntry, error) {
+	return m.fs.ReadDir(name)
+}
+
+func (m *mockParamsHelper) readFile(name string) ([]byte, error) {
+	if m.readFileErr != nil {
+		return nil, m.readFileErr
+	}
+	return m.fs.ReadFile(name)
+}
+
+func (m *mockParamsHelper) newReferenceLoader(source string) gojsonschema.JSONLoader {
+	return nil
+}
+
+func (m *mockParamsHelper) newSchema(l gojsonschema.JSONLoader) (*gojsonschema.Schema, error) {
+	return nil, m.schemaLoaderErr
+}
+
+func (m *mockParamsHelper) abs(path string) (string, error) {
+	return m.absFilePath, m.absPathErr
+}
+
+func TestNewBidderParamsValidator(t *testing.T) {
+	testCases := []struct {
+		description     string
+		paramsValidator mockParamsHelper
+		dir             string
+		expectedErr     error
+	}{
+		{
+			description: "Valid case",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+			},
+			dir: "test",
+		},
+		{
+			description:     "failed to read directory",
+			paramsValidator: mockParamsHelper{},
+			dir:             "t",
+			expectedErr:     errors.New("Failed to read JSON schemas from directory t. open t: file does not exist"),
+		},
+		{
+			description: "file name does not match the bidder name",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/anyBidder.json": {
+						Data: []byte("{}"),
+					},
+				},
+			},
+			dir:         "test",
+			expectedErr: errors.New("File test/anyBidder.json does not match a valid BidderName."),
+		},
+		{
+			description: "abs file path error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				absFilePath: "test/app.json",
+				absPathErr:  errors.New("any abs error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to get an absolute representation of the path: test/app.json, any abs error"),
+		},
+		{
+			description: "schema loader error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				schemaLoaderErr: errors.New("any schema loader error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to load json schema at : any schema loader error"),
+		},
+		{
+			description: "read file error",
+			paramsValidator: mockParamsHelper{
+				fs: fstest.MapFS{
+					"test/appnexus.json": {
+						Data: []byte("{}"),
+					},
+				},
+				readFileErr: errors.New("any read file error"),
+			},
+			dir:         "test",
+			expectedErr: errors.New("Failed to read file test/appnexus.json: any read file error"),
+		},
+	}
+
+	for _, test := range testCases {
+		t.Run(test.description, func(t *testing.T) {
+			aliasBidderToParent = map[BidderName]BidderName{"rubicon": "appnexus"}
+			paramsValidator = &test.paramsValidator
+			bidderValidator, err := NewBidderParamsValidator(test.dir)
+			if test.expectedErr == nil {
+				assert.NoError(t, err)
+				assert.Contains(t, bidderValidator.Schema("appnexus"), "{}")
+				assert.Contains(t, bidderValidator.Schema("rubicon"), "{}")
+			} else {
+				assert.Equal(t, err, test.expectedErr)
+			}
+		})
+	}
 }
diff --git a/openrtb_ext/bidders_validate_test.go b/openrtb_ext/bidders_validate_test.go
index c9528536..33148d1e 100644
--- a/openrtb_ext/bidders_validate_test.go
+++ b/openrtb_ext/bidders_validate_test.go
@@ -24,9 +24,6 @@ var validator BidderParamValidator
 // returns valid JSON for all known CoreBidderNames.
 func TestBidderParamSchemas(t *testing.T) {
 	for _, bidderName := range CoreBidderNames() {
-		if _, ok := GetAliasBidderNamesSet()[bidderName]; ok {
-			continue
-		}
 		schema := validator.Schema(bidderName)
 		if schema == "" {
 			t.Errorf("No schema exists for bidder %s. Does static/bidder-params/%s.json exist?", bidderName, bidderName)
diff --git a/privacy/activitycontrol_test.go b/privacy/activitycontrol_test.go
index 7927014a..11a9bdc7 100644
--- a/privacy/activitycontrol_test.go
+++ b/privacy/activitycontrol_test.go
@@ -1,7 +1,6 @@
 package privacy
 
 import (
-	"errors"
 	"testing"
 
 	"github.com/prebid/prebid-server/config"
@@ -12,138 +11,50 @@ import (
 func TestNewActivityControl(t *testing.T) {
 	testCases := []struct {
 		name            string
-		privacyConf     *config.AccountPrivacy
+		privacyConf     config.AccountPrivacy
 		activityControl ActivityControl
-		err             error
 	}{
 		{
-			name:            "privacy_config_is_nil",
-			privacyConf:     nil,
+			name:            "empty",
+			privacyConf:     config.AccountPrivacy{},
 			activityControl: ActivityControl{plans: nil},
-			err:             nil,
 		},
 		{
-			name: "privacy_config_is_specified_and_correct",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					SyncUser:                 getDefaultActivityConfig(),
-					FetchBids:                getDefaultActivityConfig(),
-					EnrichUserFPD:            getDefaultActivityConfig(),
-					ReportAnalytics:          getDefaultActivityConfig(),
-					TransmitUserFPD:          getDefaultActivityConfig(),
-					TransmitPreciseGeo:       getDefaultActivityConfig(),
-					TransmitUniqueRequestIds: getDefaultActivityConfig(),
-					TransmitTids:             getDefaultActivityConfig(),
+			name: "specified_and_correct",
+			privacyConf: config.AccountPrivacy{
+				AllowActivities: &config.AllowActivities{
+					SyncUser:                 getTestActivityConfig(true),
+					FetchBids:                getTestActivityConfig(true),
+					EnrichUserFPD:            getTestActivityConfig(true),
+					ReportAnalytics:          getTestActivityConfig(true),
+					TransmitUserFPD:          getTestActivityConfig(true),
+					TransmitPreciseGeo:       getTestActivityConfig(false),
+					TransmitUniqueRequestIds: getTestActivityConfig(true),
+					TransmitTids:             getTestActivityConfig(true),
 				},
 			},
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivitySyncUser:                 getDefaultActivityPlan(),
-				ActivityFetchBids:                getDefaultActivityPlan(),
-				ActivityEnrichUserFPD:            getDefaultActivityPlan(),
-				ActivityReportAnalytics:          getDefaultActivityPlan(),
-				ActivityTransmitUserFPD:          getDefaultActivityPlan(),
-				ActivityTransmitPreciseGeo:       getDefaultActivityPlan(),
-				ActivityTransmitUniqueRequestIds: getDefaultActivityPlan(),
-				ActivityTransmitTids:             getDefaultActivityPlan(),
+				ActivitySyncUser:                 getTestActivityPlan(ActivityAllow),
+				ActivityFetchBids:                getTestActivityPlan(ActivityAllow),
+				ActivityEnrichUserFPD:            getTestActivityPlan(ActivityAllow),
+				ActivityReportAnalytics:          getTestActivityPlan(ActivityAllow),
+				ActivityTransmitUserFPD:          getTestActivityPlan(ActivityAllow),
+				ActivityTransmitPreciseGeo:       getTestActivityPlan(ActivityDeny),
+				ActivityTransmitUniqueRequestIds: getTestActivityPlan(ActivityAllow),
+				ActivityTransmitTids:             getTestActivityPlan(ActivityAllow),
 			}},
-			err: nil,
-		},
-		{
-			name: "privacy_config_is_specified_and_SyncUser_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					SyncUser: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_FetchBids_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					FetchBids: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_EnrichUserFPD_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					EnrichUserFPD: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_ReportAnalytics_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					ReportAnalytics: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitUserFPD_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					TransmitUserFPD: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitPreciseGeo_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					TransmitPreciseGeo: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitUniqueRequestIds_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					TransmitUniqueRequestIds: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name: "privacy_config_is_specified_and_TransmitTids_is_incorrect",
-			privacyConf: &config.AccountPrivacy{
-				AllowActivities: config.AllowActivities{
-					TransmitTids: getIncorrectActivityConfig(),
-				},
-			},
-			activityControl: ActivityControl{plans: nil},
-			err:             errors.New("unable to parse component: bidder.bidderA.bidderB"),
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualAC, actualErr := NewActivityControl(test.privacyConf)
-			if test.err == nil {
-				assert.Equal(t, test.activityControl, actualAC)
-				assert.NoError(t, actualErr)
-			} else {
-				assert.EqualError(t, actualErr, test.err.Error())
-			}
+			actualAC := NewActivityControl(&test.privacyConf)
+			assert.Equal(t, test.activityControl, actualAC)
 		})
 	}
 }
 
-func TestActivityDefaultToDefaultResult(t *testing.T) {
+func TestCfgToDefaultResult(t *testing.T) {
 	testCases := []struct {
 		name            string
 		activityDefault *bool
@@ -168,14 +79,13 @@ func TestActivityDefaultToDefaultResult(t *testing.T) {
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualResult := activityDefaultToDefaultResult(test.activityDefault)
+			actualResult := cfgToDefaultResult(test.activityDefault)
 			assert.Equal(t, test.expectedResult, actualResult)
 		})
 	}
 }
 
-func TestAllowActivityControl(t *testing.T) {
-
+func TestActivityControlAllow(t *testing.T) {
 	testCases := []struct {
 		name            string
 		activityControl ActivityControl
@@ -193,7 +103,7 @@ func TestAllowActivityControl(t *testing.T) {
 		{
 			name: "activity_not_defined",
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivitySyncUser: getDefaultActivityPlan()}},
+				ActivitySyncUser: getTestActivityPlan(ActivityAllow)}},
 			activity:       ActivityFetchBids,
 			target:         Component{Type: "bidder", Name: "bidderA"},
 			activityResult: true,
@@ -201,7 +111,7 @@ func TestAllowActivityControl(t *testing.T) {
 		{
 			name: "activity_defined_but_not_found_default_returned",
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivityFetchBids: getDefaultActivityPlan()}},
+				ActivityFetchBids: getTestActivityPlan(ActivityAllow)}},
 			activity:       ActivityFetchBids,
 			target:         Component{Type: "bidder", Name: "bidderB"},
 			activityResult: true,
@@ -209,7 +119,7 @@ func TestAllowActivityControl(t *testing.T) {
 		{
 			name: "activity_defined_and_allowed",
 			activityControl: ActivityControl{plans: map[Activity]ActivityPlan{
-				ActivityFetchBids: getDefaultActivityPlan()}},
+				ActivityFetchBids: getTestActivityPlan(ActivityAllow)}},
 			activity:       ActivityFetchBids,
 			target:         Component{Type: "bidder", Name: "bidderA"},
 			activityResult: true,
@@ -225,13 +135,12 @@ func TestAllowActivityControl(t *testing.T) {
 	}
 }
 
-// constants
-func getDefaultActivityConfig() config.Activity {
+func getTestActivityConfig(allow bool) config.Activity {
 	return config.Activity{
 		Default: ptrutil.ToPtr(true),
 		Rules: []config.ActivityRule{
 			{
-				Allow: true,
+				Allow: allow,
 				Condition: config.ActivityCondition{
 					ComponentName: []string{"bidderA"},
 					ComponentType: []string{"bidder"},
@@ -241,32 +150,15 @@ func getDefaultActivityConfig() config.Activity {
 	}
 }
 
-func getDefaultActivityPlan() ActivityPlan {
+func getTestActivityPlan(result ActivityResult) ActivityPlan {
 	return ActivityPlan{
 		defaultResult: true,
 		rules: []Rule{
-			ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
+			ConditionRule{
+				result:        result,
+				componentName: []string{"bidderA"},
 				componentType: []string{"bidder"},
 			},
 		},
 	}
 }
-
-func getIncorrectActivityConfig() config.Activity {
-	return config.Activity{
-		Default: ptrutil.ToPtr(true),
-		Rules: []config.ActivityRule{
-			{
-				Allow: true,
-				Condition: config.ActivityCondition{
-					ComponentName: []string{"bidder.bidderA.bidderB"},
-					ComponentType: []string{"bidder"},
-				},
-			},
-		},
-	}
-}
diff --git a/privacy/component_test.go b/privacy/component_test.go
index 38aa105d..9d9d1644 100644
--- a/privacy/component_test.go
+++ b/privacy/component_test.go
@@ -1,124 +1,87 @@
 package privacy
 
 import (
-	"errors"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 )
 
-func TestComponentMatches(t *testing.T) {
+func TestComponentMatchesName(t *testing.T) {
 	testCases := []struct {
 		name   string
 		given  Component
-		target Component
+		target string
 		result bool
 	}{
 		{
-			name:   "full",
+			name:   "match",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "a", Name: "b"},
+			target: "b",
 			result: true,
 		},
 		{
-			name:   "name-wildcard",
-			given:  Component{Type: "a", Name: "*"},
-			target: Component{Type: "a", Name: "b"},
-			result: true,
-		},
-		{
-			name:   "different",
+			name:   "wrong-field",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "foo", Name: "bar"},
+			target: "a",
 			result: false,
 		},
 		{
-			name:   "different-type",
+			name:   "different-value",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "foo", Name: "b"},
+			target: "foo",
 			result: false,
 		},
 		{
-			name:   "different-name",
+			name:   "different-case",
 			given:  Component{Type: "a", Name: "b"},
-			target: Component{Type: "a", Name: "foo"},
-			result: false,
+			target: "B",
+			result: true,
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			assert.Equal(t, test.result, test.given.Matches(test.target))
+			assert.Equal(t, test.result, test.given.MatchesName(test.target))
 		})
 	}
 }
 
-func TestParseComponent(t *testing.T) {
+func TestComponentMatchesType(t *testing.T) {
 	testCases := []struct {
-		name          string
-		component     string
-		expected      Component
-		expectedError error
+		name   string
+		given  Component
+		target string
+		result bool
 	}{
 		{
-			name:          "empty",
-			component:     "",
-			expected:      Component{},
-			expectedError: errors.New("unable to parse empty component"),
-		},
-		{
-			name:          "too-many-parts",
-			component:     "bidder.bidderA.bidderB",
-			expected:      Component{},
-			expectedError: errors.New("unable to parse component: bidder.bidderA.bidderB"),
-		},
-		{
-			name:          "type-bidder",
-			component:     "bidder.bidderA",
-			expected:      Component{Type: "bidder", Name: "bidderA"},
-			expectedError: nil,
-		},
-		{
-			name:          "type-analytics",
-			component:     "analytics.bidderA",
-			expected:      Component{Type: "analytics", Name: "bidderA"},
-			expectedError: nil,
-		},
-		{
-			name:          "type-default",
-			component:     "bidderA",
-			expected:      Component{Type: "bidder", Name: "bidderA"},
-			expectedError: nil,
+			name:   "match",
+			given:  Component{Type: "a", Name: "b"},
+			target: "a",
+			result: true,
 		},
 		{
-			name:          "type-rtd",
-			component:     "rtd.test",
-			expected:      Component{Type: "rtd", Name: "test"},
-			expectedError: nil,
+			name:   "wrong-field",
+			given:  Component{Type: "a", Name: "b"},
+			target: "b",
+			result: false,
 		},
 		{
-			name:          "type-general",
-			component:     "general.test",
-			expected:      Component{Type: "general", Name: "test"},
-			expectedError: nil,
+			name:   "different-value",
+			given:  Component{Type: "a", Name: "b"},
+			target: "foo",
+			result: false,
 		},
 		{
-			name:          "type-invalid",
-			component:     "invalid.test",
-			expected:      Component{},
-			expectedError: errors.New("unable to parse component (invalid type): invalid.test"),
+			name:   "different-case",
+			given:  Component{Type: "a", Name: "b"},
+			target: "A",
+			result: true,
 		},
 	}
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			actualSN, actualErr := ParseComponent(test.component)
-			if test.expectedError == nil {
-				assert.Equal(t, test.expected, actualSN)
-				assert.NoError(t, actualErr)
-			} else {
-				assert.EqualError(t, actualErr, test.expectedError.Error())
-			}
+			assert.Equal(t, test.result, test.given.MatchesType(test.target))
 		})
 	}
 }
diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go
index be1ab0dc..a97779eb 100644
--- a/privacy/enforcement_test.go
+++ b/privacy/enforcement_test.go
@@ -76,8 +76,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     true,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -92,8 +92,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -108,8 +108,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -124,8 +124,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -156,8 +156,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDNone,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserNone,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -172,8 +172,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     true,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest16,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoReducedPrecision,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoReducedPrecision,
@@ -188,8 +188,8 @@ func TestApplyGDPR(t *testing.T) {
 				LMT:     false,
 			},
 			expectedDeviceID:   ScrubStrategyDeviceIDAll,
-			expectedDeviceIPv4: ScrubStrategyIPV4Lowest8,
-			expectedDeviceIPv6: ScrubStrategyIPV6Lowest32,
+			expectedDeviceIPv4: ScrubStrategyIPV4Subnet,
+			expectedDeviceIPv6: ScrubStrategyIPV6Subnet,
 			expectedDeviceGeo:  ScrubStrategyGeoFull,
 			expectedUser:       ScrubStrategyUserIDAndDemographic,
 			expectedUserGeo:    ScrubStrategyGeoFull,
@@ -329,7 +329,7 @@ func TestApplyToggle(t *testing.T) {
 				m.On("ScrubUser", req.User, ScrubStrategyUserIDAndDemographic, ScrubStrategyGeoFull).Return(replacedUser).Once()
 			}
 			if test.expectedScrubDeviceExecuted {
-				m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Lowest8, ScrubStrategyIPV6Lowest32, ScrubStrategyGeoFull).Return(replacedDevice).Once()
+				m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Subnet, ScrubStrategyIPV6Subnet, ScrubStrategyGeoFull).Return(replacedDevice).Once()
 			}
 
 			test.enforcement.apply(req, m)
diff --git a/privacy/rules_test.go b/privacy/rule_condition_test.go
similarity index 69%
rename from privacy/rules_test.go
rename to privacy/rule_condition_test.go
index 7e80c9ce..3a09b365 100644
--- a/privacy/rules_test.go
+++ b/privacy/rule_condition_test.go
@@ -9,17 +9,15 @@ import (
 func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 	testCases := []struct {
 		name           string
-		componentRule  ComponentEnforcementRule
+		componentRule  ConditionRule
 		target         Component
 		activityResult ActivityResult
 	}{
 		{
 			name: "activity_is_allowed",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
+			componentRule: ConditionRule{
+				result:        ActivityAllow,
+				componentName: []string{"bidderA"},
 				componentType: []string{"bidder"},
 			},
 			target:         Component{Type: "bidder", Name: "bidderA"},
@@ -27,11 +25,9 @@ func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 		},
 		{
 			name: "activity_is_not_allowed",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityDeny,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
+			componentRule: ConditionRule{
+				result:        ActivityDeny,
+				componentName: []string{"bidderA"},
 				componentType: []string{"bidder"},
 			},
 			target:         Component{Type: "bidder", Name: "bidderA"},
@@ -39,11 +35,9 @@ func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 		},
 		{
 			name: "abstain_both_clauses_do_not_match",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
+			componentRule: ConditionRule{
+				result:        ActivityAllow,
+				componentName: []string{"bidderA"},
 				componentType: []string{"bidder"},
 			},
 			target:         Component{Type: "bidder", Name: "bidderB"},
@@ -51,18 +45,16 @@ func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 		},
 		{
 			name: "activity_is_not_allowed_componentName_only",
-			componentRule: ComponentEnforcementRule{
-				result: ActivityAllow,
-				componentName: []Component{
-					{Type: "bidder", Name: "bidderA"},
-				},
+			componentRule: ConditionRule{
+				result:        ActivityAllow,
+				componentName: []string{"bidderA"},
 			},
 			target:         Component{Type: "bidder", Name: "bidderA"},
 			activityResult: ActivityAllow,
 		},
 		{
 			name: "activity_is_allowed_componentType_only",
-			componentRule: ComponentEnforcementRule{
+			componentRule: ConditionRule{
 				result:        ActivityAllow,
 				componentType: []string{"bidder"},
 			},
@@ -71,7 +63,7 @@ func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 		},
 		{
 			name: "no-conditions-allow",
-			componentRule: ComponentEnforcementRule{
+			componentRule: ConditionRule{
 				result: ActivityAllow,
 			},
 			target:         Component{Type: "bidder", Name: "bidderA"},
@@ -79,7 +71,7 @@ func TestComponentEnforcementRuleEvaluate(t *testing.T) {
 		},
 		{
 			name: "no-conditions-deny",
-			componentRule: ComponentEnforcementRule{
+			componentRule: ConditionRule{
 				result: ActivityDeny,
 			},
 			target:         Component{Type: "bidder", Name: "bidderA"},
diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go
index 0691b2c7..59e593fc 100644
--- a/privacy/scrubber_test.go
+++ b/privacy/scrubber_test.go
@@ -2,6 +2,7 @@ package privacy
 
 import (
 	"encoding/json"
+	"github.com/prebid/prebid-server/config"
 	"testing"
 
 	"github.com/prebid/openrtb/v19/openrtb2"
@@ -38,12 +39,12 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "",
 				IFA:      "",
 				IP:       "1.2.3.0",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
+				IPv6:     "2001:1db8:2233:4400::",
 				Geo:      &openrtb2.Geo{},
 			},
 			id:   ScrubStrategyDeviceIDAll,
-			ipv4: ScrubStrategyIPV4Lowest8,
-			ipv6: ScrubStrategyIPV6Lowest32,
+			ipv4: ScrubStrategyIPV4Subnet,
+			ipv6: ScrubStrategyIPV6Subnet,
 			geo:  ScrubStrategyGeoFull,
 		},
 		{
@@ -57,7 +58,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "",
 				IFA:      "",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDAll,
@@ -76,16 +77,16 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.0",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDNone,
-			ipv4: ScrubStrategyIPV4Lowest8,
+			ipv4: ScrubStrategyIPV4Subnet,
 			ipv6: ScrubStrategyIPV6None,
 			geo:  ScrubStrategyGeoNone,
 		},
 		{
-			description: "Isolated - IPv6 - Lowest 16",
+			description: "Isolated - IPv6",
 			expected: &openrtb2.Device{
 				DIDMD5:   "anyDIDMD5",
 				DIDSHA1:  "anyDIDSHA1",
@@ -95,31 +96,12 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:0",
+				IPv6:     "2001:1db8:2233:4400::",
 				Geo:      device.Geo,
 			},
 			id:   ScrubStrategyDeviceIDNone,
 			ipv4: ScrubStrategyIPV4None,
-			ipv6: ScrubStrategyIPV6Lowest16,
-			geo:  ScrubStrategyGeoNone,
-		},
-		{
-			description: "Isolated - IPv6 - Lowest 32",
-			expected: &openrtb2.Device{
-				DIDMD5:   "anyDIDMD5",
-				DIDSHA1:  "anyDIDSHA1",
-				DPIDMD5:  "anyDPIDMD5",
-				DPIDSHA1: "anyDPIDSHA1",
-				MACSHA1:  "anyMACSHA1",
-				MACMD5:   "anyMACMD5",
-				IFA:      "anyIFA",
-				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
-				Geo:      device.Geo,
-			},
-			id:   ScrubStrategyDeviceIDNone,
-			ipv4: ScrubStrategyIPV4None,
-			ipv6: ScrubStrategyIPV6Lowest32,
+			ipv6: ScrubStrategyIPV6Subnet,
 			geo:  ScrubStrategyGeoNone,
 		},
 		{
@@ -133,7 +115,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo: &openrtb2.Geo{
 					Lat:   123.46,
 					Lon:   678.89,
@@ -158,7 +140,7 @@ func TestScrubDevice(t *testing.T) {
 				MACMD5:   "anyMACMD5",
 				IFA:      "anyIFA",
 				IP:       "1.2.3.4",
-				IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+				IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 				Geo:      &openrtb2.Geo{},
 			},
 			id:   ScrubStrategyDeviceIDNone,
@@ -167,15 +149,16 @@ func TestScrubDevice(t *testing.T) {
 			geo:  ScrubStrategyGeoFull,
 		},
 	}
-
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
-		result := NewScrubber().ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo)
+		result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo)
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
 func TestScrubDeviceNil(t *testing.T) {
-	result := NewScrubber().ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone)
+	testIPMasking := getTestIPMasking()
+	result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone)
 	assert.Nil(t, result)
 }
 
@@ -292,14 +275,16 @@ func TestScrubUser(t *testing.T) {
 		},
 	}
 
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
-		result := NewScrubber().ScrubUser(user, test.scrubUser, test.scrubGeo)
+		result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(user, test.scrubUser, test.scrubGeo)
 		assert.Equal(t, test.expected, result, test.description)
 	}
 }
 
 func TestScrubUserNil(t *testing.T) {
-	result := NewScrubber().ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone)
+	testIPMasking := getTestIPMasking()
+	result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone)
 	assert.Nil(t, result)
 }
 
@@ -336,7 +321,7 @@ func TestScrubRequest(t *testing.T) {
 				},
 				Device: &openrtb2.Device{
 					IP:   "1.2.3.4",
-					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329",
 					Geo:  device.Geo,
 				},
 			},
@@ -354,7 +339,7 @@ func TestScrubRequest(t *testing.T) {
 				},
 				Device: &openrtb2.Device{
 					IP:   "1.2.3.4",
-					IPv6: "2001:0db8:0000:0000:0000:ff00:0042:8329",
+					IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329",
 					Geo:  device.Geo,
 				},
 			},
@@ -431,7 +416,7 @@ func TestScrubRequest(t *testing.T) {
 					MACSHA1:  "anyMACSHA1",
 					MACMD5:   "anyMACMD5",
 					IP:       "1.2.3.0",
-					IPv6:     "2001:0db8:0000:0000:0000:ff00:0:0",
+					IPv6:     "2001:1db8:2233:4400::",
 					Geo: &openrtb2.Geo{
 						Lat: 123.46, Lon: 678.89,
 						Metro: "some metro",
@@ -443,6 +428,7 @@ func TestScrubRequest(t *testing.T) {
 		},
 	}
 
+	testIPMasking := getTestIPMasking()
 	for _, test := range testCases {
 		t.Run(test.description, func(t *testing.T) {
 			bidRequest := &openrtb2.BidRequest{
@@ -462,127 +448,110 @@ func TestScrubRequest(t *testing.T) {
 			}
 			bidRequest.User.EIDs = []openrtb2.EID{{Source: "test"}}
 
-			result := NewScrubber().ScrubRequest(bidRequest, test.enforcement)
+			result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubRequest(bidRequest, test.enforcement)
 			assert.Equal(t, test.expected, result, test.description)
 		})
 	}
 }
 
-func TestScrubIPV4(t *testing.T) {
+func TestScrubIP(t *testing.T) {
 	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
+		IP        string
+		cleanedIP string
+		bits      int
+		maskBits  int
 	}{
 		{
-			IP:          "0.0.0.0",
-			cleanedIP:   "0.0.0.0",
-			description: "Shouldn't do anything for a 0.0.0.0 IP address",
+			IP:        "0:0:0:0:0:0:0:0",
+			cleanedIP: "::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "192.127.111.134",
-			cleanedIP:   "192.127.111.0",
-			description: "Should remove the lowest 8 bits",
+			IP:        "",
+			cleanedIP: "",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "192.127.111.0",
-			cleanedIP:   "192.127.111.0",
-			description: "Shouldn't change anything if the lowest 8 bits are already 0",
+			IP:        "1111:2222:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:2222:3333:4400::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "1111:2222:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:2222::",
+			bits:      128,
+			maskBits:  34,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
-		},
-	}
-
-	for _, test := range testCases {
-		result := scrubIPV4Lowest8(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
-	}
-}
-
-func TestScrubIPV6Lowest16Bits(t *testing.T) {
-	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
-	}{
-		{
-			IP:          "0:0:0:0",
-			cleanedIP:   "0:0:0:0",
-			description: "Shouldn't do anything for a 0:0:0:0 IP address",
+			IP:        "1111:0:3333:4444:5555:6666:7777:8888",
+			cleanedIP: "1111:0:3333:4400::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:8329",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0042:0",
-			description: "Should remove lowest 16 bits",
+			IP:        "1111::6666:7777:8888",
+			cleanedIP: "1111::",
+			bits:      128,
+			maskBits:  56,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:0",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0042:0",
-			description: "Shouldn't do anything if the lowest 16 bits are already 0",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:8329",
+			cleanedIP: "2001:1db8::ff00:0:0",
+			bits:      128,
+			maskBits:  96,
 		},
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0:0",
+			cleanedIP: "2001:1db8::ff00:0:0",
+			bits:      128,
+			maskBits:  96,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:8329",
+			cleanedIP: "2001:1db8::ff00:42:0",
+			bits:      128,
+			maskBits:  112,
 		},
-	}
-
-	for _, test := range testCases {
-		result := scrubIPV6Lowest16Bits(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
-	}
-}
-
-func TestScrubIPV6Lowest32Bits(t *testing.T) {
-	testCases := []struct {
-		IP          string
-		cleanedIP   string
-		description string
-	}{
 		{
-			IP:          "0:0:0:0",
-			cleanedIP:   "0:0:0:0",
-			description: "Shouldn't do anything for a 0:0:0:0 IP address",
+			IP:        "2001:1db8:0000:0000:0000:ff00:0042:0",
+			cleanedIP: "2001:1db8::ff00:42:0",
+			bits:      128,
+			maskBits:  112,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0042:8329",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0:0",
-			description: "Should remove lowest 32 bits",
+			IP:        "127.0.0.1",
+			cleanedIP: "127.0.0.0",
+			bits:      32,
+			maskBits:  24,
 		},
 		{
-			IP:          "2001:0db8:0000:0000:0000:ff00:0:0",
-			cleanedIP:   "2001:0db8:0000:0000:0000:ff00:0:0",
-			description: "Shouldn't do anything if the lowest 32 bits are already 0",
+			IP:        "0.0.0.0",
+			cleanedIP: "0.0.0.0",
+			bits:      32,
+			maskBits:  24,
 		},
-
 		{
-			IP:          "not an ip",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "192.127.111.134",
+			cleanedIP: "192.127.111.0",
+			bits:      32,
+			maskBits:  24,
 		},
 		{
-			IP:          "",
-			cleanedIP:   "",
-			description: "Should return an empty string for a bad IP",
+			IP:        "192.127.111.0",
+			cleanedIP: "192.127.111.0",
+			bits:      32,
+			maskBits:  24,
 		},
 	}
-
 	for _, test := range testCases {
-		result := scrubIPV6Lowest32Bits(test.IP)
-		assert.Equal(t, test.cleanedIP, result, test.description)
+		t.Run(test.IP, func(t *testing.T) {
+			// bits: ipv6 - 128, ipv4 - 32
+			result := scrubIP(test.IP, test.maskBits, test.bits)
+			assert.Equal(t, test.cleanedIP, result)
+		})
 	}
 }
 
@@ -739,7 +708,7 @@ func getTestDevice() *openrtb2.Device {
 		MACMD5:   "anyMACMD5",
 		IFA:      "anyIFA",
 		IP:       "1.2.3.4",
-		IPv6:     "2001:0db8:0000:0000:0000:ff00:0042:8329",
+		IPv6:     "2001:1db8:2233:4455:6677:ff00:0042:8329",
 		Geo: &openrtb2.Geo{
 			Lat:   123.456,
 			Lon:   678.89,
@@ -748,5 +717,15 @@ func getTestDevice() *openrtb2.Device {
 			ZIP:   "some zip",
 		},
 	}
+}
 
+func getTestIPMasking() config.AccountPrivacy {
+	return config.AccountPrivacy{
+		IPv6Config: config.IPv6{
+			54,
+		},
+		IPv4Config: config.IPv4{
+			24,
+		},
+	}
 }
diff --git a/router/router_test.go b/router/router_test.go
index 2b4ff7fd..41c2a724 100644
--- a/router/router_test.go
+++ b/router/router_test.go
@@ -37,8 +37,9 @@ func ensureHasKey(t *testing.T, data map[string]json.RawMessage, key string) {
 }
 
 func TestNewJsonDirectoryServer(t *testing.T) {
-	alias := map[string]string{"aliastest": "appnexus"}
-	handler := NewJsonDirectoryServer("../static/bidder-params", &testValidator{}, alias)
+	defaultAlias := map[string]string{"aliastest": "appnexus"}
+	yamlAlias := map[openrtb_ext.BidderName]openrtb_ext.BidderName{openrtb_ext.BidderName("alias"): openrtb_ext.BidderName("parentAlias")}
+	handler := newJsonDirectoryServer("../static/bidder-params", &testValidator{}, defaultAlias, yamlAlias)
 	recorder := httptest.NewRecorder()
 	request, _ := http.NewRequest("GET", "/whatever", nil)
 	handler(recorder, request, nil)
@@ -59,6 +60,7 @@ func TestNewJsonDirectoryServer(t *testing.T) {
 	}
 
 	ensureHasKey(t, data, "aliastest")
+	ensureHasKey(t, data, "alias")
 }
 
 func TestCheckSupportedUserSyncEndpoints(t *testing.T) {