diff --git a/CHANGELOG.md b/CHANGELOG.md index db5b8f85..4b86c929 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,28 @@ All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines. +## 4.7.0 (2025-02-11) + + +### Features + +* [Private Aggregation] Limit contributions per adtech for private aggregation +* add TEE KV request response in EventMessage +* enable_tkv_v2_browser works in local startup scripts +* Handle interestGroups in TKV v2 adapter + + +### Bug Fixes + +* Explicitly set confidential_instance_type +* Port patch fix for bidding server hash stability +* Remove common patch causing bidding server hash instability +* Remove value wrapper from signals fetched from KV V2 response +* Specify exception in except block +* Support Intel AMX CPUs in sandbox2 +* udf metric error message +* Verify buyer clouds are in SFE public key list + ## 4.6.0 (2025-01-29) diff --git a/WORKSPACE b/WORKSPACE index 798d0b5a..d620f016 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -15,13 +15,11 @@ python_register_toolchains("//builders/bazel") http_archive( name = "google_privacysandbox_servers_common", - patch_args = ["-p1"], - patches = ["//third_party:common_repo.patch"], - # 2025-1-22 - sha256 = "dd3135177278f40320844e74aee9d6f5a65949ef8ba205d81b0f1617cb07fbc5", - strip_prefix = "data-plane-shared-libraries-f1792a8385e62773e858ad77b262b9dfc2f97bb1", + # 2025-02-10 + sha256 = "ae91cd49c679f71346b6123768ff753234fc010c0dfce2847d6f3e4d6fa09ec9", + strip_prefix = "data-plane-shared-libraries-4e138f8c8b0204cf525660e53177ac5be5519dd1", urls = [ - "https://github.com/privacysandbox/data-plane-shared-libraries/archive/f1792a8385e62773e858ad77b262b9dfc2f97bb1.zip", + "https://github.com/privacysandbox/data-plane-shared-libraries/archive/4e138f8c8b0204cf525660e53177ac5be5519dd1.zip", ], ) diff --git a/api/udf/generate_bid.proto b/api/udf/generate_bid.proto index fb166e76..6badc4e9 100644 --- a/api/udf/generate_bid.proto +++ b/api/udf/generate_bid.proto @@ -141,12 +141,9 @@ message ProtectedAudienceBrowserSignals { string top_level_seller = 3 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: 'Top level seller origin/domain passed in case of component auctions.'}]; - int64 join_count = 4 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: 'Number of times the Interest Group was joined in the last 30 days.'}]; + int64 join_count = 4 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: 'Number of times the Interest Group was joined in the last "n" days.'}]; - int64 bid_count = 5 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: - 'Number of times the Interest Group bid in an auction in the last 30' - ' days.' -}]; + int64 bid_count = 5 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: 'Number of times the Interest Group bid in an auction in the last "n" days.'}]; int64 recency = 6 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: 'The most recent join time for the Interest Group expressed in' @@ -155,7 +152,7 @@ message ProtectedAudienceBrowserSignals { string prev_wins = 7 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: 'Tuple of time-ad pairs for a previous win for the Interest Group that' - ' occurred in the last 30 days. The time is specified in seconds' + ' occurred in the last "n" days. The time is specified in seconds' ' before the containing auctionBlob was requested.' }]; @@ -168,6 +165,12 @@ message ProtectedAudienceBrowserSignals { ' generateBid() execution per Interest Group. If the seller doesn\'t set' ' the limit, a default would be set by the Bidding server.' }]; + + string prev_wins_ms = 9 [(privacysandbox.apis.roma.app_api.v1.roma_field_annotation) = {description: + 'Tuple of time-ad pairs for a previous win for the Interest Group that' + ' occurred in the last "n" days. The time is specified in milliseconds' + ' before the containing auctionBlob was requested.' +}]; } // (-- LINT.ThenChange(/api/bidding_auction_servers.proto:browser_signals_bidding) --) diff --git a/production/deploy/aws/terraform/environment/demo/seller/seller.tf b/production/deploy/aws/terraform/environment/demo/seller/seller.tf index a4b318d8..7f7362d8 100644 --- a/production/deploy/aws/terraform/environment/demo/seller/seller.tf +++ b/production/deploy/aws/terraform/environment/demo/seller/seller.tf @@ -82,16 +82,16 @@ locals { # "protectedAppSignalsBuyerReportWinJsUrls": {"https://buyerA_origin.com":"https://buyerA.com/generateBid.js"} # }" - ROMA_TIMEOUT_MS = "" # Example: "10000" - ENABLE_REPORT_WIN_INPUT_NOISING = "" # Example: "true" - K_ANON_TOTAL_NUM_HASH = "" # Example: "1000" - EXPECTED_K_ANON_TO_NON_K_ANON_RATIO = "" # Example: "1.0" - K_ANON_CLIENT_TIME_OUT_MS = "" # Example: "60000" - NUM_K_ANON_SHARDS = "" # Example: "1" - NUM_NON_K_ANON_SHARDS = "" # Example: "1" - TEST_MODE_K_ANON_CACHE_TTL_SECONDS = "" # Example: "180" - TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS = "" # Example: "180" - ENABLE_K_ANON_QUERY_CACHE = "" # Example: "true" + ROMA_TIMEOUT_MS = "" # Example: "10000" + ENABLE_REPORT_WIN_INPUT_NOISING = "" # Example: "true" + K_ANON_TOTAL_NUM_HASH = "" # Example: "1000" + EXPECTED_K_ANON_TO_NON_K_ANON_RATIO = "" # Example: "1.0" + K_ANON_CLIENT_TIME_OUT_MS = "" # Example: "60000" + NUM_K_ANON_SHARDS = "" # Example: "1" + NUM_NON_K_ANON_SHARDS = "" # Example: "1" + TEST_MODE_K_ANON_CACHE_TTL_MS = "" # Example: "180" + TEST_MODE_NON_K_ANON_CACHE_TTL_MS = "" # Example: "180" + ENABLE_K_ANON_QUERY_CACHE = "" # Example: "true" # Coordinator-based attestation flags. # These flags are production-ready and you do not need to change them. diff --git a/production/deploy/gcp/terraform/environment/demo/seller/seller.tf b/production/deploy/gcp/terraform/environment/demo/seller/seller.tf index e9faf4bd..c42a845e 100644 --- a/production/deploy/gcp/terraform/environment/demo/seller/seller.tf +++ b/production/deploy/gcp/terraform/environment/demo/seller/seller.tf @@ -180,23 +180,23 @@ module "seller" { # "https://buyerC_origin.com":"https://buyerC.com/generateBid.js"}, # "protectedAppSignalsBuyerReportWinJsUrls": {"https://buyerA_origin.com":"https://buyerA.com/generateBid.js"} # }" - UDF_NUM_WORKERS = "" # Example: "64" Must be <=vCPUs in auction_machine_type. - JS_WORKER_QUEUE_LEN = "" # Example: "200". - ROMA_TIMEOUT_MS = "" # Example: "10000" - TELEMETRY_CONFIG = "" # Example: "mode: EXPERIMENT" - COLLECTOR_ENDPOINT = "" # Example: "collector-seller-1-${each.key}.sfe-gcp.com:4317" - ENABLE_OTEL_BASED_LOGGING = "" # Example: "false" - CONSENTED_DEBUG_TOKEN = "" # Example: "". Consented debugging requests increase server load in production. A high QPS of these requests can lead to unhealthy servers. - DEBUG_SAMPLE_RATE_MICRO = "0" - ENABLE_REPORT_WIN_INPUT_NOISING = "" # Example: "true" - K_ANON_TOTAL_NUM_HASH = "" # Example: "1000" - EXPECTED_K_ANON_TO_NON_K_ANON_RATIO = "" # Example: "1.0" - K_ANON_CLIENT_TIME_OUT_MS = "" # Example: "60000" - NUM_K_ANON_SHARDS = "" # Example: "1" - NUM_NON_K_ANON_SHARDS = "" # Example: "1" - TEST_MODE_K_ANON_CACHE_TTL_SECONDS = "" # Example: "180" - TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS = "" # Example: "180" - ENABLE_K_ANON_QUERY_CACHE = "" # Example: "true" + UDF_NUM_WORKERS = "" # Example: "64" Must be <=vCPUs in auction_machine_type. + JS_WORKER_QUEUE_LEN = "" # Example: "200". + ROMA_TIMEOUT_MS = "" # Example: "10000" + TELEMETRY_CONFIG = "" # Example: "mode: EXPERIMENT" + COLLECTOR_ENDPOINT = "" # Example: "collector-seller-1-${each.key}.sfe-gcp.com:4317" + ENABLE_OTEL_BASED_LOGGING = "" # Example: "false" + CONSENTED_DEBUG_TOKEN = "" # Example: "". Consented debugging requests increase server load in production. A high QPS of these requests can lead to unhealthy servers. + DEBUG_SAMPLE_RATE_MICRO = "0" + ENABLE_REPORT_WIN_INPUT_NOISING = "" # Example: "true" + K_ANON_TOTAL_NUM_HASH = "" # Example: "1000" + EXPECTED_K_ANON_TO_NON_K_ANON_RATIO = "" # Example: "1.0" + K_ANON_CLIENT_TIME_OUT_MS = "" # Example: "60000" + NUM_K_ANON_SHARDS = "" # Example: "1" + NUM_NON_K_ANON_SHARDS = "" # Example: "1" + TEST_MODE_K_ANON_CACHE_TTL_MS = "" # Example: "180" + TEST_MODE_NON_K_ANON_CACHE_TTL_MS = "" # Example: "180" + ENABLE_K_ANON_QUERY_CACHE = "" # Example: "true" # Coordinator-based attestation flags. # These flags are production-ready and you do not need to change them. # Reach out to the Privacy Sandbox B&A team to enroll with Coordinators. diff --git a/production/deploy/gcp/terraform/services/autoscaling/main.tf b/production/deploy/gcp/terraform/services/autoscaling/main.tf index ff59877d..99aa625c 100644 --- a/production/deploy/gcp/terraform/services/autoscaling/main.tf +++ b/production/deploy/gcp/terraform/services/autoscaling/main.tf @@ -67,6 +67,7 @@ resource "google_compute_instance_template" "frontends" { enable_vtpm = true } confidential_instance_config { + confidential_instance_type = "SEV" enable_confidential_compute = true } @@ -223,6 +224,7 @@ resource "google_compute_instance_template" "backends" { enable_vtpm = true } confidential_instance_config { + confidential_instance_type = "SEV" enable_confidential_compute = true } diff --git a/services/bidding_service/byob/generate_bid_byob_dispatch_client.cc b/services/bidding_service/byob/generate_bid_byob_dispatch_client.cc index 38d2af64..9f936e27 100644 --- a/services/bidding_service/byob/generate_bid_byob_dispatch_client.cc +++ b/services/bidding_service/byob/generate_bid_byob_dispatch_client.cc @@ -51,29 +51,22 @@ absl::Status GenerateBidByobDispatchClient::LoadSync(std::string version, // Get UDF blob for the given code and register it with the BYOB service. PS_ASSIGN_OR_RETURN(UdfBlob udf_blob, UdfBlob::Create(std::move(code))); - absl::Notification notif; - absl::Status load_status; - PS_ASSIGN_OR_RETURN( - std::string new_code_token, - byob_service_.Register(udf_blob(), notif, load_status, num_workers_)); - // TODO(b/368624844): Make duration configurable by taking in this in Create. - notif.WaitForNotificationWithTimeout(absl::Seconds(120)); + PS_ASSIGN_OR_RETURN(std::string new_code_token, + byob_service_.Register(udf_blob(), num_workers_)); - if (load_status.ok()) { - // Acquire lock before updating info about the most recently loaded code - // blob. - if (code_mutex_.TryLock()) { - code_token_ = std::move(new_code_token); - code_version_ = std::move(version); - code_hash_ = new_code_hash; - code_mutex_.Unlock(); - } + // Acquire lock before updating info about the most recently loaded code + // blob. + if (code_mutex_.TryLock()) { + code_token_ = std::move(new_code_token); + code_version_ = std::move(version); + code_hash_ = new_code_hash; + code_mutex_.Unlock(); } - return load_status; + return absl::OkStatus(); } absl::Status GenerateBidByobDispatchClient::Execute( - roma_service::GenerateProtectedAudienceBidRequest request, + const roma_service::GenerateProtectedAudienceBidRequest& request, absl::Duration timeout, absl::AnyInvocable< void(absl::StatusOr< @@ -88,7 +81,7 @@ absl::Status GenerateBidByobDispatchClient::Execute( response) mutable { std::move(callback)(std::move(response)); }, - std::move(request), /*metadata=*/{}, code_token_) + request, /*metadata=*/{}, code_token_) .status(); } diff --git a/services/bidding_service/byob/generate_bid_byob_dispatch_client.h b/services/bidding_service/byob/generate_bid_byob_dispatch_client.h index 6d20a9b6..614cbd28 100644 --- a/services/bidding_service/byob/generate_bid_byob_dispatch_client.h +++ b/services/bidding_service/byob/generate_bid_byob_dispatch_client.h @@ -67,7 +67,7 @@ class GenerateBidByobDispatchClient // processed by the implementing class. This should not be confused // with the output of the execution itself, which is sent to callback. absl::Status Execute( - roma_service::GenerateProtectedAudienceBidRequest request, + const roma_service::GenerateProtectedAudienceBidRequest& request, absl::Duration timeout, absl::AnyInvocable< void(absl::StatusOr< diff --git a/services/bidding_service/generate_bids_binary_reactor.cc b/services/bidding_service/generate_bids_binary_reactor.cc index f8ea4c6e..5b051511 100644 --- a/services/bidding_service/generate_bids_binary_reactor.cc +++ b/services/bidding_service/generate_bids_binary_reactor.cc @@ -64,31 +64,35 @@ BuildProtectedAudienceBidRequest(RawRequest& raw_request, std::move(*ig_for_bidding.mutable_trusted_bidding_signals()); // Populate (oneof) device signals. - if (ig_for_bidding.has_android_signals() && - ig_for_bidding.android_signals().IsInitialized()) { + if (ig_for_bidding.has_android_signals_for_bidding() && + ig_for_bidding.android_signals_for_bidding().IsInitialized()) { roma_service::ProtectedAudienceAndroidSignals* android_signals = bid_request.mutable_android_signals(); android_signals->set_top_level_seller(raw_request.top_level_seller()); - } else if (ig_for_bidding.has_browser_signals() && - ig_for_bidding.browser_signals().IsInitialized()) { + } else if (ig_for_bidding.has_browser_signals_for_bidding() && + ig_for_bidding.browser_signals_for_bidding().IsInitialized()) { roma_service::ProtectedAudienceBrowserSignals* browser_signals = bid_request.mutable_browser_signals(); browser_signals->set_top_window_hostname(raw_request.publisher_name()); browser_signals->set_seller(raw_request.seller()); browser_signals->set_top_level_seller(raw_request.top_level_seller()); browser_signals->set_join_count( - ig_for_bidding.browser_signals().join_count()); + ig_for_bidding.browser_signals_for_bidding().join_count()); browser_signals->set_bid_count( - ig_for_bidding.browser_signals().bid_count()); - if (ig_for_bidding.browser_signals().has_recency_ms()) { + ig_for_bidding.browser_signals_for_bidding().bid_count()); + if (ig_for_bidding.browser_signals_for_bidding().has_recency_ms()) { browser_signals->set_recency( - ig_for_bidding.browser_signals().recency_ms()); + ig_for_bidding.browser_signals_for_bidding().recency_ms()); } else { - browser_signals->set_recency(ig_for_bidding.browser_signals().recency() * - 1000); + browser_signals->set_recency( + ig_for_bidding.browser_signals_for_bidding().recency() * 1000); } - *browser_signals->mutable_prev_wins() = std::move( - *ig_for_bidding.mutable_browser_signals()->mutable_prev_wins()); + *browser_signals->mutable_prev_wins() = + std::move(*ig_for_bidding.mutable_browser_signals_for_bidding() + ->mutable_prev_wins()); + *browser_signals->mutable_prev_wins_ms() = + std::move(*ig_for_bidding.mutable_browser_signals_for_bidding() + ->mutable_prev_wins_ms()); browser_signals->set_multi_bid_limit(raw_request.multi_bid_limit() > 0 ? raw_request.multi_bid_limit() : kDefaultMultiBidLimit); @@ -287,7 +291,7 @@ void GenerateBidsBinaryReactor::ExecuteForInterestGroup(int ig_index) { // Make asynchronous execute call using the BYOB client. PS_VLOG(kNoisyInfo) << "Starting UDF execution for IG: " << ig_name; absl::Status execute_status = byob_client_->Execute( - std::move(bid_request), roma_timeout_duration_, + bid_request, roma_timeout_duration_, [this, ig_index, ig_name, logging_enabled, debug_reporting_enabled]( absl::StatusOr bid_response_status) mutable { diff --git a/services/bidding_service/generate_bids_binary_reactor_test.cc b/services/bidding_service/generate_bids_binary_reactor_test.cc index 2091492e..a031b248 100644 --- a/services/bidding_service/generate_bids_binary_reactor_test.cc +++ b/services/bidding_service/generate_bids_binary_reactor_test.cc @@ -319,14 +319,19 @@ TEST_F(GenerateBidsBinaryReactorTest, CreatesRequestForBrowser) { EXPECT_EQ(request.browser_signals().multi_bid_limit(), 2); EXPECT_EQ(request.browser_signals().seller(), raw_request.seller()); EXPECT_TRUE(request.browser_signals().top_level_seller().empty()); - EXPECT_EQ(request.browser_signals().join_count(), - ig_for_bidding.browser_signals().join_count()); + EXPECT_EQ( + request.browser_signals().join_count(), + ig_for_bidding.browser_signals_for_bidding().join_count()); EXPECT_EQ(request.browser_signals().bid_count(), - ig_for_bidding.browser_signals().bid_count()); - EXPECT_EQ(request.browser_signals().recency(), - ig_for_bidding.browser_signals().recency() * 1000); + ig_for_bidding.browser_signals_for_bidding().bid_count()); + EXPECT_EQ( + request.browser_signals().recency(), + ig_for_bidding.browser_signals_for_bidding().recency() * 1000); EXPECT_EQ(request.browser_signals().prev_wins(), - ig_for_bidding.browser_signals().prev_wins()); + ig_for_bidding.browser_signals_for_bidding().prev_wins()); + EXPECT_EQ( + request.browser_signals().prev_wins_ms(), + ig_for_bidding.browser_signals_for_bidding().prev_wins_ms()); std::move(callback)( roma_service::GenerateProtectedAudienceBidResponse()); return absl::OkStatus(); @@ -342,9 +347,10 @@ TEST_F(GenerateBidsBinaryReactorTest, CreatesRequestForBrowserWithRecencyMs) { {ig_for_bidding}, kTestAuctionSignals, kTestBuyerSignals, raw_request, /*enable_debug_reporting=*/true, /*logging_enabled=*/true); ASSERT_EQ(raw_request.interest_group_for_bidding_size(), 1); - ASSERT_TRUE(raw_request.interest_group_for_bidding(0).has_browser_signals()); ASSERT_TRUE(raw_request.interest_group_for_bidding(0) - .browser_signals() + .has_browser_signals_for_bidding()); + ASSERT_TRUE(raw_request.interest_group_for_bidding(0) + .browser_signals_for_bidding() .has_recency_ms()); ASSERT_TRUE(raw_request.top_level_seller().empty()); @@ -361,14 +367,19 @@ TEST_F(GenerateBidsBinaryReactorTest, CreatesRequestForBrowserWithRecencyMs) { raw_request.publisher_name()); EXPECT_EQ(request.browser_signals().seller(), raw_request.seller()); EXPECT_TRUE(request.browser_signals().top_level_seller().empty()); - EXPECT_EQ(request.browser_signals().join_count(), - ig_for_bidding.browser_signals().join_count()); + EXPECT_EQ( + request.browser_signals().join_count(), + ig_for_bidding.browser_signals_for_bidding().join_count()); EXPECT_EQ(request.browser_signals().bid_count(), - ig_for_bidding.browser_signals().bid_count()); - EXPECT_EQ(request.browser_signals().recency(), - ig_for_bidding.browser_signals().recency_ms()); + ig_for_bidding.browser_signals_for_bidding().bid_count()); + EXPECT_EQ( + request.browser_signals().recency(), + ig_for_bidding.browser_signals_for_bidding().recency_ms()); EXPECT_EQ(request.browser_signals().prev_wins(), - ig_for_bidding.browser_signals().prev_wins()); + ig_for_bidding.browser_signals_for_bidding().prev_wins()); + EXPECT_EQ( + request.browser_signals().prev_wins_ms(), + ig_for_bidding.browser_signals_for_bidding().prev_wins_ms()); std::move(callback)( roma_service::GenerateProtectedAudienceBidResponse()); return absl::OkStatus(); @@ -381,14 +392,15 @@ TEST_F(GenerateBidsBinaryReactorTest, CreatesRequestForBrowseForComponentAuction) { GenerateBidsRawRequest raw_request; IGForBidding ig_for_bidding = MakeARandomInterestGroupForBiddingFromBrowser(); - ig_for_bidding.mutable_browser_signals()->clear_recency_ms(); + ig_for_bidding.mutable_browser_signals_for_bidding()->clear_recency_ms(); BuildGenerateBidsRawRequestForComponentAuction( {ig_for_bidding}, kTestAuctionSignals, kTestBuyerSignals, raw_request, false); ASSERT_EQ(raw_request.interest_group_for_bidding_size(), 1); - ASSERT_TRUE(raw_request.interest_group_for_bidding(0).has_browser_signals()); + ASSERT_TRUE(raw_request.interest_group_for_bidding(0) + .has_browser_signals_for_bidding()); ASSERT_FALSE(raw_request.interest_group_for_bidding(0) - .browser_signals() + .browser_signals_for_bidding() .has_recency_ms()); ASSERT_FALSE(raw_request.top_level_seller().empty()); @@ -406,14 +418,19 @@ TEST_F(GenerateBidsBinaryReactorTest, EXPECT_EQ(request.browser_signals().seller(), raw_request.seller()); EXPECT_EQ(request.browser_signals().top_level_seller(), raw_request.top_level_seller()); - EXPECT_EQ(request.browser_signals().join_count(), - ig_for_bidding.browser_signals().join_count()); + EXPECT_EQ( + request.browser_signals().join_count(), + ig_for_bidding.browser_signals_for_bidding().join_count()); EXPECT_EQ(request.browser_signals().bid_count(), - ig_for_bidding.browser_signals().bid_count()); - EXPECT_EQ(request.browser_signals().recency(), - ig_for_bidding.browser_signals().recency() * 1000); + ig_for_bidding.browser_signals_for_bidding().bid_count()); + EXPECT_EQ( + request.browser_signals().recency(), + ig_for_bidding.browser_signals_for_bidding().recency() * 1000); EXPECT_EQ(request.browser_signals().prev_wins(), - ig_for_bidding.browser_signals().prev_wins()); + ig_for_bidding.browser_signals_for_bidding().prev_wins()); + EXPECT_EQ( + request.browser_signals().prev_wins_ms(), + ig_for_bidding.browser_signals_for_bidding().prev_wins_ms()); std::move(callback)( roma_service::GenerateProtectedAudienceBidResponse()); return absl::OkStatus(); @@ -430,9 +447,10 @@ TEST_F(GenerateBidsBinaryReactorTest, {ig_for_bidding}, kTestAuctionSignals, kTestBuyerSignals, raw_request, true); ASSERT_EQ(raw_request.interest_group_for_bidding_size(), 1); - ASSERT_TRUE(raw_request.interest_group_for_bidding(0).has_browser_signals()); ASSERT_TRUE(raw_request.interest_group_for_bidding(0) - .browser_signals() + .has_browser_signals_for_bidding()); + ASSERT_TRUE(raw_request.interest_group_for_bidding(0) + .browser_signals_for_bidding() .has_recency_ms()); ASSERT_FALSE(raw_request.top_level_seller().empty()); @@ -450,14 +468,19 @@ TEST_F(GenerateBidsBinaryReactorTest, EXPECT_EQ(request.browser_signals().seller(), raw_request.seller()); EXPECT_EQ(request.browser_signals().top_level_seller(), raw_request.top_level_seller()); - EXPECT_EQ(request.browser_signals().join_count(), - ig_for_bidding.browser_signals().join_count()); + EXPECT_EQ( + request.browser_signals().join_count(), + ig_for_bidding.browser_signals_for_bidding().join_count()); EXPECT_EQ(request.browser_signals().bid_count(), - ig_for_bidding.browser_signals().bid_count()); - EXPECT_EQ(request.browser_signals().recency(), - ig_for_bidding.browser_signals().recency_ms()); + ig_for_bidding.browser_signals_for_bidding().bid_count()); + EXPECT_EQ( + request.browser_signals().recency(), + ig_for_bidding.browser_signals_for_bidding().recency_ms()); EXPECT_EQ(request.browser_signals().prev_wins(), - ig_for_bidding.browser_signals().prev_wins()); + ig_for_bidding.browser_signals_for_bidding().prev_wins()); + EXPECT_EQ( + request.browser_signals().prev_wins_ms(), + ig_for_bidding.browser_signals_for_bidding().prev_wins_ms()); std::move(callback)( roma_service::GenerateProtectedAudienceBidResponse()); return absl::OkStatus(); @@ -473,7 +496,8 @@ TEST_F(GenerateBidsBinaryReactorTest, CreatesRequestForAndroid) { {ig_for_bidding}, kTestAuctionSignals, kTestBuyerSignals, raw_request, /*enable_debug_reporting=*/false, /*logging_enabled=*/true); ASSERT_EQ(raw_request.interest_group_for_bidding_size(), 1); - ASSERT_TRUE(raw_request.interest_group_for_bidding(0).has_android_signals()); + ASSERT_TRUE(raw_request.interest_group_for_bidding(0) + .has_android_signals_for_bidding()); ASSERT_TRUE(raw_request.top_level_seller().empty()); GenerateBidsRawResponse expected_raw_response; @@ -501,7 +525,8 @@ TEST_F(GenerateBidsBinaryReactorTest, {ig_for_bidding}, kTestAuctionSignals, kTestBuyerSignals, raw_request, true); ASSERT_EQ(raw_request.interest_group_for_bidding_size(), 1); - ASSERT_TRUE(raw_request.interest_group_for_bidding(0).has_android_signals()); + ASSERT_TRUE(raw_request.interest_group_for_bidding(0) + .has_android_signals_for_bidding()); ASSERT_FALSE(raw_request.top_level_seller().empty()); GenerateBidsRawResponse expected_raw_response; diff --git a/services/bidding_service/generate_bids_reactor.cc b/services/bidding_service/generate_bids_reactor.cc index 8c2b276d..40573d4e 100644 --- a/services/bidding_service/generate_bids_reactor.cc +++ b/services/bidding_service/generate_bids_reactor.cc @@ -62,6 +62,7 @@ constexpr char kBidCount[] = "bidCount"; constexpr char kRecency[] = "recency"; constexpr char kMultiBidLimit[] = "multiBidLimit"; constexpr char kPrevWins[] = "prevWins"; +constexpr char kPrevWinsMs[] = "prevWinsMs"; constexpr char kJsonStringEnd[] = R"JSON(",")JSON"; constexpr char kJsonStringValueStart[] = R"JSON(":")JSON"; constexpr char kJsonValueStart[] = R"JSON(":)JSON"; @@ -69,12 +70,11 @@ constexpr char kJsonValueEnd[] = R"JSON(,")JSON"; constexpr char kJsonEmptyString[] = R"JSON("")JSON"; constexpr char kEmptyDeviceSignals[] = R"JSON({})JSON"; -std::string MakeBrowserSignalsForScript(absl::string_view publisher_name, - absl::string_view seller, - absl::string_view top_level_seller, - const BrowserSignals& browser_signals, - uint32_t data_version, - int32_t multi_bid_limit) { +std::string MakeBrowserSignalsForScript( + absl::string_view publisher_name, absl::string_view seller, + absl::string_view top_level_seller, + const BrowserSignalsForBidding& browser_signals, uint32_t data_version, + int32_t multi_bid_limit) { std::string device_signals_str = absl::StrCat( R"JSON({")JSON", kTopWindowHostname, kJsonStringValueStart, publisher_name, kJsonStringEnd, kSeller, kJsonStringValueStart, seller); @@ -82,12 +82,15 @@ std::string MakeBrowserSignalsForScript(absl::string_view publisher_name, absl::StrAppend(&device_signals_str, kJsonStringEnd, kTopLevelSeller, kJsonStringValueStart, top_level_seller); } + int64_t recency_ms; if (browser_signals.has_recency_ms()) { recency_ms = browser_signals.recency_ms(); } else { recency_ms = browser_signals.recency() * 1000; } + + // TODO(b/394397742): Deprecate prevWins in favor of prevWinsMs. absl::StrAppend( &device_signals_str, kJsonStringEnd, kJoinCount, kJsonValueStart, browser_signals.join_count(), kJsonValueEnd, kBidCount, kJsonValueStart, @@ -96,6 +99,9 @@ std::string MakeBrowserSignalsForScript(absl::string_view publisher_name, recency_ms, kJsonValueEnd, kPrevWins, kJsonValueStart, browser_signals.prev_wins().empty() ? kJsonEmptyString : browser_signals.prev_wins(), + kJsonValueEnd, kPrevWinsMs, kJsonValueStart, + browser_signals.prev_wins_ms().empty() ? kJsonEmptyString + : browser_signals.prev_wins_ms(), kJsonValueEnd, kDataVersion, kJsonValueStart, data_version, kJsonValueEnd, kMultiBidLimit, kJsonValueStart, multi_bid_limit > 0 ? multi_bid_limit : kDefaultMultiBidLimit, "}"); @@ -218,21 +224,24 @@ absl::StatusOr BuildGenerateBidRequest( // IG must have device signals to participate in Bidding. google::protobuf::util::MessageDifferencer differencer; - if (interest_group.has_browser_signals() && - interest_group.browser_signals().IsInitialized() && - !differencer.Equals(BrowserSignals::default_instance(), - interest_group.browser_signals())) { + if (interest_group.has_browser_signals_for_bidding() && + interest_group.browser_signals_for_bidding().IsInitialized() && + !differencer.Equals(BrowserSignalsForBidding::default_instance(), + interest_group.browser_signals_for_bidding())) { generate_bid_request.input[ArgIndex(GenerateBidArgs::kDeviceSignals)] = std::make_shared(MakeBrowserSignalsForScript( raw_request.publisher_name(), raw_request.seller(), - raw_request.top_level_seller(), interest_group.browser_signals(), + raw_request.top_level_seller(), + interest_group.browser_signals_for_bidding(), raw_request.data_version(), raw_request.multi_bid_limit())); - } else if (interest_group.has_android_signals() && - interest_group.android_signals().IsInitialized() && - !differencer.Equals(AndroidSignals::default_instance(), - interest_group.android_signals())) { - PS_ASSIGN_OR_RETURN(std::string serialized_android_signals, - ProtoToJson(interest_group.android_signals())); + } else if (interest_group.has_android_signals_for_bidding() && + interest_group.android_signals_for_bidding().IsInitialized() && + !differencer.Equals( + AndroidSignalsForBidding::default_instance(), + interest_group.android_signals_for_bidding())) { + PS_ASSIGN_OR_RETURN( + std::string serialized_android_signals, + ProtoToJson(interest_group.android_signals_for_bidding())); generate_bid_request.input[ArgIndex(GenerateBidArgs::kDeviceSignals)] = std::make_shared((serialized_android_signals.empty()) ? kEmptyDeviceSignals diff --git a/services/bidding_service/generate_bids_reactor_test.cc b/services/bidding_service/generate_bids_reactor_test.cc index 7e94c640..6abae288 100644 --- a/services/bidding_service/generate_bids_reactor_test.cc +++ b/services/bidding_service/generate_bids_reactor_test.cc @@ -54,11 +54,11 @@ constexpr char bar_browser_signals[] = constexpr char kExpectedBrowserSignalsWithRecencyMs[] = R"json({"topWindowHostname":"www.example-publisher.com","seller":"https://www.example-ssp.com","joinCount":5,"bidCount":25,"recency":123456000,"prevWins":[[1,"1868"],[1,"1954"]],"dataVersion":1787})json"; absl::string_view kComponentBrowserSignals = - R"json({"topWindowHostname":"www.example-publisher.com","seller":"https://www.example-ssp.com","topLevelSeller":"https://www.example-top-ssp.com","joinCount":5,"bidCount":25,"recency":1684134092000,"prevWins":[[1,"1689"],[1,"1776"]],"dataVersion":1787,"multiBidLimit":2})json"; + R"json({"topWindowHostname":"www.example-publisher.com","seller":"https://www.example-ssp.com","topLevelSeller":"https://www.example-top-ssp.com","joinCount":5,"bidCount":25,"recency":1684134092000,"prevWins":[[1,"1689"],[1,"1776"]],"prevWinsMs":[[1000,"1689"],[1000,"1776"]],"dataVersion":1787,"multiBidLimit":2})json"; absl::string_view kComponentBrowserSignalsWithRecencyMs = - R"json({"topWindowHostname":"www.example-publisher.com","seller":"https://www.example-ssp.com","topLevelSeller":"https://www.example-top-ssp.com","joinCount":5,"bidCount":25,"recency":123456000,"prevWins":[[1,"1689"],[1,"1776"]],"dataVersion":1787,"multiBidLimit":2})json"; + R"json({"topWindowHostname":"www.example-publisher.com","seller":"https://www.example-ssp.com","topLevelSeller":"https://www.example-top-ssp.com","joinCount":5,"bidCount":25,"recency":123456000,"prevWins":[[1,"1689"],[1,"1776"]],"prevWinsMs":[[1000,"1689"],[1000,"1776"]],"dataVersion":1787,"multiBidLimit":2})json"; absl::string_view kComponentBrowserSignalsWithMultiBidLimit = - R"json({"topWindowHostname":"www.example-publisher.com","seller":"https://www.example-ssp.com","topLevelSeller":"https://www.example-top-ssp.com","joinCount":5,"bidCount":25,"recency":1684134092000,"prevWins":[[1,"1689"],[1,"1776"]],"dataVersion":1787,"multiBidLimit":3})json"; + R"json({"topWindowHostname":"www.example-publisher.com","seller":"https://www.example-ssp.com","topLevelSeller":"https://www.example-top-ssp.com","joinCount":5,"bidCount":25,"recency":1684134092000,"prevWins":[[1,"1689"],[1,"1776"]],"prevWinsMs":[[1000,"1689"],[1000,"1776"]],"dataVersion":1787,"multiBidLimit":3})json"; using ::google::protobuf::TextFormat; using ::google::protobuf::util::MessageToJsonString; @@ -258,6 +258,7 @@ class GenerateBidsReactorTest : public testing::Test { .enable_buyer_debug_url_generation = false, }; } + request_.set_request_ciphertext(raw_request.SerializeAsString()); grpc::CallbackServerContext context; GenerateBidsReactor reactor(&context, dispatcher_, &request_, &response, @@ -306,12 +307,17 @@ IGForBidding GetIGForBiddingFoo() { interest_group.mutable_ad_render_ids()->Add(kIgFooFirstAdRenderId); interest_group.mutable_ad_render_ids()->Add(kIgFooSecondAdRenderId); - BrowserSignals browser_signals; - interest_group.mutable_browser_signals()->set_join_count(5); - interest_group.mutable_browser_signals()->set_bid_count(25); - interest_group.mutable_browser_signals()->set_recency(1684134092); - interest_group.mutable_browser_signals()->set_prev_wins( - MakeRandomPreviousWins(interest_group.ad_render_ids(), true)); + interest_group.mutable_browser_signals_for_bidding()->set_join_count(5); + interest_group.mutable_browser_signals_for_bidding()->set_bid_count(25); + interest_group.mutable_browser_signals_for_bidding()->set_recency(1684134092); + interest_group.mutable_browser_signals_for_bidding()->set_prev_wins( + MakeRandomPreviousWins(interest_group.ad_render_ids(), + /* set_times_to_one= */ true, + /* time_in_ms= */ false)); + interest_group.mutable_browser_signals_for_bidding()->set_prev_wins_ms( + MakeRandomPreviousWins(interest_group.ad_render_ids(), + /* set_times_to_one= */ true)); + return interest_group; } @@ -326,11 +332,17 @@ IGForBidding GetIGForBiddingBar(bool make_browser_signals = true) { interest_group.mutable_ad_render_ids()->Add(kIgBarSecondAdRenderId); if (make_browser_signals) { - interest_group.mutable_browser_signals()->set_join_count(5); - interest_group.mutable_browser_signals()->set_bid_count(25); - interest_group.mutable_browser_signals()->set_recency(1684134093); - interest_group.mutable_browser_signals()->set_prev_wins( - MakeRandomPreviousWins(interest_group.ad_render_ids(), true)); + interest_group.mutable_browser_signals_for_bidding()->set_join_count(5); + interest_group.mutable_browser_signals_for_bidding()->set_bid_count(25); + interest_group.mutable_browser_signals_for_bidding()->set_recency( + 1684134093); + interest_group.mutable_browser_signals_for_bidding()->set_prev_wins( + MakeRandomPreviousWins(interest_group.ad_render_ids(), + /* set_times_to_one= */ true, + /* time_in_ms= */ false)); + interest_group.mutable_browser_signals_for_bidding()->set_prev_wins_ms( + MakeRandomPreviousWins(interest_group.ad_render_ids(), + /* set_times_to_one= */ true)); } return interest_group; } @@ -377,7 +389,7 @@ void CheckCorrectnessOfIg(std::string& serialized_actual, // Expected IG needs trusted bidding signals and device signals cleared since // they will not be in the actual bar. These are not passed as part of the // serialized IG, but as separate parameters to GenerateBid. - expected.clear_DeviceSignals(); + expected.clear_DeviceSignalsForBidding(); expected.clear_trusted_bidding_signals(); // Since UBS will not be equal after re-serialization, clear those as well in // both. @@ -738,7 +750,7 @@ TEST_F(GenerateBidsReactorTest, *ads.mutable_response_ciphertext() = raw_response.SerializeAsString(); std::vector igs; auto ig = GetIGForBiddingBar(); - ig.mutable_browser_signals()->set_recency_ms(123456000); + ig.mutable_browser_signals_for_bidding()->set_recency_ms(123456000); igs.push_back(std::move(ig)); EXPECT_CALL(dispatcher_, BatchExecute) @@ -806,7 +818,7 @@ TEST_F(GenerateBidsReactorTest, InterestGroupForBidding ig_foo = GetIGForBiddingFoo(); AdWithBid bid = GetAdWithBidFromIgFoo(kTestRenderUrl, 1); bid.set_allow_component_auction(true); - ig_foo.mutable_browser_signals()->set_recency_ms(123456000); + ig_foo.mutable_browser_signals_for_bidding()->set_recency_ms(123456000); Response ads; GenerateBidsResponse::GenerateBidsRawResponse raw_response; *raw_response.add_bids() = bid; diff --git a/services/buyer_frontend_service/BUILD b/services/buyer_frontend_service/BUILD index 253f5ef8..6304d12a 100644 --- a/services/buyer_frontend_service/BUILD +++ b/services/buyer_frontend_service/BUILD @@ -56,6 +56,7 @@ cc_library( "//services/common/util:request_metadata", "//services/common/util:request_response_constants", "//services/common/util/priority_vector:priority_vector_utils", + "//services/seller_frontend_service/util:buyer_input_proto_utils", "@com_github_grpc_grpc//:grpc++", "@com_google_absl//absl/functional:any_invocable", "@com_google_absl//absl/status", @@ -102,12 +103,14 @@ cc_test( ], deps = [ ":buyer_frontend_service", + "//api:bidding_auction_servers_cc_proto_builder", "//services/buyer_frontend_service/util:bidding_signals", "//services/buyer_frontend_service/util:buyer_frontend_test_utils", "//services/common/chaffing:transcoding_utils", "//services/common/encryption:mock_crypto_client_wrapper", "//services/common/test:mocks", "//services/common/test:random", + "//services/common/test/utils:proto_utils", "//services/common/test/utils:test_init", "//services/common/test/utils:test_utils", "@com_google_googletest//:gtest_main", diff --git a/services/buyer_frontend_service/buyer_frontend_main.cc b/services/buyer_frontend_service/buyer_frontend_main.cc index cfec7801..5e1646fb 100644 --- a/services/buyer_frontend_service/buyer_frontend_main.cc +++ b/services/buyer_frontend_service/buyer_frontend_main.cc @@ -257,28 +257,9 @@ absl::Status RunServer() { bool enable_bidding_compression = config_client.GetBooleanParameter(ENABLE_BIDDING_COMPRESSION); - if (buyer_tkv_v2_server_addr == kIgnoredPlaceholderValue) { - if (use_tkv_v2_browser) { - return absl::InvalidArgumentError( - "Missing: Buyer Trusted KV server address"); - } - PS_LOG(WARNING, SystemLogContext()) - << "BUYER_TKV_V2_SERVER_ADDR is set to " << kIgnoredPlaceholderValue - << ", this can affect Android auctions"; - // Consider the placeholder string as being empty. - buyer_tkv_v2_server_addr = ""; - } - if (bidding_server_addr.empty()) { return absl::InvalidArgumentError("Missing: Bidding server address"); } - if (!use_tkv_v2_browser && buyer_kv_server_addr.empty()) { - return absl::InvalidArgumentError("Missing: Buyer KV BYOS server address"); - } - if (use_tkv_v2_browser && buyer_tkv_v2_server_addr.empty()) { - return absl::InvalidArgumentError( - "Missing: Buyer Trusted KV server address"); - } BiddingSignalsFetchMode bidding_signals_fetch_mode = BiddingSignalsFetchMode::REQUIRED; if (config_client.GetStringParameter(BIDDING_SIGNALS_FETCH_MODE) == @@ -288,11 +269,17 @@ absl::Status RunServer() { kSignalsNotFetched) { bidding_signals_fetch_mode = BiddingSignalsFetchMode::NOT_FETCHED; } + + if (buyer_tkv_v2_server_addr == kIgnoredPlaceholderValue) { + buyer_tkv_v2_server_addr = ""; + } + if (buyer_kv_server_addr == kIgnoredPlaceholderValue) { + buyer_kv_server_addr = ""; + } + bool tkv_invalid = use_tkv_v2_browser && buyer_tkv_v2_server_addr.empty(); + bool http_kv_invalid = !use_tkv_v2_browser && buyer_kv_server_addr.empty(); if (bidding_signals_fetch_mode != BiddingSignalsFetchMode::NOT_FETCHED && - (buyer_kv_server_addr.empty() || - buyer_kv_server_addr == kIgnoredPlaceholderValue) && - (buyer_tkv_v2_server_addr.empty() || - buyer_tkv_v2_server_addr == kIgnoredPlaceholderValue)) { + (tkv_invalid || http_kv_invalid)) { return absl::InvalidArgumentError( "The server is configured to perform the trusted bidding signals fetch " "(which is the default) yet no host has been provided, BYOS or Trusted " @@ -332,7 +319,7 @@ absl::Status RunServer() { std::make_unique( std::move(buyer_kv_async_http_client)); } else { - PS_LOG(INFO) << "Using TKV V2 for browser traffic"; + PS_LOG(INFO, SystemLogContext()) << "Using TKV V2 for browser traffic"; } if (!buyer_tkv_v2_server_addr.empty()) { @@ -344,10 +331,11 @@ absl::Status RunServer() { kv_async_client = std::make_unique( key_fetcher_manager.get(), std::move(kv_v2_stub)); } else { - PS_LOG(WARNING) << "TKV V2 endpoint not set. All CLIENT_TYPE_ANDROID " - "protected audience requests will fail."; + PS_LOG(WARNING, SystemLogContext()) + << "TKV V2 endpoint not set. All CLIENT_TYPE_ANDROID " + "protected audience requests will fail."; if (use_tkv_v2_browser) { - PS_LOG(WARNING) + PS_LOG(WARNING, SystemLogContext()) << " TKV V2 endpoint not set, but ENABLE_TKV_V2_BROWSER is true. " "All CLIENT_TYPE_BROWSER requests will fail."; } @@ -384,7 +372,7 @@ absl::Status RunServer() { bidding_signals_fetch_mode, config_client.GetBooleanParameter(PROPAGATE_BUYER_SIGNALS_TO_TKV), }, - enable_buyer_frontend_benchmarking); + *executor, enable_buyer_frontend_benchmarking); grpc::EnableDefaultHealthCheckService(true); grpc::reflection::InitProtoReflectionServerBuilderPlugin(); diff --git a/services/buyer_frontend_service/buyer_frontend_service.cc b/services/buyer_frontend_service/buyer_frontend_service.cc index 543b8238..5d93a10f 100644 --- a/services/buyer_frontend_service/buyer_frontend_service.cc +++ b/services/buyer_frontend_service/buyer_frontend_service.cc @@ -33,7 +33,7 @@ BuyerFrontEndService::BuyerFrontEndService( key_fetcher_manager, std::unique_ptr crypto_client, std::unique_ptr kv_async_client, const GetBidsConfig config, - bool enable_benchmarking) + server_common::Executor& executor, bool enable_benchmarking) : bidding_signals_async_provider_( config.bidding_signals_fetch_mode == BiddingSignalsFetchMode::NOT_FETCHED @@ -53,7 +53,8 @@ BuyerFrontEndService::BuyerFrontEndService( kv_async_client_(config.bidding_signals_fetch_mode == BiddingSignalsFetchMode::NOT_FETCHED ? nullptr - : std::move(kv_async_client)) { + : std::move(kv_async_client)), + executor_(executor) { if (config_.is_protected_app_signals_enabled) { protected_app_signals_bidding_async_client_ = std::make_unique( @@ -64,6 +65,7 @@ BuyerFrontEndService::BuyerFrontEndService( BuyerFrontEndService::BuyerFrontEndService(ClientRegistry client_registry, const GetBidsConfig config, + server_common::Executor& executor, bool enable_benchmarking) : bidding_signals_async_provider_( config.bidding_signals_fetch_mode == @@ -80,7 +82,8 @@ BuyerFrontEndService::BuyerFrontEndService(ClientRegistry client_registry, kv_async_client_(config.bidding_signals_fetch_mode == BiddingSignalsFetchMode::NOT_FETCHED ? nullptr - : std::move(client_registry.kv_async_client)) {} + : std::move(client_registry.kv_async_client)), + executor_(executor) {} grpc::ServerUnaryReactor* BuyerFrontEndService::GetBids( grpc::CallbackServerContext* context, const GetBidsRequest* request, @@ -93,7 +96,7 @@ grpc::ServerUnaryReactor* BuyerFrontEndService::GetBids( *bidding_async_client_, config_, protected_app_signals_bidding_async_client_.get(), key_fetcher_manager_.get(), crypto_client_.get(), kv_async_client_.get(), - enable_benchmarking_); + executor_, enable_benchmarking_); reactor->Execute(); return reactor.release(); } diff --git a/services/buyer_frontend_service/buyer_frontend_service.h b/services/buyer_frontend_service/buyer_frontend_service.h index 09a26fdd..6c493dd2 100644 --- a/services/buyer_frontend_service/buyer_frontend_service.h +++ b/services/buyer_frontend_service/buyer_frontend_service.h @@ -24,6 +24,7 @@ #include "services/buyer_frontend_service/providers/bidding_signals_async_provider.h" #include "services/common/clients/bidding_server/bidding_async_client.h" #include "services/common/clients/kv_server/kv_async_client.h" +#include "src/concurrent/executor.h" #include "src/encryption/key_fetcher/interface/key_fetcher_manager_interface.h" namespace privacy_sandbox::bidding_auction_servers { @@ -56,10 +57,12 @@ class BuyerFrontEndService final : public BuyerFrontEnd::CallbackService { key_fetcher_manager, std::unique_ptr crypto_client, std::unique_ptr kv_async_client, - const GetBidsConfig config, bool enable_benchmarking = false); + const GetBidsConfig config, server_common::Executor& executor, + bool enable_benchmarking = false); explicit BuyerFrontEndService(ClientRegistry client_registry, const GetBidsConfig config, + server_common::Executor& executor, bool enable_benchmarking = false); // Returns bid for the top eligible ad candidate. @@ -98,6 +101,7 @@ class BuyerFrontEndService final : public BuyerFrontEnd::CallbackService { std::unique_ptr protected_app_signals_bidding_async_client_; std::unique_ptr kv_async_client_; + server_common::Executor& executor_; }; } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/buyer_frontend_service/buyer_frontend_service_test.cc b/services/buyer_frontend_service/buyer_frontend_service_test.cc index 5192207d..b2ce9aa2 100644 --- a/services/buyer_frontend_service/buyer_frontend_service_test.cc +++ b/services/buyer_frontend_service/buyer_frontend_service_test.cc @@ -191,6 +191,7 @@ class BuyerFrontEndServiceTest : public ::testing::Test { grpc::ClientContext client_context_; GetBidsRequest request_; GetBidsResponse response_; + MockExecutor executor_; }; std::unique_ptr GetValidBiddingAsyncClientMock() { @@ -260,7 +261,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -297,7 +298,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -332,7 +333,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true, ClientType::CLIENT_TYPE_ANDROID); @@ -363,7 +364,7 @@ TEST_F( std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -399,7 +400,7 @@ TEST_F( std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -453,7 +454,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -503,7 +504,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); @@ -555,7 +556,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -590,7 +591,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -643,7 +644,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -678,7 +679,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -728,7 +729,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -763,7 +764,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -816,7 +817,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -851,7 +852,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -878,7 +879,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -913,7 +914,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -941,7 +942,7 @@ TEST_F( std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -980,7 +981,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/true, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -1020,7 +1021,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/false, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -1055,7 +1056,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/false, /*add_protected_audience_input=*/true); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -1084,7 +1085,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - CreateDefaultGetBidsConfig()); + CreateDefaultGetBidsConfig(), executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/false, /*add_protected_audience_input=*/false); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -1112,7 +1113,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); request_ = CreateGetBidsRequest(/*add_protected_signals_input=*/false, /*add_protected_audience_input=*/false); auto start_bfe_result = StartLocalService(&buyer_frontend_service); @@ -1139,7 +1140,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_signals_async_provider), std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client)), - config); + config, executor_); GetBidsRequest::GetBidsRawRequest raw_request; raw_request.set_is_chaff(true); raw_request.mutable_log_context()->set_generation_id(kTestGenerationId); @@ -1177,7 +1178,7 @@ TEST_F(BuyerFrontEndServiceTest, std::move(bidding_async_client), std::move(protected_app_signals_bidding_async_client), std::move(kv_async_client)), - config); + config, executor_); GetBidsRequest::GetBidsRawRequest raw_request; raw_request.set_is_chaff(true); diff --git a/services/buyer_frontend_service/get_bids_unary_reactor.cc b/services/buyer_frontend_service/get_bids_unary_reactor.cc index 3c4ab4ee..dabbf043 100644 --- a/services/buyer_frontend_service/get_bids_unary_reactor.cc +++ b/services/buyer_frontend_service/get_bids_unary_reactor.cc @@ -28,6 +28,7 @@ #include "services/common/util/priority_vector/priority_vector_utils.h" #include "services/common/util/request_metadata.h" #include "services/common/util/request_response_constants.h" +#include "services/seller_frontend_service/util/buyer_input_proto_utils.h" #include "kv_buyer_signals_adapter.h" @@ -96,8 +97,8 @@ void LogIgMetric(const GetBidsRequest::GetBidsRawRequest& raw_request, int ad_render_ids = 0; int component_ads = 0; int igs_count = 0; - for (const BuyerInput::InterestGroup& interest_group : - raw_request.buyer_input().interest_groups()) { + for (const BuyerInputForBidding::InterestGroupForBidding& interest_group : + raw_request.buyer_input_for_bidding().interest_groups()) { igs_count += 1; user_bidding_signals += interest_group.user_bidding_signals().size(); for (const auto& bidding_signal_key : @@ -139,7 +140,7 @@ GetBidsUnaryReactor::GetBidsUnaryReactor( ProtectedAppSignalsBiddingAsyncClient* pas_bidding_async_client, server_common::KeyFetcherManagerInterface* key_fetcher_manager, CryptoClientWrapperInterface* crypto_client, KVAsyncClient* kv_async_client, - bool enable_benchmarking) + server_common::Executor& executor, bool enable_benchmarking) : context_(&context), request_(&get_bids_request), get_bids_response_(&get_bids_response), @@ -182,7 +183,8 @@ GetBidsUnaryReactor::GetBidsUnaryReactor( enable_cancellation_(config.enable_cancellation), enable_enforce_kanon_(config.enable_kanon && raw_request_.enforce_kanon()), - bidding_signals_fetch_mode_(config.bidding_signals_fetch_mode) { + bidding_signals_fetch_mode_(config.bidding_signals_fetch_mode), + executor_(executor) { if (enable_benchmarking) { std::string request_id = FormatTime(absl::Now()); benchmarking_logger_ = @@ -215,12 +217,12 @@ GetBidsUnaryReactor::GetBidsUnaryReactor( BiddingAsyncClient& bidding_async_client, const GetBidsConfig& config, server_common::KeyFetcherManagerInterface* key_fetcher_manager, CryptoClientWrapperInterface* crypto_client, KVAsyncClient* kv_async_client, - bool enable_benchmarking) + server_common::Executor& executor, bool enable_benchmarking) : GetBidsUnaryReactor(context, get_bids_request, get_bids_response, bidding_signals_async_provider, bidding_async_client, config, /*pas_bidding_async_client=*/nullptr, key_fetcher_manager, crypto_client, kv_async_client, - enable_benchmarking) {} + executor, enable_benchmarking) {} void GetBidsUnaryReactor::OnAllBidsDone(bool any_successful_bids) { if (enable_cancellation_ && context_->IsCancelled()) { @@ -362,10 +364,12 @@ grpc::Status GetBidsUnaryReactor::DecryptRequest() { int GetBidsUnaryReactor::GetNumberOfMaximumBiddingCalls() { int num_expected_calls = 0; if (config_.is_protected_audience_enabled && - raw_request_.buyer_input().interest_groups_size() > 0) { + (!raw_request_.buyer_input().interest_groups().empty() || + !raw_request_.buyer_input_for_bidding().interest_groups().empty())) { PS_VLOG(5, log_context_) << "Interest groups found in the request"; ++num_expected_calls; } + if (config_.is_protected_app_signals_enabled && raw_request_.has_protected_app_signals_buyer_input()) { PS_VLOG(5, log_context_) << "Protected app signals found in the request"; @@ -407,6 +411,11 @@ void GetBidsUnaryReactor::CancellableExecute() { return; } + if (raw_request_.has_buyer_input()) { + *raw_request_.mutable_buyer_input_for_bidding() = + ToBuyerInputForBidding(raw_request_.buyer_input()); + } + const int num_bidding_calls = GetNumberOfMaximumBiddingCalls(); if (num_bidding_calls == 0) { // This is unlikely to happen since we already have this check in place @@ -444,8 +453,6 @@ void GetBidsUnaryReactor::CancellableExecuteChaffRequest() { if (generator_.has_value()) { chaff_request_duration = request_duration_dist(*generator_); } - absl::SleepFor(absl::Milliseconds((int)chaff_request_duration)); - // Produce chaff response. size_t chaff_response_size = 0; std::uniform_int_distribution chaff_response_size_dist( @@ -472,10 +479,12 @@ void GetBidsUnaryReactor::CancellableExecuteChaffRequest() { return; } - PS_VLOG(kNoisyInfo, log_context_) << "Chaff request encrypted successfully"; + PS_VLOG(kNoisyInfo, log_context_) << "Chaff response encrypted successfully"; get_bids_response_->set_response_ciphertext( aead_encrypt->encrypted_data().ciphertext()); - FinishWithStatus(grpc::Status::OK); + + executor_.RunAfter(absl::Milliseconds((int)chaff_request_duration), + [this]() { FinishWithStatus(grpc::Status::OK); }); } void GetBidsUnaryReactor::LogInitiatedRequestErrorMetrics( @@ -646,7 +655,8 @@ void GetBidsUnaryReactor::MayGetProtectedAudienceBidsV1( } void GetBidsUnaryReactor::HandleV2Failure(const absl::Status& status, - absl::string_view error_message) { + absl::string_view error_message, + EventMessage::KvSignal bid_signal) { LogIfError( metric_context_->AccumulateMetric( 1, metric::kBfeBiddingSignalsResponseError)); @@ -656,6 +666,9 @@ void GetBidsUnaryReactor::HandleV2Failure(const absl::Status& status, async_task_tracker_.TaskCompleted(TaskStatus::ERROR, [this, &status]() { bid_errors_.push_back(status.ToString()); }); + if (server_common::log::PS_VLOG_IS_ON(kKVLog)) { + log_context_.SetEventMessageField(std::move(bid_signal)); + } } void GetBidsUnaryReactor::MayGetProtectedAudienceBidsV2( @@ -681,12 +694,16 @@ void GetBidsUnaryReactor::MayGetProtectedAudienceBidsV2( << maybe_bidding_signals_request.status(); return; } + EventMessage::KvSignal bid_signal = KvEventMessage( + (*maybe_bidding_signals_request)->ShortDebugString(), log_context_); + auto status = kv_async_client_->ExecuteInternal( *std::move(maybe_bidding_signals_request), client_context, CancellationWrapper( context_, enable_cancellation_, - [this, kv_request](KVLookUpResult kv_look_up_result, - ResponseMetadata response_metadata) mutable { + [this, kv_request, bid_signal = std::move(bid_signal)]( + KVLookUpResult kv_look_up_result, + ResponseMetadata response_metadata) mutable { { // Only logs KV request and response sizes if fetching signals // succeeds. @@ -699,16 +716,21 @@ void GetBidsUnaryReactor::MayGetProtectedAudienceBidsV2( } if (!kv_look_up_result.ok()) { HandleV2Failure(kv_look_up_result.status(), - "GetBiddingSignals request failed with status:"); + "GetBiddingSignals request failed with status:", + std::move(bid_signal)); return; } auto signals = ConvertV2BiddingSignalsToV1(*std::move(kv_look_up_result)); if (!signals.ok()) { HandleV2Failure(signals.status(), - "Failed converting TKV response. "); + "Failed converting TKV response. ", + std::move(bid_signal)); return; } + SetKvEventMessage("KVAsyncGrpcClient", + *((*signals)->trusted_signals), + std::move(bid_signal), log_context_); // Sends protected audience bid request to bidding service. PrepareAndGenerateProtectedAudienceBid(*std::move(signals)); }, @@ -733,7 +755,8 @@ void GetBidsUnaryReactor::MayGetProtectedAudienceBids() { return; } - if (raw_request_.buyer_input().interest_groups().empty()) { + if (raw_request_.buyer_input().interest_groups().empty() && + raw_request_.buyer_input_for_bidding().interest_groups().empty()) { PS_VLOG(kNoisyWarn, log_context_) << "No interest groups found, skipping bidding for protected audience"; return; @@ -758,7 +781,7 @@ void GetBidsUnaryReactor::PrepareAndGenerateProtectedAudienceBid( uint32_t data_version = bidding_signals->data_version; absl::StatusOr parsed_bidding_signals = ParseTrustedBiddingSignals(std::move(bidding_signals), - raw_request_.buyer_input()); + raw_request_.buyer_input_for_bidding()); if (parsed_bidding_signals.ok()) { get_bids_raw_response_->mutable_update_interest_group_list()->Swap( &(parsed_bidding_signals->update_igs)); diff --git a/services/buyer_frontend_service/get_bids_unary_reactor.h b/services/buyer_frontend_service/get_bids_unary_reactor.h index 3d98c44f..0485c62d 100644 --- a/services/buyer_frontend_service/get_bids_unary_reactor.h +++ b/services/buyer_frontend_service/get_bids_unary_reactor.h @@ -43,6 +43,7 @@ #include "services/common/util/async_task_tracker.h" #include "services/common/util/cancellation_wrapper.h" #include "services/common/util/client_contexts.h" +#include "src/concurrent/executor.h" #include "src/encryption/key_fetcher/interface/key_fetcher_manager_interface.h" namespace privacy_sandbox::bidding_auction_servers { @@ -93,7 +94,8 @@ class GetBidsUnaryReactor : public grpc::ServerUnaryReactor { BiddingAsyncClient& bidding_async_client, const GetBidsConfig& config, server_common::KeyFetcherManagerInterface* key_fetcher_manager, CryptoClientWrapperInterface* crypto_client, - KVAsyncClient* kv_async_client, bool enable_benchmarking = false); + KVAsyncClient* kv_async_client, server_common::Executor& executor, + bool enable_benchmarking = false); explicit GetBidsUnaryReactor( grpc::CallbackServerContext& context, @@ -105,7 +107,8 @@ class GetBidsUnaryReactor : public grpc::ServerUnaryReactor { ProtectedAppSignalsBiddingAsyncClient* pas_bidding_async_client, server_common::KeyFetcherManagerInterface* key_fetcher_manager, CryptoClientWrapperInterface* crypto_client, - KVAsyncClient* kv_async_client, bool enable_benchmarking = false); + KVAsyncClient* kv_async_client, server_common::Executor& executor, + bool enable_benchmarking = false); // GetBidsUnaryReactor is neither copyable nor movable. GetBidsUnaryReactor(const GetBidsUnaryReactor&) = delete; @@ -249,7 +252,8 @@ class GetBidsUnaryReactor : public grpc::ServerUnaryReactor { void MayGetProtectedAudienceBidsV2( const BiddingSignalsRequest& bidding_signals_request); void HandleV2Failure(const absl::Status& status, - absl::string_view error_message); + absl::string_view error_message, + EventMessage::KvSignal bid_signal); // Compression used in the request object; the response will use the same. CompressionType compression_type_; @@ -265,6 +269,8 @@ class GetBidsUnaryReactor : public grpc::ServerUnaryReactor { // Should the debug data be exported based on reply from bidding bool should_export_debug_ = false; + + server_common::Executor& executor_; }; } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/buyer_frontend_service/get_bids_unary_reactor_test.cc b/services/buyer_frontend_service/get_bids_unary_reactor_test.cc index ad13f0a1..3075bead 100644 --- a/services/buyer_frontend_service/get_bids_unary_reactor_test.cc +++ b/services/buyer_frontend_service/get_bids_unary_reactor_test.cc @@ -19,6 +19,7 @@ #include #include "absl/synchronization/notification.h" +#include "api/bidding_auction_servers_cc_proto_builder.h" #include "google/protobuf/util/message_differencer.h" #include "gtest/gtest.h" #include "services/buyer_frontend_service/util/buyer_frontend_test_utils.h" @@ -31,10 +32,10 @@ #include "services/common/metric/server_definition.h" #include "services/common/test/mocks.h" #include "services/common/test/random.h" +#include "services/common/test/utils/proto_utils.h" #include "services/common/test/utils/test_init.h" #include "services/common/test/utils/test_utils.h" #include "src/encryption/key_fetcher/interface/key_fetcher_manager_interface.h" - namespace privacy_sandbox::bidding_auction_servers { namespace { @@ -184,8 +185,9 @@ class GetBidUnaryReactorTest : public ::testing::Test { SetupMockCryptoClientWrapper(*crypto_client_); raw_request_ = MakeARandomGetBidsRawRequest(); - auto interest_group = - raw_request_.mutable_buyer_input()->mutable_interest_groups()->Add(); + auto interest_group = raw_request_.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->Add(); interest_group->set_name(kTestInterestGroupName); interest_group->add_bidding_signals_keys("key"); request_.set_request_ciphertext(raw_request_.SerializeAsString()); @@ -206,6 +208,7 @@ class GetBidUnaryReactorTest : public ::testing::Test { key_fetcher_manager_; std::unique_ptr kv_async_client_ = std::make_unique(); + MockExecutor executor_; }; TEST_F(GetBidUnaryReactorTest, LoadsBiddingSignalsAndCallsBiddingServer) { @@ -230,7 +233,7 @@ TEST_F(GetBidUnaryReactorTest, LoadsBiddingSignalsAndCallsBiddingServer) { GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); // Wait for reactor to set response_. @@ -261,7 +264,7 @@ TEST_F(GetBidUnaryReactorTest, LoadsBiddingSignalsAndCallsBiddingServerV2) { GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); // Wait for reactor to set response_. notification.WaitForNotification(); @@ -304,7 +307,7 @@ TEST_F(GetBidUnaryReactorTest, GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); // Wait for reactor to set response_. notification.WaitForNotification(); @@ -333,7 +336,7 @@ TEST_F(GetBidUnaryReactorTest, GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); // Wait for reactor to set response_. notification.WaitForNotification(); @@ -376,7 +379,7 @@ TEST_F(GetBidUnaryReactorTest, GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); // Wait for reactor to set response_. notification.WaitForNotification(); @@ -412,7 +415,7 @@ TEST_F(GetBidUnaryReactorTest, GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); // Wait for reactor to set response_. notification.WaitForNotification(); @@ -469,7 +472,7 @@ TEST_F(GetBidUnaryReactorTest, VerifyLogContextPropagates) { GetBidsUnaryReactor get_bids_unary_reactor( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); get_bids_unary_reactor.Execute(); notification.WaitForNotification(); } @@ -513,7 +516,7 @@ TEST_F(GetBidUnaryReactorTest, VerifyLogContextPropagatesV2) { GetBidsUnaryReactor get_bids_unary_reactor( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); get_bids_unary_reactor.Execute(); notification.WaitForNotification(); } @@ -538,10 +541,18 @@ TEST_F(GetBidUnaryReactorTest, HandleChaffRequest) { config_proto)) ->Get(&request); + EXPECT_CALL(executor_, RunAfter) + .Times(1) + .WillOnce([](const absl::Duration duration, + absl::AnyInvocable closure) { + closure(); + server_common::TaskId id; + return id; + }); GetBidsUnaryReactor class_under_test( context_, request, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); ASSERT_FALSE(response_.response_ciphertext().empty()); @@ -582,10 +593,18 @@ TEST_F(GetBidUnaryReactorTest, HandleChaffRequestV2) { config_proto)) ->Get(&request); + EXPECT_CALL(executor_, RunAfter) + .Times(1) + .WillOnce([](const absl::Duration duration, + absl::AnyInvocable closure) { + closure(); + server_common::TaskId id; + return id; + }); GetBidsUnaryReactor class_under_test( context_, request, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); ASSERT_FALSE(response_.response_ciphertext().empty()); @@ -627,8 +646,9 @@ TEST_F(GetBidUnaryReactorTest, ValidatePriorityVectorFiltering) { } })JSON"; - auto interest_group = - raw_request_.mutable_buyer_input()->mutable_interest_groups()->Add(); + auto interest_group = raw_request_.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->Add(); interest_group->set_name("test_ig_2"); interest_group->add_bidding_signals_keys("key"); @@ -664,8 +684,97 @@ TEST_F(GetBidUnaryReactorTest, ValidatePriorityVectorFiltering) { GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); + + class_under_test.Execute(); + notification.WaitForNotification(); +} +TEST_F(GetBidUnaryReactorTest, ValidatePriorityVectorFilteringV2) { + get_bids_config_.is_tkv_v2_browser_enabled = true; + std::string compression_group = + R"JSON( + [ + { + "id": 0, + "keyGroupOutputs": [ + { + "tags": [ + "keys" + ], + "keyValues": { + "key": { + "value": "[123,456]" + } + } + }, + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "test_ig": { + "value": "{\"priorityVector\":{\"entry\":-1}, \"updateIfOlderThanMs\": 123}" + } + } + } + ] + }, + { + "id": 1, + "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "test_ig_2": { + "value": "{\"priorityVector\":{\"entry\":1}, \"updateIfOlderThanMs\": 123}" + } + } + } + ] + } + ])JSON"; + kv_server::v2::GetValuesResponse response = + ParseTextOrDie( + absl::StrFormat(kCompressionGroupWrapper, + absl::CEscape(RemoveWhiteSpaces(compression_group)))); + SetupBiddingProviderMockV2(kv_async_client_.get(), response); + + *raw_request_.mutable_buyer_input()->mutable_interest_groups()->Add() = + BuyerInput_InterestGroupBuilder() + .SetName("test_ig_2") + .AddBiddingSignalsKeys("key"); + raw_request_.set_priority_signals(R"JSON({"entry": 1.0})JSON"); + request_.set_request_ciphertext(raw_request_.SerializeAsString()); + + absl::Notification notification; + EXPECT_CALL(bidding_client_mock_, ExecuteInternal) + .WillOnce([¬ification]( + std::unique_ptr + generate_bids_raw_request, + grpc::ClientContext* context, auto on_done, + absl::Duration timeout, RequestConfig request_config) { + // 'test_ig_2's is the only IG with a priority greater than zero, so it + // should not have been filtered out. + EXPECT_EQ(generate_bids_raw_request->interest_group_for_bidding_size(), + 1); + EXPECT_EQ( + generate_bids_raw_request->interest_group_for_bidding(0).name(), + "test_ig_2"); + + std::move(on_done)( + std::make_unique(), + /*response_metadata=*/{}); + notification.Notify(); + return absl::OkStatus(); + }); + get_bids_config_.priority_vector_enabled = true; + GetBidsUnaryReactor class_under_test( + context_, request_, response_, &bidding_signals_provider_, + bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); notification.WaitForNotification(); } @@ -681,7 +790,7 @@ TEST_F(GetBidUnaryReactorTest, DoesNotCallBiddingForEmptyBiddingSignals) { GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); } @@ -722,7 +831,7 @@ TEST_F(GetBidUnaryReactorTest, GetBidsUnaryReactor class_under_test( context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); notification.WaitForNotification(); @@ -764,7 +873,7 @@ TEST_F(GetBidUnaryReactorTest, SkipsKVLookupWhenSignalsFetchModeNotFetched) { GetBidsUnaryReactor class_under_test( context_, request_, response_, /*bidding_signals_async_provider=*/nullptr, bidding_client_mock_, get_bids_config_, key_fetcher_manager_.get(), - crypto_client_.get(), /*kv_async_client=*/nullptr); + crypto_client_.get(), /*kv_async_client=*/nullptr, executor_); class_under_test.Execute(); notification.WaitForNotification(); @@ -812,6 +921,7 @@ class GetProtectedAppSignalsTest : public ::testing::Test { key_fetcher_manager_; std::unique_ptr kv_async_client_ = std::make_unique(); + MockExecutor executor_; }; auto EqProtectedAppSignals(const ProtectedAppSignals& expected) { @@ -892,7 +1002,7 @@ TEST_F(GetProtectedAppSignalsTest, CorrectGenerateBidSentToBiddingService) { context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, &protected_app_signals_bidding_client_mock_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); } @@ -916,7 +1026,7 @@ TEST_F(GetProtectedAppSignalsTest, TimeoutIsRespected) { context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, &protected_app_signals_bidding_client_mock_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); } @@ -955,7 +1065,7 @@ TEST_F(GetProtectedAppSignalsTest, RespectsFeatureFlagOff) { context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, &protected_app_signals_bidding_client_mock_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); notification.WaitForNotification(); } @@ -984,7 +1094,7 @@ TEST_F(GetProtectedAppSignalsTest, RespectsProtectedAudienceFeatureFlagOff) { context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, &protected_app_signals_bidding_client_mock_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); } @@ -1041,7 +1151,7 @@ TEST_F(GetProtectedAppSignalsTest, GetBidsResponseAggregatedBackToSfe) { context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, &protected_app_signals_bidding_client_mock_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); bids_counter.Wait(); @@ -1128,7 +1238,7 @@ TEST_F(GetProtectedAppSignalsTest, context_, request_, response_, &bidding_signals_provider_, bidding_client_mock_, get_bids_config_, &protected_app_signals_bidding_client_mock_, key_fetcher_manager_.get(), - crypto_client_.get(), kv_async_client_.get()); + crypto_client_.get(), kv_async_client_.get(), executor_); class_under_test.Execute(); bids_counter.Wait(); diff --git a/services/buyer_frontend_service/kv_buyer_signals_adapter.cc b/services/buyer_frontend_service/kv_buyer_signals_adapter.cc index 9b1e553b..20c9b23a 100644 --- a/services/buyer_frontend_service/kv_buyer_signals_adapter.cc +++ b/services/buyer_frontend_service/kv_buyer_signals_adapter.cc @@ -30,6 +30,7 @@ using std::vector; namespace { inline constexpr char kKeys[] = "keys"; +inline constexpr char kInterestGroupNames[] = "interestGroupNames"; inline constexpr char kClient[] = "Bna.PA.Buyer.20240930"; inline constexpr char kHostname[] = "hostname"; inline constexpr char kClientType[] = "client_type"; @@ -51,9 +52,9 @@ kv_server::v2::GetValuesRequest GetRequest( return req; } -UDFArgument BuildArgument(vector keys) { +UDFArgument BuildArgument(std::string tag, vector keys) { UDFArgument arg; - arg.mutable_tags()->add_values()->set_string_value(kKeys); + arg.mutable_tags()->add_values()->set_string_value(std::move(tag)); auto* key_list = arg.mutable_data()->mutable_list_value(); for (auto& key : keys) { key_list->add_values()->set_string_value(std::move(key)); @@ -61,7 +62,7 @@ UDFArgument BuildArgument(vector keys) { return arg; } -absl::Status ValidateInterestGroups(const BuyerInput& buyer_input) { +absl::Status ValidateInterestGroups(const BuyerInputForBidding& buyer_input) { if (buyer_input.interest_groups().empty()) { return absl::InvalidArgumentError("No interest groups in the buyer input"); } @@ -78,8 +79,9 @@ absl::Status ValidateInterestGroups(const BuyerInput& buyer_input) { absl::StatusOr> ConvertV2BiddingSignalsToV1( std::unique_ptr response) { - PS_ASSIGN_OR_RETURN(auto trusted_signals, - ConvertKvV2ResponseToV1String({kKeys}, *response)); + PS_ASSIGN_OR_RETURN( + auto trusted_signals, + ConvertKvV2ResponseToV1String({kKeys, kInterestGroupNames}, *response)); return std::make_unique(BiddingSignals{ std::make_unique(std::move(trusted_signals))}); } @@ -88,7 +90,8 @@ absl::StatusOr> CreateV2BiddingRequest(const BiddingSignalsRequest& bidding_signals_request, bool propagate_buyer_signals_to_tkv) { auto& bids_request = bidding_signals_request.get_bids_raw_request_; - PS_RETURN_IF_ERROR(ValidateInterestGroups(bids_request.buyer_input())); + PS_RETURN_IF_ERROR( + ValidateInterestGroups(bids_request.buyer_input_for_bidding())); std::unique_ptr req = std::make_unique( GetRequest(bids_request)); @@ -105,12 +108,14 @@ CreateV2BiddingRequest(const BiddingSignalsRequest& bidding_signals_request, int compression_and_partition_id = 0; // TODO (b/369181315): this needs to be reworked to include multiple IGs's // keys per partition. - for (auto& ig : bids_request.buyer_input().interest_groups()) { + for (auto& ig : bids_request.buyer_input_for_bidding().interest_groups()) { kv_server::v2::RequestPartition* partition = req->add_partitions(); + *partition->add_arguments() = + BuildArgument(kInterestGroupNames, {ig.name()}); for (auto& key : ig.bidding_signals_keys()) { std::vector keys; keys.push_back(key); - *partition->add_arguments() = BuildArgument(std::move(keys)); + *partition->add_arguments() = BuildArgument(kKeys, std::move(keys)); partition->set_id(compression_and_partition_id); partition->set_compression_group_id(compression_and_partition_id); } diff --git a/services/buyer_frontend_service/kv_buyer_signals_adapter_test.cc b/services/buyer_frontend_service/kv_buyer_signals_adapter_test.cc index 0d1000b4..23b1daae 100644 --- a/services/buyer_frontend_service/kv_buyer_signals_adapter_test.cc +++ b/services/buyer_frontend_service/kv_buyer_signals_adapter_test.cc @@ -24,6 +24,7 @@ #include "google/protobuf/text_format.h" #include "gtest/gtest.h" #include "services/common/test/utils/test_utils.h" +#include "services/common/util/json_util.h" #include "src/core/test/utils/proto_test_utils.h" namespace privacy_sandbox::bidding_auction_servers { @@ -43,6 +44,16 @@ TEST(KvBuyerSignalsAdapter, Convert) { { "id": 0, "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_0": { + "value": "{\"priorityVector\":{\"someSignal\":0}, \"updateIfOlderThanMs\": 10000}" + } + } + }, { "tags": [ "keys" @@ -69,6 +80,16 @@ TEST(KvBuyerSignalsAdapter, Convert) { { "id": 1, "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_1": { + "value": "{\"priorityVector\":{\"someSignal\":1}, \"updateIfOlderThanMs\": 10000}" + } + } + }, { "tags": [ "keys" @@ -95,18 +116,28 @@ TEST(KvBuyerSignalsAdapter, Convert) { CHECK_OK(result) << result.status(); std::string expected_parsed_signals = R"json( - { - "keys": { - "hello": { - "value": "world" - }, - "hello2": { - "value": "world2" - } + { + "keys": { + "hello": "world", + "hello2": "world2" + }, + "perInterestGroupData": { + "ig_name_0": { + "priorityVector": { "someSignal": 0 }, + "updateIfOlderThanMs": 10000 + }, + "ig_name_1": { + "priorityVector": { "someSignal": 1 }, + "updateIfOlderThanMs": 10000 + } } })json"; - ASSERT_EQ(*((*result)->trusted_signals), - RemoveWhiteSpaces(expected_parsed_signals)); + auto actual = ParseJsonString(*((*result)->trusted_signals)); + auto expected = ParseJsonString(expected_parsed_signals); + ASSERT_TRUE(actual.ok()) << actual.status(); + ASSERT_TRUE(expected.ok()) << expected.status(); + ASSERT_EQ(*actual, *expected) + << SerializeJsonDoc(*actual) << SerializeJsonDoc(*expected); } TEST(KvBuyerSignalsAdapter, MultipleCompressionGroups) { @@ -116,6 +147,16 @@ TEST(KvBuyerSignalsAdapter, MultipleCompressionGroups) { { "id": 0, "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_0": { + "value": "{\"priorityVector\":{\"someSignal\":0}, \"updateIfOlderThanMs\": 10000}" + } + } + }, { "tags": [ "keys" @@ -142,6 +183,16 @@ TEST(KvBuyerSignalsAdapter, MultipleCompressionGroups) { { "id": 1, "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_1": { + "value": "{\"priorityVector\":{\"someSignal\":1}, \"updateIfOlderThanMs\": 10000}" + } + } + }, { "tags": [ "keys" @@ -161,6 +212,16 @@ TEST(KvBuyerSignalsAdapter, MultipleCompressionGroups) { { "id": 3, "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_2": { + "value": "{\"priorityVector\":{\"someSignal\":2}, \"updateIfOlderThanMs\": 10000}" + } + } + }, { "tags": [ "keys" @@ -187,6 +248,16 @@ TEST(KvBuyerSignalsAdapter, MultipleCompressionGroups) { { "id": 4, "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_3": { + "value": "{\"priorityVector\":{\"someSignal\":3}, \"updateIfOlderThanMs\": 10000}" + } + } + }, { "tags": [ "keys" @@ -220,24 +291,34 @@ TEST(KvBuyerSignalsAdapter, MultipleCompressionGroups) { CHECK_OK(result) << result.status(); std::string expected_parsed_signals = R"json( - { - "keys": { - "hello": { - "value": "world" - }, - "hello2": { - "value": "world2" - }, - "hello44": { - "value": "world44" - }, - "hello24": { - "value": "world24" - } + { + "keys": { + "hello": "world", + "hello2": "world2", + "hello44": "world44", + "hello24": "world24" + }, + "perInterestGroupData": { + "ig_name_0": { + "priorityVector": { "someSignal": 0 }, + "updateIfOlderThanMs": 10000 + }, + "ig_name_1": { + "priorityVector": { "someSignal": 1 }, + "updateIfOlderThanMs": 10000 + }, + "ig_name_2": { + "priorityVector": { "someSignal": 2 }, + "updateIfOlderThanMs": 10000 + }, + "ig_name_3": { + "priorityVector": { "someSignal": 3 }, + "updateIfOlderThanMs": 10000 + } } })json"; - ASSERT_EQ(*((*result)->trusted_signals), - RemoveWhiteSpaces(expected_parsed_signals)); + ASSERT_EQ(ParseJsonString(*((*result)->trusted_signals)), + ParseJsonString(expected_parsed_signals)); } TEST(KvBuyerSignalsAdapter, MalformedJson) { @@ -313,9 +394,9 @@ TEST(KvBuyerSignalsAdapter, EmptyJson) { ASSERT_FALSE(result.ok()); } -BuyerInput::InterestGroup MakeAnInterestGroup(const std::string& id, - int keys_number) { - BuyerInput::InterestGroup interest_group; +BuyerInputForBidding::InterestGroupForBidding MakeAnInterestGroupForBidding( + const std::string& id, int keys_number) { + BuyerInputForBidding::InterestGroupForBidding interest_group; interest_group.set_name(absl::StrCat("ig_name_", id)); for (int i = 0; i < keys_number; i++) { interest_group.mutable_bidding_signals_keys()->Add( @@ -346,6 +427,10 @@ TEST(KvBuyerSignalsAdapter, CreateV2BiddingRequestSuccess) { partitions { id: 0 compression_group_id: 0 + arguments { + tags { values { string_value: "interestGroupNames" } } + data { list_value { values { string_value: "ig_name_0" } } } + } arguments { tags { values { string_value: "keys" } } data { @@ -362,6 +447,10 @@ TEST(KvBuyerSignalsAdapter, CreateV2BiddingRequestSuccess) { partitions { id: 1 compression_group_id: 1 + arguments { + tags { values { string_value: "interestGroupNames" } } + data { list_value { values { string_value: "ig_name_1" } } } + } arguments { tags { values { string_value: "keys" } } data { @@ -397,8 +486,9 @@ TEST(KvBuyerSignalsAdapter, CreateV2BiddingRequestSuccess) { std::move(consented_debug_configuration); *bids_request.mutable_log_context() = std::move(log_context); for (int i = 0; i < 2; i++) { - *bids_request.mutable_buyer_input()->mutable_interest_groups()->Add() = - MakeAnInterestGroup(std::to_string(i), 2); + *bids_request.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->Add() = MakeAnInterestGroupForBidding(std::to_string(i), 2); } BiddingSignalsRequest bidding_signals_request(bids_request, {}); auto maybe_result = CreateV2BiddingRequest(bidding_signals_request); @@ -456,6 +546,10 @@ TEST(KvBuyerSignalsAdapter, partitions { id: 0 compression_group_id: 0 + arguments { + tags { values { string_value: "interestGroupNames" } } + data { list_value { values { string_value: "ig_name_0" } } } + } arguments { tags { values { string_value: "keys" } } data { @@ -472,6 +566,10 @@ TEST(KvBuyerSignalsAdapter, partitions { id: 1 compression_group_id: 1 + arguments { + tags { values { string_value: "interestGroupNames" } } + data { list_value { values { string_value: "ig_name_1" } } } + } arguments { tags { values { string_value: "keys" } } data { @@ -508,8 +606,9 @@ TEST(KvBuyerSignalsAdapter, std::move(consented_debug_configuration); *bids_request.mutable_log_context() = std::move(log_context); for (int i = 0; i < 2; i++) { - *bids_request.mutable_buyer_input()->mutable_interest_groups()->Add() = - MakeAnInterestGroup(std::to_string(i), 2); + *bids_request.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->Add() = MakeAnInterestGroupForBidding(std::to_string(i), 2); } BiddingSignalsRequest bidding_signals_request(bids_request, {}); auto maybe_result = diff --git a/services/buyer_frontend_service/providers/http_bidding_signals_async_provider.cc b/services/buyer_frontend_service/providers/http_bidding_signals_async_provider.cc index d2c571ba..f29115d2 100644 --- a/services/buyer_frontend_service/providers/http_bidding_signals_async_provider.cc +++ b/services/buyer_frontend_service/providers/http_bidding_signals_async_provider.cc @@ -41,12 +41,24 @@ void HttpBiddingSignalsAsyncProvider::Get( absl::StatusOr> output = std::make_unique(); - for (const auto& interest_group : - bidding_signals_request.get_bids_raw_request_.buyer_input() - .interest_groups()) { - request->interest_group_names.emplace(interest_group.name()); - request->keys.insert(interest_group.bidding_signals_keys().begin(), - interest_group.bidding_signals_keys().end()); + if (bidding_signals_request.get_bids_raw_request_ + .has_buyer_input_for_bidding()) { + for (const auto& interest_group : + bidding_signals_request.get_bids_raw_request_.buyer_input_for_bidding() + .interest_groups()) { + request->interest_group_names.emplace(interest_group.name()); + request->keys.insert(interest_group.bidding_signals_keys().begin(), + interest_group.bidding_signals_keys().end()); + } + } else { + // Client supplied the buyer_input field instead. + for (const auto& interest_group : + bidding_signals_request.get_bids_raw_request_.buyer_input() + .interest_groups()) { + request->interest_group_names.emplace(interest_group.name()); + request->keys.insert(interest_group.bidding_signals_keys().begin(), + interest_group.bidding_signals_keys().end()); + } } auto status = http_buyer_kv_async_client_->Execute( diff --git a/services/buyer_frontend_service/util/bidding_signals.cc b/services/buyer_frontend_service/util/bidding_signals.cc index 74d74461..ca978db5 100644 --- a/services/buyer_frontend_service/util/bidding_signals.cc +++ b/services/buyer_frontend_service/util/bidding_signals.cc @@ -33,14 +33,14 @@ namespace { // tries to return any result as flexibly as possible. // Adtechs may be informed of errors in the KV response via metrics. void ParsePerInterestGroupData(rapidjson::Value& per_interest_group_data, - const BuyerInput& buyer_input, + const BuyerInputForBidding& buyer_input, BiddingSignalJsonComponents& result) { if (!per_interest_group_data.IsObject()) { return; } for (int i = 0; i < buyer_input.interest_groups_size(); i++) { - const BuyerInput::InterestGroup& interest_group = + const BuyerInputForBidding::InterestGroupForBidding& interest_group = buyer_input.interest_groups(i); if (interest_group.name().empty()) { continue; @@ -80,7 +80,7 @@ void ParsePerInterestGroupData(rapidjson::Value& per_interest_group_data, absl::StatusOr ParseTrustedBiddingSignals( std::unique_ptr bidding_signals, - const BuyerInput& buyer_input) { + const BuyerInputForBidding& buyer_input) { if (!bidding_signals || !bidding_signals->trusted_signals || bidding_signals->trusted_signals->empty()) { return absl::InvalidArgumentError(kGetBiddingSignalsSuccessButEmpty); diff --git a/services/buyer_frontend_service/util/bidding_signals.h b/services/buyer_frontend_service/util/bidding_signals.h index a97f88c5..3413aa7e 100644 --- a/services/buyer_frontend_service/util/bidding_signals.h +++ b/services/buyer_frontend_service/util/bidding_signals.h @@ -84,7 +84,7 @@ struct BiddingSignalJsonComponents { // basic validations. absl::StatusOr ParseTrustedBiddingSignals( std::unique_ptr bidding_signals, - const BuyerInput& buyer_input); + const BuyerInputForBidding& buyer_input); } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/buyer_frontend_service/util/bidding_signals_test.cc b/services/buyer_frontend_service/util/bidding_signals_test.cc index b96546bf..804eb5dc 100644 --- a/services/buyer_frontend_service/util/bidding_signals_test.cc +++ b/services/buyer_frontend_service/util/bidding_signals_test.cc @@ -84,7 +84,7 @@ TEST(ParsePerInterestGroupData, SkipsNonObjectPerInterestGroupData) { "perInterestGroupData": 123 } )JSON"); - BuyerInput input; + BuyerInputForBidding input; auto output = ParseTrustedBiddingSignals(std::move(bidding_signals), input); EXPECT_TRUE(output.ok()); EXPECT_TRUE(output->update_igs.interest_groups().empty()); @@ -92,10 +92,10 @@ TEST(ParsePerInterestGroupData, SkipsNonObjectPerInterestGroupData) { TEST(ParsePerInterestGroupData, ParsesUpdateInterestGroupDataInBuyerInputOrder) { - BuyerInput input; - BuyerInput::InterestGroup input_ig_first; + BuyerInputForBidding input; + BuyerInputForBidding::InterestGroupForBidding input_ig_first; input_ig_first.set_name("first"); - BuyerInput::InterestGroup input_ig_second; + BuyerInputForBidding::InterestGroupForBidding input_ig_second; input_ig_second.set_name("second"); *input.add_interest_groups() = std::move(input_ig_first); *input.add_interest_groups() = std::move(input_ig_second); @@ -129,8 +129,8 @@ TEST(ParsePerInterestGroupData, } TEST(ParsePerInterestGroupData, SkipBadUpdateIfOlderThanMsValue) { - BuyerInput input; - BuyerInput::InterestGroup input_ig_first; + BuyerInputForBidding input; + BuyerInputForBidding::InterestGroupForBidding input_ig_first; input_ig_first.set_name("first"); *input.add_interest_groups() = std::move(input_ig_first); auto bidding_signals = std::make_unique(); @@ -154,10 +154,10 @@ TEST(ParsePerInterestGroupData, SkipBadUpdateIfOlderThanMsValue) { } TEST(ParsePerInterestGroupData, ParsesPriorityVector) { - BuyerInput input; - BuyerInput::InterestGroup input_ig_first; + BuyerInputForBidding input; + BuyerInputForBidding::InterestGroupForBidding input_ig_first; input_ig_first.set_name("first_ig_name"); - BuyerInput::InterestGroup input_ig_second; + BuyerInputForBidding::InterestGroupForBidding input_ig_second; input_ig_second.set_name("second_ig_name"); *input.add_interest_groups() = std::move(input_ig_first); *input.add_interest_groups() = std::move(input_ig_second); diff --git a/services/buyer_frontend_service/util/proto_factory.cc b/services/buyer_frontend_service/util/proto_factory.cc index 2d1cb395..76cb9728 100644 --- a/services/buyer_frontend_service/util/proto_factory.cc +++ b/services/buyer_frontend_service/util/proto_factory.cc @@ -96,7 +96,7 @@ absl::StatusOr GetSignalsForIG( // Copy properties from IG from device to IG for Bidding. // Note: trusted bidding signals and keys are not copied. void CopyIGFromDeviceToIGForBidding( - const BuyerInput::InterestGroup& ig_from_device, + const BuyerInputForBidding::InterestGroupForBidding& ig_from_device, GenerateBidsRequest::GenerateBidsRawRequest::InterestGroupForBidding* mutable_ig_for_bidding) { mutable_ig_for_bidding->set_name(ig_from_device.name()); @@ -119,10 +119,10 @@ void CopyIGFromDeviceToIGForBidding( // Set device signals. if (ig_from_device.has_browser_signals() && ig_from_device.browser_signals().IsInitialized()) { - mutable_ig_for_bidding->mutable_browser_signals()->CopyFrom( + mutable_ig_for_bidding->mutable_browser_signals_for_bidding()->CopyFrom( ig_from_device.browser_signals()); } else if (ig_from_device.has_android_signals()) { - mutable_ig_for_bidding->mutable_android_signals()->CopyFrom( + mutable_ig_for_bidding->mutable_android_signals_for_bidding()->CopyFrom( ig_from_device.android_signals()); } } @@ -134,7 +134,8 @@ PrepareGenerateBidsRequestResult PrepareGenerateBidsRequest( const PriorityVectorConfig& priority_vector_config, const PrepareGenerateBidsRequestOptions& options) { auto generate_bids_raw_request = std::make_unique(); - const BuyerInput& buyer_input = get_bids_raw_request.buyer_input(); + const BuyerInputForBidding& buyer_input = + get_bids_raw_request.buyer_input_for_bidding(); absl::flat_hash_map interest_group_priorities; if (priority_vector_config.priority_vector_enabled) { diff --git a/services/buyer_frontend_service/util/proto_factory_test.cc b/services/buyer_frontend_service/util/proto_factory_test.cc index 290b30c8..3dd908da 100644 --- a/services/buyer_frontend_service/util/proto_factory_test.cc +++ b/services/buyer_frontend_service/util/proto_factory_test.cc @@ -91,55 +91,31 @@ TEST(CreateGenerateBidsRequestTest, SetsAllFieldsFromInputParamsForAndroid) { // 1. Set Interest Group For Bidding for (const auto& bidding_ig : expected_raw_output.interest_group_for_bidding()) { - auto input_ig = std::make_unique(); + auto input_ig = + std::make_unique(); input_ig->set_name(bidding_ig.name()); - input_ig->clear_user_bidding_signals(); - if (!bidding_ig.user_bidding_signals().empty()) { - input_ig->set_user_bidding_signals(bidding_ig.user_bidding_signals()); - } - - if (!bidding_ig.ad_render_ids().empty()) { - input_ig->mutable_ad_render_ids()->CopyFrom(bidding_ig.ad_render_ids()); - } - if (!bidding_ig.ad_component_render_ids().empty()) { - input_ig->mutable_component_ads()->CopyFrom( - bidding_ig.ad_component_render_ids()); - } - - input_ig->clear_bidding_signals_keys(); - if (bidding_ig.trusted_bidding_signals_keys_size() > 0) { - input_ig->mutable_bidding_signals_keys()->MergeFrom( - bidding_ig.trusted_bidding_signals_keys()); - } + input_ig->set_user_bidding_signals(bidding_ig.user_bidding_signals()); + input_ig->mutable_ad_render_ids()->CopyFrom(bidding_ig.ad_render_ids()); + input_ig->mutable_component_ads()->CopyFrom( + bidding_ig.ad_component_render_ids()); + input_ig->mutable_bidding_signals_keys()->MergeFrom( + bidding_ig.trusted_bidding_signals_keys()); // 5. Set Device Signals. - if (bidding_ig.has_browser_signals() && - bidding_ig.browser_signals().IsInitialized()) { + if (bidding_ig.has_browser_signals_for_bidding() && + bidding_ig.browser_signals_for_bidding().IsInitialized()) { input_ig->mutable_browser_signals()->CopyFrom( - bidding_ig.browser_signals()); - // wipe other field - if (input_ig->has_android_signals()) { - input_ig->clear_android_signals(); - } - } else if (bidding_ig.has_android_signals()) { + bidding_ig.browser_signals_for_bidding()); + } else if (bidding_ig.has_android_signals_for_bidding()) { input_ig->mutable_android_signals()->CopyFrom( - bidding_ig.android_signals()); - if (input_ig->has_browser_signals()) { - input_ig->clear_browser_signals(); - } - } else { - if (input_ig->has_android_signals()) { - input_ig->clear_android_signals(); - } - if (input_ig->has_browser_signals()) { - input_ig->clear_browser_signals(); - } + bidding_ig.android_signals_for_bidding()); } // Move Interest Group to Buyer Input - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - input_ig.release()); + input.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->AddAllocated(input_ig.release()); } // 2. Set Auction Signals. input.set_auction_signals(expected_raw_output.auction_signals()); @@ -177,54 +153,30 @@ TEST(CreateGenerateBidsRequestTest, SetsAllFieldsFromInputParamsForBrowser) { // 1. Set Interest Group For Bidding for (const auto& bidding_ig : expected_raw_output.interest_group_for_bidding()) { - auto input_ig = std::make_unique(); + auto input_ig = + std::make_unique(); input_ig->set_name(bidding_ig.name()); - input_ig->clear_user_bidding_signals(); - if (!bidding_ig.user_bidding_signals().empty()) { - input_ig->set_user_bidding_signals(bidding_ig.user_bidding_signals()); - } - - if (!bidding_ig.ad_render_ids().empty()) { - input_ig->mutable_ad_render_ids()->CopyFrom(bidding_ig.ad_render_ids()); - } - if (!bidding_ig.ad_component_render_ids().empty()) { - input_ig->mutable_component_ads()->CopyFrom( - bidding_ig.ad_component_render_ids()); - } - - input_ig->clear_bidding_signals_keys(); - if (bidding_ig.trusted_bidding_signals_keys_size() > 0) { - input_ig->mutable_bidding_signals_keys()->MergeFrom( - bidding_ig.trusted_bidding_signals_keys()); - } + input_ig->set_user_bidding_signals(bidding_ig.user_bidding_signals()); + input_ig->mutable_ad_render_ids()->CopyFrom(bidding_ig.ad_render_ids()); + input_ig->mutable_component_ads()->CopyFrom( + bidding_ig.ad_component_render_ids()); + input_ig->mutable_bidding_signals_keys()->MergeFrom( + bidding_ig.trusted_bidding_signals_keys()); // 5. Set Device Signals. - if (bidding_ig.has_browser_signals() && - bidding_ig.browser_signals().IsInitialized()) { + if (bidding_ig.has_browser_signals_for_bidding() && + bidding_ig.browser_signals_for_bidding().IsInitialized()) { input_ig->mutable_browser_signals()->CopyFrom( - bidding_ig.browser_signals()); - // wipe other field - if (input_ig->has_android_signals()) { - input_ig->clear_android_signals(); - } - } else if (bidding_ig.has_android_signals()) { + bidding_ig.browser_signals_for_bidding()); + } else if (bidding_ig.has_android_signals_for_bidding()) { input_ig->mutable_android_signals()->CopyFrom( - bidding_ig.android_signals()); - if (input_ig->has_browser_signals()) { - input_ig->clear_browser_signals(); - } - } else { - if (input_ig->has_android_signals()) { - input_ig->clear_android_signals(); - } - if (input_ig->has_browser_signals()) { - input_ig->clear_browser_signals(); - } + bidding_ig.android_signals_for_bidding()); } // Move Interest Group to Buyer Input - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - input_ig.release()); + input.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->AddAllocated(input_ig.release()); } // 2. Set Auction Signals. @@ -299,8 +251,8 @@ TEST(CreateGenerateBidsRequestTest, SetsAllFieldsFromInputParamsForTestIG) { GenBidsRawReq::InterestGroupForBidding* ig_for_bidding = expected_raw_output.mutable_interest_group_for_bidding()->Add(); ig_for_bidding->set_name(ig_with_two_ads->name()); - ig_for_bidding->mutable_browser_signals()->CopyFrom( - ig_with_two_ads->browser_signals()); + ig_for_bidding->mutable_browser_signals_for_bidding()->CopyFrom( + ToBrowserSignalsForBidding(ig_with_two_ads->browser_signals())); ig_for_bidding->mutable_ad_render_ids()->MergeFrom( ig_with_two_ads->ad_render_ids()); ig_for_bidding->mutable_trusted_bidding_signals_keys()->MergeFrom( @@ -309,10 +261,10 @@ TEST(CreateGenerateBidsRequestTest, SetsAllFieldsFromInputParamsForTestIG) { MakeTrustedBiddingSignalsForIG(*ig_for_bidding)); // Move Input Interest Group to Buyer Input. - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - ig_with_two_ads.release()); + *input.mutable_buyer_input_for_bidding()->mutable_interest_groups()->Add() = + ToInterestGroupForBidding(*ig_with_two_ads.get()); // Check that exactly 1 IG is in the input. - ASSERT_EQ(input.buyer_input().interest_groups_size(), 1); + ASSERT_EQ(input.buyer_input_for_bidding().interest_groups_size(), 1); // 2. Set Auction Signals. input.set_auction_signals(expected_raw_output.auction_signals()); @@ -391,9 +343,9 @@ TEST(CreateGenerateBidsRequestTest, input_ig->mutable_bidding_signals_keys()->Clear(); ASSERT_EQ(input_ig->bidding_signals_keys_size(), 0); - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - input_ig.release()); - ASSERT_EQ(input.buyer_input().interest_groups_size(), 1); + *input.mutable_buyer_input_for_bidding()->mutable_interest_groups()->Add() = + ToInterestGroupForBidding(*input_ig.get()); + ASSERT_EQ(input.buyer_input_for_bidding().interest_groups_size(), 1); auto raw_output = PrepareGenerateBidsRequest( @@ -422,9 +374,9 @@ TEST(CreateGenerateBidsRequestTest, HandlesIGWithDuplicateBiddingSignalsKeys) { ASSERT_EQ(input_ig->bidding_signals_keys_size(), 1); input_ig->add_bidding_signals_keys("key1"); ASSERT_EQ(input_ig->bidding_signals_keys_size(), 2); - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - input_ig.release()); - ASSERT_EQ(input.buyer_input().interest_groups_size(), 1); + *input.mutable_buyer_input_for_bidding()->mutable_interest_groups()->Add() = + ToInterestGroupForBidding(*input_ig.get()); + ASSERT_EQ(input.buyer_input_for_bidding().interest_groups_size(), 1); auto bidding_signals = std::make_unique(); bidding_signals->trusted_signals = @@ -455,9 +407,9 @@ TEST(CreateGenerateBidsRequestTest, input_ig->add_bidding_signals_keys("key1"); input_ig->add_bidding_signals_keys("key2"); ASSERT_EQ(input_ig->bidding_signals_keys_size(), 2); - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - input_ig.release()); - ASSERT_EQ(input.buyer_input().interest_groups_size(), 1); + *input.mutable_buyer_input_for_bidding()->mutable_interest_groups()->Add() = + ToInterestGroupForBidding(*input_ig.get()); + ASSERT_EQ(input.buyer_input_for_bidding().interest_groups_size(), 1); auto bidding_signals = std::make_unique(); bidding_signals->trusted_signals = @@ -485,8 +437,8 @@ GetBidsRequest::GetBidsRawRequest MakeGetBidsRawRequestWithoutBiddingSignals() { input_ig->set_name("ig"); input_ig->mutable_bidding_signals_keys()->Clear(); input_ig->add_bidding_signals_keys("key2"); - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - input_ig.release()); + *input.mutable_buyer_input_for_bidding()->mutable_interest_groups()->Add() = + ToInterestGroupForBidding(*input_ig.get()); return input; } @@ -689,7 +641,8 @@ TEST(CreateGenerateBidsRequestTest, VerifyPriorityVectorFiltering) { GetBidsRequest::GetBidsRawRequest input; for (const auto& bidding_ig : expected_raw_output.interest_group_for_bidding()) { - auto input_ig = std::make_unique(); + auto input_ig = + std::make_unique(); input_ig->set_name(bidding_ig.name()); input_ig->set_user_bidding_signals(bidding_ig.user_bidding_signals()); input_ig->mutable_ad_render_ids()->CopyFrom(bidding_ig.ad_render_ids()); @@ -698,8 +651,9 @@ TEST(CreateGenerateBidsRequestTest, VerifyPriorityVectorFiltering) { input_ig->mutable_bidding_signals_keys()->MergeFrom( bidding_ig.trusted_bidding_signals_keys()); input_ig->mutable_android_signals()->CopyFrom(bidding_ig.android_signals()); - input.mutable_buyer_input()->mutable_interest_groups()->AddAllocated( - input_ig.release()); + input.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->AddAllocated(input_ig.release()); } input.set_auction_signals(expected_raw_output.auction_signals()); diff --git a/services/common/clients/code_dispatcher/byob/byob_dispatch_client.h b/services/common/clients/code_dispatcher/byob/byob_dispatch_client.h index fa8b3d2b..441b9a6f 100644 --- a/services/common/clients/code_dispatcher/byob/byob_dispatch_client.h +++ b/services/common/clients/code_dispatcher/byob/byob_dispatch_client.h @@ -51,7 +51,7 @@ class ByobDispatchClient : public UdfCodeLoaderInterface { // processed by the implementing class. This should not be confused // with the output of the execution itself, which is sent to callback. virtual absl::Status Execute( - ServiceRequest request, absl::Duration timeout, + const ServiceRequest& request, absl::Duration timeout, absl::AnyInvocable) &&> callback) = 0; }; diff --git a/services/common/clients/http_kv_server/buyer/buyer_key_value_async_http_client.cc b/services/common/clients/http_kv_server/buyer/buyer_key_value_async_http_client.cc index 0a168da4..70681fee 100644 --- a/services/common/clients/http_kv_server/buyer/buyer_key_value_async_http_client.cc +++ b/services/common/clients/http_kv_server/buyer/buyer_key_value_async_http_client.cc @@ -84,29 +84,14 @@ absl::Status BuyerKeyValueAsyncHttpClient::Execute( request_size += header.size(); } request_size += request.url.size(); - EventMessage::KvSignal bid_signal = [&]() { - EventMessage::KvSignal signal; - if (!AllowAnyEventLogging(context.log)) { - return signal; - } - signal.set_request(request.url); - for (absl::string_view header : request.headers) { - signal.add_request_header(header); - } - return signal; - }(); + EventMessage::KvSignal bid_signal = + KvEventMessage(request.url, context.log, request.headers); auto done_callback = [on_done = std::move(on_done), request_size, bid_signal = std::move(bid_signal), context]( absl::StatusOr httpResponse) mutable { if (httpResponse.ok()) { - if (server_common::log::PS_VLOG_IS_ON(kKVLog)) { - PS_VLOG(kKVLog, context.log) << "BuyerKeyValueAsyncHttpClient response " - "exported in EventMessage if consented"; - if (AllowAnyEventLogging(context.log)) { - bid_signal.set_response(httpResponse->body); - context.log.SetEventMessageField(std::move(bid_signal)); - } - } + SetKvEventMessage("BuyerKeyValueAsyncHttpClient", httpResponse->body, + std::move(bid_signal), context.log); size_t response_size = httpResponse->body.size(); uint32_t data_version_value = 0; if (auto dv_header_it = diff --git a/services/common/clients/http_kv_server/seller/seller_key_value_async_http_client.cc b/services/common/clients/http_kv_server/seller/seller_key_value_async_http_client.cc index 11fbbadc..74b0bbc9 100644 --- a/services/common/clients/http_kv_server/seller/seller_key_value_async_http_client.cc +++ b/services/common/clients/http_kv_server/seller/seller_key_value_async_http_client.cc @@ -79,30 +79,14 @@ absl::Status SellerKeyValueAsyncHttpClient::Execute( request_size += header.size(); } request_size += request.url.size(); - EventMessage::KvSignal score_signal = [&]() { - EventMessage::KvSignal signal; - if (!AllowAnyEventLogging(context.log)) { - return signal; - } - signal.set_request(request.url); - for (absl::string_view header : request.headers) { - signal.add_request_header(header); - } - return signal; - }(); + EventMessage::KvSignal score_signal = + KvEventMessage(request.url, context.log, request.headers); auto done_callback = [on_done = std::move(on_done), request_size, score_signal = std::move(score_signal), context]( absl::StatusOr httpResponse) mutable { if (httpResponse.ok()) { - if (server_common::log::PS_VLOG_IS_ON(kKVLog)) { - PS_VLOG(kKVLog, context.log) - << "SellerKeyValueAsyncHttpClient response exported in " - "EventMessage if consented"; - if (AllowAnyEventLogging(context.log)) { - score_signal.set_response(httpResponse->body); - context.log.SetEventMessageField(std::move(score_signal)); - } - } + SetKvEventMessage("SellerKeyValueAsyncHttpClient", httpResponse->body, + std::move(score_signal), context.log); size_t response_size = httpResponse->body.size(); uint32_t data_version_value = 0; if (auto dv_header_it = diff --git a/services/common/clients/kv_server/BUILD b/services/common/clients/kv_server/BUILD index c6144025..14a5b967 100644 --- a/services/common/clients/kv_server/BUILD +++ b/services/common/clients/kv_server/BUILD @@ -103,6 +103,7 @@ cc_test( ], deps = [ ":kv_v2", + "//services/common/test/utils:proto_utils", "//services/common/test/utils:test_utils", "@com_google_googletest//:gtest_main", ], diff --git a/services/common/clients/kv_server/kv_v2.cc b/services/common/clients/kv_server/kv_v2.cc index 290c9afd..e7723123 100644 --- a/services/common/clients/kv_server/kv_v2.cc +++ b/services/common/clients/kv_server/kv_v2.cc @@ -28,8 +28,11 @@ namespace privacy_sandbox::bidding_auction_servers { namespace { inline constexpr char kTags[] = "tags"; +inline constexpr char kInterestGroupNames[] = "interestGroupNames"; +inline constexpr char kPerInterestGroupData[] = "perInterestGroupData"; inline constexpr char kKeyGroupOutputs[] = "keyGroupOutputs"; inline constexpr char kKeyValues[] = "keyValues"; +inline constexpr char kValue[] = "value"; absl::StatusOr GetIgSignalsForKeyTag( std::string_view key_tag, std::vector& docs) { @@ -61,7 +64,7 @@ absl::StatusOr GetIgSignalsForKeyTag( for (auto& key_group_output : key_group_outputs->value.GetArray()) { auto tags = key_group_output.FindMember(kTags); if (tags == key_group_output.MemberEnd() || tags->value.Size() != 1 || - tags->value[0].GetString() != key_tag) { + std::string(tags->value[0].GetString()) != key_tag) { continue; } auto key_values = key_group_output.FindMember(kKeyValues); @@ -75,9 +78,38 @@ absl::StatusOr GetIgSignalsForKeyTag( for (auto& key_value_pair : key_values->value.GetObject()) { if (ig_signals.FindMember(key_value_pair.name) == ig_signals.MemberEnd()) { - ig_signals.AddMember(key_value_pair.name.Move(), - key_value_pair.value.Move(), - ig_signals.GetAllocator()); + // Get the value inside the value wrapper and add it to the + // ig_signals. + if (!key_value_pair.value.IsObject()) { + PS_VLOG(8) << __func__ << "Skip value for key " + << key_value_pair.name.GetString() + << " because value is not object"; + continue; + } + auto value = key_value_pair.value.FindMember(kValue); + if (value != key_value_pair.value.MemberEnd()) { + if (std::string(tags->value[0].GetString()) == + kInterestGroupNames) { + if (auto ig_value = ParseJsonString(value->value.GetString()); + ig_value.ok()) { + ig_signals.AddMember( + key_value_pair.name.Move(), + rapidjson::Value(*ig_value, ig_signals.GetAllocator()) + .Move(), + ig_signals.GetAllocator()); + } + continue; + } + ig_signals.AddMember(key_value_pair.name.Move(), + value->value.Move(), + ig_signals.GetAllocator()); + + } else { + PS_VLOG(8) << __func__ + << "Unable to find value inside the value wrapper " + "from the key value pair with key:" + << key_value_pair.name.GetString(); + } } else { PS_VLOG(8) << __func__ << "Key value response has multiple different values " @@ -119,9 +151,16 @@ absl::StatusOr ConvertKvV2ResponseToV1String( } for (auto& [key_tag, ig_signals] : ig_signals_map) { if (!ig_signals.ObjectEmpty()) { - top_level_doc.AddMember( - rapidjson::StringRef(key_tag.data(), key_tag.length()), - ig_signals.Move(), top_level_doc.GetAllocator()); + if (key_tag == kInterestGroupNames) { + // TKV V2 protocol returns an interestGroupNames tag. However, BYOS + // expects perInterestGroupData in the response. + top_level_doc.AddMember(kPerInterestGroupData, ig_signals.Move(), + top_level_doc.GetAllocator()); + } else { + top_level_doc.AddMember( + rapidjson::StringRef(key_tag.data(), key_tag.length()), + ig_signals.Move(), top_level_doc.GetAllocator()); + } } } if (top_level_doc.ObjectEmpty()) { @@ -137,17 +176,9 @@ bool UseKvV2(ClientType client_type, bool is_tkv_v2_browser_enabled, } if (client_type == ClientType::CLIENT_TYPE_ANDROID) { - // TODO(b/378676724): Remove test_mode conditions. All CLIENT_TYPE_ANDROID - // requests should go to TKV V2. - // TEST_MODE=false. Android requests MUST go to TKV V2 - if (!is_test_mode_enabled) { - return true; - } - // TEST_MODE=true. Android requests go to TKV V2 only if TKV V2 address is - // not empty. - if (!is_tkv_v2_empty) { - return true; - } + // TODO(b/378676724): Remove test_mode conditions. + // Android must use TKV, unless TEST_MODE=false and TKV V2 address is empty + return !(is_test_mode_enabled && is_tkv_v2_empty); } return false; } diff --git a/services/common/clients/kv_server/kv_v2_test.cc b/services/common/clients/kv_server/kv_v2_test.cc index d8a13dc0..a44a8cdb 100644 --- a/services/common/clients/kv_server/kv_v2_test.cc +++ b/services/common/clients/kv_server/kv_v2_test.cc @@ -23,6 +23,7 @@ #include "absl/strings/str_format.h" #include "google/protobuf/text_format.h" #include "gtest/gtest.h" +#include "services/common/test/utils/proto_utils.h" #include "services/common/test/utils/test_utils.h" #include "services/common/util/json_util.h" @@ -31,7 +32,6 @@ namespace { using google::protobuf::TextFormat; TEST(ConvertKvV2ResponseToV1String, ConvertKeepsOnlyGivenTags) { - kv_server::v2::GetValuesResponse response; constexpr char kCompressionGroup[] = R"JSON( [ { @@ -76,35 +76,29 @@ TEST(ConvertKvV2ResponseToV1String, ConvertKeepsOnlyGivenTags) { ] } ])JSON"; - ASSERT_TRUE(TextFormat::ParseFromString( - absl::StrFormat( + kv_server::v2::GetValuesResponse response = + ParseTextOrDie(absl::StrFormat( R"pb( compression_groups { compression_group_id: 33 content: "%s" })pb", - absl::CEscape(kCompressionGroup)), - &response)); + absl::CEscape(kCompressionGroup))); auto result = ConvertKvV2ResponseToV1String({"keys"}, response); ASSERT_TRUE(result.ok()) << result.status(); std::string expected_parsed_signals = R"json( { "keys": { - "hello": { - "value": "world" - }, - "hello2": { - "value": "world2" - } + "hello": "world", + "hello2":"world2" } })json"; auto actual_signals_json = ParseJsonString(*result); auto expected_signals_json = ParseJsonString(expected_parsed_signals); ASSERT_TRUE(actual_signals_json.ok()) << actual_signals_json.status(); ASSERT_TRUE(expected_signals_json.ok()) << expected_signals_json.status(); - ASSERT_EQ(actual_signals_json, expected_signals_json); + ASSERT_EQ(*actual_signals_json, *expected_signals_json); } TEST(ConvertKvV2ResponseToV1String, MultipleCompressionGroups) { - kv_server::v2::GetValuesResponse response; constexpr char kCompressionGroup1[] = R"JSON( [ { @@ -195,8 +189,9 @@ TEST(ConvertKvV2ResponseToV1String, MultipleCompressionGroups) { } ])JSON"; - ASSERT_TRUE(TextFormat::ParseFromString( - absl::StrFormat(R"( + kv_server::v2::GetValuesResponse response = + ParseTextOrDie( + absl::StrFormat(R"( compression_groups { compression_group_id : 33 content : "%s" @@ -206,38 +201,28 @@ TEST(ConvertKvV2ResponseToV1String, MultipleCompressionGroups) { content : "%s" } )", - absl::CEscape(kCompressionGroup1), - absl::CEscape(kCompressionGroup2)), - &response)); + absl::CEscape(kCompressionGroup1), + absl::CEscape(kCompressionGroup2))); auto result = ConvertKvV2ResponseToV1String({"keys"}, response); ASSERT_TRUE(result.ok()) << result.status(); std::string expected_parsed_signals = R"json( { "keys": { - "hello": { - "value": "world" - }, - "hello2": { - "value": "world2" - }, - "hello44": { - "value": "world44" - }, - "hello24": { - "value": "world24" - } + "hello": "world", + "hello2": "world2", + "hello44": "world44", + "hello24": "world24" } })json"; auto actual_signals_json = ParseJsonString(*result); auto expected_signals_json = ParseJsonString(expected_parsed_signals); ASSERT_TRUE(actual_signals_json.ok()) << actual_signals_json.status(); ASSERT_TRUE(expected_signals_json.ok()) << expected_signals_json.status(); - ASSERT_EQ(actual_signals_json, expected_signals_json); + ASSERT_EQ(*actual_signals_json, *expected_signals_json); } TEST(ConvertKvV2ResponseToV1String, ConvertMultipleKeyTags) { - kv_server::v2::GetValuesResponse response; constexpr char kCompressionGroup[] = R"JSON( [ { @@ -281,14 +266,14 @@ TEST(ConvertKvV2ResponseToV1String, ConvertMultipleKeyTags) { ] } ])JSON"; - ASSERT_TRUE(TextFormat::ParseFromString( - absl::StrFormat(R"( + kv_server::v2::GetValuesResponse response = + ParseTextOrDie( + absl::StrFormat(R"( compression_groups { compression_group_id : 33 content : "%s" })", - absl::CEscape(kCompressionGroup)), - &response)); + absl::CEscape(kCompressionGroup))); auto result = ConvertKvV2ResponseToV1String( {"renderUrls", "adComponentRenderUrls"}, response); ASSERT_TRUE(result.ok()) << result.status(); @@ -296,25 +281,20 @@ TEST(ConvertKvV2ResponseToV1String, ConvertMultipleKeyTags) { R"json( { "renderUrls": { - "hello": { - "value": "world" - } + "hello": "world" }, "adComponentRenderUrls": { - "hello2": { - "value": "world2" - } + "hello2": "world2" } })json"; auto actual_signals_json = ParseJsonString(*result); auto expected_signals_json = ParseJsonString(expected_parsed_signals); ASSERT_TRUE(actual_signals_json.ok()) << actual_signals_json.status(); ASSERT_TRUE(expected_signals_json.ok()) << expected_signals_json.status(); - ASSERT_EQ(actual_signals_json, expected_signals_json); + ASSERT_EQ(*actual_signals_json, *expected_signals_json); } TEST(ConvertKvV2ResponseToV1String, MultipleCompressionGroupsMultipleKeyTags) { - kv_server::v2::GetValuesResponse response; constexpr char kCompressionGroup1[] = R"JSON( [ { @@ -403,8 +383,9 @@ TEST(ConvertKvV2ResponseToV1String, MultipleCompressionGroupsMultipleKeyTags) { } ])JSON"; - ASSERT_TRUE(TextFormat::ParseFromString( - absl::StrFormat(R"( + kv_server::v2::GetValuesResponse response = + ParseTextOrDie( + absl::StrFormat(R"( compression_groups { compression_group_id : 33 content : "%s" @@ -414,9 +395,8 @@ TEST(ConvertKvV2ResponseToV1String, MultipleCompressionGroupsMultipleKeyTags) { content : "%s" } )", - absl::CEscape(kCompressionGroup1), - absl::CEscape(kCompressionGroup2)), - &response)); + absl::CEscape(kCompressionGroup1), + absl::CEscape(kCompressionGroup2))); auto result = ConvertKvV2ResponseToV1String( {"renderUrls", "adComponentRenderUrls"}, response); ASSERT_TRUE(result.ok()) << result.status(); @@ -424,34 +404,23 @@ TEST(ConvertKvV2ResponseToV1String, MultipleCompressionGroupsMultipleKeyTags) { R"JSON( { "renderUrls": { - "hello": { - "value": "world" - }, - "hello2": { - "value": "world2" - }, - "hello44": { - "value": "world44" - } + "hello": "world", + "hello2": "world2", + "hello44": "world44" }, "adComponentRenderUrls": { - "hello3": { - "value": "world3" - }, - "hello24": { - "value": "world24" - } + "hello3": "world3", + "hello24": "world24" } })JSON"; auto actual_signals_json = ParseJsonString(*result); auto expected_signals_json = ParseJsonString(expected_parsed_signals); ASSERT_TRUE(actual_signals_json.ok()) << actual_signals_json.status(); ASSERT_TRUE(expected_signals_json.ok()) << expected_signals_json.status(); - ASSERT_EQ(actual_signals_json, expected_signals_json); + ASSERT_EQ(*actual_signals_json, *expected_signals_json); } TEST(ConvertKvV2ResponseToV1String, MalformedJson) { - kv_server::v2::GetValuesResponse response; constexpr char kCompressionGroup[] = R"JSON( [ { @@ -496,33 +465,55 @@ TEST(ConvertKvV2ResponseToV1String, MalformedJson) { ] } ])JSON"; - ASSERT_TRUE(TextFormat::ParseFromString( - absl::StrFormat(R"( + kv_server::v2::GetValuesResponse response = + ParseTextOrDie( + absl::StrFormat(R"( compression_groups { compression_group_id : 33 content : "%s" })", - absl::CEscape(kCompressionGroup)), - &response)); + absl::CEscape(kCompressionGroup))); auto result = ConvertKvV2ResponseToV1String({"keys"}, response); ASSERT_FALSE(result.ok()); } +TEST(ConvertKvV2ResponseToV1String, IgnoresKeyValueWithNonValueObject) { + constexpr char kCompressionGroup[] = R"JSON( + [ + { + "id": 0, + "keyGroupOutputs": [ + { + "tags": [ + "keys" + ], + "keyValues": { + "hello": "not an object" + } + } + ] + } + ])JSON"; + kv_server::v2::GetValuesResponse response = + ParseTextOrDie(absl::StrFormat( + R"pb( + compression_groups { compression_group_id: 0 content: "%s" })pb", + absl::CEscape(kCompressionGroup))); + auto result = ConvertKvV2ResponseToV1String({"keys"}, response); + ASSERT_TRUE(result.ok()) << result.status(); + EXPECT_EQ(*result, ""); +} + TEST(ConvertKvV2ResponseToV1String, EmptyJson) { - kv_server::v2::GetValuesResponse response; - ASSERT_TRUE(TextFormat::ParseFromString( - R"( - compression_groups { - compression_group_id : 33 - content : "" - })", - &response)); + kv_server::v2::GetValuesResponse response = + ParseTextOrDie( + R"pb( + compression_groups { compression_group_id: 33 content: "" })pb"); auto result = ConvertKvV2ResponseToV1String({"keys"}, response); ASSERT_FALSE(result.ok()); } TEST(ConvertKvV2ResponseToV1String, KeyTagsNotFoundReturnEmtpyString) { - kv_server::v2::GetValuesResponse response; constexpr char kCompressionGroup[] = R"JSON( [ { @@ -541,14 +532,14 @@ TEST(ConvertKvV2ResponseToV1String, KeyTagsNotFoundReturnEmtpyString) { ] } ])JSON"; - ASSERT_TRUE(TextFormat::ParseFromString( - absl::StrFormat(R"( + kv_server::v2::GetValuesResponse response = + ParseTextOrDie( + absl::StrFormat(R"( compression_groups { compression_group_id : 33 content : "%s" })", - absl::CEscape(kCompressionGroup)), - &response)); + absl::CEscape(kCompressionGroup))); auto result = ConvertKvV2ResponseToV1String({"keys"}, response); ASSERT_TRUE(result.ok()); EXPECT_EQ("", *result); @@ -589,5 +580,135 @@ TEST(UseKvV2, AndroidTestModeTrueAddressNotEmptyReturnsTrue) { /*is_tkv_v2_empty=*/false)); } +TEST(ConvertKvV2ResponseToV1String, ParsesIgNamesOutputsCorrectly) { + constexpr char kCompressionGroup[] = R"JSON( + [ + { + "id": 0, + "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_0": { + "value": "{\"priorityVector\":{\"signal1\":1}, \"updateIfOlderThanMs\": 10000}" + } + } + } + ] + } + ])JSON"; + kv_server::v2::GetValuesResponse response = + ParseTextOrDie(absl::StrFormat( + R"pb( + compression_groups { compression_group_id: 0 content: "%s" })pb", + absl::CEscape(kCompressionGroup))); + auto result = ConvertKvV2ResponseToV1String({"interestGroupNames"}, response); + ASSERT_TRUE(result.ok()) << result.status(); + std::string expected_parsed_signals = + R"json( + { + "perInterestGroupData": { + "ig_name_0": { + "priorityVector": { "signal1": 1 }, + "updateIfOlderThanMs": 10000 + } + } + })json"; + auto actual_signals_json = ParseJsonString(*result); + auto expected_signals_json = ParseJsonString(expected_parsed_signals); + ASSERT_TRUE(actual_signals_json.ok()) << actual_signals_json.status(); + ASSERT_TRUE(expected_signals_json.ok()) << expected_signals_json.status(); + ASSERT_EQ(*actual_signals_json, *expected_signals_json) + << SerializeJsonDoc(*actual_signals_json) + << SerializeJsonDoc(*expected_signals_json); +} + +TEST(ConvertKvV2ResponseToV1String, IgnoresIgNamesWithNonObject) { + constexpr char kCompressionGroup[] = R"JSON( + [ + { + "id": 0, + "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_0": "\"not an object - will ignore\"" + } + } + ] + } + ])JSON"; + kv_server::v2::GetValuesResponse response = + ParseTextOrDie(absl::StrFormat( + R"pb( + compression_groups { compression_group_id: 0 content: "%s" })pb", + absl::CEscape(kCompressionGroup))); + auto result = ConvertKvV2ResponseToV1String({"interestGroupNames"}, response); + ASSERT_TRUE(result.ok()) << result.status(); + EXPECT_EQ(*result, ""); +} + +TEST(ConvertKvV2ResponseToV1String, IgnoresIgNamesWithoutValueField) { + constexpr char kCompressionGroup[] = R"JSON( + [ + { + "id": 0, + "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_0": { + "a": "doesn't have value" + } + } + } + ] + } + ])JSON"; + kv_server::v2::GetValuesResponse response = + ParseTextOrDie(absl::StrFormat( + R"pb( + compression_groups { compression_group_id: 0 content: "%s" })pb", + absl::CEscape(kCompressionGroup))); + auto result = ConvertKvV2ResponseToV1String({"interestGroupNames"}, response); + ASSERT_TRUE(result.ok()) << result.status(); + EXPECT_EQ(*result, ""); +} + +TEST(ConvertKvV2ResponseToV1String, IgnoresIgNamesWithInvalidJsonValue) { + constexpr char kCompressionGroup[] = R"JSON( + [ + { + "id": 0, + "keyGroupOutputs": [ + { + "tags": [ + "interestGroupNames" + ], + "keyValues": { + "ig_name_0": { + "value": "invalid, unescaped json" + } + } + } + ] + } + ])JSON"; + kv_server::v2::GetValuesResponse response = + ParseTextOrDie(absl::StrFormat( + R"pb( + compression_groups { compression_group_id: 0 content: "%s" })pb", + absl::CEscape(kCompressionGroup))); + auto result = ConvertKvV2ResponseToV1String({"interestGroupNames"}, response); + ASSERT_TRUE(result.ok()) << result.status(); + EXPECT_EQ(*result, ""); +} + } // namespace } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/common/feature_flags.cc b/services/common/feature_flags.cc index 789e08f3..8d008362 100644 --- a/services/common/feature_flags.cc +++ b/services/common/feature_flags.cc @@ -35,3 +35,6 @@ ABSL_FLAG(bool, enable_kanon, true, ABSL_FLAG(bool, enable_buyer_private_aggregate_reporting, true, "If true, private aggregate reports from the buyers will be " "processed and sent to the device."); + +ABSL_FLAG(int, per_adtech_paapi_contributions_limit, 100, + "Max number of private aggregate contributions per adtech"); diff --git a/services/common/feature_flags.h b/services/common/feature_flags.h index d8e2fa06..9465244c 100644 --- a/services/common/feature_flags.h +++ b/services/common/feature_flags.h @@ -24,5 +24,6 @@ ABSL_DECLARE_FLAG(bool, enable_temporary_unlimited_egress); ABSL_DECLARE_FLAG(bool, enable_cancellation); ABSL_DECLARE_FLAG(bool, enable_kanon); ABSL_DECLARE_FLAG(bool, enable_buyer_private_aggregate_reporting); +ABSL_DECLARE_FLAG(int, per_adtech_paapi_contributions_limit); #endif // SERVICES_COMMON_FEATURE_FLAGS_H_ diff --git a/services/common/loggers/request_log_context.h b/services/common/loggers/request_log_context.h index b9b2b767..36e84555 100644 --- a/services/common/loggers/request_log_context.h +++ b/services/common/loggers/request_log_context.h @@ -152,6 +152,35 @@ inline bool AllowAnyUdfLogging(RequestLogContext& log_context) { AllowAnyEventLogging(log_context); } +inline EventMessage::KvSignal KvEventMessage( + absl::string_view request, RequestLogContext& log_context, + absl::Span headers = {}) { + EventMessage::KvSignal signal; + if (!AllowAnyEventLogging(log_context)) { + return signal; + } + signal.set_request(request); + for (absl::string_view header : headers) { + signal.add_request_header(header); + } + return signal; +} + +inline void SetKvEventMessage(absl::string_view kv_client, + absl::string_view response, + EventMessage::KvSignal kv_signal, + RequestLogContext& log_context) { + if (!server_common::log::PS_VLOG_IS_ON(kKVLog)) { + return; + } + PS_VLOG(kKVLog, log_context) + << kv_client << " response exported in EventMessage if consented"; + if (AllowAnyEventLogging(log_context)) { + kv_signal.set_response(response); + log_context.SetEventMessageField(std::move(kv_signal)); + } +} + inline bool RandomSample(int sample_rate_micro, absl::BitGenRef bitgen) { absl::discrete_distribution dist( {1e6 - sample_rate_micro, (double)sample_rate_micro}); diff --git a/services/common/metric/server_definition.h b/services/common/metric/server_definition.h index 1ae104fb..d37cc484 100644 --- a/services/common/metric/server_definition.h +++ b/services/common/metric/server_definition.h @@ -254,7 +254,7 @@ inline constexpr server_common::metrics::Definition< inline constexpr server_common::metrics::Definition< double, server_common::metrics::Privacy::kImpacting, server_common::metrics::Instrument::kHistogram> - kNonKAnonCacheHitPercentage("sfe.k_anon_cache.hit_percentage", + kNonKAnonCacheHitPercentage("sfe.non_k_anon_cache.hit_percentage", "Percentage of hashes that can be found in " "non-k-anon cache", kPercentHistogram, 1, 0); diff --git a/services/common/metric/udf_metric.h b/services/common/metric/udf_metric.h index 6dbb49bf..086a6b82 100644 --- a/services/common/metric/udf_metric.h +++ b/services/common/metric/udf_metric.h @@ -57,7 +57,7 @@ void CustomMetricCallBack( if (!log_metric.ok()) { wrapper.io_proto.set_output_string("fail log metric"); PS_LOG(ERROR, (*roma_request_context)->GetLogContext()) - << "Failed call LogUDFMetrics" << result.message(); + << "Failed call LogUDFMetrics" << log_metric.message(); return; } wrapper.io_proto.set_output_string("log metric success"); diff --git a/services/common/test/BUILD b/services/common/test/BUILD index da258852..08ef4ce1 100644 --- a/services/common/test/BUILD +++ b/services/common/test/BUILD @@ -66,6 +66,7 @@ cc_library( "//services/common/test/utils:cbor_test_utils", "//services/common/util:request_response_constants", "//services/seller_frontend_service/test:app_test_utils", + "//services/seller_frontend_service/util:buyer_input_proto_utils", "@com_github_grpc_grpc//:grpc++", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/time", diff --git a/services/common/test/mocks.h b/services/common/test/mocks.h index c9c164b8..7bf0333d 100644 --- a/services/common/test/mocks.h +++ b/services/common/test/mocks.h @@ -208,7 +208,7 @@ class ByobDispatchClientMock MOCK_METHOD(absl::Status, LoadSync, (std::string version, std::string code), (override)); MOCK_METHOD(absl::Status, Execute, - (ServiceRequest request, absl::Duration timeout, + (const ServiceRequest& request, absl::Duration timeout, absl::AnyInvocable) &&>), (override)); }; diff --git a/services/common/test/random.cc b/services/common/test/random.cc index 796da478..9ea07aca 100644 --- a/services/common/test/random.cc +++ b/services/common/test/random.cc @@ -134,10 +134,10 @@ google::protobuf::ListValue MakeARandomListOfNumbers() { std::string MakeRandomPreviousWins( const google::protobuf::RepeatedPtrField& ad_render_ids, - bool set_times_to_one) { + bool set_times_to_one, bool time_in_ms) { std::string previous_wins = "["; for (int i = 0; i < ad_render_ids.size(); i++) { - int time_val = 1; + int time_val = 1 * (time_in_ms ? 1000 : 1); if (!set_times_to_one) { time_val = (unsigned)time(NULL); } @@ -151,14 +151,30 @@ std::string MakeRandomPreviousWins( return previous_wins; } -BrowserSignals MakeRandomBrowserSignalsForIG( +BrowserSignalsForBidding MakeRandomBrowserSignalsForBiddingForIG( const google::protobuf::RepeatedPtrField& ad_render_ids) { + BrowserSignalsForBidding browser_signals; + browser_signals.set_join_count(MakeARandomInt(0, 10)); + browser_signals.set_bid_count(MakeARandomInt(0, 50)); + browser_signals.set_recency(60); // secs + browser_signals.set_recency_ms(60000); + browser_signals.set_prev_wins(MakeRandomPreviousWins( + ad_render_ids, /* set_times_to_one= */ false, /* time_in_ms= */ false)); + browser_signals.set_prev_wins_ms( + MakeRandomPreviousWins(ad_render_ids, /* set_times_to_one= */ false)); + return browser_signals; +} + +BrowserSignals MakeRandomBrowserSignalsForIG( + const google::protobuf::RepeatedPtrField& ad_render_ids, + bool internal) { BrowserSignals browser_signals; browser_signals.set_join_count(MakeARandomInt(0, 10)); browser_signals.set_bid_count(MakeARandomInt(0, 50)); browser_signals.set_recency(60); // secs browser_signals.set_recency_ms(60000); - browser_signals.set_prev_wins(MakeRandomPreviousWins(ad_render_ids)); + browser_signals.set_prev_wins(MakeRandomPreviousWins( + ad_render_ids, /* set_times_to_one= */ false, /* time_in_ms= */ false)); return browser_signals; } @@ -390,11 +406,13 @@ InterestGroupForBidding MakeARandomInterestGroupForBidding( } if (build_android_signals) { // Empty message for now. - ig_for_bidding.mutable_android_signals(); + ig_for_bidding.mutable_android_signals_for_bidding(); } else { - ig_for_bidding.mutable_browser_signals()->CopyFrom( - MakeRandomBrowserSignalsForIG(ig_for_bidding.ad_render_ids())); + ig_for_bidding.mutable_browser_signals_for_bidding()->CopyFrom( + MakeRandomBrowserSignalsForBiddingForIG( + ig_for_bidding.ad_render_ids())); } + return ig_for_bidding; } @@ -434,11 +452,11 @@ InterestGroupForBidding MakeALargeInterestGroupForBiddingForLatencyTesting() { } InterestGroupForBidding MakeARandomInterestGroupForBiddingFromAndroid() { - return MakeARandomInterestGroupForBidding(true); + return MakeARandomInterestGroupForBidding(true, false); } InterestGroupForBidding MakeARandomInterestGroupForBiddingFromBrowser() { - return MakeARandomInterestGroupForBidding(false); + return MakeARandomInterestGroupForBidding(false, false); } GenerateBidsRequest::GenerateBidsRawRequest @@ -700,7 +718,7 @@ ScoreAdsResponse::AdScore MakeARandomAdScore( int rejection_reason_ig_per_owner) { ScoreAdsResponse::AdScore ad_score; float bid = MakeARandomNumber(1, 2.5); - float score = MakeARandomNumber(1, 2.5); + float score = MakeARandomNumber(100, 250); ad_score.set_desirability(score); ad_score.set_render(MakeARandomString()); ad_score.set_interest_group_name(MakeARandomString()); @@ -710,7 +728,7 @@ ScoreAdsResponse::AdScore MakeARandomAdScore( absl::StrCat("interest_group_origin_", MakeARandomString())); ad_score.set_ad_metadata(MakeARandomString()); ad_score.set_allow_component_auction(false); - ad_score.set_bid(MakeARandomNumber(1, 2.5)); + ad_score.set_bid(MakeARandomNumber(3, 4.5)); *ad_score.mutable_debug_report_urls() = MakeARandomDebugReportUrls(); *ad_score.mutable_win_reporting_urls() = MakeARandomWinReportingUrls(); for (int index = 0; index < hob_buyer_entries; index++) { @@ -831,12 +849,22 @@ BuyerInput MakeARandomBuyerInput() { return buyer_input; } +BuyerInputForBidding MakeARandomBuyerInputForBidding() { + BuyerInputForBidding buyer_input; + std::unique_ptr ig = + MakeARandomInterestGroup(/*for_android=*/false); + BuyerInputForBidding::InterestGroupForBidding ig_for_bidding = + ToInterestGroupForBidding(*ig.get()); + *buyer_input.mutable_interest_groups()->Add() = ig_for_bidding; + return buyer_input; +} + ProtectedAuctionInput MakeARandomProtectedAuctionInput(ClientType client_type) { ProtectedAuctionInput input; input.set_publisher_name(MakeARandomString()); input.set_generation_id(MakeARandomString()); - google::protobuf::Map buyer_inputs; - buyer_inputs.emplace(MakeARandomString(), MakeARandomBuyerInput()); + google::protobuf::Map buyer_inputs; + buyer_inputs.emplace(MakeARandomString(), MakeARandomBuyerInputForBidding()); absl::StatusOr encoded_buyer_input; switch (client_type) { case CLIENT_TYPE_BROWSER: diff --git a/services/common/test/random.h b/services/common/test/random.h index cdf679c2..4aeaf92f 100644 --- a/services/common/test/random.h +++ b/services/common/test/random.h @@ -35,6 +35,7 @@ #include "services/common/test/utils/cbor_test_utils.h" #include "services/common/util/request_response_constants.h" #include "services/seller_frontend_service/test/app_test_utils.h" +#include "services/seller_frontend_service/util/buyer_input_proto_utils.h" #include "src/util/status_macro/status_macros.h" // helper functions to generate random objects for testing @@ -88,11 +89,15 @@ google::protobuf::ListValue MakeARandomListOfNumbers(); std::string MakeRandomPreviousWins( const google::protobuf::RepeatedPtrField& ad_render_ids, - bool set_times_to_one = false); + bool set_times_to_one = false, bool time_in_ms = true); -BrowserSignals MakeRandomBrowserSignalsForIG( +BrowserSignalsForBidding MakeRandomBrowserSignalsForBiddingForIG( const google::protobuf::RepeatedPtrField& ad_render_ids); +BrowserSignals MakeRandomBrowserSignalsForIG( + const google::protobuf::RepeatedPtrField& ad_render_ids, + bool internal = false); + // Must manually delete/take ownership of underlying pointer std::unique_ptr MakeAnInterestGroupSentFromDevice(); @@ -127,10 +132,14 @@ InterestGroupForBidding MakeAnInterestGroupForBiddingSentFromDevice(); // build_android_signals: If false, will insert random values into // browser signals, otherwise will insert random values into android signals. +// populate_old_device_signal_fields is added just for this CL to have the UTs +// pass and is removed in the next CL. InterestGroupForBidding MakeARandomInterestGroupForBidding( bool build_android_signals, bool set_user_bidding_signals_to_empty_struct = false); +// populate_old_device_signal_fields is added just for this CL to have the UTs +// pass and is removed in the next CL. InterestGroupForBidding MakeALargeInterestGroupForBiddingForLatencyTesting(); InterestGroupForBidding MakeARandomInterestGroupForBiddingFromAndroid(); @@ -199,16 +208,17 @@ GetBidsRequest MakeARandomGetBidsRequest(); template T MakeARandomProtectedAuctionInput(int num_buyers = 2, bool enable_debug_reporting = true) { - google::protobuf::Map buyer_inputs; + google::protobuf::Map buyer_inputs; for (int i = 0; i < num_buyers; i++) { - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; auto ig_with_two_ads = MakeAnInterestGroupSentFromDevice(); - buyer_input.mutable_interest_groups()->AddAllocated( - ig_with_two_ads.release()); + *buyer_input.mutable_interest_groups()->Add() = + ToInterestGroupForBidding(*ig_with_two_ads.get()); buyer_inputs.emplace(absl::StrFormat("ad_tech_%d.com", i), buyer_input); } absl::StatusOr encoded_buyer_input = GetEncodedBuyerInputMap(buyer_inputs); + T protected_auction_input; protected_auction_input.set_generation_id(MakeARandomString()); *protected_auction_input.mutable_buyer_input() = @@ -292,6 +302,8 @@ struct TestComponentAuctionResultData { BuyerInput MakeARandomBuyerInput(); +BuyerInputForBidding MakeARandomBuyerInputForBidding(); + ProtectedAuctionInput MakeARandomProtectedAuctionInput(ClientType client_type); // Populates fields for a auction result object for a single seller auction. diff --git a/services/common/test/utils/cbor_test_utils.cc b/services/common/test/utils/cbor_test_utils.cc index 5cb7540c..747641ee 100644 --- a/services/common/test/utils/cbor_test_utils.cc +++ b/services/common/test/utils/cbor_test_utils.cc @@ -43,6 +43,8 @@ using ::google::protobuf::RepeatedPtrField; using BiddingGroupMap = ::google::protobuf::Map; using BuyerInputMap = ::google::protobuf::Map; +using BuyerInputForBiddingMap = + ::google::protobuf::Map; using BuyerInputMapEncoded = ::google::protobuf::Map; using InteractionUrlMap = ::google::protobuf::Map; @@ -129,9 +131,9 @@ absl::StatusOr> DecodeJsonArrayPrevWins( return result; } -absl::Status CborSerializeBrowserSignals(absl::string_view key, - const BrowserSignals& browser_signals, - cbor_item_t& interest_group_root) { +absl::Status CborSerializeBrowserSignals( + absl::string_view key, const BrowserSignalsForBidding& browser_signals, + cbor_item_t& interest_group_root) { ScopedCbor browser_signals_map(cbor_new_definite_map(kNumBrowserSignalKeys)); PS_RETURN_IF_ERROR(CborSerializeKeyValue(kJoinCount, &cbor_build_uint64, @@ -182,6 +184,7 @@ absl::Status CborSerializeBrowserSignals(absl::string_view key, "Unable to serialize the complete prev wins data"); } } + struct cbor_pair kv = { .key = cbor_move(cbor_build_stringn(key.data(), key.size())), .value = *browser_signals_map}; @@ -194,7 +197,8 @@ absl::Status CborSerializeBrowserSignals(absl::string_view key, } absl::Status CborSerializeInterestGroup( - const BuyerInput::InterestGroup& interest_group, cbor_item_t& root) { + const BuyerInputForBidding::InterestGroupForBidding& interest_group, + cbor_item_t& root) { cbor_item_t* interest_group_serialized = cbor_new_definite_map(kNumInterestGroupKeys); PS_RETURN_IF_ERROR(CborSerializeString(kName, interest_group.name(), @@ -307,7 +311,7 @@ absl::StatusOr CborEncodeProtectedAuctionProto( } absl::StatusOr GetEncodedBuyerInputMap( - const BuyerInputMap& buyer_inputs) { + const BuyerInputForBiddingMap& buyer_inputs) { BuyerInputMapEncoded encoded_buyer_input; for (const auto& [owner, buyer_input] : buyer_inputs) { // Serialize the list of interest groups. diff --git a/services/common/test/utils/cbor_test_utils.h b/services/common/test/utils/cbor_test_utils.h index e8a0dfaa..af01b228 100644 --- a/services/common/test/utils/cbor_test_utils.h +++ b/services/common/test/utils/cbor_test_utils.h @@ -43,7 +43,8 @@ absl::StatusOr CborEncodeProtectedAuctionProto( // BuyerInput Map (note that keys in the map are not encoded). absl::StatusOr> GetEncodedBuyerInputMap( - const google::protobuf::Map& buyer_inputs); + const google::protobuf::Map& + buyer_inputs); // Converts the passed in CBOR data handle to the serialized CBOR byte-string. std::string SerializeCbor(cbor_item_t* root); diff --git a/services/common/test/utils/test_utils.cc b/services/common/test/utils/test_utils.cc index 5f690fe2..dc665e38 100644 --- a/services/common/test/utils/test_utils.cc +++ b/services/common/test/utils/test_utils.cc @@ -49,9 +49,11 @@ GetBidsRequest::GetBidsRawRequest CreateGetBidsRawRequest( kTestContextualPasAdRenderId; } if (add_protected_audience_input) { - *raw_request.mutable_buyer_input() = MakeARandomBuyerInput(); - auto interest_group = - raw_request.mutable_buyer_input()->mutable_interest_groups()->Add(); + *raw_request.mutable_buyer_input_for_bidding() = + MakeARandomBuyerInputForBidding(); + auto interest_group = raw_request.mutable_buyer_input_for_bidding() + ->mutable_interest_groups() + ->Add(); interest_group->set_name("ig_name"); interest_group->add_bidding_signals_keys("key"); } @@ -64,6 +66,7 @@ GetBidsRequest CreateGetBidsRequest(bool add_protected_signals_input, GetBidsRequest get_bids_request; auto raw_request = CreateGetBidsRawRequest( add_protected_signals_input, add_protected_audience_input, client_type); + raw_request.PrintDebugString(); get_bids_request.set_request_ciphertext(raw_request.SerializeAsString()); get_bids_request.set_key_id(kTestKeyId); return get_bids_request; diff --git a/services/common/util/priority_vector/priority_vector_utils.cc b/services/common/util/priority_vector/priority_vector_utils.cc index b86c3ccc..6126b2a0 100644 --- a/services/common/util/priority_vector/priority_vector_utils.cc +++ b/services/common/util/priority_vector/priority_vector_utils.cc @@ -28,7 +28,7 @@ namespace privacy_sandbox::bidding_auction_servers { namespace { -absl::Duration GetRecency(const BrowserSignals& browser_signals) { +absl::Duration GetRecency(const BrowserSignalsForBidding& browser_signals) { int64_t recency_ms; if (browser_signals.has_recency_ms()) { recency_ms = browser_signals.recency_ms(); @@ -57,8 +57,9 @@ double CalculateDotProduct(const rapidjson::Document& vector1, return priority; } -void PopulateDeviceSignalFields(rapidjson::Document& priority_signals, - const BrowserSignals& browser_signals) { +void PopulateDeviceSignalFields( + rapidjson::Document& priority_signals, + const BrowserSignalsForBidding& browser_signals) { absl::AnyInvocable add_or_update_value = [&priority_signals](const char* key, double value) { rapidjson::Value key_value(key, priority_signals.GetAllocator()); @@ -163,12 +164,13 @@ absl::StatusOr GetBuyerPrioritySignals( } absl::flat_hash_map CalculateInterestGroupPriorities( - rapidjson::Document& priority_signals, const BuyerInput& buyer_input, + rapidjson::Document& priority_signals, + const BuyerInputForBidding& buyer_input, const absl::flat_hash_map& per_ig_priority_vectors) { absl::flat_hash_map priorities_by_ig; - for (const BuyerInput::InterestGroup& interest_group : + for (const BuyerInputForBidding::InterestGroupForBidding& interest_group : buyer_input.interest_groups()) { PopulateDeviceSignalFields(priority_signals, interest_group.browser_signals()); diff --git a/services/common/util/priority_vector/priority_vector_utils.h b/services/common/util/priority_vector/priority_vector_utils.h index 2dea0408..7314f8ed 100644 --- a/services/common/util/priority_vector/priority_vector_utils.h +++ b/services/common/util/priority_vector/priority_vector_utils.h @@ -60,7 +60,8 @@ absl::StatusOr GetBuyerPrioritySignals( // groups without an entry in per_ig_priority_vectors will be assigned a // priority of 0. absl::flat_hash_map CalculateInterestGroupPriorities( - rapidjson::Document& priority_signals, const BuyerInput& buyer_input, + rapidjson::Document& priority_signals, + const BuyerInputForBidding& buyer_input, const absl::flat_hash_map& per_ig_priority_vectors); diff --git a/services/common/util/priority_vector/priority_vector_utils_test.cc b/services/common/util/priority_vector/priority_vector_utils_test.cc index 3796c94d..b1950d80 100644 --- a/services/common/util/priority_vector/priority_vector_utils_test.cc +++ b/services/common/util/priority_vector/priority_vector_utils_test.cc @@ -53,12 +53,12 @@ PerBuyerConfigMap CreatePerBuyerConfig( return per_buyer_config; } -BuyerInput::InterestGroup CreateInterestGroup(const std::string& name, - absl::Duration ig_age) { - BrowserSignals browser_signals; +BuyerInputForBidding::InterestGroupForBidding CreateInterestGroup( + const std::string& name, absl::Duration ig_age) { + BrowserSignalsForBidding browser_signals; browser_signals.set_recency_ms(absl::ToInt64Milliseconds(ig_age)); - BuyerInput::InterestGroup interest_group; + BuyerInputForBidding::InterestGroupForBidding interest_group; interest_group.set_name(name); *interest_group.mutable_browser_signals() = std::move(browser_signals); @@ -171,7 +171,7 @@ TEST_F(PriorityVectorUtilsTest, priority_vector_doc.GetAllocator()); absl::Duration ig_age = absl::Minutes(30); - BuyerInput::InterestGroup interest_group = + BuyerInputForBidding::InterestGroupForBidding interest_group = CreateInterestGroup("ig_name", ig_age); rapidjson::Value priority_vector_copy(rapidjson::kObjectType); @@ -180,7 +180,7 @@ TEST_F(PriorityVectorUtilsTest, per_ig_priority_vectors[interest_group.name()] = std::move(priority_vector_copy); - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; *buyer_input.mutable_interest_groups()->Add() = std::move(interest_group); double expected_priority = diff --git a/services/inference_sidecar/common/sandbox/sandbox_executor.cc b/services/inference_sidecar/common/sandbox/sandbox_executor.cc index 559ba549..970f935d 100644 --- a/services/inference_sidecar/common/sandbox/sandbox_executor.cc +++ b/services/inference_sidecar/common/sandbox/sandbox_executor.cc @@ -150,6 +150,11 @@ void AllowTensorFlow(sandbox2::PolicyBuilder& builder) { builder.AllowReaddir(); } +void AllowAmx(sandbox2::PolicyBuilder& builder) { + // arch_prctl is required for Intel AMX. + builder.AllowSyscall(__NR_arch_prctl); +} + std::unique_ptr MakePolicy() { if (absl::GetFlag(FLAGS_testonly_disable_sandbox)) { return sandbox2::PolicyBuilder() @@ -167,6 +172,7 @@ std::unique_ptr MakePolicy() { AllowAws(builder); AllowPyTorch(builder); AllowTensorFlow(builder); + AllowAmx(builder); builder.AllowStaticStartup().AllowLogForwarding(); return builder.BuildOrDie(); diff --git a/services/seller_frontend_service/BUILD b/services/seller_frontend_service/BUILD index cf80945c..4de9cbd5 100644 --- a/services/seller_frontend_service/BUILD +++ b/services/seller_frontend_service/BUILD @@ -87,6 +87,7 @@ cc_library( "//services/seller_frontend_service/k_anon:k_anon_utils", "//services/seller_frontend_service/private_aggregation:private_aggregation_helper", "//services/seller_frontend_service/providers:seller_frontend_providers", + "//services/seller_frontend_service/util:buyer_input_proto_utils", "//services/seller_frontend_service/util:encryption_util", "//services/seller_frontend_service/util:framing_utils", "//services/seller_frontend_service/util:key_fetcher_utils", @@ -275,6 +276,7 @@ cc_test( "//services/common/test:random", "//services/common/test/utils:test_init", "//services/common/util:oblivious_http_utils", + "//services/seller_frontend_service/util:buyer_input_proto_utils", "//services/seller_frontend_service/util:framing_utils", "//services/seller_frontend_service/util:select_ad_reactor_test_utils", "@com_github_google_quiche//quiche:oblivious_http_unstable_api", diff --git a/services/seller_frontend_service/kv_seller_signals_adapter_test.cc b/services/seller_frontend_service/kv_seller_signals_adapter_test.cc index f667ca3c..14149bc1 100644 --- a/services/seller_frontend_service/kv_seller_signals_adapter_test.cc +++ b/services/seller_frontend_service/kv_seller_signals_adapter_test.cc @@ -528,20 +528,12 @@ TEST(KvSellerSignalsAdapter, ConvertV2ResponseToV1ScoringSignalsSuccess) { R"JSON( { "renderUrls": { - "www.exampleRenderUrl.com/ad1": { - "value": "scoringSignalForAd1" - }, - "www.exampleRenderUrl.com/ad2": { - "value": "scoringSignalForAd2" - } + "www.exampleRenderUrl.com/ad1": "scoringSignalForAd1", + "www.exampleRenderUrl.com/ad2": "scoringSignalForAd2" }, "adComponentRenderUrls": { - "www.exampleRenderUrl.com/adComponent1": { - "value": "scoringSignalForAdComponent1" - }, - "www.exampleRenderUrl.com/adComponent2": { - "value": "scoringSignalForAdComponent2" - } + "www.exampleRenderUrl.com/adComponent1": "scoringSignalForAdComponent1", + "www.exampleRenderUrl.com/adComponent2": "scoringSignalForAdComponent2" } })JSON"; auto actual_signals_json = ParseJsonString(*((*result)->scoring_signals)); diff --git a/services/seller_frontend_service/private_aggregation/private_aggregation_helper.cc b/services/seller_frontend_service/private_aggregation/private_aggregation_helper.cc index 525d127e..1802795f 100644 --- a/services/seller_frontend_service/private_aggregation/private_aggregation_helper.cc +++ b/services/seller_frontend_service/private_aggregation/private_aggregation_helper.cc @@ -370,6 +370,7 @@ void HandlePrivateAggregationContributions( // Groups PrivateAggregateContributions per AdTech ContributionsPerAdTechMap GroupContributionsByAdTech( + int per_adtech_paapi_contributions_limit, const PrivateAggregateReportingResponses& responses) { absl::flat_hash_map> @@ -382,7 +383,9 @@ ContributionsPerAdTechMap GroupContributionsByAdTech( std::vector()) .first->second; for (const auto& contribution : response.contributions()) { - contributions_list.push_back(&contribution); + if (contributions_list.size() < per_adtech_paapi_contributions_limit) { + contributions_list.push_back(&contribution); + } } } return contributions_map; @@ -678,10 +681,11 @@ CborDecodePAggIgContributions(cbor_item_t& serialized_ig_contributions) { absl::Status CborSerializePAggResponse( const PrivateAggregateReportingResponses& responses, - ErrorHandler error_handler, cbor_item_t& root) { + int per_adtech_paapi_contributions_limit, ErrorHandler error_handler, + cbor_item_t& root) { // Group contributions by AdTech. - ContributionsPerAdTechMap grouped_contributions = - GroupContributionsByAdTech(responses); + ContributionsPerAdTechMap grouped_contributions = GroupContributionsByAdTech( + per_adtech_paapi_contributions_limit, responses); ScopedCbor all_serialized_adtech_contributions( cbor_new_definite_array(grouped_contributions.size())); for (const auto& [adtech_origin, contributions] : grouped_contributions) { diff --git a/services/seller_frontend_service/private_aggregation/private_aggregation_helper.h b/services/seller_frontend_service/private_aggregation/private_aggregation_helper.h index 0e92e096..e3c9a2f9 100644 --- a/services/seller_frontend_service/private_aggregation/private_aggregation_helper.h +++ b/services/seller_frontend_service/private_aggregation/private_aggregation_helper.h @@ -68,6 +68,7 @@ void HandlePrivateAggregationContributions( // Groups PrivateAggregateContributions per AdTech ContributionsPerAdTechMap GroupContributionsByAdTech( + int per_adtech_paapi_contributions_limit, const PrivateAggregateReportingResponses& responses); // Converts array of 64 bit integers in Bucket128Bit to @@ -109,7 +110,8 @@ CborDecodePAggIgContributions(cbor_item_t& serialized_ig_contributions); // PrivateAggregateContributions to create paggResponse. absl::Status CborSerializePAggResponse( const PrivateAggregateReportingResponses& responses, - ErrorHandler error_handler, cbor_item_t& root); + int per_adtech_paapi_contributions_limit, ErrorHandler error_handler, + cbor_item_t& root); // Decodes reporting_origin and PrivateAggregateContributions from serialized // igContributions and returns list of PrivateAggregateReportingResponse. diff --git a/services/seller_frontend_service/private_aggregation/private_aggregation_helper_test.cc b/services/seller_frontend_service/private_aggregation/private_aggregation_helper_test.cc index a1cbe713..74b0697e 100644 --- a/services/seller_frontend_service/private_aggregation/private_aggregation_helper_test.cc +++ b/services/seller_frontend_service/private_aggregation/private_aggregation_helper_test.cc @@ -39,6 +39,7 @@ constexpr absl::string_view kTestIgNameWin = "testIgNameWin"; constexpr absl::string_view kTestIgNameLoss = "testIgNameLoss"; constexpr absl::string_view kTestIgOwner = "testIgOwner"; constexpr absl::string_view kTestSeller = "kTestSeller"; +constexpr int kPerAdtechPaapiContributionsLimit = 2; constexpr int losing_ig_idx = 1; constexpr int winning_ig_idx = 2; @@ -149,6 +150,12 @@ TEST(GroupContributionsByAdTechTest, ReturnsMapOfAdTechAndContributions) { PrivateAggregateReportingResponse response1; PrivateAggregateContribution* contribution1 = response1.add_contributions(); *contribution1 = GetTestContributionWithIntegers(EVENT_TYPE_WIN, ""); + // This contribution is expected to be dropped since + // per_adtech_paapi_contributions_limit is 2 + *response1.add_contributions() = + GetTestContributionWithIntegers(EVENT_TYPE_WIN, ""); + *response1.add_contributions() = + GetTestContributionWithIntegers(EVENT_TYPE_LOSS, ""); response1.set_adtech_origin(kTestIgOwner); PrivateAggregateReportingResponse response2; PrivateAggregateContribution* contribution2 = response2.add_contributions(); @@ -159,12 +166,14 @@ TEST(GroupContributionsByAdTechTest, ReturnsMapOfAdTechAndContributions) { responses.Add(std::move(response1)); responses.Add(std::move(response2)); - auto result = GroupContributionsByAdTech(responses); + auto result = + GroupContributionsByAdTech(kPerAdtechPaapiContributionsLimit, responses); ASSERT_EQ(result.size(), 2); - ASSERT_EQ(result[kTestIgOwner].size(), 1); + ASSERT_EQ(result[kTestIgOwner].size(), 2); google::protobuf::util::MessageDifferencer differencer; ASSERT_TRUE(differencer.Compare(*result[kTestIgOwner][0], *contribution1)); + ASSERT_TRUE(differencer.Compare(*result[kTestIgOwner][1], *contribution1)); ASSERT_EQ(result[kTestSeller].size(), 1); ASSERT_TRUE(differencer.Compare(*result[kTestSeller][0], *contribution2)); } @@ -266,7 +275,7 @@ TEST(CborSerializePAggContribution, SuccessfullySerializesEventContributions) { responses.Add(std::move(expected_response)); ContributionsPerAdTechMap contributions_per_adtech = - GroupContributionsByAdTech(responses); + GroupContributionsByAdTech(kPerAdtechPaapiContributionsLimit, responses); ASSERT_EQ(contributions_per_adtech.size(), 1); ASSERT_EQ(contributions_per_adtech[kTestIgOwner].size(), 2); ScopedCbor cbor_data_root(cbor_new_definite_map(1)); @@ -323,7 +332,7 @@ TEST(CborSerializePAggContribution, SuccessfullySerailizesIgContributions) { PrivateAggregateReportingResponses responses; responses.Add()->CopyFrom(expected_response); ContributionsPerAdTechMap contributions_per_adtech = - GroupContributionsByAdTech(responses); + GroupContributionsByAdTech(kPerAdtechPaapiContributionsLimit, responses); ASSERT_EQ(contributions_per_adtech.size(), 1); ASSERT_EQ(contributions_per_adtech[kTestIgOwner].size(), 2); ScopedCbor cbor_data_root(cbor_new_definite_map(1)); @@ -368,6 +377,7 @@ TEST(CborSerializePAggContribution, SuccessfullySerailizesIgContributions) { } TEST(CborSerializePAggContribution, SuccessfullySerailizesPAggResponse) { + int per_adtech_paapi_contributions_limit = 100; PrivateAggregateReportingResponse seller_pagg_response; PrivateAggregateReportingResponse buyer_pagg_response; PrivateAggregateContribution contribution1 = @@ -386,7 +396,8 @@ TEST(CborSerializePAggContribution, SuccessfullySerailizesPAggResponse) { auto* cbor_internal = cbor_data_root.get(); auto err_handler = [](const grpc::Status& status) {}; auto result = - CborSerializePAggResponse(responses, err_handler, *cbor_internal); + CborSerializePAggResponse(responses, per_adtech_paapi_contributions_limit, + err_handler, *cbor_internal); ASSERT_TRUE(result.ok()) << result; absl::Span contribution_map(cbor_map_handle(cbor_internal), cbor_map_size(cbor_internal)); diff --git a/services/seller_frontend_service/runtime_flags.h b/services/seller_frontend_service/runtime_flags.h index bdec4eb3..306d2117 100644 --- a/services/seller_frontend_service/runtime_flags.h +++ b/services/seller_frontend_service/runtime_flags.h @@ -80,10 +80,10 @@ inline constexpr absl::string_view K_ANON_CLIENT_TIME_OUT_MS = inline constexpr absl::string_view NUM_K_ANON_SHARDS = "NUM_K_ANON_SHARDS"; inline constexpr absl::string_view NUM_NON_K_ANON_SHARDS = "NUM_NON_K_ANON_SHARDS"; -inline constexpr absl::string_view TEST_MODE_K_ANON_CACHE_TTL_SECONDS = - "TEST_MODE_K_ANON_CACHE_TTL_SECONDS"; -inline constexpr absl::string_view TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS = - "TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS"; +inline constexpr absl::string_view TEST_MODE_K_ANON_CACHE_TTL_MS = + "TEST_MODE_K_ANON_CACHE_TTL_MS"; +inline constexpr absl::string_view TEST_MODE_NON_K_ANON_CACHE_TTL_MS = + "TEST_MODE_NON_K_ANON_CACHE_TTL_MS"; inline constexpr absl::string_view ENABLE_K_ANON_QUERY_CACHE = "ENABLE_K_ANON_QUERY_CACHE"; @@ -123,8 +123,8 @@ inline constexpr std::array kFlags = { K_ANON_CLIENT_TIME_OUT_MS, NUM_K_ANON_SHARDS, NUM_NON_K_ANON_SHARDS, - TEST_MODE_K_ANON_CACHE_TTL_SECONDS, - TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS, + TEST_MODE_K_ANON_CACHE_TTL_MS, + TEST_MODE_NON_K_ANON_CACHE_TTL_MS, ENABLE_K_ANON_QUERY_CACHE}; inline std::vector GetServiceFlags() { diff --git a/services/seller_frontend_service/select_ad_reactor.cc b/services/seller_frontend_service/select_ad_reactor.cc index 30a93976..caf606b0 100644 --- a/services/seller_frontend_service/select_ad_reactor.cc +++ b/services/seller_frontend_service/select_ad_reactor.cc @@ -46,6 +46,7 @@ #include "services/seller_frontend_service/k_anon/k_anon_utils.h" #include "services/seller_frontend_service/kv_seller_signals_adapter.h" #include "services/seller_frontend_service/private_aggregation/private_aggregation_helper.h" +#include "services/seller_frontend_service/util/buyer_input_proto_utils.h" #include "services/seller_frontend_service/util/key_fetcher_utils.h" #include "services/seller_frontend_service/util/web_utils.h" #include "src/communication/ohttp_utils.h" @@ -63,7 +64,8 @@ using ScoreAdsRawRequest = ScoreAdsRequest::ScoreAdsRawRequest; using AdScore = ScoreAdsResponse::AdScore; using AdWithBidMetadata = ScoreAdsRequest::ScoreAdsRawRequest::AdWithBidMetadata; -using DecodedBuyerInputs = absl::flat_hash_map; +using DecodedBuyerInputs = + absl::flat_hash_map; using EncodedBuyerInputs = ::google::protobuf::Map; using KVLookUpResult = absl::StatusOr>; @@ -90,7 +92,8 @@ SelectAdReactor::SelectAdReactor( const TrustedServersConfigClient& config_client, const ReportWinMap& report_win_map, bool enable_cancellation, bool enable_kanon, bool enable_buyer_private_aggregate_reporting, - bool fail_fast, int max_buyers_solicited) + int per_adtech_paapi_contributions_limit, bool fail_fast, + int max_buyers_solicited) : request_context_(context), request_(request), response_(response), @@ -122,6 +125,8 @@ SelectAdReactor::SelectAdReactor( enable_enforce_kanon_(enable_kanon), enable_buyer_private_aggregate_reporting_( enable_buyer_private_aggregate_reporting), + per_adtech_paapi_contributions_limit_( + per_adtech_paapi_contributions_limit), priority_signals_vector_(rapidjson::kObjectType), async_task_tracker_( 0, log_context_, // NumTasksToTrack is set appropriately in Execute() @@ -221,10 +226,10 @@ AdWithBidMetadata SelectAdReactor::BuildAdWithBidMetadata( result.set_interest_group_owner(interest_group_owner); // Finally, find the AdWithBid's IG and copy the last fields from there. - const BuyerInput& buyer_input = + const BuyerInputForBidding& buyer_input_for_bidding = buyer_inputs_->find(interest_group_owner)->second; int interest_group_idx = 0; - for (const auto& interest_group : buyer_input.interest_groups()) { + for (const auto& interest_group : buyer_input_for_bidding.interest_groups()) { if (interest_group.name() == result.interest_group_name()) { result.set_interest_group_origin(interest_group.origin()); if (request_->client_type() == CLIENT_TYPE_BROWSER) { @@ -756,8 +761,9 @@ void SelectAdReactor::Execute() { } std::unique_ptr -SelectAdReactor::CreateGetBidsRequest(const std::string& buyer_ig_owner, - const BuyerInput& buyer_input) { +SelectAdReactor::CreateGetBidsRequest( + const std::string& buyer_ig_owner, + const BuyerInputForBidding& buyer_input_for_bidding) { auto get_bids_request = std::make_unique(); get_bids_request->set_is_chaff(false); get_bids_request->set_seller(auction_config_.seller()); @@ -786,7 +792,11 @@ SelectAdReactor::CreateGetBidsRequest(const std::string& buyer_ig_owner, } } - *get_bids_request->mutable_buyer_input() = buyer_input; + *get_bids_request->mutable_buyer_input() = + ToBuyerInput(buyer_input_for_bidding); + *get_bids_request->mutable_buyer_input_for_bidding() = + buyer_input_for_bidding; + get_bids_request->set_top_level_seller(auction_config_.top_level_seller()); std::visit( [&get_bids_request, &buyer_debug_id, @@ -812,11 +822,13 @@ SelectAdReactor::CreateGetBidsRequest(const std::string& buyer_ig_owner, protected_auction_input_); if (!is_protected_audience_enabled_ && - get_bids_request->mutable_buyer_input()->interest_groups_size() > 0) { + get_bids_request->mutable_buyer_input_for_bidding() + ->interest_groups_size() > 0) { PS_VLOG(kNoisyWarn) << "Clearing interest groups in the input since protected " "audience support is disabled"; - get_bids_request->mutable_buyer_input()->clear_interest_groups(); + get_bids_request->mutable_buyer_input_for_bidding() + ->clear_interest_groups(); } if (config_client_.HasParameter(ENABLE_PRIORITY_VECTOR) && @@ -1248,12 +1260,17 @@ void SelectAdReactor::CancellableFetchScoringSignalsV2( return; } grpc::ClientContext* client_context = client_contexts_.Add(); + + EventMessage::KvSignal score_signal = KvEventMessage( + (*maybe_scoring_signals_request)->ShortDebugString(), log_context_); + auto status = clients_.kv_async_client->ExecuteInternal( *std::move(maybe_scoring_signals_request), client_context, CancellationWrapper( request_context_, enable_cancellation_, - [this, kv_request](KVLookUpResult kv_look_up_result, - ResponseMetadata response_metadata) mutable { + [this, kv_request, score_signal = std::move(score_signal)]( + KVLookUpResult kv_look_up_result, + ResponseMetadata response_metadata) mutable { { // Only logs KV request and response sizes if fetching signals // succeeds. @@ -1278,10 +1295,17 @@ void SelectAdReactor::CancellableFetchScoringSignalsV2( ErrorCode::SERVER_SIDE); OnScoreAdsDone( std::make_unique()); + if (server_common::log::PS_VLOG_IS_ON(kKVLog)) { + log_context_.SetEventMessageField(std::move(score_signal)); + } return; } auto signals = ConvertV2ResponseToV1ScoringSignals( *std::move(kv_look_up_result)); + + SetKvEventMessage("KVAsyncGrpcClient", + *((*signals)->scoring_signals), + std::move(score_signal), log_context_); maybe_scoring_signals_ = *std::move(signals); fetch_scoring_signals_query_kanon_tracker_.TaskCompleted( TaskStatus::SUCCESS); @@ -1304,11 +1328,12 @@ void SelectAdReactor::CancellableFetchScoringSignals() { scoring_signals_request.seller_kv_experiment_group_id_ = absl::StrCat( auction_config_.code_experiment_spec().seller_kv_experiment_group_id()); } - if (UseKvV2( - request_->client_type(), is_tkv_v2_browser_enabled_, - config_client_.GetBooleanParameter(TEST_MODE), - config_client_.GetStringParameter(TRUSTED_KEY_VALUE_V2_SIGNALS_HOST) - .empty())) { + absl::string_view kv_v2_signals_host = + config_client_.GetStringParameter(TRUSTED_KEY_VALUE_V2_SIGNALS_HOST); + if (UseKvV2(request_->client_type(), is_tkv_v2_browser_enabled_, + config_client_.GetBooleanParameter(TEST_MODE), + kv_v2_signals_host.empty() || + kv_v2_signals_host == kIgnoredPlaceholderValue)) { CancellableFetchScoringSignalsV2(scoring_signals_request); } else { CancellableFetchScoringSignalsV1(scoring_signals_request); @@ -1552,8 +1577,8 @@ void SelectAdReactor::OnScoreAdsDone( if (HaveClientVisibleErrors()) { error = std::move(error_); } - absl::StatusOr non_encrypted_response = - GetNonEncryptedResponse(high_score, error, ghost_winners); + absl::StatusOr non_encrypted_response = GetNonEncryptedResponse( + high_score, error, ghost_winners, per_adtech_paapi_contributions_limit_); if (!non_encrypted_response.ok()) { FinishWithStatus(grpc::Status(grpc::INTERNAL, kInternalServerError)); return; diff --git a/services/seller_frontend_service/select_ad_reactor.h b/services/seller_frontend_service/select_ad_reactor.h index 0c7a7ad2..6f03976d 100644 --- a/services/seller_frontend_service/select_ad_reactor.h +++ b/services/seller_frontend_service/select_ad_reactor.h @@ -118,7 +118,7 @@ class SelectAdReactor : public grpc::ServerUnaryReactor { const ReportWinMap& report_win_map, bool enable_cancellation = false, bool enable_kanon = false, bool enable_buyer_private_aggregate_reporting = false, - bool fail_fast = true, + int per_adtech_paapi_contributions_limit = 0, bool fail_fast = true, int max_buyers_solicited = metric::kMaxBuyersSolicited); // Initiate the asynchronous execution of the SelectAdRequest. @@ -136,7 +136,8 @@ class SelectAdReactor : public grpc::ServerUnaryReactor { virtual absl::StatusOr GetNonEncryptedResponse( const std::optional& high_score, const std::optional& error, - const AdScores* ghost_winning_scores = nullptr) = 0; + const AdScores* ghost_winning_scores = nullptr, + int per_adtech_paapi_contributions_limit = 0) = 0; // Decodes the plaintext payload and returns a `ProtectedAudienceInput` proto. // Any errors while decoding are reported to error accumulator object. @@ -150,13 +151,13 @@ class SelectAdReactor : public grpc::ServerUnaryReactor { // Returns the decoded BuyerInput from the encoded/compressed BuyerInput. // Any errors while decoding are reported to error accumulator object. - virtual absl::flat_hash_map + virtual absl::flat_hash_map GetDecodedBuyerinputs(const google::protobuf::Map& encoded_buyer_inputs) = 0; virtual std::unique_ptr CreateGetBidsRequest(const std::string& buyer_ig_owner, - const BuyerInput& buyer_input); + const BuyerInputForBidding& buyer_input); virtual std::unique_ptr CreateScoreAdsRequest(); @@ -203,14 +204,14 @@ class SelectAdReactor : public grpc::ServerUnaryReactor { } else { bool is_any_buyer_input_valid = false; std::set observed_errors; - for (const auto& [buyer, buyer_input] : *buyer_inputs_) { + for (const auto& [buyer, buyer_input_for_bidding] : *buyer_inputs_) { bool any_error = false; if (buyer.empty()) { observed_errors.insert(kEmptyInterestGroupOwner); any_error = true; } - if (buyer_input.interest_groups().empty() && - !buyer_input.has_protected_app_signals()) { + if (buyer_input_for_bidding.interest_groups().empty() && + !buyer_input_for_bidding.has_protected_app_signals()) { observed_errors.insert(absl::StrFormat( kMissingInterestGroupsAndProtectedSignals, buyer)); any_error = true; @@ -453,7 +454,7 @@ class SelectAdReactor : public grpc::ServerUnaryReactor { RequestLogContext log_context_; // Decompressed and decoded buyer inputs. - absl::StatusOr> + absl::StatusOr> buyer_inputs_; // Used to log metric, same life time as reactor. @@ -494,6 +495,7 @@ class SelectAdReactor : public grpc::ServerUnaryReactor { bool enable_buyer_private_aggregate_reporting_; + int per_adtech_paapi_contributions_limit_; // Pseudo random number generator for use in chaffing. std::optional generator_; diff --git a/services/seller_frontend_service/select_ad_reactor_app.cc b/services/seller_frontend_service/select_ad_reactor_app.cc index 075f978a..e3b244ab 100644 --- a/services/seller_frontend_service/select_ad_reactor_app.cc +++ b/services/seller_frontend_service/select_ad_reactor_app.cc @@ -21,6 +21,7 @@ #include "services/common/compression/gzip.h" #include "services/common/util/hash_util.h" #include "services/common/util/request_response_constants.h" +#include "services/seller_frontend_service/util/buyer_input_proto_utils.h" #include "services/seller_frontend_service/util/framing_utils.h" #include "services/seller_frontend_service/util/proto_mapping_util.h" #include "src/communication/encoding_utils.h" @@ -31,7 +32,8 @@ namespace privacy_sandbox::bidding_auction_servers { using BiddingGroupsMap = ::google::protobuf::Map; using EncodedBuyerInputs = ::google::protobuf::Map; -using DecodedBuyerInputs = absl::flat_hash_map; +using DecodedBuyerInputs = + absl::flat_hash_map; using ReportErrorSignature = std::function error_visibility_with_loc, const std::string& msg, ErrorCode error_code)>; @@ -73,7 +75,7 @@ SelectAdReactorForApp::SelectAdReactorForApp( const TrustedServersConfigClient& config_client, const ReportWinMap& report_win_map, bool enable_cancellation, bool enable_kanon, bool enable_buyer_private_aggregate_reporting, - bool fail_fast) + int per_adtech_paapi_contributions_limit, bool fail_fast) : SelectAdReactor(context, request, response, clients, config_client, report_win_map, enable_cancellation, enable_kanon, fail_fast) {} @@ -81,7 +83,8 @@ SelectAdReactorForApp::SelectAdReactorForApp( absl::StatusOr SelectAdReactorForApp::GetNonEncryptedResponse( const std::optional& high_score, const std::optional& error, - const AdScores* ghost_winning_scores) { + const AdScores* ghost_winning_scores, + int per_adtech_paapi_contributions_limit) { AuctionResult auction_result; if (high_score) { auction_result = AdScoreToAuctionResult( @@ -161,7 +164,9 @@ DecodedBuyerInputs SelectAdReactorForApp::GetDecodedBuyerinputs( continue; } - decoded_buyer_inputs.insert({owner, std::move(buyer_input)}); + BuyerInputForBidding buyer_input_for_bidding = + ToBuyerInputForBidding(buyer_input); + decoded_buyer_inputs.insert({owner, std::move(buyer_input_for_bidding)}); } return decoded_buyer_inputs; @@ -175,7 +180,15 @@ void SelectAdReactorForApp::MayPopulateProtectedAppSignalsBuyerInput( "hence not populating PAS buyer input"; // We don't want to forward the protected signals when feature is disabled, // even if client sent them erroneously. - get_bids_raw_request->mutable_buyer_input()->clear_protected_app_signals(); + if (get_bids_raw_request->has_buyer_input()) { + get_bids_raw_request->mutable_buyer_input() + ->clear_protected_app_signals(); + } + + if (get_bids_raw_request->has_buyer_input_for_bidding()) { + get_bids_raw_request->mutable_buyer_input_for_bidding() + ->clear_protected_app_signals(); + } return; } @@ -186,7 +199,27 @@ void SelectAdReactorForApp::MayPopulateProtectedAppSignalsBuyerInput( .empty()) { PS_VLOG(8, log_context_) << "No protected app signals in buyer inputs from client"; - get_bids_raw_request->mutable_buyer_input()->clear_protected_app_signals(); + if (get_bids_raw_request->has_buyer_input()) { + get_bids_raw_request->mutable_buyer_input() + ->clear_protected_app_signals(); + } + + return; + } + + if (!get_bids_raw_request->buyer_input_for_bidding() + .has_protected_app_signals() || + get_bids_raw_request->buyer_input_for_bidding() + .protected_app_signals() + .app_install_signals() + .empty()) { + PS_VLOG(8, log_context_) + << "No protected app signals in buyer inputs from client"; + + if (get_bids_raw_request->has_buyer_input_for_bidding()) { + get_bids_raw_request->mutable_buyer_input_for_bidding() + ->clear_protected_app_signals(); + } return; } @@ -195,9 +228,11 @@ void SelectAdReactorForApp::MayPopulateProtectedAppSignalsBuyerInput( auto* protected_app_signals_buyer_input = get_bids_raw_request->mutable_protected_app_signals_buyer_input(); protected_app_signals_buyer_input->mutable_protected_app_signals()->Swap( - get_bids_raw_request->mutable_buyer_input() + get_bids_raw_request->mutable_buyer_input_for_bidding() ->mutable_protected_app_signals()); get_bids_raw_request->mutable_buyer_input()->clear_protected_app_signals(); + get_bids_raw_request->mutable_buyer_input_for_bidding() + ->clear_protected_app_signals(); // Add contextual Protected App Signals data to PAS buyer input. auto& per_buyer_config = request_->auction_config().per_buyer_config(); @@ -224,8 +259,9 @@ void SelectAdReactorForApp::MayPopulateProtectedAppSignalsBuyerInput( } std::unique_ptr -SelectAdReactorForApp::CreateGetBidsRequest(const std::string& buyer_ig_owner, - const BuyerInput& buyer_input) { +SelectAdReactorForApp::CreateGetBidsRequest( + const std::string& buyer_ig_owner, + const BuyerInputForBidding& buyer_input) { auto request = SelectAdReactor::CreateGetBidsRequest(buyer_ig_owner, buyer_input); // Debug reporting is not supported for Android. diff --git a/services/seller_frontend_service/select_ad_reactor_app.h b/services/seller_frontend_service/select_ad_reactor_app.h index 1ff75a94..b50cd346 100644 --- a/services/seller_frontend_service/select_ad_reactor_app.h +++ b/services/seller_frontend_service/select_ad_reactor_app.h @@ -38,7 +38,7 @@ class SelectAdReactorForApp : public SelectAdReactor { const ReportWinMap& report_win_map, bool enable_cancellation = false, bool enable_kanon = false, bool enable_buyer_private_aggregate_reporting = false, - bool fail_fast = true); + int per_adtech_paapi_contributions_limit = 0, bool fail_fast = true); virtual ~SelectAdReactorForApp() = default; // SelectAdReactorForApp is neither copyable nor movable. @@ -49,7 +49,8 @@ class SelectAdReactorForApp : public SelectAdReactor { absl::StatusOr GetNonEncryptedResponse( const std::optional& high_score, const std::optional& error, - const AdScores* ghost_winning_scores = nullptr) override; + const AdScores* ghost_winning_scores = nullptr, + int per_adtech_paapi_contributions_limit = 0) override; [[deprecated]] ProtectedAudienceInput GetDecodedProtectedAudienceInput( absl::string_view encoded_data) override; @@ -57,9 +58,9 @@ class SelectAdReactorForApp : public SelectAdReactor { ProtectedAuctionInput GetDecodedProtectedAuctionInput( absl::string_view encoded_data) override; - absl::flat_hash_map GetDecodedBuyerinputs( - const google::protobuf::Map& - encoded_buyer_inputs) override; + absl::flat_hash_map + GetDecodedBuyerinputs(const google::protobuf::Map& + encoded_buyer_inputs) override; // Protected App Signals (PAS) related methods follow. @@ -76,7 +77,7 @@ class SelectAdReactorForApp : public SelectAdReactor { // PAS buyer inputs populated properly. std::unique_ptr CreateGetBidsRequest( const std::string& buyer_ig_owner, - const BuyerInput& buyer_input) override; + const BuyerInputForBidding& buyer_input) override; // Populates PAS bids in the scoring request to be sent to auction service. void MayPopulateProtectedAppSignalsBids( diff --git a/services/seller_frontend_service/select_ad_reactor_app_test.cc b/services/seller_frontend_service/select_ad_reactor_app_test.cc index c9fca249..ff840d1e 100644 --- a/services/seller_frontend_service/select_ad_reactor_app_test.cc +++ b/services/seller_frontend_service/select_ad_reactor_app_test.cc @@ -924,6 +924,7 @@ TEST_F(SelectAdReactorPASTest, KAnonHashesAreQueried) { RunReactorRequest( config_, clients_, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); } @@ -1036,6 +1037,7 @@ TEST_F(SelectAdReactorPASTest, AdConsideredNonKAnonIfAdRenderHashIsNotKAnon) { RunReactorRequest( config_, clients_, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); } @@ -1375,6 +1377,68 @@ TEST_F(SelectAdReactorPASTest, config_, clients_, request_with_context.select_ad_request); } +TEST_F(SelectAdReactorPASTest, FailsWhenPlaceholderSetForTkvV2Address) { + this->config_.SetOverride(kIgnoredPlaceholderValue, + TRUSTED_KEY_VALUE_V2_SIGNALS_HOST); + config_.SetOverride(kTrue, TEST_MODE); + auto request_with_context = + CreateSelectAdRequest(kSellerOriginDomain, + /*add_interest_group=*/true, + /*add_protected_app_signals=*/true, + /*app_install_signals=*/std::nullopt); + + // Setup BFE to return a PAS bid. + auto mock_get_bids = [this](std::unique_ptr + get_bids_raw_request, + grpc::ClientContext* context, + GetBidDoneCallback on_done, + absl::Duration timeout, + RequestConfig request_config) { + EXPECT_FALSE(get_bids_raw_request->enable_debug_reporting()); + auto response = std::make_unique(); + response->mutable_protected_app_signals_bids()->Add(GetTestPASAdWithBid()); + std::move(on_done)(std::move(response), /*response_metadata=*/{}); + return absl::OkStatus(); + }; + auto setup_mock_buyer = + [&mock_get_bids](std::unique_ptr buyer) { + EXPECT_CALL(*buyer, ExecuteInternal).WillRepeatedly(mock_get_bids); + return buyer; + }; + auto MockBuyerFactoryCall = [setup_mock_buyer](absl::string_view hostname) { + return setup_mock_buyer(std::make_unique()); + }; + EXPECT_CALL(buyer_front_end_async_client_factory_mock_, Get(_)) + .WillRepeatedly(MockBuyerFactoryCall); + MockEntriesCallOnBuyerFactory( + request_with_context.protected_auction_input.buyer_input(), + buyer_front_end_async_client_factory_mock_); + + EXPECT_CALL(kv_async_client_, ExecuteInternal).Times(0); + + auto expected_get_bids_response = + std::make_unique(); + expected_get_bids_response->mutable_protected_app_signals_bids()->Add( + GetTestPASAdWithBid()); + expected_buyer_bids_.emplace(kSampleBuyer, + std::move(expected_get_bids_response)); + // Calls V1 to get scoring signals. + SetupScoringProviderMock(scoring_signals_provider_, expected_buyer_bids_); + EXPECT_CALL(scoring_client_, ExecuteInternal) + .WillOnce([](std::unique_ptr request, + grpc::ClientContext* context, ScoreAdsDoneCallback on_done, + absl::Duration timeout, RequestConfig request_config) { + EXPECT_FALSE(request->enable_debug_reporting()); + std::move(on_done)( + std::make_unique(), + /*response_metadata=*/{}); + return absl::OkStatus(); + }); + SelectAdResponse encrypted_response = + RunReactorRequest( + config_, clients_, request_with_context.select_ad_request); +} + TEST_F(SelectAdReactorPASTest, DisablesDebugReporting) { auto request_with_context = CreateSelectAdRequest(kSellerOriginDomain, @@ -1497,7 +1561,7 @@ TEST_F(SelectAdReactorPASTest, EnableUnlimitedEgressPropagatedToGetBids) { std::vector< std::pair>> entries; - auto setup_mock_buyer = [](const BuyerInput& buyer_input, + auto setup_mock_buyer = [](const BuyerInputForBidding& buyer_input, absl::string_view buyer_ig_owner) { auto buyer = std::make_unique(); EXPECT_CALL(*buyer, ExecuteInternal) @@ -1517,7 +1581,7 @@ TEST_F(SelectAdReactorPASTest, EnableUnlimitedEgressPropagatedToGetBids) { }; for (const auto& buyer_ig_owner : request_with_context.select_ad_request.auction_config().buyer_list()) { - const BuyerInput& buyer_input = DecodeBuyerInput( + const BuyerInputForBidding& buyer_input = DecodeBuyerInput( buyer_ig_owner, request_with_context.protected_auction_input.buyer_input().at( buyer_ig_owner), diff --git a/services/seller_frontend_service/select_ad_reactor_invalid.cc b/services/seller_frontend_service/select_ad_reactor_invalid.cc index 0a00d004..db61094e 100644 --- a/services/seller_frontend_service/select_ad_reactor_invalid.cc +++ b/services/seller_frontend_service/select_ad_reactor_invalid.cc @@ -55,7 +55,8 @@ void SelectAdReactorInvalid::Execute() { absl::StatusOr SelectAdReactorInvalid::GetNonEncryptedResponse( const std::optional& high_score, const std::optional& error, - const AdScores* ghost_winning_scores) { + const AdScores* ghost_winning_scores, + int per_adtech_paapi_contributions_limit) { return ""; } @@ -69,7 +70,7 @@ ProtectedAuctionInput SelectAdReactorInvalid::GetDecodedProtectedAuctionInput( return {}; } -absl::flat_hash_map +absl::flat_hash_map SelectAdReactorInvalid::GetDecodedBuyerinputs( const google::protobuf::Map& encoded_buyer_inputs) { diff --git a/services/seller_frontend_service/select_ad_reactor_invalid.h b/services/seller_frontend_service/select_ad_reactor_invalid.h index 44cba178..6b679259 100644 --- a/services/seller_frontend_service/select_ad_reactor_invalid.h +++ b/services/seller_frontend_service/select_ad_reactor_invalid.h @@ -49,7 +49,8 @@ class SelectAdReactorInvalid : public SelectAdReactor { absl::StatusOr GetNonEncryptedResponse( const std::optional& high_score, const std::optional& error, - const AdScores* ghost_winning_scores = nullptr) override; + const AdScores* ghost_winning_scores = nullptr, + int per_adtech_paapi_contributions_limit = 0) override; [[deprecated]] ProtectedAudienceInput GetDecodedProtectedAudienceInput( absl::string_view encoded_data) override; @@ -57,9 +58,9 @@ class SelectAdReactorInvalid : public SelectAdReactor { ProtectedAuctionInput GetDecodedProtectedAuctionInput( absl::string_view encoded_data) override; - absl::flat_hash_map GetDecodedBuyerinputs( - const google::protobuf::Map& - encoded_buyer_inputs) override; + absl::flat_hash_map + GetDecodedBuyerinputs(const google::protobuf::Map& + encoded_buyer_inputs) override; KAnonJoinCandidate GetKAnonJoinCandidate( const ScoreAdsResponse::AdScore& score) override; diff --git a/services/seller_frontend_service/select_ad_reactor_test.cc b/services/seller_frontend_service/select_ad_reactor_test.cc index bbb302f5..125be5b7 100644 --- a/services/seller_frontend_service/select_ad_reactor_test.cc +++ b/services/seller_frontend_service/select_ad_reactor_test.cc @@ -230,8 +230,8 @@ TYPED_TEST(SellerFrontEndServiceTest, FetchesBidsFromAllBuyers) { int buyer_input_count = this->protected_auction_input_.buyer_input_size(); EXPECT_EQ(buyer_input_count, 2); - auto SetupMockBuyer = [this](const BuyerInput& buyer_input, - absl::string_view buyer_ig_owner) { + auto setup_mock_buyer = [this](const BuyerInputForBidding& buyer_input, + absl::string_view buyer_ig_owner) { auto buyer = std::make_unique(); EXPECT_CALL(*buyer, ExecuteInternal) .Times(1) @@ -248,8 +248,8 @@ TYPED_TEST(SellerFrontEndServiceTest, FetchesBidsFromAllBuyers) { buyer_ig_owner); EXPECT_EQ(get_bids_request->client_type(), this->request_.client_type()); - EXPECT_TRUE( - diff.Compare(buyer_input, get_bids_request->buyer_input())); + EXPECT_TRUE(diff.Compare( + buyer_input, get_bids_request->buyer_input_for_bidding())); BuyerBlobVersions empty_versions; EXPECT_FALSE( diff.Compare(empty_versions, get_bids_request->blob_versions())); @@ -292,8 +292,8 @@ TYPED_TEST(SellerFrontEndServiceTest, FetchesBidsFromAllBuyers) { ErrorVisibility::CLIENT_VISIBLE) << "\n\n"; EXPECT_CALL(buyer_clients, Get(buyer_ig_owner)) - .WillOnce([SetupMockBuyer, buyer_input](absl::string_view hostname) { - return SetupMockBuyer(buyer_input, hostname); + .WillOnce([setup_mock_buyer, buyer_input](absl::string_view hostname) { + return setup_mock_buyer(buyer_input, hostname); }); entries.emplace_back(buyer_ig_owner, @@ -344,8 +344,8 @@ TYPED_TEST(SellerFrontEndServiceTest, int buyer_input_count = this->protected_auction_input_.buyer_input_size(); EXPECT_EQ(buyer_input_count, num_buyers); - auto SetupMockBuyer = [this](const BuyerInput& buyer_input, - absl::string_view buyer_ig_owner) { + auto setup_mock_buyer = [this](const BuyerInputForBidding& buyer_input, + absl::string_view buyer_ig_owner) { auto buyer = std::make_unique(); EXPECT_CALL(*buyer, ExecuteInternal) .Times(1) @@ -359,8 +359,8 @@ TYPED_TEST(SellerFrontEndServiceTest, diff.ReportDifferencesToString(&diff_output); EXPECT_EQ(get_bids_request->client_type(), this->request_.client_type()); - EXPECT_TRUE( - diff.Compare(buyer_input, get_bids_request->buyer_input())); + EXPECT_TRUE(diff.Compare( + buyer_input, get_bids_request->buyer_input_for_bidding())); EXPECT_EQ(this->request_.auction_config().auction_signals(), get_bids_request->auction_signals()); EXPECT_EQ(this->protected_auction_input_.publisher_name(), @@ -407,10 +407,10 @@ TYPED_TEST(SellerFrontEndServiceTest, EXPECT_FALSE(error_accumulator.HasErrors()); EXPECT_CALL(buyer_clients, Get(buyer_ig_owner)) .Times(::testing::AtMost(1)) - .WillOnce([SetupMockBuyer, buyer_input, + .WillOnce([setup_mock_buyer, buyer_input, &num_buyers_solicited](absl::string_view hostname) { ++num_buyers_solicited; - return SetupMockBuyer(buyer_input, hostname); + return setup_mock_buyer(buyer_input, hostname); }); entries.emplace_back(buyer_ig_owner, std::make_shared()); @@ -463,7 +463,7 @@ TYPED_TEST(SellerFrontEndServiceTest, FetchesTwoBidsGivenThreeBuyers) { int buyer_input_count = this->protected_auction_input_.buyer_input_size(); EXPECT_EQ(buyer_input_count, num_buyers); - auto SetupMockBuyer = [this](const BuyerInput& buyer_input) { + auto setup_mock_buyer = [this](const BuyerInputForBidding& buyer_input) { auto buyer = std::make_unique(); EXPECT_CALL(*buyer, ExecuteInternal) .Times(1) @@ -477,8 +477,8 @@ TYPED_TEST(SellerFrontEndServiceTest, FetchesTwoBidsGivenThreeBuyers) { diff.ReportDifferencesToString(&diff_output); EXPECT_EQ(get_values_request->client_type(), this->request_.client_type()); - EXPECT_TRUE( - diff.Compare(buyer_input, get_values_request->buyer_input())); + EXPECT_TRUE(diff.Compare( + buyer_input, get_values_request->buyer_input_for_bidding())); EXPECT_EQ(this->request_.auction_config().auction_signals(), get_values_request->auction_signals()); EXPECT_EQ(this->protected_auction_input_.publisher_name(), @@ -507,10 +507,10 @@ TYPED_TEST(SellerFrontEndServiceTest, FetchesTwoBidsGivenThreeBuyers) { EXPECT_FALSE(error_accumulator.HasErrors()); EXPECT_CALL(buyer_clients, Get(buyer_ig_owner)) .Times(::testing::AtMost(1)) - .WillOnce([SetupMockBuyer, buyer_input, + .WillOnce([setup_mock_buyer, buyer_input, &num_buyers_solicited](absl::string_view hostname) { ++num_buyers_solicited; - return SetupMockBuyer(buyer_input); + return setup_mock_buyer(buyer_input); }); entries.emplace_back(buyer_ig_owner, std::make_shared()); @@ -562,7 +562,7 @@ TYPED_TEST(SellerFrontEndServiceTest, int buyer_input_count = this->protected_auction_input_.buyer_input_size(); EXPECT_EQ(buyer_input_count, 2); - auto SetupMockBuyer = [this](const BuyerInput& buyer_input) { + auto setup_mock_buyer = [this](const BuyerInputForBidding& buyer_input) { auto buyer = std::make_unique(); EXPECT_CALL(*buyer, ExecuteInternal) .Times(1) @@ -574,8 +574,8 @@ TYPED_TEST(SellerFrontEndServiceTest, google::protobuf::util::MessageDifferencer diff; std::string diff_output; diff.ReportDifferencesToString(&diff_output); - EXPECT_TRUE( - diff.Compare(buyer_input, get_values_request->buyer_input())); + EXPECT_TRUE(diff.Compare( + buyer_input, get_values_request->buyer_input_for_bidding())); EXPECT_EQ(this->protected_auction_input_.enable_debug_reporting(), get_values_request->enable_debug_reporting()); EXPECT_EQ(request_config.chaff_request_size, 0); @@ -598,8 +598,8 @@ TYPED_TEST(SellerFrontEndServiceTest, error_accumulator); EXPECT_FALSE(error_accumulator.HasErrors()); EXPECT_CALL(buyer_clients, Get(buyer_ig_owner)) - .WillOnce([SetupMockBuyer, buyer_input](absl::string_view hostname) { - return SetupMockBuyer(buyer_input); + .WillOnce([setup_mock_buyer, buyer_input](absl::string_view hostname) { + return setup_mock_buyer(buyer_input); }); entries.emplace_back(buyer_ig_owner, std::make_shared()); @@ -1072,11 +1072,12 @@ TYPED_TEST(SellerFrontEndServiceTest, ReturnsWinningAdAfterScoring) { std::vector contributions = { win_object_contribution, loss_object_contribution, win_int_contribution, loss_int_contribution}; - for (const auto& [buyer, buyerInput] : decoded_buyer_inputs) { - EXPECT_EQ(buyerInput.interest_groups_size(), 1); + for (const auto& [buyer, buyer_input_for_bidding] : decoded_buyer_inputs) { + EXPECT_EQ(buyer_input_for_bidding.interest_groups_size(), 1); auto get_bid_response = BuildGetBidsResponseWithSingleAd( buyer_to_ad_url.at(buyer), - {.interest_group_name = buyerInput.interest_groups().Get(0).name(), + {.interest_group_name = + buyer_input_for_bidding.interest_groups().Get(0).name(), .contributions = contributions}); EXPECT_FALSE(get_bid_response.bids(0).render().empty()); EXPECT_EQ(get_bid_response.bids(0).ad_components_size(), @@ -1127,13 +1128,13 @@ TYPED_TEST(SellerFrontEndServiceTest, ReturnsWinningAdAfterScoring) { this->protected_auction_input_.buyer_input() .find(bid.interest_group_owner()) ->second; - BuyerInput decoded_buyer_input = + BuyerInputForBidding decoded_buyer_input = DecodeBuyerInput(bid.interest_group_owner(), encoded_buyer_input, error_accumulator); EXPECT_FALSE(error_accumulator.HasErrors()); EXPECT_EQ(decoded_buyer_input.interest_groups_size(), 1); - for (const BuyerInput::InterestGroup& interest_group : - decoded_buyer_input.interest_groups()) { + for (const BuyerInputForBidding::InterestGroupForBidding& + interest_group : decoded_buyer_input.interest_groups()) { if (interest_group.name() == bid.interest_group_name()) { EXPECT_EQ(bid.join_count(), interest_group.browser_signals().join_count()); @@ -1188,7 +1189,8 @@ TYPED_TEST(SellerFrontEndServiceTest, ReturnsWinningAdAfterScoring) { this->config_, clients, this->request_, this->report_win_map_, /*max_buyers_solicited=*/3, /*enable_kanon=*/false, - /*enable_buyer_private_aggregate_reporting=*/true); + /*enable_buyer_private_aggregate_reporting=*/true, + /*per_adtech_paapi_contributions_limit=*/100); scoring_done.Wait(); AuctionResult auction_result = @@ -1439,13 +1441,13 @@ TYPED_TEST(SellerFrontEndServiceTest, this->protected_auction_input_.buyer_input() .find(ad_with_bid_metadata.interest_group_owner()) ->second; - BuyerInput decoded_buyer_input = + BuyerInputForBidding decoded_buyer_input = DecodeBuyerInput(ad_with_bid_metadata.interest_group_owner(), encoded_buyer_input, error_accumulator); EXPECT_FALSE(error_accumulator.HasErrors()); EXPECT_EQ(decoded_buyer_input.interest_groups_size(), 1); - for (const BuyerInput::InterestGroup& interest_group : - decoded_buyer_input.interest_groups()) { + for (const BuyerInputForBidding::InterestGroupForBidding& + interest_group : decoded_buyer_input.interest_groups()) { if (interest_group.name() == ad_with_bid_metadata.interest_group_name()) { EXPECT_EQ(ad_with_bid_metadata.join_count(), @@ -1523,7 +1525,7 @@ TYPED_TEST(SellerFrontEndServiceTest, ReturnsBiddingGroups) { const std::string unexpected_ig_1 = "unexpected_interest_group_1"; const std::string unexpected_ig_2 = "unexpected_interest_group_2"; const std::string buyer = "ad_tech_A.com"; - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; buyer_input.mutable_interest_groups()->Add()->set_name(expected_ig_1); buyer_input.mutable_interest_groups()->Add()->set_name(expected_ig_2); buyer_input.mutable_interest_groups()->Add()->set_name(unexpected_ig_1); @@ -1532,7 +1534,7 @@ TYPED_TEST(SellerFrontEndServiceTest, ReturnsBiddingGroups) { // Setup a SelectAdRequest with the aforementioned buyer input. this->request_ = SelectAdRequest(); this->request_.mutable_auction_config()->set_seller(kSellerOriginDomain); - google::protobuf::Map buyer_inputs; + google::protobuf::Map buyer_inputs; buyer_inputs.emplace(buyer, buyer_input); auto encoded_buyer_inputs = GetEncodedBuyerInputMap(buyer_inputs); EXPECT_TRUE(encoded_buyer_inputs.ok()); @@ -2550,7 +2552,7 @@ TYPED_TEST(SellerFrontEndServiceTest, ChaffingEnabled_SendsChaffRequest) { GetBidsResponse::GetBidsRawResponse response; // Set up buyer calls for chaff buyer. - auto MockGetBids = + auto mock_get_bids = [response]( std::unique_ptr get_bids_request, grpc::ClientContext* context, GetBidDoneCallback on_done, @@ -2566,21 +2568,21 @@ TYPED_TEST(SellerFrontEndServiceTest, ChaffingEnabled_SendsChaffRequest) { /*response_metadata=*/{}); return absl::OkStatus(); }; - auto SetupMockBuyer = - [MockGetBids](std::unique_ptr buyer) { + auto setup_mock_buyer = + [mock_get_bids](std::unique_ptr buyer) { EXPECT_CALL(*buyer, ExecuteInternal) .Times(testing::Exactly(1)) - .WillRepeatedly(MockGetBids); + .WillRepeatedly(mock_get_bids); return buyer; }; - auto MockBuyerFactoryCall = - [SetupMockBuyer](absl::string_view chaff_buyer_name) { - return SetupMockBuyer(std::make_unique()); - }; + auto mock_buyer_Factory_call = [setup_mock_buyer]( + absl::string_view chaff_buyer_name) { + return setup_mock_buyer(std::make_unique()); + }; EXPECT_CALL(buyer_front_end_async_client_factory_mock, Get(chaff_buyer_name)) .Times(testing::Exactly(1)) - .WillRepeatedly(MockBuyerFactoryCall); + .WillRepeatedly(mock_buyer_Factory_call); // Have the Entries() call on the buyer factory return the 'real' and chaff // buyer. @@ -2624,6 +2626,139 @@ TYPED_TEST(SellerFrontEndServiceTest, ChaffingEnabled_SendsChaffRequest) { EXPECT_EQ(auction_result->bid(), winning_bid); } +TYPED_TEST(SellerFrontEndServiceTest, VerifyChaffCandidatesAreRandomlyChosen) { + // For chaffing, it's expected that the chaff request candidates list is + // shuffled so the chaff buyer is chosen randomly. This test simulates 100 + // SelectAd requests, each producing two chaff buyer candidates (A and B). The + // test confirms that both A and B are selected as the sole chaff buyer at + // least once, demonstrating that the chaff candidate list is shuffled before + // a buyer is chosen. + this->config_.SetOverride(kTrue, ENABLE_CHAFFING); + + MockAsyncProvider + scoring_signals_provider; + ScoringAsyncClientMock scoring_client; + // KV V2 Client + KVAsyncClientMock kv_async_client; + BuyerFrontEndAsyncClientFactoryMock buyer_front_end_async_client_factory_mock; + BuyerBidsResponseMap expected_buyer_bids; + std::unique_ptr key_fetcher_manager = + std::make_unique(); + EXPECT_CALL(*key_fetcher_manager, GetPrivateKey) + .WillRepeatedly(Return(GetPrivateKey())); + const float winning_bid = 999.0F; + + // Set up buyer calls for chaff buyer 1. + std::string chaff_buyer1_name = "chaff_buyer1"; + GetBidsResponse::GetBidsRawResponse response; + absl::Time buyer1_invocation_time; + auto mock_get_bids1 = + [response, &buyer1_invocation_time]( + std::unique_ptr get_bids_request, + grpc::ClientContext* context, GetBidDoneCallback on_done, + absl::Duration timeout, RequestConfig request_config) { + EXPECT_TRUE(get_bids_request->is_chaff()); + std::move(on_done)( + std::make_unique(response), + /*response_metadata=*/{}); + buyer1_invocation_time = absl::Now(); + return absl::OkStatus(); + }; + auto setup_mock_buyer1 = + [mock_get_bids1](std::unique_ptr buyer) { + EXPECT_CALL(*buyer, ExecuteInternal).WillRepeatedly(mock_get_bids1); + return buyer; + }; + auto mock_buyer_Factory_call1 = [setup_mock_buyer1]( + absl::string_view chaff_buyer_name) { + return setup_mock_buyer1(std::make_unique()); + }; + EXPECT_CALL(buyer_front_end_async_client_factory_mock, Get(chaff_buyer1_name)) + .WillRepeatedly(mock_buyer_Factory_call1); + + std::string chaff_buyer2_name = "chaff_buyer2"; + absl::Time buyer2_invocation_time; + auto mock_get_bids2 = + [response, &buyer2_invocation_time]( + std::unique_ptr get_bids_request, + grpc::ClientContext* context, GetBidDoneCallback on_done, + absl::Duration timeout, RequestConfig request_config) { + EXPECT_TRUE(get_bids_request->is_chaff()); + std::move(on_done)( + std::make_unique(response), + /*response_metadata=*/{}); + buyer2_invocation_time = absl::Now(); + return absl::OkStatus(); + }; + auto setup_mock_buyer2 = + [mock_get_bids2](std::unique_ptr buyer) { + EXPECT_CALL(*buyer, ExecuteInternal).WillRepeatedly(mock_get_bids2); + return buyer; + }; + auto mock_buyer_Factory_call2 = [setup_mock_buyer2]( + absl::string_view chaff_buyer_name) { + return setup_mock_buyer2(std::make_unique()); + }; + EXPECT_CALL(buyer_front_end_async_client_factory_mock, Get(chaff_buyer2_name)) + .WillRepeatedly(mock_buyer_Factory_call2); + + // Mock Entries() on the buyer factory return the 'real' and chaff buyers. + std::vector< + std::pair>> + entries; + entries.emplace_back(kSampleBuyer, // The non-chaff buyer. + std::make_shared()); + entries.emplace_back(chaff_buyer1_name, + std::make_shared()); + entries.emplace_back(chaff_buyer2_name, + std::make_shared()); + EXPECT_CALL(buyer_front_end_async_client_factory_mock, Entries) + .WillRepeatedly(Return(std::move(entries))); + + // Run the reactor 100 times and validate that, on separate runs, both chaff + // request candidates are chosen as the only chaff buyer. + bool buyer1_invoked_first = false, buyer2_invoked_first = false; + for (int i = 0; i < 100; i++) { + absl::Time test_start_time = absl::Now(); + auto [request_with_context, clients] = + GetSelectAdRequestAndClientRegistryForTest< + typename TypeParam::InputType, TypeParam::kUseKvV2ForBrowser>( + CLIENT_TYPE_BROWSER, winning_bid, &scoring_signals_provider, + scoring_client, buyer_front_end_async_client_factory_mock, + &kv_async_client, key_fetcher_manager.get(), expected_buyer_bids, + kSellerOriginDomain, false, "", false, false, {}, false, {}, + nullptr, MakeARandomString()); + SelectAdRequest select_ad_request = + std::move(request_with_context.select_ad_request); + *select_ad_request.mutable_auction_config()->mutable_buyer_list()->Add() = + chaff_buyer1_name; + *select_ad_request.mutable_auction_config()->mutable_buyer_list()->Add() = + chaff_buyer2_name; + (void)RunReactorRequest(this->config_, clients, + select_ad_request); + + // Both chaff buyers were invoked; ignore this run since the test needs to + // look at cases where only 1 chaff request was sent. + if (buyer1_invocation_time > test_start_time && + buyer2_invocation_time > test_start_time) { + continue; + } + + // Verify that in different runs, buyer1 and buyer2 were each invoked, + // implying the server randomly selects a chaff buyer from the list of + // candidates. + buyer1_invoked_first = + buyer1_invoked_first || (buyer1_invocation_time > test_start_time); + buyer2_invoked_first = + buyer2_invoked_first || (buyer2_invocation_time > test_start_time); + if (buyer1_invoked_first && buyer2_invoked_first) { + return; + } + } + + FAIL() << "Chaff request candidates are likely not being shuffled"; +} + TYPED_TEST(SellerFrontEndServiceTest, VerifyPrioritySignals) { const absl::string_view kPriorityJsonTemplate = R"({"priority_field_key":$0})"; @@ -2662,9 +2797,9 @@ TYPED_TEST(SellerFrontEndServiceTest, VerifyPrioritySignals) { KVAsyncClientMock kv_async_client; bool success = true; - auto SetupMockBuyer = [&expected_priority_signals_jsons, &success]( - const BuyerInput& buyer_input, - absl::string_view buyer_ig_owner) { + auto setup_mock_buyer = [&expected_priority_signals_jsons, &success]( + const BuyerInputForBidding& buyer_input, + absl::string_view buyer_ig_owner) { auto buyer = std::make_unique(); EXPECT_CALL(*buyer, ExecuteInternal) .Times(1) @@ -2695,13 +2830,13 @@ TYPED_TEST(SellerFrontEndServiceTest, VerifyPrioritySignals) { entries; for (const auto& buyer_ig_owner : this->request_.auction_config().buyer_list()) { - const BuyerInput& buyer_input = DecodeBuyerInput( + const BuyerInputForBidding& buyer_input = DecodeBuyerInput( buyer_ig_owner, this->protected_auction_input_.buyer_input().at(buyer_ig_owner), error_accumulator); EXPECT_CALL(buyer_clients, Get(buyer_ig_owner)) - .WillOnce([SetupMockBuyer, buyer_input](absl::string_view hostname) { - return SetupMockBuyer(buyer_input, hostname); + .WillOnce([setup_mock_buyer, buyer_input](absl::string_view hostname) { + return setup_mock_buyer(buyer_input, hostname); }); entries.emplace_back(buyer_ig_owner, diff --git a/services/seller_frontend_service/select_ad_reactor_web.cc b/services/seller_frontend_service/select_ad_reactor_web.cc index 5885cc92..0d83e9ee 100644 --- a/services/seller_frontend_service/select_ad_reactor_web.cc +++ b/services/seller_frontend_service/select_ad_reactor_web.cc @@ -38,7 +38,8 @@ using ::google::protobuf::RepeatedPtrField; using BiddingGroupsMap = ::google::protobuf::Map; using EncodedBuyerInputs = ::google::protobuf::Map; -using DecodedBuyerInputs = absl::flat_hash_map; +using DecodedBuyerInputs = + absl::flat_hash_map; using ReportErrorSignature = std::function error_visibility_with_loc, const std::string& msg, ErrorCode error_code)>; @@ -70,10 +71,12 @@ SelectAdReactorForWeb::SelectAdReactorForWeb( const TrustedServersConfigClient& config_client, const ReportWinMap& report_win_map, bool enable_cancellation, bool enable_kanon, bool enable_buyer_private_aggregate_reporting, - bool fail_fast, int max_buyers_solicited) + int per_adtech_paapi_contributions_limit, bool fail_fast, + int max_buyers_solicited) : SelectAdReactor(context, request, response, clients, config_client, report_win_map, enable_cancellation, enable_kanon, - enable_buyer_private_aggregate_reporting, fail_fast, + enable_buyer_private_aggregate_reporting, + per_adtech_paapi_contributions_limit, fail_fast, max_buyers_solicited) {} KAnonJoinCandidate SelectAdReactorForWeb::GetKAnonJoinCandidate( @@ -85,7 +88,8 @@ KAnonJoinCandidate SelectAdReactorForWeb::GetKAnonJoinCandidate( absl::StatusOr SelectAdReactorForWeb::GetNonEncryptedResponse( const std::optional& high_score, const std::optional& error, - const AdScores* ghost_winning_scores) { + const AdScores* ghost_winning_scores, + int per_adtech_paapi_contributions_limit) { auto error_handler = absl::bind_front(&SelectAdReactorForWeb::FinishWithStatus, this); std::string encoded_data; @@ -156,6 +160,7 @@ absl::StatusOr SelectAdReactorForWeb::GetNonEncryptedResponse( Encode(high_score, GetBiddingGroups(shared_buyer_bids_map_, *buyer_inputs_), shared_ig_updates_map_, error, error_handler, + per_adtech_paapi_contributions_limit, auction_config_.ad_auction_result_nonce(), std::move(kanon_data))); PS_VLOG(kPlain, log_context_) << "AuctionResult:\n" << (decode_lambda()); diff --git a/services/seller_frontend_service/select_ad_reactor_web.h b/services/seller_frontend_service/select_ad_reactor_web.h index b6937b81..9786148c 100644 --- a/services/seller_frontend_service/select_ad_reactor_web.h +++ b/services/seller_frontend_service/select_ad_reactor_web.h @@ -39,7 +39,8 @@ class SelectAdReactorForWeb : public SelectAdReactor { const ReportWinMap& report_win_map, bool enable_cancellation = false, bool enable_kanon = false, bool enable_buyer_private_aggregate_reporting = false, - bool fail_fast = true, int max_buyers_solicited = 2); + int per_adtech_paapi_contributions_limit = 100, bool fail_fast = true, + int max_buyers_solicited = 2); virtual ~SelectAdReactorForWeb() = default; // SelectAdReactorForWeb is neither copyable nor movable. @@ -50,7 +51,8 @@ class SelectAdReactorForWeb : public SelectAdReactor { absl::StatusOr GetNonEncryptedResponse( const std::optional& high_score, const std::optional& error, - const AdScores* ghost_winning_scores = nullptr) override; + const AdScores* ghost_winning_scores = nullptr, + int per_adtech_paapi_contributions_limit = 0) override; [[deprecated]] ProtectedAudienceInput GetDecodedProtectedAudienceInput( absl::string_view encoded_data) override; @@ -58,9 +60,9 @@ class SelectAdReactorForWeb : public SelectAdReactor { ProtectedAuctionInput GetDecodedProtectedAuctionInput( absl::string_view encoded_data) override; - absl::flat_hash_map GetDecodedBuyerinputs( - const google::protobuf::Map& - encoded_buyer_inputs) override; + absl::flat_hash_map + GetDecodedBuyerinputs(const google::protobuf::Map& + encoded_buyer_inputs) override; KAnonJoinCandidate GetKAnonJoinCandidate( const ScoreAdsResponse::AdScore& score) override; diff --git a/services/seller_frontend_service/select_ad_reactor_web_test.cc b/services/seller_frontend_service/select_ad_reactor_web_test.cc index 122ad460..49f6315c 100644 --- a/services/seller_frontend_service/select_ad_reactor_web_test.cc +++ b/services/seller_frontend_service/select_ad_reactor_web_test.cc @@ -450,20 +450,20 @@ TYPED_TEST(SelectAdReactorForWebTest, VerifyBadInputGetsValidated) { protected_auction_input.clear_generation_id(); protected_auction_input.clear_publisher_name(); - google::protobuf::Map buyer_input_map; + google::protobuf::Map buyer_input_map; // A Buyer input with IGs. - BuyerInput input_with_igs; + BuyerInputForBidding input_with_igs; // We don't validate anything about IGs. input_with_igs.mutable_interest_groups()->Add(); buyer_input_map.emplace(kSampleBuyer, input_with_igs); // A Buyer input with no IGs is fine as long as there is at least one buyer // in the buyer input map with IGs. - BuyerInput input_with_no_igs; + BuyerInputForBidding input_with_no_igs; buyer_input_map.emplace(kSampleBuyer2, input_with_no_igs); // Malformed buyer input (empty interest group owner name). - BuyerInput ok_buyer_input; + BuyerInputForBidding ok_buyer_input; auto* ok_interest_groups = ok_buyer_input.mutable_interest_groups(); auto* ok_interest_group = ok_interest_groups->Add(); ok_interest_group->set_name(kSampleInterestGroupName); @@ -605,8 +605,8 @@ TYPED_TEST(SelectAdReactorForWebTest, // Set up a buyer input map with no usable buyer/IGs that could be used to get // a bid and expect that this is reported as an error. - google::protobuf::Map buyer_input_map; - BuyerInput input_with_igs; + google::protobuf::Map buyer_input_map; + BuyerInputForBidding input_with_igs; input_with_igs.mutable_interest_groups()->Add(); buyer_input_map.emplace(kEmptyBuyer, input_with_igs); auto encoded_buyer_inputs = GetEncodedBuyerInputMap(buyer_input_map); @@ -1088,6 +1088,7 @@ TYPED_TEST(SelectAdReactorForWebTest, VerifyPopulatedKAnonAuctionResultData) { this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); ABSL_LOG(INFO) << "Encrypted SelectAdResponse:\n" @@ -1295,6 +1296,7 @@ TYPED_TEST(SelectAdReactorForWebTest, this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); ABSL_LOG(INFO) << "Encrypted SelectAdResponse:\n" << MessageToJson(response_with_proto); @@ -1489,6 +1491,7 @@ TYPED_TEST(SelectAdReactorForWebTest, this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); ABSL_LOG(INFO) << "Encrypted SelectAdResponse:\n" << MessageToJson(response_with_cbor); @@ -1693,6 +1696,7 @@ TYPED_TEST(SelectAdReactorForWebTest, QueriesAllRequiredHashes) { this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); ABSL_LOG(INFO) << "Encrypted SelectAdResponse:\n" @@ -1815,6 +1819,7 @@ TYPED_TEST(SelectAdReactorForWebTest, SetsKAnonStatusOnScoringRequests) { this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); ABSL_LOG(INFO) << "Encrypted SelectAdResponse:\n" @@ -1944,6 +1949,7 @@ TYPED_TEST(SelectAdReactorForWebTest, this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); } @@ -2040,6 +2046,7 @@ TYPED_TEST(SelectAdReactorForWebTest, this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); } @@ -2132,6 +2139,7 @@ TYPED_TEST(SelectAdReactorForWebTest, this->config_, clients, request_with_context.select_ad_request, /*enable_kanon=*/true, /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, /*fail_fast=*/false, /*report_win_map=*/test_report_win_map); } diff --git a/services/seller_frontend_service/select_auction_result_reactor.cc b/services/seller_frontend_service/select_auction_result_reactor.cc index b3ed2d28..5bca845b 100644 --- a/services/seller_frontend_service/select_auction_result_reactor.cc +++ b/services/seller_frontend_service/select_auction_result_reactor.cc @@ -382,7 +382,8 @@ SelectAuctionResultReactor::SelectAuctionResultReactor( grpc::CallbackServerContext* context, const SelectAdRequest* request, SelectAdResponse* response, const ClientRegistry& clients, const TrustedServersConfigClient& config_client, bool enable_cancellation, - bool enable_buyer_private_aggregate_reporting, bool enable_kanon) + bool enable_buyer_private_aggregate_reporting, + int per_adtech_paapi_contributions_limit, bool enable_kanon) : request_context_(context), request_(request), response_(response), diff --git a/services/seller_frontend_service/select_auction_result_reactor.h b/services/seller_frontend_service/select_auction_result_reactor.h index 6de585db..64b452bd 100644 --- a/services/seller_frontend_service/select_auction_result_reactor.h +++ b/services/seller_frontend_service/select_auction_result_reactor.h @@ -54,6 +54,7 @@ class SelectAuctionResultReactor : public grpc::ServerUnaryReactor { const TrustedServersConfigClient& config_client, bool enable_cancellation = false, bool enable_buyer_private_aggregate_reporting = false, + int per_adtech_paapi_contributions_limit = 100, bool enable_kanon = false); virtual ~SelectAuctionResultReactor() = default; diff --git a/services/seller_frontend_service/select_auction_result_reactor_test.cc b/services/seller_frontend_service/select_auction_result_reactor_test.cc index 005e3d33..115a1bea 100644 --- a/services/seller_frontend_service/select_auction_result_reactor_test.cc +++ b/services/seller_frontend_service/select_auction_result_reactor_test.cc @@ -39,7 +39,8 @@ SelectAdResponse RunRequest(const TrustedServersConfigClient& config_client, SelectAuctionResultReactor reactor( &context, &request, &response, clients, config_client, /*enable_cancellation=*/false, - /*enable_buyer_private_aggregate_reporting=*/false, enable_kanon); + /*enable_buyer_private_aggregate_reporting=*/true, + /*per_adtech_paapi_contributions_limit=*/100, enable_kanon); reactor.Execute(); return response; } diff --git a/services/seller_frontend_service/seller_frontend_main.cc b/services/seller_frontend_service/seller_frontend_main.cc index 3cd197ff..fa4a67f3 100644 --- a/services/seller_frontend_service/seller_frontend_main.cc +++ b/services/seller_frontend_service/seller_frontend_main.cc @@ -191,10 +191,10 @@ KAnonCacheManagerConfig GetKAnonCacheManagerConfig( config_client.GetBooleanParameter(ENABLE_K_ANON_QUERY_CACHE)}; if (config_client.GetBooleanParameter(TEST_MODE)) { - config.k_anon_ttl = absl::Seconds( - config_client.GetInt64Parameter(TEST_MODE_K_ANON_CACHE_TTL_SECONDS)); - config.non_k_anon_ttl = absl::Seconds(config_client.GetInt64Parameter( - TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS)); + config.k_anon_ttl = absl::Milliseconds( + config_client.GetInt64Parameter(TEST_MODE_K_ANON_CACHE_TTL_MS)); + config.non_k_anon_ttl = absl::Milliseconds( + config_client.GetInt64Parameter(TEST_MODE_NON_K_ANON_CACHE_TTL_MS)); } return config; } @@ -301,9 +301,9 @@ absl::StatusOr GetConfigClient( config_client.SetFlag(FLAGS_num_k_anon_shards, NUM_K_ANON_SHARDS); config_client.SetFlag(FLAGS_num_non_k_anon_shards, NUM_NON_K_ANON_SHARDS); config_client.SetFlag(FLAGS_test_mode_k_anon_cache_ttl_seconds, - TEST_MODE_K_ANON_CACHE_TTL_SECONDS); + TEST_MODE_K_ANON_CACHE_TTL_MS); config_client.SetFlag(FLAGS_test_mode_non_k_anon_cache_ttl_seconds, - TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS); + TEST_MODE_NON_K_ANON_CACHE_TTL_MS); config_client.SetFlag(FLAGS_enable_k_anon_query_cache, ENABLE_K_ANON_QUERY_CACHE); if (absl::GetFlag(FLAGS_init_config_client)) { @@ -366,10 +366,12 @@ absl::Status RunServer() { GetConfigClient(config_util.GetConfigParameterPrefix())); // InitTelemetry right after config_client being initialized // Do not log to SystemLogContext() before InitTelemetry - InitTelemetry( - config_util, config_client, metric::kSfe, - FetchIgOwnerList(ParseIgOwnerToBfeDomainMap( - config_client.GetStringParameter(BUYER_SERVER_HOSTS)))); + const auto buyer_server_hosts_map = ParseIgOwnerToBfeDomainMap( + config_client.GetStringParameter(BUYER_SERVER_HOSTS)); + ABSL_CHECK_OK(buyer_server_hosts_map) + << "Error in fetching IG Owner to BFE domain map."; + InitTelemetry(config_util, config_client, metric::kSfe, + FetchIgOwnerList(*buyer_server_hosts_map)); PS_LOG(INFO, SystemLogContext()) << "server parameters:\n" << config_client.DebugString(); @@ -419,9 +421,10 @@ absl::Status RunServer() { GetKAnonCacheManagerConfig(config_client)); } - PS_ASSIGN_OR_RETURN(std::unique_ptr - public_key_fetcher, - CreateSfePublicKeyFetcher(config_client)); + PS_ASSIGN_OR_RETURN( + std::unique_ptr + public_key_fetcher, + CreateSfePublicKeyFetcher(config_client, *buyer_server_hosts_map)); SellerFrontEndService seller_frontend_service( &config_client, CreateKeyFetcherManager(config_client, std::move(public_key_fetcher)), diff --git a/services/seller_frontend_service/seller_frontend_service.cc b/services/seller_frontend_service/seller_frontend_service.cc index c93d3e5e..a7e0cbe1 100644 --- a/services/seller_frontend_service/seller_frontend_service.cc +++ b/services/seller_frontend_service/seller_frontend_service.cc @@ -42,7 +42,8 @@ std::unique_ptr GetSelectAdReactor( SelectAdResponse* response, const ClientRegistry& clients, const TrustedServersConfigClient& config_client, const ReportWinMap& report_win_map, bool enable_cancellation, - bool enable_kanon, bool enable_buyer_private_aggregate_reporting) { + bool enable_kanon, bool enable_buyer_private_aggregate_reporting, + int per_adtech_paapi_contributions_limit) { switch (request->client_type()) { case CLIENT_TYPE_ANDROID: return std::make_unique( @@ -52,7 +53,8 @@ std::unique_ptr GetSelectAdReactor( return std::make_unique( context, request, response, clients, config_client, report_win_map, enable_cancellation, enable_kanon, - enable_buyer_private_aggregate_reporting); + enable_buyer_private_aggregate_reporting, + per_adtech_paapi_contributions_limit); default: return std::make_unique( context, request, response, clients, config_client, report_win_map); @@ -90,14 +92,16 @@ grpc::ServerUnaryReactor* SellerFrontEndService::SelectAd( auto reactor = std::make_unique( context, request, response, clients_, config_client_, enable_cancellation_, - /*enable_buyer_private_aggregate_reporting=*/false, enable_kanon_); + /*enable_buyer_private_aggregate_reporting=*/false, + /*per_adtech_paapi_contributions_limit=*/100, enable_kanon_); reactor->Execute(); return reactor.release(); } std::unique_ptr reactor = GetSelectAdReactor(context, request, response, clients_, config_client_, report_win_map_, enable_cancellation_, enable_kanon_, - enable_buyer_private_aggregate_reporting_); + enable_buyer_private_aggregate_reporting_, + per_adtech_paapi_contributions_limit_); reactor->Execute(); return reactor.release(); } diff --git a/services/seller_frontend_service/seller_frontend_service.h b/services/seller_frontend_service/seller_frontend_service.h index 386a0875..f51db9ba 100644 --- a/services/seller_frontend_service/seller_frontend_service.h +++ b/services/seller_frontend_service/seller_frontend_service.h @@ -159,7 +159,9 @@ class SellerFrontEndService final : public SellerFrontEnd::CallbackService { enable_kanon_(absl::GetFlag(FLAGS_enable_kanon)), report_win_map_(std::move(report_win_map)), enable_buyer_private_aggregate_reporting_( - absl::GetFlag(FLAGS_enable_buyer_private_aggregate_reporting)) { + absl::GetFlag(FLAGS_enable_buyer_private_aggregate_reporting)), + per_adtech_paapi_contributions_limit_( + absl::GetFlag(FLAGS_per_adtech_paapi_contributions_limit)) { if (config_client_.HasParameter(SELLER_CLOUD_PLATFORMS_MAP)) { seller_cloud_platforms_map_ = ParseSellerCloudPlarformMap( config_client_.GetStringParameter(SELLER_CLOUD_PLATFORMS_MAP)); @@ -173,7 +175,9 @@ class SellerFrontEndService final : public SellerFrontEnd::CallbackService { enable_cancellation_(absl::GetFlag(FLAGS_enable_cancellation)), enable_kanon_(absl::GetFlag(FLAGS_enable_kanon)), enable_buyer_private_aggregate_reporting_( - absl::GetFlag(FLAGS_enable_buyer_private_aggregate_reporting)) { + absl::GetFlag(FLAGS_enable_buyer_private_aggregate_reporting)), + per_adtech_paapi_contributions_limit_( + absl::GetFlag(FLAGS_per_adtech_paapi_contributions_limit)) { if (config_client_.HasParameter(SELLER_CLOUD_PLATFORMS_MAP)) { seller_cloud_platforms_map_ = ParseSellerCloudPlarformMap( config_client_.GetStringParameter(SELLER_CLOUD_PLATFORMS_MAP)); @@ -237,6 +241,7 @@ class SellerFrontEndService final : public SellerFrontEnd::CallbackService { const bool enable_kanon_; ReportWinMap report_win_map_; const bool enable_buyer_private_aggregate_reporting_; + int per_adtech_paapi_contributions_limit_; }; } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/seller_frontend_service/seller_frontend_service_test.cc b/services/seller_frontend_service/seller_frontend_service_test.cc index 51c059b9..ab7c2762 100644 --- a/services/seller_frontend_service/seller_frontend_service_test.cc +++ b/services/seller_frontend_service/seller_frontend_service_test.cc @@ -73,7 +73,8 @@ using ScoreAdsDoneCallback = absl::AnyInvocable< void(absl::StatusOr>, ResponseMetadata) &&>; using EncodedBuyerInputs = ::google::protobuf::Map; -using DecodedBuyerInputs = ::google::protobuf::Map; +using DecodedBuyerInputs = + ::google::protobuf::Map; // Maintains ownership of clients struct SellerFrontEndClientOwner { @@ -132,8 +133,8 @@ class SellerFrontEndServiceTest : public ::testing::Test { config_.SetOverride("0", K_ANON_CLIENT_TIME_OUT_MS); config_.SetOverride("0", NUM_K_ANON_SHARDS); config_.SetOverride("0", NUM_NON_K_ANON_SHARDS); - config_.SetOverride("0", TEST_MODE_K_ANON_CACHE_TTL_SECONDS); - config_.SetOverride("0", TEST_MODE_NON_K_ANON_CACHE_TTL_SECONDS); + config_.SetOverride("0", TEST_MODE_K_ANON_CACHE_TTL_MS); + config_.SetOverride("0", TEST_MODE_NON_K_ANON_CACHE_TTL_MS); config_.SetOverride(kTrue, ENABLE_K_ANON_QUERY_CACHE); } @@ -197,7 +198,7 @@ TYPED_TEST(SellerFrontEndServiceTest, this->config_.SetOverride(kSampleSellerDomain, SELLER_ORIGIN_DOMAIN); this->config_.SetOverride(kTrue, ALLOW_COMPRESSED_AUCTION_CONFIG); - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; buyer_input.mutable_interest_groups()->Add()->set_name( kSampleInterestGroupName); DecodedBuyerInputs decoded_buyer_inputs; @@ -804,7 +805,7 @@ TYPED_TEST(SellerFrontEndServiceTest, SendsChaffOnMissingBuyerClient) { TYPED_TEST(SellerFrontEndServiceTest, SendsChaffOnEmptyGetBidsResponse) { this->config_.SetOverride(kSampleSellerDomain, SELLER_ORIGIN_DOMAIN); - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; buyer_input.mutable_interest_groups()->Add()->set_name( kSampleInterestGroupName); @@ -942,7 +943,7 @@ void SetupBuyerClientMock( TYPED_TEST(SellerFrontEndServiceTest, RawRequestFinishWithSuccess) { this->config_.SetOverride(kSampleSellerDomain, SELLER_ORIGIN_DOMAIN); - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; buyer_input.mutable_interest_groups()->Add()->set_name( kSampleInterestGroupName); DecodedBuyerInputs decoded_buyer_inputs; @@ -1048,7 +1049,7 @@ TYPED_TEST(SellerFrontEndServiceTest, RawRequestFinishWithSuccess) { TYPED_TEST(SellerFrontEndServiceTest, ErrorsWhenCannotContactSellerKVServer) { this->config_.SetOverride(kSampleSellerDomain, SELLER_ORIGIN_DOMAIN); - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; buyer_input.mutable_interest_groups()->Add()->set_name( kSampleInterestGroupName); DecodedBuyerInputs decoded_buyer_inputs; @@ -1164,7 +1165,7 @@ TYPED_TEST(SellerFrontEndServiceTest, BuyerClientFailsWithCorrectOverallStatus) { this->config_.SetOverride(kSampleSellerDomain, SELLER_ORIGIN_DOMAIN); - BuyerInput buyer_input; + BuyerInputForBidding buyer_input; buyer_input.mutable_interest_groups()->Add()->set_name( kSampleInterestGroupName); DecodedBuyerInputs decoded_buyer_inputs; diff --git a/services/seller_frontend_service/test/app_test_utils.cc b/services/seller_frontend_service/test/app_test_utils.cc index 40589b5c..bb7a05de 100644 --- a/services/seller_frontend_service/test/app_test_utils.cc +++ b/services/seller_frontend_service/test/app_test_utils.cc @@ -24,8 +24,9 @@ namespace privacy_sandbox::bidding_auction_servers { using EncodedBuyerInputs = ::google::protobuf::Map; using DecodedBuyerInputs = ::google::protobuf::Map; +using DecodedBuyerInputForBiddings = + ::google::protobuf::Map; -// Encodes and compresses the passed in buyer inputs. EncodedBuyerInputs GetProtoEncodedBuyerInputs( const DecodedBuyerInputs& buyer_inputs) { EncodedBuyerInputs encoded_buyer_inputs; @@ -38,4 +39,17 @@ EncodedBuyerInputs GetProtoEncodedBuyerInputs( return encoded_buyer_inputs; } +// Encodes and compresses the passed in buyer inputs. +EncodedBuyerInputs GetProtoEncodedBuyerInputs( + const DecodedBuyerInputForBiddings& buyer_inputs) { + EncodedBuyerInputs encoded_buyer_inputs; + for (const auto& [buyer, buyer_input] : buyer_inputs) { + absl::StatusOr compressed_buyer_input = + GzipCompress(buyer_input.SerializeAsString()); + DCHECK(compressed_buyer_input.ok()) << compressed_buyer_input.status(); + encoded_buyer_inputs.emplace(buyer, std::move(*compressed_buyer_input)); + } + return encoded_buyer_inputs; +} + } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/seller_frontend_service/test/app_test_utils.h b/services/seller_frontend_service/test/app_test_utils.h index 71b6b951..1782d27e 100644 --- a/services/seller_frontend_service/test/app_test_utils.h +++ b/services/seller_frontend_service/test/app_test_utils.h @@ -26,6 +26,10 @@ namespace privacy_sandbox::bidding_auction_servers { google::protobuf::Map GetProtoEncodedBuyerInputs( const google::protobuf::Map& buyer_inputs); +google::protobuf::Map GetProtoEncodedBuyerInputs( + const google::protobuf::Map& + buyer_inputs); + } // namespace privacy_sandbox::bidding_auction_servers #endif // SERVICES_SELLER_FRONTEND_SERVICE_TEST_APP_TEST_UTILS_H_ diff --git a/services/seller_frontend_service/util/BUILD b/services/seller_frontend_service/util/BUILD index 3d249bd0..bdeeb811 100644 --- a/services/seller_frontend_service/util/BUILD +++ b/services/seller_frontend_service/util/BUILD @@ -136,6 +136,7 @@ cc_test( "//services/seller_frontend_service/private_aggregation:private_aggregation_helper", "//services/seller_frontend_service/test:constants", "//services/seller_frontend_service/test:kanon_test_utils", + "//services/seller_frontend_service/util:buyer_input_proto_utils", "@com_google_absl//absl/container:flat_hash_map", "@com_google_absl//absl/status", "@com_google_googletest//:gtest", @@ -207,6 +208,7 @@ cc_library( "//services/common/loggers:request_log_context", "//services/common/util:json_util", "//services/seller_frontend_service:runtime_flags", + "//services/seller_frontend_service/util:startup_param_parser", "@google_privacysandbox_servers_common//src/encryption/key_fetcher:fake_key_fetcher_manager", "@google_privacysandbox_servers_common//src/encryption/key_fetcher:key_fetcher_manager", "@google_privacysandbox_servers_common//src/encryption/key_fetcher:private_key_fetcher", @@ -341,3 +343,35 @@ cc_test( "@google_privacysandbox_servers_common//src/encryption/key_fetcher:fake_key_fetcher_manager", ], ) + +cc_library( + name = "buyer_input_proto_utils", + srcs = [ + "buyer_input_proto_utils.cc", + ], + hdrs = [ + "buyer_input_proto_utils.h", + ], + visibility = [ + "//services:__subpackages__", + "//tools/secure_invoke:__subpackages__", + ], + deps = [ + "//api:bidding_auction_servers_cc_proto", + "//services/common/util:json_util", + "@rapidjson", + ], +) + +cc_test( + name = "buyer_input_proto_utils_test", + size = "small", + srcs = [ + "buyer_input_proto_utils_test.cc", + ], + deps = [ + ":buyer_input_proto_utils", + "@com_google_googletest//:gtest", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/services/seller_frontend_service/util/buyer_input_proto_utils.cc b/services/seller_frontend_service/util/buyer_input_proto_utils.cc new file mode 100644 index 00000000..93bcb5ec --- /dev/null +++ b/services/seller_frontend_service/util/buyer_input_proto_utils.cc @@ -0,0 +1,163 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "services/seller_frontend_service/util/buyer_input_proto_utils.h" + +#include + +#include "rapidjson/document.h" +#include "services/common/util/json_util.h" + +namespace privacy_sandbox::bidding_auction_servers { + +absl::StatusOr ToPrevWinsMs(absl::string_view prev_wins) { + absl::StatusOr document = ParseJsonString(prev_wins); + + if (!document->IsArray()) { + return absl::InvalidArgumentError(""); + } + + for (rapidjson::SizeType i = 0; i < document->Size(); i++) { + rapidjson::Value& innerArray = (*document)[i]; + if (!(innerArray.IsArray() && innerArray.Size() == 2 && + innerArray[0].IsInt() && innerArray[1].IsString())) { + return absl::InvalidArgumentError(""); + } + + int firstValue = innerArray[0].GetInt() * 1000; + innerArray[0].SetInt(firstValue); + } + + return SerializeJsonDoc(*document); +} + +BrowserSignalsForBidding ToBrowserSignalsForBidding( + const BrowserSignals& signals) { + BrowserSignalsForBidding signals_for_bidding; + signals_for_bidding.set_join_count(signals.join_count()); + signals_for_bidding.set_bid_count(signals.bid_count()); + signals_for_bidding.set_recency(signals.recency()); + signals_for_bidding.set_prev_wins(signals.prev_wins()); + + if (signals.has_recency_ms()) { + signals_for_bidding.set_recency_ms(signals.recency_ms()); + } + + absl::StatusOr prev_wins_ms = + ToPrevWinsMs(signals_for_bidding.prev_wins()); + if (prev_wins_ms.ok()) { + signals_for_bidding.set_prev_wins_ms(*prev_wins_ms); + } + + return signals_for_bidding; +} + +BuyerInputForBidding::InterestGroupForBidding ToInterestGroupForBidding( + const BuyerInput::InterestGroup& interest_group) { + BuyerInputForBidding::InterestGroupForBidding interest_group_for_bidding; + interest_group_for_bidding.set_name(interest_group.name()); + + for (const auto& key : interest_group.bidding_signals_keys()) { + interest_group_for_bidding.add_bidding_signals_keys(key); + } + + for (const auto& ad_render_id : interest_group.ad_render_ids()) { + interest_group_for_bidding.add_ad_render_ids(ad_render_id); + } + + for (const auto& component_ad : interest_group.component_ads()) { + interest_group_for_bidding.add_component_ads(component_ad); + } + + if (!interest_group.user_bidding_signals().empty()) { + interest_group_for_bidding.set_user_bidding_signals( + interest_group.user_bidding_signals()); + } + + if (interest_group.has_browser_signals()) { + *interest_group_for_bidding.mutable_browser_signals() = + ToBrowserSignalsForBidding(interest_group.browser_signals()); + } else if (interest_group.has_android_signals()) { + (void)*interest_group_for_bidding.mutable_android_signals(); + } + + if (!interest_group.origin().empty()) { + interest_group_for_bidding.set_origin(interest_group.origin()); + } + + return interest_group_for_bidding; +} + +BuyerInputForBidding ToBuyerInputForBidding(const BuyerInput& buyer_input) { + BuyerInputForBidding buyer_input_for_bidding; + for (const auto& buyer_interest_group : buyer_input.interest_groups()) { + *buyer_input_for_bidding.mutable_interest_groups()->Add() = + ToInterestGroupForBidding(buyer_interest_group); + } + + if (buyer_input.has_protected_app_signals()) { + buyer_input_for_bidding.mutable_protected_app_signals()->CopyFrom( + buyer_input.protected_app_signals()); + } + + return buyer_input_for_bidding; +} + +BuyerInput ToBuyerInput(const BuyerInputForBidding& buyer_input_for_bidding) { + BuyerInput buyer_input; + + for (const auto& ig_for_bidding : buyer_input_for_bidding.interest_groups()) { + BuyerInput::InterestGroup* interest_group = + buyer_input.add_interest_groups(); + interest_group->set_name(ig_for_bidding.name()); + + for (const auto& key : ig_for_bidding.bidding_signals_keys()) { + interest_group->add_bidding_signals_keys(key); + } + + for (const auto& ad_render_id : ig_for_bidding.ad_render_ids()) { + interest_group->add_ad_render_ids(ad_render_id); + } + + for (const auto& component_ad : ig_for_bidding.component_ads()) { + interest_group->add_component_ads(component_ad); + } + + interest_group->set_user_bidding_signals( + ig_for_bidding.user_bidding_signals()); + interest_group->set_origin(ig_for_bidding.origin()); + + if (ig_for_bidding.has_browser_signals()) { + BrowserSignals* browser_signals = + interest_group->mutable_browser_signals(); + const BrowserSignalsForBidding& signals_for_bidding = + ig_for_bidding.browser_signals(); + browser_signals->set_join_count(signals_for_bidding.join_count()); + browser_signals->set_bid_count(signals_for_bidding.bid_count()); + browser_signals->set_recency(signals_for_bidding.recency()); + browser_signals->set_prev_wins(signals_for_bidding.prev_wins()); + if (signals_for_bidding.has_recency_ms()) { + browser_signals->set_recency_ms(signals_for_bidding.recency_ms()); + } + } else if (ig_for_bidding.has_android_signals()) { + (void)*interest_group->mutable_android_signals(); + } + } + + *buyer_input.mutable_protected_app_signals() = + buyer_input_for_bidding.protected_app_signals(); + return buyer_input; +} + +} // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/seller_frontend_service/util/buyer_input_proto_utils.h b/services/seller_frontend_service/util/buyer_input_proto_utils.h new file mode 100644 index 00000000..687d76a5 --- /dev/null +++ b/services/seller_frontend_service/util/buyer_input_proto_utils.h @@ -0,0 +1,42 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SERVICES_SELLER_FRONTEND_SERVICE_UTIL_BUYER_INPUT_PROTO_UTILS_H +#define SERVICES_SELLER_FRONTEND_SERVICE_UTIL_BUYER_INPUT_PROTO_UTILS_H + +#include +#include +#include +#include + +#include "absl/status/statusor.h" +#include "api/bidding_auction_servers.pb.h" + +namespace privacy_sandbox::bidding_auction_servers { + +absl::StatusOr ToPrevWinsMs(absl::string_view prev_wins); + +BrowserSignalsForBidding ToBrowserSignalsForBidding( + const BrowserSignals& signals); + +BuyerInputForBidding::InterestGroupForBidding ToInterestGroupForBidding( + const BuyerInput::InterestGroup& interest_group); + +BuyerInputForBidding ToBuyerInputForBidding(const BuyerInput& buyer_input); + +BuyerInput ToBuyerInput(const BuyerInputForBidding& buyer_input_for_bidding); + +} // namespace privacy_sandbox::bidding_auction_servers + +#endif // SERVICES_SELLER_FRONTEND_SERVICE_UTIL_BUYER_INPUT_PROTO_UTILS_H diff --git a/services/seller_frontend_service/util/buyer_input_proto_utils_test.cc b/services/seller_frontend_service/util/buyer_input_proto_utils_test.cc new file mode 100644 index 00000000..aead1259 --- /dev/null +++ b/services/seller_frontend_service/util/buyer_input_proto_utils_test.cc @@ -0,0 +1,278 @@ +// Copyright 2025 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "services/seller_frontend_service/util/buyer_input_proto_utils.h" + +#include + +#include + +#include + +#include "gtest/gtest.h" + +namespace privacy_sandbox::bidding_auction_servers { +namespace { + +std::string GenerateRandomString(int len) { + return std::to_string(ToUnixNanos(absl::Now())); +} + +int64_t GenerateRandomInt64(int64_t min, int64_t max) { + std::default_random_engine curr_time_generator(ToUnixNanos(absl::Now())); + return std::uniform_int_distribution(min, max)(curr_time_generator); +} + +// Helper function to set random values for all fields in a message. +void SetRandomMessage(const google::protobuf::Descriptor* descriptor, + google::protobuf::Message* message) { + for (int i = 0; i < descriptor->field_count(); ++i) { + const google::protobuf::FieldDescriptor* field = descriptor->field(i); + + if (field->is_repeated()) { + const int kNumRepeatedElements = 5; + for (int j = 0; j < kNumRepeatedElements; j++) { + switch (field->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + message->GetReflection()->AddUInt32( + message, field, GenerateRandomInt64(INT32_MIN, INT32_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + message->GetReflection()->AddInt64( + message, field, GenerateRandomInt64(INT64_MIN, INT64_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + message->GetReflection()->AddUInt32( + message, field, GenerateRandomInt64(0, UINT32_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + message->GetReflection()->AddUInt64( + message, field, GenerateRandomInt64(0, UINT64_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + message->GetReflection()->AddDouble( + message, field, static_cast(std::rand()) / RAND_MAX); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + message->GetReflection()->AddFloat( + message, field, + static_cast(std::rand()) / static_cast(RAND_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + message->GetReflection()->AddBool(message, field, + std::rand() % 2 == 0); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { + const google::protobuf::EnumDescriptor* enum_desc = + field->enum_type(); + const int enum_value_count = enum_desc->value_count(); + message->GetReflection()->AddEnum( + message, field, + enum_desc->value(std::rand() % enum_value_count)); + } break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + message->GetReflection()->AddString(message, field, + GenerateRandomString(10)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + SetRandomMessage( + field->message_type(), + message->GetReflection()->AddMessage(message, field)); + break; + } + } + } else { + switch (field->cpp_type()) { + case google::protobuf::FieldDescriptor::CPPTYPE_INT32: + message->GetReflection()->SetInt32( + message, field, GenerateRandomInt64(INT32_MIN, INT32_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_INT64: + message->GetReflection()->SetInt64( + message, field, GenerateRandomInt64(INT64_MIN, INT64_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT32: + message->GetReflection()->SetUInt32( + message, field, GenerateRandomInt64(0, UINT32_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_UINT64: + message->GetReflection()->SetUInt64( + message, field, GenerateRandomInt64(0, UINT64_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_DOUBLE: + message->GetReflection()->SetDouble( + message, field, static_cast(std::rand()) / RAND_MAX); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_FLOAT: + message->GetReflection()->SetFloat( + message, field, + static_cast(std::rand()) / static_cast(RAND_MAX)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_BOOL: + message->GetReflection()->SetBool(message, field, + std::rand() % 2 == 0); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_ENUM: { + const google::protobuf::EnumDescriptor* enum_desc = + field->enum_type(); + const int enum_value_count = enum_desc->value_count(); + message->GetReflection()->SetEnum( + message, field, enum_desc->value(std::rand() % enum_value_count)); + } break; + case google::protobuf::FieldDescriptor::CPPTYPE_STRING: + message->GetReflection()->SetString(message, field, + GenerateRandomString(10)); + break; + case google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE: + SetRandomMessage( + field->message_type(), + message->GetReflection()->MutableMessage(message, field)); + break; + } + } + } +} + +BuyerInput GenerateBuyerInput() { + BuyerInput buyer_input; + SetRandomMessage(buyer_input.GetDescriptor(), &buyer_input); + + // Special handling for oneof fields. + const google::protobuf::OneofDescriptor* oneof_desc = + BuyerInput::InterestGroup::descriptor()->FindOneofByName("DeviceSignals"); + + for (int i = 0; i < buyer_input.interest_groups_size(); ++i) { + BuyerInput::InterestGroup* interest_group = + buyer_input.mutable_interest_groups(i); + + const int field_index = std::rand() % oneof_desc->field_count(); + const google::protobuf::FieldDescriptor* field = + oneof_desc->field(field_index); + + if (field->message_type() == AndroidSignals::descriptor()) { + google::protobuf::Message* android_signals = + interest_group->GetReflection()->MutableMessage(interest_group, + field); + SetRandomMessage(android_signals->GetDescriptor(), android_signals); + } else { + // buyer_input will already have browser_signals populated in + // SetRandomMessage() since it comes after android_signals numerically in + // the proto definition, i.e. no need to explicitly set it. + } + } + + return buyer_input; +} + +TEST(BuyerInputProtoUtilsTest, ToPrevWinsMs) { + std::string input = + R"JSON([[123,"ad_render_id_1"],[456,"ad_render_id_2"]])JSON"; + std::string expected = + R"JSON([[123000,"ad_render_id_1"],[456000,"ad_render_id_2"]])JSON"; + absl::StatusOr actual = ToPrevWinsMs(input); + ASSERT_TRUE(actual.ok()); + EXPECT_EQ(*actual, expected); +} + +// This test uses proto reflection to set every field on a BuyerInput proto to +// a random value, and then runs it through ToBuyerInputForBidding() to verify +// every field is mapped between the BuyerInput and BuyerInputForBidding protos. +// This is to protect against the scenario a new field is added in BuyerInput +// and ToBuyerInputForBidding() isn't updated to map the new field when +// converting to BuyerInputForBidding. +TEST(BuyerInputProtoUtilsTest, ToBuyerInputForBidding) { + BuyerInput buyer_input = GenerateBuyerInput(); + for (auto& ig : *buyer_input.mutable_interest_groups()) { + if (ig.has_browser_signals()) { + // GenerateBuyerInput will set prev_wins to a random string, so we + // override its value to a valid prev_wins JSON. + std::string input = + R"JSON([[123,"ad_render_id_1"],[456,"ad_render_id_2"]])JSON"; + ig.mutable_browser_signals()->set_prev_wins(input); + } + } + + BuyerInputForBidding buyer_input_for_bidding = + ToBuyerInputForBidding(buyer_input); + for (auto& ig : *buyer_input_for_bidding.mutable_interest_groups()) { + if (ig.has_browser_signals()) { + // Verify prev_wins is converted to prev_wins_ms, and then clear the field + // to compare the rest of the fields in both protos below. + EXPECT_FALSE(ig.browser_signals().prev_wins_ms().empty()); + ig.mutable_browser_signals()->clear_prev_wins_ms(); + } + } + + EXPECT_EQ(buyer_input.DebugString(), buyer_input_for_bidding.DebugString()); +} + +BuyerInputForBidding GenerateBuyerInputForBidding() { + BuyerInputForBidding buyer_input_for_bidding; + SetRandomMessage(buyer_input_for_bidding.GetDescriptor(), + &buyer_input_for_bidding); + + // Special handling for oneof fields. + const google::protobuf::OneofDescriptor* oneof_desc = + BuyerInputForBidding::InterestGroupForBidding::descriptor() + ->FindOneofByName("DeviceSignals"); + + for (int i = 0; i < buyer_input_for_bidding.interest_groups_size(); ++i) { + BuyerInputForBidding::InterestGroupForBidding* interest_group = + buyer_input_for_bidding.mutable_interest_groups(i); + + const int field_index = std::rand() % oneof_desc->field_count(); + const google::protobuf::FieldDescriptor* field = + oneof_desc->field(field_index); + + if (field->message_type() == AndroidSignalsForBidding::descriptor()) { + google::protobuf::Message* android_signals = + interest_group->GetReflection()->MutableMessage(interest_group, + field); + SetRandomMessage(android_signals->GetDescriptor(), android_signals); + } else { + // buyer_input_for_bidding will already have browser_signals populated in + // SetRandomMessage() since it comes after android_signals numerically in + // the proto definition, i.e. no need to explicitly set it. + } + } + + return buyer_input_for_bidding; +} + +// This test uses proto reflection to set every field on a BuyerInputForBidding +// proto to a random value, and then runs it through ToBuyerInput() to verify +// every field is mapped between the BuyerInputForBidding and BuyerInput protos. +// This is to protect against the scenario where a new field is added in +// BuyerInputForBidding and ToBuyerInput() isn't updated to map the new field +// when converting to BuyerInput. +TEST(BuyerInputProtoUtilsTest, ToBuyerInput) { + BuyerInputForBidding buyer_input_for_bidding = GenerateBuyerInputForBidding(); + BuyerInput buyer_input = ToBuyerInput(buyer_input_for_bidding); + + for (auto& ig : *buyer_input_for_bidding.mutable_interest_groups()) { + if (ig.has_browser_signals()) { + ig.mutable_browser_signals()->clear_prev_wins_ms(); + } + } + + for (int i = 0; i < buyer_input_for_bidding.interest_groups_size(); i++) { + const auto& ig = buyer_input_for_bidding.interest_groups(i); + ASSERT_EQ(ig.DebugString(), buyer_input.interest_groups(i).DebugString()); + } + + EXPECT_EQ(buyer_input.DebugString(), buyer_input_for_bidding.DebugString()); +} + +} // namespace +} // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/seller_frontend_service/util/config_param_parser.cc b/services/seller_frontend_service/util/config_param_parser.cc index b708f32e..fb885efb 100644 --- a/services/seller_frontend_service/util/config_param_parser.cc +++ b/services/seller_frontend_service/util/config_param_parser.cc @@ -120,13 +120,10 @@ ParseIgOwnerToBfeDomainMap(absl::string_view ig_owner_to_bfe_domain) { } std::vector FetchIgOwnerList( - const absl::StatusOr< - absl::flat_hash_map>& + const absl::flat_hash_map& ig_owner_to_bfe_domain_map) { - ABSL_CHECK_OK(ig_owner_to_bfe_domain_map) - << "Error in fetching IG Owner to BFE domain map."; std::vector ig_list; - for (const auto& [ig_owner, bfe_domain] : *ig_owner_to_bfe_domain_map) { + for (const auto& [ig_owner, bfe_domain] : ig_owner_to_bfe_domain_map) { ig_list.push_back(ig_owner); } diff --git a/services/seller_frontend_service/util/config_param_parser.h b/services/seller_frontend_service/util/config_param_parser.h index fd154d55..6f1b0339 100644 --- a/services/seller_frontend_service/util/config_param_parser.h +++ b/services/seller_frontend_service/util/config_param_parser.h @@ -41,8 +41,7 @@ ParseIgOwnerToBfeDomainMap(absl::string_view ig_owner_to_bfe_domain); // Iterate over the above IgOwnerToBfeDomain Map into a span of IgOwners. std::vector FetchIgOwnerList( - const absl::StatusOr< - absl::flat_hash_map>& + const absl::flat_hash_map& ig_owner_to_bfe_domain_map); // Parses a JSON string containing a map of sellers to cloud platforms diff --git a/services/seller_frontend_service/util/key_fetcher_utils.cc b/services/seller_frontend_service/util/key_fetcher_utils.cc index 675c835f..b9c9b5d7 100644 --- a/services/seller_frontend_service/util/key_fetcher_utils.cc +++ b/services/seller_frontend_service/util/key_fetcher_utils.cc @@ -82,8 +82,27 @@ ParseCloudPlatformPublicKeysMap( return per_platform_endpoints; } +absl::Status ValidateSfePublicKeyEndpoints( + const PlatformToPublicKeyServiceEndpointMap& endpoints_map, + const absl::flat_hash_map& + buyer_server_hosts) { + for (const auto& entry : buyer_server_hosts) { + if (!endpoints_map.contains(entry.second.cloud_platform)) { + return absl::InvalidArgumentError( + "Invalid runtime configs provided - a supplied buyer cloud " + "platform in BUYER_SERVER_HOSTS is not present " + "in SFE_PUBLIC_KEYS_ENDPOINTS"); + } + } + + return absl::OkStatus(); +} + absl::StatusOr> -CreateSfePublicKeyFetcher(const TrustedServersConfigClient& config_client) { +CreateSfePublicKeyFetcher( + const TrustedServersConfigClient& config_client, + const absl::flat_hash_map& + buyer_server_hosts) { if (config_client.GetBooleanParameter(TEST_MODE)) { return nullptr; } @@ -97,6 +116,9 @@ CreateSfePublicKeyFetcher(const TrustedServersConfigClient& config_client) { PlatformToPublicKeyServiceEndpointMap endpoints_map, ParseCloudPlatformPublicKeysMap( config_client.GetStringParameter(SFE_PUBLIC_KEYS_ENDPOINTS))); + PS_RETURN_IF_ERROR( + ValidateSfePublicKeyEndpoints(endpoints_map, buyer_server_hosts)); + return PublicKeyFetcherFactory::Create(endpoints_map, SystemLogContext()); } diff --git a/services/seller_frontend_service/util/key_fetcher_utils.h b/services/seller_frontend_service/util/key_fetcher_utils.h index bd3aaa58..f88f9561 100644 --- a/services/seller_frontend_service/util/key_fetcher_utils.h +++ b/services/seller_frontend_service/util/key_fetcher_utils.h @@ -18,10 +18,12 @@ #define SERVICES_SELLER_FRONTEND_SERVICE_UTIL_KEY_FETCHER_UTILS_H_ #include +#include #include #include "api/bidding_auction_servers.pb.h" #include "services/common/clients/config/trusted_server_config_client.h" +#include "services/seller_frontend_service/util/config_param_parser.h" #include "src/encryption/key_fetcher/interface/public_key_fetcher_interface.h" namespace privacy_sandbox::bidding_auction_servers { @@ -48,9 +50,19 @@ using PlatformToPublicKeyServiceEndpointMap = absl::flat_hash_map< absl::StatusOr ParseCloudPlatformPublicKeysMap(absl::string_view public_keys_endpoint_map_str); +// Validates the cloud platform of every buyer is present in the SFE's public +// key endpoint map. +absl::Status ValidateSfePublicKeyEndpoints( + const PlatformToPublicKeyServiceEndpointMap& endpoints_map, + const absl::flat_hash_map& + buyer_server_hosts); + // Creates an instance of PublicKeyFetcherInterface. absl::StatusOr> -CreateSfePublicKeyFetcher(const TrustedServersConfigClient& config_client); +CreateSfePublicKeyFetcher( + const TrustedServersConfigClient& config_client, + const absl::flat_hash_map& + buyer_server_hosts); server_common::CloudPlatform ProtoCloudPlatformToScpCloudPlatform( EncryptionCloudPlatform cloud_platform); diff --git a/services/seller_frontend_service/util/key_fetcher_utils_test.cc b/services/seller_frontend_service/util/key_fetcher_utils_test.cc index 40f852bd..30f3062e 100644 --- a/services/seller_frontend_service/util/key_fetcher_utils_test.cc +++ b/services/seller_frontend_service/util/key_fetcher_utils_test.cc @@ -22,13 +22,25 @@ #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" +#include "include/gtest/gtest.h" #include "services/common/public_key_url_allowlist.h" namespace privacy_sandbox::bidding_auction_servers { namespace { +using ::testing::ContainsRegex; using ::testing::Return; +constexpr absl::string_view kBuyerHostMapForSingleBuyer = + R"json( + { + "https://bid1.com": { + "url": "dns:///bfe-dev.bidding1.com:443", + "cloudPlatform": "AWS" + } + } + )json"; + TEST(KeyFetcherUtilsTest, ParseCloudPlatformPublicKeysMap_ValidInput) { constexpr absl::string_view platform_format = R"json( { @@ -95,5 +107,29 @@ TEST(ProtoCloudPlatformToScpCloudPlatformTest, ReturnsLocalForUnspecified) { EncryptionCloudPlatform::ENCRYPTION_CLOUD_PLATFORM_UNSPECIFIED)); } +TEST(KeyFetcherUtilsTest, + ValidateSfePublicKeyEndpoints_FailsOnNoKeysForBuyerCloudPlatform) { + constexpr absl::string_view platform_format = R"json( +{ + "GCP": "%s" +} +)json"; + std::string per_platform_public_key_endpoints = + absl::StrFormat(platform_format, kGCPProdPublicKeyEndpoint); + auto cloud_public_keys_map = + ParseCloudPlatformPublicKeysMap(per_platform_public_key_endpoints); + ASSERT_TRUE(cloud_public_keys_map.ok()); + + auto map = ParseIgOwnerToBfeDomainMap(kBuyerHostMapForSingleBuyer); + ASSERT_TRUE(map.ok()); + + auto error = ValidateSfePublicKeyEndpoints(*cloud_public_keys_map, *map); + ASSERT_FALSE(error.ok()); + EXPECT_EQ( + error.message(), + "Invalid runtime configs provided - a supplied buyer cloud platform in " + "BUYER_SERVER_HOSTS is not present in SFE_PUBLIC_KEYS_ENDPOINTS"); +} + } // namespace } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/seller_frontend_service/util/proto_mapping_util.cc b/services/seller_frontend_service/util/proto_mapping_util.cc index fc8a093b..9b8578fe 100644 --- a/services/seller_frontend_service/util/proto_mapping_util.cc +++ b/services/seller_frontend_service/util/proto_mapping_util.cc @@ -132,7 +132,8 @@ absl::StatusOr PackageAuctionResultForWeb( const std::optional& error, OhttpHpkeDecryptedMessage& decrypted_request, RequestLogContext& log_context, - std::unique_ptr kanon_data = nullptr) { + std::unique_ptr kanon_data = nullptr, + int per_adtech_paapi_contributions_limit = 0) { std::string error_msg; absl::Notification wait_for_error_callback; auto error_handler = [&wait_for_error_callback, @@ -149,7 +150,8 @@ absl::StatusOr PackageAuctionResultForWeb( Encode(high_score, maybe_bidding_group_map.has_value() ? *maybe_bidding_group_map : IgsWithBidsMap(), - update_group_map, error, error_handler, auction_result_nonce, + update_group_map, error, error_handler, + per_adtech_paapi_contributions_limit, auction_result_nonce, std::move(kanon_data)); if (!serialized_data.ok()) { wait_for_error_callback.WaitForNotification(); @@ -293,7 +295,7 @@ absl::StatusOr CreateNonChaffAuctionResultCiphertext( return PackageAuctionResultForWeb( auction_result_nonce, ad_score, bidding_group_map, update_group_map, /*error =*/std::nullopt, decrypted_request, log_context, - std::move(kanon_data)); + std::move(kanon_data), /*per_adtech_paapi_contributions_limit=*/0); default: return PackageAuctionResultForInvalid(client_type); } @@ -523,7 +525,8 @@ ProtectedAuctionInput DecryptProtectedAuctionInput( IgsWithBidsMap GetBiddingGroups( const BuyerBidsResponseMap& shared_buyer_bids_map, - const absl::flat_hash_map& buyer_inputs) { + const absl::flat_hash_map& + buyer_inputs) { IgsWithBidsMap bidding_groups; for (const auto& [buyer, ad_with_bids] : shared_buyer_bids_map) { // Mapping from buyer to interest groups that are associated with non-zero diff --git a/services/seller_frontend_service/util/proto_mapping_util.h b/services/seller_frontend_service/util/proto_mapping_util.h index 84891d47..144452e2 100644 --- a/services/seller_frontend_service/util/proto_mapping_util.h +++ b/services/seller_frontend_service/util/proto_mapping_util.h @@ -162,9 +162,12 @@ ProtectedAuctionInput DecryptProtectedAuctionInput( // Gets the bidding groups after scoring is done. google::protobuf::Map -GetBiddingGroups( - const BuyerBidsResponseMap& shared_buyer_bids_map, - const absl::flat_hash_map& buyer_inputs); +GetBiddingGroups(const BuyerBidsResponseMap& shared_buyer_bids_map, + const absl::flat_hash_map& buyer_inputs); + +BuyerInput GenerateExternalBuyerInput( + const BuyerInputForBidding& buyer_input_for_bidding); } // namespace privacy_sandbox::bidding_auction_servers diff --git a/services/seller_frontend_service/util/proto_mapping_util_test.cc b/services/seller_frontend_service/util/proto_mapping_util_test.cc index 2107e5f6..54c9c771 100644 --- a/services/seller_frontend_service/util/proto_mapping_util_test.cc +++ b/services/seller_frontend_service/util/proto_mapping_util_test.cc @@ -626,6 +626,8 @@ TEST_F(CreateAuctionResultCiphertextTest, ConvertsAdScoreForAndroid) { TEST_F(CreateAuctionResultCiphertextTest, ConvertsAdScoreForWeb) { AuctionResult expected = MapBasicScoreFieldsToAuctionResult(this->valid_score_); + // For web, the bid is encoded from the buyer_bid field + expected.set_bid(this->valid_score_.buyer_bid()); // IG Origin is Android exclusive and will not be parsed or encoded for web. expected.clear_interest_group_origin(); expected.mutable_bidding_groups()->insert(this->bidding_group_map_.begin(), @@ -648,6 +650,8 @@ TEST_F(CreateAuctionResultCiphertextTest, ConvertsAdScoreForWeb) { TEST_F(CreateAuctionResultCiphertextTest, ConvertsAdScoreForWebWithNonce) { AuctionResult expected = MapBasicScoreFieldsToAuctionResult(this->valid_score_); + // For web, the bid is encoded from the buyer_bid field + expected.set_bid(this->valid_score_.buyer_bid()); // IG Origin is Android exclusive and will not be parsed or encoded for web. expected.clear_interest_group_origin(); expected.mutable_bidding_groups()->insert(this->bidding_group_map_.begin(), diff --git a/services/seller_frontend_service/util/select_ad_reactor_test_utils.h b/services/seller_frontend_service/util/select_ad_reactor_test_utils.h index 3c76c6b1..309179d7 100644 --- a/services/seller_frontend_service/util/select_ad_reactor_test_utils.h +++ b/services/seller_frontend_service/util/select_ad_reactor_test_utils.h @@ -81,7 +81,7 @@ inline constexpr uint32_t kDefaultSellerDataVersion = 1776; inline constexpr char kValidScoringSignalsJson[] = R"JSON({"someAdRenderUrl":{"someKey":"someValue"}})JSON"; inline constexpr char kValidScoringSignalsJsonKvV2[] = - R"JSON({"renderUrls":{"someKey":{"value":"someValue"}}})JSON"; + R"JSON({"renderUrls":{"someKey":"someValue"}})JSON"; inline constexpr char kKvV2CompressionGroup[] = R"pb(compression_groups { compression_group_id: 33 @@ -224,13 +224,15 @@ SelectAdResponse RunRequest( const ClientRegistry& clients, const SelectAdRequest& request, const ReportWinMap& report_win_map, const int max_buyers_solicited = 2, const bool enable_kanon = false, - const bool enable_buyer_private_aggregate_reporting = false) { + const bool enable_buyer_private_aggregate_reporting = false, + int per_adtech_paapi_contributions_limit = 100) { grpc::CallbackServerContext context; SelectAdResponse response; T reactor(&context, &request, &response, clients, config_client, report_win_map, /*enable_cancellation=*/false, enable_kanon, enable_buyer_private_aggregate_reporting, + per_adtech_paapi_contributions_limit, /*fail_fast=*/true, max_buyers_solicited); reactor.Execute(); return response; @@ -295,16 +297,17 @@ EncryptedSelectAdRequestWithContext GetSampleSelectAdRequest( bool is_consented_debug = false, absl::string_view top_level_seller = "", EncryptionCloudPlatform top_seller_cloud_platform = EncryptionCloudPlatform::ENCRYPTION_CLOUD_PLATFORM_UNSPECIFIED, - bool enable_unlimited_egress = false, bool enforce_kanon = false) { - BuyerInput buyer_input; + bool enable_unlimited_egress = false, bool enforce_kanon = false, + std::string generation_id = kSampleGenerationId) { + BuyerInputForBidding buyer_input; auto* interest_group = buyer_input.mutable_interest_groups()->Add(); interest_group->set_name(kSampleInterestGroupName); *interest_group->mutable_bidding_signals_keys()->Add() = "[]"; google::protobuf::RepeatedPtrField ad_render_ids; ad_render_ids.Add(MakeARandomString()); interest_group->mutable_browser_signals()->CopyFrom( - MakeRandomBrowserSignalsForIG(ad_render_ids)); - google::protobuf::Map decoded_buyer_inputs; + MakeRandomBrowserSignalsForBiddingForIG(ad_render_ids)); + google::protobuf::Map decoded_buyer_inputs; decoded_buyer_inputs.emplace(kSampleBuyer, buyer_input); google::protobuf::Map encoded_buyer_inputs; switch (client_type) { @@ -323,7 +326,7 @@ EncryptedSelectAdRequestWithContext GetSampleSelectAdRequest( SelectAdRequest request; T protected_auction_input; - protected_auction_input.set_generation_id(kSampleGenerationId); + protected_auction_input.set_generation_id(generation_id); protected_auction_input.set_enable_unlimited_egress(enable_unlimited_egress); protected_auction_input.set_enforce_kanon(enforce_kanon); if (is_consented_debug) { @@ -417,12 +420,13 @@ GetSelectAdRequestAndClientRegistryForTest( ServerComponentAuctionParams server_component_auction_params = {}, bool enforce_kanon = false, const std::vector& kanon_ghost_winners = {}, - std::unique_ptr k_anon_cache_manager = nullptr) { + std::unique_ptr k_anon_cache_manager = nullptr, + std::string generation_id = kSampleGenerationId) { auto encrypted_request_with_context = GetSampleSelectAdRequest( client_type, seller_origin_domain, /*is_consented_debug=*/false, top_level_seller, server_component_auction_params.top_level_cloud_platform, - /*enable_unlimited_egress=*/false, /*enforce_kanon=*/enforce_kanon); + /*enable_unlimited_egress=*/false, enforce_kanon, generation_id); // Sets up buyer client while populating the expected buyer bids that can // then be used to setup the scoring signals provider. @@ -551,14 +555,16 @@ SelectAdResponse RunReactorRequest( const ClientRegistry& clients, const SelectAdRequest& request, bool enable_kanon = false, bool enable_buyer_private_aggregate_reporting = false, - bool fail_fast = false, const ReportWinMap& report_win_map = {}) { + int per_adtech_paapi_contributions_limit = 100, bool fail_fast = false, + const ReportWinMap& report_win_map = {}) { metric::SfeContextMap()->Get(&request); grpc::CallbackServerContext context; SelectAdResponse response; T reactor(&context, &request, &response, clients, config_client, report_win_map, /*enable_cancellation=*/false, enable_kanon, - enable_buyer_private_aggregate_reporting, fail_fast); + enable_buyer_private_aggregate_reporting, + per_adtech_paapi_contributions_limit, fail_fast); reactor.Execute(); return response; } diff --git a/services/seller_frontend_service/util/web_utils.cc b/services/seller_frontend_service/util/web_utils.cc index d13f85fb..c5ce50a3 100644 --- a/services/seller_frontend_service/util/web_utils.cc +++ b/services/seller_frontend_service/util/web_utils.cc @@ -28,6 +28,7 @@ #include "rapidjson/stringbuffer.h" #include "rapidjson/writer.h" #include "services/common/compression/gzip.h" +#include "services/common/util/json_util.h" #include "services/seller_frontend_service/private_aggregation/private_aggregation_helper.h" #include "services/seller_frontend_service/util/cbor_common_util.h" #include "src/util/status_macro/status_macros.h" @@ -42,7 +43,8 @@ using BiddingGroupMap = using InteractionUrlMap = ::google::protobuf::Map; using RepeatedStringProto = ::google::protobuf::RepeatedPtrField; using EncodedBuyerInputs = ::google::protobuf::Map; -using DecodedBuyerInputs = absl::flat_hash_map; +using DecodedBuyerInputs = + absl::flat_hash_map; using GhostWinnerForTopLevelAuction = AuctionResult::KAnonGhostWinner::GhostWinnerForTopLevelAuction; using GhostWinnerPrivateAggregationSignals = @@ -102,19 +104,24 @@ RepeatedStringProto DecodeStringArray(absl::Span span, } // Collects the prevWins arrays into a JSON array and stringifies the result. -absl::StatusOr GetStringifiedPrevWins( +std::pair GetStringifiedPrevWins( absl::Span prev_wins_entries, absl::string_view owner, ErrorAccumulator& error_accumulator, bool fail_fast) { - rapidjson::Document document; - document.SetArray(); - rapidjson::Document::AllocatorType& allocator = document.GetAllocator(); + rapidjson::Document document_seconds; + document_seconds.SetArray(); + rapidjson::Document::AllocatorType& allocator_seconds = + document_seconds.GetAllocator(); + + rapidjson::Document document_ms; + document_ms.SetArray(); + rapidjson::Document::AllocatorType& allocator_ms = document_ms.GetAllocator(); // Previous win entries should be in the form [relative_time, ad_render_id] // where relative_time is an int and ad_render_id is a string. for (const cbor_item_t* prev_win : prev_wins_entries) { bool is_valid = IsTypeValid(&cbor_isa_array, prev_win, kPrevWinsEntry, kArray, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, ""); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, std::make_pair("", "")); if (!is_valid) { continue; } @@ -124,7 +131,8 @@ absl::StatusOr GetStringifiedPrevWins( absl::StrFormat(kPrevWinsNotCorrectLengthError, owner); error_accumulator.ReportError(ErrorVisibility::CLIENT_VISIBLE, error, ErrorCode::CLIENT_SIDE); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, ""); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + std::make_pair("", "")); } // cbor_array_get calls cbor_incref() on the returned object, so call @@ -132,12 +140,12 @@ absl::StatusOr GetStringifiedPrevWins( ScopedCbor relative_time(cbor_array_get(prev_win, kRelativeTimeIndex)); IsTypeValid(&cbor_is_int, *relative_time, kPrevWinsTimeEntry, kInt, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, ""); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, std::make_pair("", "")); ScopedCbor maybe_ad_render_id(cbor_array_get(prev_win, kAdRenderIdIndex)); IsTypeValid(&cbor_isa_string, *maybe_ad_render_id, kPrevWinsAdRenderIdEntry, kString, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, ""); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, std::make_pair("", "")); if (error_accumulator.HasErrors()) { // No point in processing invalid data but we may continue to validate the @@ -148,28 +156,45 @@ absl::StatusOr GetStringifiedPrevWins( const int time = cbor_get_int(*relative_time); const std::string ad_render_id = CborDecodeString(*maybe_ad_render_id); - // Convert to JSON array and add to the running JSON document. - rapidjson::Value array(rapidjson::kArrayType); - array.PushBack(time, allocator); - rapidjson::Value ad_render_id_value(rapidjson::kStringType); - ad_render_id_value.SetString(ad_render_id.c_str(), ad_render_id.length(), - allocator); - array.PushBack(ad_render_id_value, allocator); - document.PushBack(array, allocator); - } - - rapidjson::StringBuffer string_buffer; - rapidjson::Writer writer(string_buffer); - document.Accept(writer); - return string_buffer.GetString(); + // Convert to JSON array and add to both JSON documents. + rapidjson::Value array_seconds(rapidjson::kArrayType); + array_seconds.PushBack(time, allocator_seconds); + rapidjson::Value ad_render_id_value_seconds(rapidjson::kStringType); + ad_render_id_value_seconds.SetString( + ad_render_id.c_str(), ad_render_id.length(), allocator_seconds); + array_seconds.PushBack(ad_render_id_value_seconds, allocator_seconds); + document_seconds.PushBack(array_seconds.Move(), allocator_seconds); + + rapidjson::Value array_ms(rapidjson::kArrayType); + array_ms.PushBack(time * 1000, allocator_ms); + rapidjson::Value ad_render_id_value_ms(rapidjson::kStringType); + ad_render_id_value_ms.SetString(ad_render_id.c_str(), ad_render_id.length(), + allocator_ms); + array_ms.PushBack(ad_render_id_value_ms, allocator_ms); + document_ms.PushBack(array_ms.Move(), allocator_ms); + } + + absl::StatusOr prev_wins_seconds = + SerializeJsonDoc(document_seconds); + absl::StatusOr prev_wins_ms = SerializeJsonDoc(document_ms); + + if (!prev_wins_seconds.ok() || !prev_wins_ms.ok()) { + const absl::StatusOr& first_error = + !prev_wins_seconds.ok() ? prev_wins_seconds : prev_wins_ms; + PS_VLOG(5) << "Unable to serialize prev_wins JSON documents: "; + PS_VLOG(5) << first_error.status(); + return {"", ""}; + } + + return {*prev_wins_seconds, *prev_wins_ms}; } // Decodes browser signals object and sets it in the 'buyer_interest_group'. -BrowserSignals DecodeBrowserSignals(const cbor_item_t* root, - absl::string_view owner, - ErrorAccumulator& error_accumulator, - bool fail_fast) { - BrowserSignals signals; +BrowserSignalsForBidding DecodeBrowserSignals( + const cbor_item_t* root, absl::string_view owner, + ErrorAccumulator& error_accumulator, bool fail_fast, + BuyerInputForBidding& buyer_input_for_bidding) { + BrowserSignalsForBidding signals; bool is_signals_valid_type = IsTypeValid(&cbor_isa_map, root, kBrowserSignals, kMap, error_accumulator); RETURN_IF_PREV_ERRORS(error_accumulator, /*fail_fast=*/!is_signals_valid_type, @@ -227,10 +252,13 @@ BrowserSignals DecodeBrowserSignals(const cbor_item_t* root, if (is_win_valid_type) { absl::Span prev_wins_entries( cbor_array_handle(signal.value), cbor_array_size(signal.value)); - absl::StatusOr prev_wins = GetStringifiedPrevWins( - prev_wins_entries, owner, error_accumulator, fail_fast); - *signals.mutable_prev_wins() = std::move(*prev_wins); + std::pair prev_wins = + GetStringifiedPrevWins(prev_wins_entries, owner, + error_accumulator, fail_fast); + *signals.mutable_prev_wins() = std::move(prev_wins.first); + *signals.mutable_prev_wins_ms() = std::move(prev_wins.second); } + break; } case 4: { // RecencyMs. @@ -514,6 +542,7 @@ absl::Status CborSerializeScoreAdResponse( const ScoreAdsResponse::AdScore& ad_score, const BiddingGroupMap& bidding_group_map, const UpdateGroupMap& update_group_map, + int per_adtech_paapi_contributions_limit, absl::string_view ad_auction_result_nonce, std::unique_ptr kanon_auction_result_data, ErrorHandler error_handler, cbor_item_t& root) { @@ -532,7 +561,8 @@ absl::Status CborSerializeScoreAdResponse( error_handler, root)); if (ad_score.top_level_contributions().size() > 0) { PS_RETURN_IF_ERROR(CborSerializePAggResponse( - ad_score.top_level_contributions(), error_handler, root)); + ad_score.top_level_contributions(), + per_adtech_paapi_contributions_limit, error_handler, root)); } PS_RETURN_IF_ERROR( CborSerializeUpdateGroups(update_group_map, error_handler, root)); @@ -1592,7 +1622,8 @@ absl::StatusOr Encode( const BiddingGroupMap& bidding_group_map, const UpdateGroupMap& update_group_map, const std::optional& error, - ErrorHandler error_handler, absl::string_view ad_auction_result_nonce, + ErrorHandler error_handler, int per_adtech_paapi_contributions_limit, + absl::string_view ad_auction_result_nonce, std::unique_ptr kanon_auction_result_data) { // CBOR data's root handle. When serializing the auction result to CBOR, we // use this handle to keep the temporary data. @@ -1611,8 +1642,8 @@ absl::StatusOr Encode( } else if (high_score) { PS_RETURN_IF_ERROR(CborSerializeScoreAdResponse( *high_score, bidding_group_map, update_group_map, - ad_auction_result_nonce, std::move(kanon_auction_result_data), - error_handler, *cbor_internal)); + per_adtech_paapi_contributions_limit, ad_auction_result_nonce, + std::move(kanon_auction_result_data), error_handler, *cbor_internal)); } else if (kanon_auction_result_data != nullptr && kanon_auction_result_data->kanon_ghost_winners != nullptr) { // "nonce" must be added before "kAnonGhostWinners". @@ -1695,8 +1726,8 @@ DecodedBuyerInputs DecodeBuyerInputs( ErrorAccumulator& error_accumulator, bool fail_fast) { DecodedBuyerInputs decoded_buyer_inputs; for (const auto& [owner, compressed_buyer_input] : encoded_buyer_inputs) { - BuyerInput buyer_input = DecodeBuyerInput(owner, compressed_buyer_input, - error_accumulator, fail_fast); + BuyerInputForBidding buyer_input = DecodeBuyerInput( + owner, compressed_buyer_input, error_accumulator, fail_fast); RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, decoded_buyer_inputs); decoded_buyer_inputs.insert({owner, std::move(buyer_input)}); @@ -1705,11 +1736,12 @@ DecodedBuyerInputs DecodeBuyerInputs( return decoded_buyer_inputs; } -BuyerInput DecodeBuyerInput(absl::string_view owner, - absl::string_view compressed_buyer_input, - ErrorAccumulator& error_accumulator, - bool fail_fast) { - BuyerInput buyer_input; +BuyerInputForBidding DecodeBuyerInput(absl::string_view owner, + absl::string_view compressed_buyer_input, + ErrorAccumulator& error_accumulator, + bool fail_fast) { + BuyerInputForBidding buyer_input_for_bidding; + const absl::StatusOr decompressed_buyer_input = GzipDecompress(compressed_buyer_input); if (!decompressed_buyer_input.ok()) { @@ -1717,7 +1749,7 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, ErrorVisibility::CLIENT_VISIBLE, absl::StrFormat(kMalformedCompressedIgError, owner), ErrorCode::CLIENT_SIDE); - return buyer_input; + return buyer_input_for_bidding; } cbor_load_result result; @@ -1730,23 +1762,25 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, ErrorVisibility::CLIENT_VISIBLE, absl::StrFormat(kInvalidBuyerInputCborError, owner), ErrorCode::CLIENT_SIDE); - return buyer_input; + return buyer_input_for_bidding; } bool is_buyer_input_valid_type = IsTypeValid( &cbor_isa_array, *root, kBuyerInput, kArray, error_accumulator); RETURN_IF_PREV_ERRORS(error_accumulator, - /*fail_fast=*/!is_buyer_input_valid_type, buyer_input); + /*fail_fast=*/!is_buyer_input_valid_type, + buyer_input_for_bidding); absl::Span interest_groups(cbor_array_handle(*root), cbor_array_size(*root)); for (const cbor_item_t* interest_group : interest_groups) { - auto* buyer_interest_group = buyer_input.add_interest_groups(); + auto* buyer_interest_group = buyer_input_for_bidding.add_interest_groups(); bool is_igs_valid_type = IsTypeValid(&cbor_isa_map, interest_group, kBuyerInputEntry, kMap, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); if (!is_igs_valid_type) { continue; @@ -1758,7 +1792,8 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, bool is_key_valid_type = IsTypeValid(&cbor_isa_string, ig_entry.key, kBuyerInputKey, kString, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); if (!is_key_valid_type) { continue; @@ -1771,7 +1806,8 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, bool is_name_valid_type = IsTypeValid(&cbor_isa_string, ig_entry.value, kInterestGroupName, kString, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); if (is_name_valid_type) { buyer_interest_group->set_name(CborDecodeString(ig_entry.value)); } @@ -1781,7 +1817,8 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, bool is_bs_valid_type = IsTypeValid(&cbor_isa_array, ig_entry.value, kIgBiddingSignalKeys, kArray, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); if (is_bs_valid_type) { absl::Span bidding_signals_list( @@ -1798,7 +1835,8 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, bool is_bs_valid_type = IsTypeValid(&cbor_isa_string, ig_entry.value, kUserBiddingSignals, kString, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); if (is_bs_valid_type) { *buyer_interest_group->mutable_user_bidding_signals() = @@ -1809,7 +1847,8 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, case 3: { // Ad render IDs. bool is_ad_render_valid_type = IsTypeValid( &cbor_isa_array, ig_entry.value, kAds, kArray, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); if (is_ad_render_valid_type) { absl::Span ads(cbor_array_handle(ig_entry.value), @@ -1823,7 +1862,8 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, bool is_component_valid_type = IsTypeValid(&cbor_isa_array, ig_entry.value, kAdComponent, kArray, error_accumulator); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); if (is_component_valid_type) { absl::Span component_ads( @@ -1837,8 +1877,10 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, case 5: { // Browser signals. *buyer_interest_group->mutable_browser_signals() = DecodeBrowserSignals(ig_entry.value, kIgBiddingSignalKeysEntry, - error_accumulator, fail_fast); - RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, buyer_input); + error_accumulator, fail_fast, + buyer_input_for_bidding); + RETURN_IF_PREV_ERRORS(error_accumulator, fail_fast, + buyer_input_for_bidding); } default: PS_VLOG(5) << "Serialized CBOR IG has an unexpected key: " @@ -1847,7 +1889,7 @@ BuyerInput DecodeBuyerInput(absl::string_view owner, } } - return buyer_input; + return buyer_input_for_bidding; } absl::Status CborSerializeReportingUrls( diff --git a/services/seller_frontend_service/util/web_utils.h b/services/seller_frontend_service/util/web_utils.h index b2e8b75f..1de9aa13 100644 --- a/services/seller_frontend_service/util/web_utils.h +++ b/services/seller_frontend_service/util/web_utils.h @@ -81,6 +81,8 @@ inline constexpr char kBrowserSignalsRecency[] = "browserSignals[x].recency"; inline constexpr char kBrowserSignalsRecencyMs[] = "browserSignals[x].recencyMs"; inline constexpr char kBrowserSignalsPrevWins[] = "browserSignals[x].prevWins"; +inline constexpr char kBrowserSignalsPrevWinsMs[] = + "browserSignals[x].prevWinsMs"; inline constexpr char kPrevWinsEntry[] = "browserSignals[x].prevWins[y]"; inline constexpr char kPrevWinsTimeEntry[] = "browserSignals[x].prevWins[y][0]"; inline constexpr char kPrevWinsAdRenderIdEntry[] = @@ -144,6 +146,7 @@ absl::StatusOr Encode( const UpdateGroupMap& update_group_map, const std::optional& error, const std::function& error_handler, + int per_adtech_paapi_contributions_limit = 0, absl::string_view ad_auction_result_nonce = "", std::unique_ptr kanon_auction_result_data = nullptr); @@ -344,16 +347,16 @@ absl::Status CborSerializeUpdateGroups( // Decodes the decompressed but CBOR encoded BuyerInput map to a mapping from // owner => BuyerInput. Errors are reported to `error_accumulator`. -absl::flat_hash_map DecodeBuyerInputs( +absl::flat_hash_map DecodeBuyerInputs( const google::protobuf::Map& encoded_buyer_inputs, ErrorAccumulator& error_accumulator, bool fail_fast = true); // Decompresses and the decodes the CBOR encoded and compressed BuyerInput. // Errors are reported to `error_accumulator`. -BuyerInput DecodeBuyerInput(absl::string_view owner, - absl::string_view compressed_buyer_input, - ErrorAccumulator& error_accumulator, - bool fail_fast = true); +BuyerInputForBidding DecodeBuyerInput(absl::string_view owner, + absl::string_view compressed_buyer_input, + ErrorAccumulator& error_accumulator, + bool fail_fast = true); // Minimally encodes an unsigned int into CBOR. Caller is responsible for // decrementing the reference once done with the returned int. diff --git a/services/seller_frontend_service/util/web_utils_test.cc b/services/seller_frontend_service/util/web_utils_test.cc index cddce613..148acf0e 100644 --- a/services/seller_frontend_service/util/web_utils_test.cc +++ b/services/seller_frontend_service/util/web_utils_test.cc @@ -39,6 +39,7 @@ #include "services/seller_frontend_service/private_aggregation/private_aggregation_helper.h" #include "services/seller_frontend_service/test/constants.h" #include "services/seller_frontend_service/test/kanon_test_utils.h" +#include "services/seller_frontend_service/util/buyer_input_proto_utils.h" #include "services/seller_frontend_service/util/cbor_common_util.h" #include "src/core/test/utils/proto_test_utils.h" @@ -141,14 +142,19 @@ struct cbor_item_t* BuildSampleCborInterestGroup( BuildIntMapPair(kRecencyMs, cbor_interest_group_config.recency_ms.value()))); } + // Build prevWins arrays. cbor_item_t* child_arr_1 = cbor_new_definite_array(2); - EXPECT_TRUE(cbor_array_push(child_arr_1, cbor_move(cbor_build_uint64(-20)))); + int child_arr_1_int_val = -20; + EXPECT_TRUE(cbor_array_push( + child_arr_1, cbor_move(cbor_build_uint64(child_arr_1_int_val)))); EXPECT_TRUE(cbor_array_push( child_arr_1, cbor_move(cbor_build_stringn( kSampleAdRenderId1, sizeof(kSampleAdRenderId1) - 1)))); cbor_item_t* child_arr_2 = cbor_new_definite_array(2); - EXPECT_TRUE(cbor_array_push(child_arr_2, cbor_move(cbor_build_uint64(-100)))); + int child_arr_2_int_val = -100; + EXPECT_TRUE(cbor_array_push( + child_arr_2, cbor_move(cbor_build_uint64(child_arr_2_int_val)))); EXPECT_TRUE(cbor_array_push( child_arr_2, cbor_move(cbor_build_stringn( kSampleAdRenderId2, sizeof(kSampleAdRenderId2) - 1)))); @@ -161,6 +167,7 @@ struct cbor_item_t* BuildSampleCborInterestGroup( browser_signals, {cbor_move(cbor_build_stringn(kPrevWins, sizeof(kPrevWins) - 1)), cbor_move(parent_arr)})); + cbor_item_t* browser_signals_key = cbor_build_stringn(kBrowserSignals, sizeof(kBrowserSignals) - 1); EXPECT_TRUE(cbor_map_add(interest_group, {cbor_move(browser_signals_key), @@ -256,6 +263,7 @@ PrivateAggregateReportingResponses GetTestPrivateAggregateReportingResponses() { PrivateAggregateContribution contribution1 = GetTestContributionWithIntegers(EVENT_TYPE_CUSTOM, kTestEvent1); contribution1.set_ig_idx(1); + *buyer_pagg_response.add_contributions() = contribution1; *buyer_pagg_response.add_contributions() = std::move(contribution1); buyer_pagg_response.set_adtech_origin(kTestBuyerOrigin); PrivateAggregateReportingResponses responses; @@ -399,14 +407,14 @@ void TestDecode(const CborInterestGroupConfig& cbor_interest_group_config) { if (cbor_interest_group_config.recency_ms) { signals->set_recency_ms(cbor_interest_group_config.recency_ms.value()); } + std::string prev_wins_json_str = absl::StrFormat( R"([[-20,"%s"],[-100,"%s"]])", kSampleAdRenderId1, kSampleAdRenderId2); signals->set_prev_wins(prev_wins_json_str); - BuyerInput buyer_input; - *buyer_input.add_interest_groups() = expected_ig; - expected_ig.mutable_browser_signals()->set_prev_wins(prev_wins_json_str); - google::protobuf::Map buyer_inputs; + BuyerInputForBidding buyer_input; + *buyer_input.add_interest_groups() = ToInterestGroupForBidding(expected_ig); + google::protobuf::Map buyer_inputs; buyer_inputs.emplace(kSampleIgOwner, std::move(buyer_input)); absl::StatusOr encoded_buyer_inputs = @@ -447,7 +455,7 @@ void TestDecode(const CborInterestGroupConfig& cbor_interest_group_config) { bi_differencer.ReportDifferencesToString(&bi_differences); ABSL_LOG(INFO) << "\nExpected BuyerInput:\n" << expected_buyer_input.DebugString(); - BuyerInput actual_buyer_input = + BuyerInputForBidding actual_buyer_input = DecodeBuyerInputs(actual.buyer_input(), error_accumulator) .begin() ->second; @@ -541,7 +549,7 @@ TEST(ChromeRequestUtils, Decode_FailOnMalformedCompresedBytestring) { Decode(serialized_cbor, error_accumulator); ASSERT_FALSE(error_accumulator.HasErrors()); - absl::flat_hash_map buyer_inputs = + absl::flat_hash_map buyer_inputs = DecodeBuyerInputs(actual.buyer_input(), error_accumulator); ASSERT_TRUE(error_accumulator.HasErrors()); EXPECT_TRUE(ContainsClientError( @@ -882,7 +890,8 @@ TEST(ChromeResponseUtils, SerializesBothWinnerAndKAnonGhostWinner) { auto response_with_cbor = Encode( winner, /*bidding_group_map=*/{}, /*update_group_map=*/{}, - /*error=*/std::nullopt, [](const grpc::Status& status) {}, "", + /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, "", std::move(kanon_auction_result_data)); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -966,6 +975,7 @@ TEST(ChromeResponseUtils, SerializesKAnonGhostWinnerAndDoesntSetChaff) { std::nullopt, /*bidding_group_map=*/{}, /*update_group_map=*/{}, /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, kSampleAdAuctionResultNonce, std::move(kanon_auction_result_data)); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -1149,6 +1159,7 @@ TEST(ChromeResponseUtils, EncodesNonceWithError) { /*high_score=*/ std::nullopt, /*bidding_group_map=*/{}, /*update_group_map=*/{}, error, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, kSampleAdAuctionResultNonce); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -1165,6 +1176,7 @@ TEST(ChromeResponseUtils, EncodesNonceWithChaff) { std::nullopt, /*bidding_group_map=*/{}, /*update_group_map=*/{}, /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, kSampleAdAuctionResultNonce); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -1181,6 +1193,7 @@ TEST(ChromeResponseUtils, EncodesNonceWithAdScore) { auto response_with_cbor = Encode( winner, /*bidding_group_map=*/{}, /*update_group_map=*/{}, /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, kSampleAdAuctionResultNonce); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -1200,6 +1213,7 @@ TEST(ChromeResponseUtils, DoesNotContainNonceWithErrorWhenEmpty) { /*high_score=*/ std::nullopt, /*bidding_group_map=*/{}, /*update_group_map=*/{}, error, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, /*ad_auction_result_nonce=*/""); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -1218,6 +1232,7 @@ TEST(ChromeResponseUtils, DoesNotContainNonceWithChaffWhenEmpty) { std::nullopt, /*bidding_group_map=*/{}, /*update_group_map=*/{}, /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, /*ad_auction_result_nonce=*/""); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -1236,6 +1251,7 @@ TEST(ChromeResponseUtils, DoesNotContainNonceWithAdScoreWhenEmpty) { auto response_with_cbor = Encode( winner, /*bidding_group_map=*/{}, /*update_group_map=*/{}, /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, /*ad_auction_result_nonce=*/""); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); @@ -1390,6 +1406,7 @@ TEST(ChromeResponseUtils, EncodesKAnonData) { auto response_with_cbor = Encode( winner, /*bidding_group_map=*/{}, /*update_group_map=*/{}, /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, /*ad_auction_result_nonce=*/"", std::move(kanon_auction_result_data)); ASSERT_TRUE(response_with_cbor.ok()) << response_with_cbor.status(); absl::StatusOr decoded_result = @@ -1679,7 +1696,8 @@ TEST(ChromeResponseUtils, VerifyMinimalResponseEncoding) { auto ret = Encode( std::move(winner), bidding_group_map, update_group_map, - /*error=*/std::nullopt, [](auto error) {}, kSampleAdAuctionResultNonce, + /*error=*/std::nullopt, [](auto error) {}, + /*per_adtech_paapi_contributions_limit=*/100, kSampleAdAuctionResultNonce, std::move(kanon_auction_result_data)); ASSERT_TRUE(ret.ok()) << ret.status(); @@ -1739,8 +1757,10 @@ TEST(ChromeResponseUtils, VerifyMinimalResponseEncodingWithBuyerReportingId) { bidding_group_map = GetTestBiddingGroupMap(); UpdateGroupMap update_group_map = GetTestUpdateGroupMap(); - auto ret = Encode(std::move(winner), bidding_group_map, update_group_map, - /*error=*/std::nullopt, [](auto error) {}); + auto ret = Encode( + std::move(winner), bidding_group_map, update_group_map, + /*error=*/std::nullopt, [](auto error) {}, + /*per_adtech_paapi_contributions_limit=*/1); ASSERT_TRUE(ret.ok()) << ret.status(); // Conversion can be verified at: https://cbor.me/ EXPECT_THAT( diff --git a/third_party/common_repo.patch b/third_party/common_repo.patch deleted file mode 100644 index 62f0f912..00000000 --- a/third_party/common_repo.patch +++ /dev/null @@ -1,118 +0,0 @@ -diff --git a/src/roma/byob/container/BUILD.bazel b/src/roma/byob/container/BUILD.bazel -index 4f7ae70b..50a43f30 100644 ---- a/src/roma/byob/container/BUILD.bazel -+++ b/src/roma/byob/container/BUILD.bazel -@@ -15,10 +15,11 @@ - load("@aspect_bazel_lib//lib:copy_file.bzl", "copy_file") - load("@aspect_bazel_lib//lib:expand_template.bzl", "expand_template") - load("@rules_cc//cc:defs.bzl", "cc_binary") -+load("@rules_oci//oci:defs.bzl", "oci_image", "oci_load") - load("@rules_pkg//pkg:mappings.bzl", "pkg_attributes", "pkg_files", "pkg_mkdirs") - load("@rules_pkg//pkg:tar.bzl", "pkg_tar") - load("//src/roma/byob/config:container.bzl", "roma_container_dir", "roma_container_root_dir") --load("//third_party:container_deps.bzl", "DISTROLESS_USERS", "container_image") -+load("//third_party:container_deps.bzl", "DISTROLESS_USERS") - - [ - expand_template( -@@ -161,36 +162,69 @@ pkg_files( - for user in DISTROLESS_USERS - ] - --[ -- genrule( -- name = "gen_byob_runtime_container_tar_{}".format(arch), -- outs = ["byob_runtime_base_{}.tar".format(arch)], -- cmd_bash = """echo FROM {base_image} | docker buildx build --output=type=tar,dest=$@ --file=- .""".format( -- base_image = container_image( -- "runtime-debian-nondebug-nonroot", -- arch, -- ).uri, -- ), -- visibility = ["//visibility:public"], -- ) -- for arch in ("arm64", "amd64") --] -- - alias( -- name = "byob_runtime_base.tar", -+ name = "runtime-debian-nondebug-nonroot", - actual = select({ -- "@platforms//cpu:aarch64": ":byob_runtime_base_arm64.tar", -- "@platforms//cpu:x86_64": ":byob_runtime_base_amd64.tar", -+ "@platforms//cpu:aarch64": "@runtime-debian-nondebug-nonroot-arm64", -+ "@platforms//cpu:x86_64": "@runtime-debian-nondebug-nonroot-amd64", - }), -- visibility = ["//visibility:public"], - ) - -+[ -+ oci_image( -+ name = "byob_runtime_image_{}".format(user.flavor), -+ base = ":runtime-debian-nondebug-nonroot", -+ tars = [":byob_runtime_tar_{}".format(user.flavor)], -+ ) -+ for user in DISTROLESS_USERS -+] -+ -+_server_image = "byob_server:v1-{flavor}" -+ -+[ -+ oci_load( -+ name = "byob_runtime_image_{}_tar".format(user.flavor), -+ image = ":byob_runtime_image_{}".format(user.flavor), -+ repo_tags = [_server_image.format(flavor = user.flavor)], -+ ) -+ for user in DISTROLESS_USERS -+] -+ -+[ -+ filegroup( -+ name = "byob_runtime_image_{}.tar".format(user.flavor), -+ srcs = [":byob_runtime_image_{}_tar".format(user.flavor)], -+ output_group = "tarball", -+ ) -+ for user in DISTROLESS_USERS -+] -+ -+# warning: this is not hermetic, it uses the local docker client to load an -+# image, create a container then export that image using a static container name -+[ -+ genrule( -+ name = "gen_byob_runtime_container_tar_{}".format(user.flavor), -+ srcs = [":byob_runtime_image_{}.tar".format(user.flavor)], -+ outs = ["byob_runtime_container_{}.tar".format(user.flavor)], -+ cmd_bash = """ -+docker load -i "$(location :byob_runtime_image_{flavor}.tar)" -+CID=$$(docker create --entrypoint /server/bin/run_workers --privileged "{image_uri}") -+docker export $$CID -o $@ -+docker rm $$CID -+""".format( -+ flavor = user.flavor, -+ image_uri = _server_image.format(flavor = user.flavor), -+ ), -+ visibility = ["//visibility:public"], -+ ) -+ for user in DISTROLESS_USERS -+] -+ - [ - genrule( - name = "gen_byob_runtime_container_with_dir_tar_{}".format(user.flavor), - srcs = [ -- ":byob_runtime_base.tar", -- ":byob_runtime_tar_{}.tar".format(user.flavor), -+ ":byob_runtime_container_{}.tar".format(user.flavor), - ":container_config_{}.tar".format(user.user), - ], - outs = ["byob_runtime_container_with_dir_{}.tar".format(user.flavor)], -@@ -198,8 +232,7 @@ alias( - TMPDIR="$$(mktemp --directory roma_container_tmp.XXXXXXXXXX)" - MACHINE="$$(uname --machine)" - mkdir --parents --mode=700 "$$TMPDIR/{roma_container_dir}/{roma_container_root_dir}" --tar --extract --file="$(location :byob_runtime_base.tar)" --directory="$$TMPDIR/{roma_container_dir}/{roma_container_root_dir}" --tar --extract --file="$(location :byob_runtime_tar_{flavor}.tar)" --directory="$$TMPDIR/{roma_container_dir}/{roma_container_root_dir}" -+tar --extract --file="$(location :byob_runtime_container_{flavor}.tar)" --directory="$$TMPDIR/{roma_container_dir}/{roma_container_root_dir}" - tar --extract --file="$(location :container_config_{flavor}.tar)" --directory="$$TMPDIR/{roma_container_dir}" - - readonly ROOT_DIR="$${{TMPDIR}}/{roma_container_dir}/{roma_container_root_dir}" diff --git a/tools/cost_estimation/README.md b/tools/cost_estimation/README.md index 38216448..7d53decd 100644 --- a/tools/cost_estimation/README.md +++ b/tools/cost_estimation/README.md @@ -1,7 +1,7 @@ # Cost Estimation Tool For highlevel overview of the cost estimation tool refer to the -[Cost estimation tool explainer](https://github.com/privacysandbox/protected-auction-services-docs/blob/main/). +[Cost estimation tool explainer](https://github.com/privacysandbox/protected-auction-services-docs/blob/main/bidding_auction_cost_estimation_tool.md). The cost estimation tool helps estimate usage and cost for deployed and running Bidding and Auction (B&A) services. It does so by downloading metrics collected by the system and applying a set of diff --git a/tools/cost_estimation/cost_tool.py b/tools/cost_estimation/cost_tool.py index 071567ab..7641154d 100644 --- a/tools/cost_estimation/cost_tool.py +++ b/tools/cost_estimation/cost_tool.py @@ -13,6 +13,7 @@ # limitations under the License. """Tool for estimating costs.""" + import json import yaml import logging diff --git a/tools/cost_estimation/cost_tool_test.py b/tools/cost_estimation/cost_tool_test.py index abf5fe26..65949465 100644 --- a/tools/cost_estimation/cost_tool_test.py +++ b/tools/cost_estimation/cost_tool_test.py @@ -14,7 +14,6 @@ """Tests for cost_tool.""" - import unittest from unittest.mock import patch, mock_open, MagicMock from tools.cost_estimation.cost_tool import CostTool diff --git a/tools/cost_estimation/estimator.py b/tools/cost_estimation/estimator.py index 8adbca83..2d49415f 100644 --- a/tools/cost_estimation/estimator.py +++ b/tools/cost_estimation/estimator.py @@ -107,7 +107,7 @@ def run(self): # only add values from metrics that can be converted to floats float(value) self._expression_evaluator.add_to_context(metric, value) - except: + except Exception: pass # Add all the defined values to the context. diff --git a/tools/cost_estimation/estimator_test.py b/tools/cost_estimation/estimator_test.py index 31ec8444..b374e7e0 100644 --- a/tools/cost_estimation/estimator_test.py +++ b/tools/cost_estimation/estimator_test.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""Tests for estimator module. """ +"""Tests for estimator module.""" import unittest from unittest.mock import MagicMock diff --git a/tools/cost_estimation/render_test.py b/tools/cost_estimation/render_test.py index a90cb26f..b86e1d37 100644 --- a/tools/cost_estimation/render_test.py +++ b/tools/cost_estimation/render_test.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Tests for the render module.""" + import unittest from tools.cost_estimation.render import CsvRenderer diff --git a/tools/cost_estimation/sql_expr.py b/tools/cost_estimation/sql_expr.py index 0d7dd9b5..17fad9e4 100644 --- a/tools/cost_estimation/sql_expr.py +++ b/tools/cost_estimation/sql_expr.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -""" Module for sql based execution and expression evaluation. """ +"""Module for sql based execution and expression evaluation.""" import sqlite3 import tempfile diff --git a/tools/cost_estimation/sql_expr_test.py b/tools/cost_estimation/sql_expr_test.py index 5bb03bfc..f59a7a5c 100644 --- a/tools/cost_estimation/sql_expr_test.py +++ b/tools/cost_estimation/sql_expr_test.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Tests for sql_expr.""" + import unittest import sqlite3 diff --git a/tools/cost_estimation/utils.py b/tools/cost_estimation/utils.py index ef1808a9..d95a3049 100644 --- a/tools/cost_estimation/utils.py +++ b/tools/cost_estimation/utils.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. """Utils functions.""" + import logging import tempfile import os diff --git a/tools/cost_estimation/utils_test.py b/tools/cost_estimation/utils_test.py index 59620bdd..2c2960e1 100644 --- a/tools/cost_estimation/utils_test.py +++ b/tools/cost_estimation/utils_test.py @@ -11,7 +11,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. -""""Unit tests for utils.""" +"""Unit tests for utils.""" import unittest from unittest.mock import patch, mock_open diff --git a/tools/debug/common b/tools/debug/common index 04fb7d4b..8f1bf3c2 100644 --- a/tools/debug/common +++ b/tools/debug/common @@ -28,6 +28,7 @@ PS_VERBOSITY="${PS_VERBOSITY:-10}" ENABLE_KANON="${ENABLE_KANON:-false}" ENABLE_PROTECTED_AUDIENCE="${ENABLE_PROTECTED_AUDIENCE:-true}" ENABLE_PROTECTED_APP_SIGNALS="${ENABLE_PROTECTED_APP_SIGNALS:-false}" +ENABLE_TKV_V2_BROWSER="${ENABLE_TKV_V2_BROWSER:-false}" TELEMETRY_CONFIG="${TELEMETRY_CONFIG:-mode: EXPERIMENT metric_export_interval_ms: 600000}" # Pass in custom docker arguments as a string such as: diff --git a/tools/debug/start_bfe b/tools/debug/start_bfe index 6f564700..cf03360d 100755 --- a/tools/debug/start_bfe +++ b/tools/debug/start_bfe @@ -44,7 +44,8 @@ SERVER_START_CMD=$(cat << END --enable_protected_audience=${ENABLE_PROTECTED_AUDIENCE} \ --bfe_tcmalloc_background_release_rate_bytes_per_second=4096 \ --bfe_tcmalloc_max_total_thread_cache_bytes=10737418240 \ ---enable_protected_app_signals=${ENABLE_PROTECTED_APP_SIGNALS} && exit +--enable_protected_app_signals=${ENABLE_PROTECTED_APP_SIGNALS} \ +--enable_tkv_v2_browser=${ENABLE_TKV_V2_BROWSER} && exit END ) diff --git a/tools/debug/start_sfe b/tools/debug/start_sfe index 06bf860a..9ee34ec8 100755 --- a/tools/debug/start_sfe +++ b/tools/debug/start_sfe @@ -58,7 +58,8 @@ SERVER_START_CMD=$(cat << END --enable_protected_audience=${ENABLE_PROTECTED_AUDIENCE} \ --sfe_tcmalloc_background_release_rate_bytes_per_second=4096 \ --sfe_tcmalloc_max_total_thread_cache_bytes=10737418240 \ ---enable_protected_app_signals=${ENABLE_PROTECTED_APP_SIGNALS} && exit +--enable_protected_app_signals=${ENABLE_PROTECTED_APP_SIGNALS} \ +--enable_tkv_v2_browser=${ENABLE_TKV_V2_BROWSER} && exit END ) diff --git a/tools/secure_invoke/README.md b/tools/secure_invoke/README.md index b6ae334f..1c8ce2cc 100644 --- a/tools/secure_invoke/README.md +++ b/tools/secure_invoke/README.md @@ -373,11 +373,11 @@ The example below queries a live public key endpoint, picks the first key, and p `secure_invoke`: ```bash -# Setup arguments. -INPUT_PATH=... -SFE_HOST_ADDRESS=... -CLIENT_IP=... -LIVE_KEY_ENDPOINT="https://..." +# Setup arguments (and replace the placeholders). +INPUT_PATH="" +SFE_HOST_ADDRESS="" +CLIENT_IP="" +LIVE_KEY_ENDPOINT="" # Setup keys. LIVE_KEYS=$(curl ${LIVE_KEY_ENDPOINT}) @@ -395,6 +395,16 @@ KEY_ID=$(echo $LIVE_KEYS | jq -r .keys[0].id) -key_id=${KEY_ID} ``` +Notes about `LIVE_KEY_ENDPOINT`: + +- `LIVE_KEY_ENDPOINT` refers to the public key hosting service endpoint from where public keys are + fetched. +- For AWS, use the following: + LIVE_KEY_ENDPOINT="" +- For GCP, use the following: + LIVE_KEY_ENDPOINT="" +- We will add/update public key endpoints as support for other cloud platforms is added. + ### Interacting with Local Servers If you want to send a request to servers running on localhost, make sure to specify the diff --git a/tools/secure_invoke/flags.cc b/tools/secure_invoke/flags.cc index 03591312..cb88b948 100644 --- a/tools/secure_invoke/flags.cc +++ b/tools/secure_invoke/flags.cc @@ -80,7 +80,7 @@ ABSL_FLAG(std::optional, enable_debug_info, std::nullopt, "Set to true to send a request to an SFE server with " "DebugInfo enabled for this request"); -ABSL_FLAG(bool, enforce_kanon, false, +ABSL_FLAG(std::optional, enforce_kanon, std::nullopt, "If set to true SFE will validate run k-anonymity check on the ads " "participating in the auction"); diff --git a/tools/secure_invoke/flags.h b/tools/secure_invoke/flags.h index a1a22d4c..5cece8e8 100644 --- a/tools/secure_invoke/flags.h +++ b/tools/secure_invoke/flags.h @@ -40,7 +40,7 @@ ABSL_DECLARE_FLAG(std::string, key_id); ABSL_DECLARE_FLAG(std::optional, enable_debug_reporting); ABSL_DECLARE_FLAG(std::optional, enable_debug_info); ABSL_DECLARE_FLAG(std::optional, enable_unlimited_egress); -ABSL_DECLARE_FLAG(bool, enforce_kanon); +ABSL_DECLARE_FLAG(std::optional, enforce_kanon); ABSL_DECLARE_FLAG(std::string, pas_buyer_input_json); #endif // TOOLS_SECURE_INVOKE_FLAGS_H_ diff --git a/tools/secure_invoke/payload_generator/payload_packaging.cc b/tools/secure_invoke/payload_generator/payload_packaging.cc index a74096a5..ac5384b1 100644 --- a/tools/secure_invoke/payload_generator/payload_packaging.cc +++ b/tools/secure_invoke/payload_generator/payload_packaging.cc @@ -88,7 +88,7 @@ ProtectedAuctionInput GetProtectedAuctionInput( std::optional enable_debug_reporting = std::nullopt, std::optional enable_debug_info = std::nullopt, std::optional enable_unlimited_egress = std::nullopt, - bool enforce_kanon = false) { + std::optional enforce_kanon = std::nullopt) { CHECK(input_json != nullptr) << "Input JSON must be non null"; rapidjson::Value& protected_auction_json = (*input_json)[kProtectedAuctionInputField]; @@ -111,13 +111,15 @@ ProtectedAuctionInput GetProtectedAuctionInput( protected_auction_input.set_enable_unlimited_egress( *enable_unlimited_egress); } - protected_auction_input.set_enforce_kanon(enforce_kanon); + if (enforce_kanon) { + protected_auction_input.set_enforce_kanon(*enforce_kanon); + } CHECK(protected_auction_input_parse.ok()) << protected_auction_input_parse; return protected_auction_input; } -absl::flat_hash_map GetProtectedAppSignals( +absl::flat_hash_map GetProtectedAppSignals( ClientType client_type, absl::string_view protected_app_signals_json) { if (client_type == ClientType::CLIENT_TYPE_BROWSER || protected_app_signals_json.empty()) { @@ -127,12 +129,13 @@ absl::flat_hash_map GetProtectedAppSignals( auto protected_app_signals_obj = ParseJsonString(protected_app_signals_json); CHECK_OK(protected_app_signals_obj); - absl::flat_hash_map protected_app_signals_map; + absl::flat_hash_map + protected_app_signals_map; google::protobuf::json::ParseOptions parse_options; parse_options.ignore_unknown_fields = true; for (auto it = protected_app_signals_obj->MemberBegin(); it != protected_app_signals_obj->MemberEnd(); ++it) { - BuyerInput buyer_input_proto; + BuyerInputForBidding buyer_input_proto; auto serialized_buyer_input = SerializeJsonDoc(it->value.GetObject()); CHECK_OK(serialized_buyer_input); auto buyer_input_parse = google::protobuf::util::JsonStringToMessage( @@ -145,7 +148,7 @@ absl::flat_hash_map GetProtectedAppSignals( return protected_app_signals_map; } -google::protobuf::Map GetBuyerInputMap( +google::protobuf::Map GetBuyerInputMap( ClientType client_type, rapidjson::Document* input_json, absl::string_view protected_app_signals_json) { CHECK(input_json != nullptr) << "Input JSON must be non null"; @@ -158,15 +161,15 @@ google::protobuf::Map GetBuyerInputMap( rapidjson::Value& buyer_map_json = (*input_json)[kProtectedAuctionInputField][kBuyerInputMapField]; - absl::flat_hash_map buyer_input_map; - absl::flat_hash_map protected_app_signals = + absl::flat_hash_map buyer_input_map; + absl::flat_hash_map protected_app_signals = GetProtectedAppSignals(client_type, protected_app_signals_json); for (auto& buyer_input : buyer_map_json.GetObject()) { std::string buyer_input_json = ValueToJson(&buyer_input.value); google::protobuf::json::ParseOptions parse_options; parse_options.ignore_unknown_fields = true; - BuyerInput buyer_input_proto; + BuyerInputForBidding buyer_input_proto; auto buyer_input_parse = google::protobuf::util::JsonStringToMessage( buyer_input_json, &buyer_input_proto, parse_options); CHECK(buyer_input_parse.ok()) << buyer_input_parse; @@ -184,8 +187,8 @@ google::protobuf::Map GetBuyerInputMap( } buyer_input_map.merge(protected_app_signals); - return google::protobuf::Map(buyer_input_map.begin(), - buyer_input_map.end()); + return google::protobuf::Map( + buyer_input_map.begin(), buyer_input_map.end()); } SelectAdRequest::ComponentAuctionResult GetComponentAuctionResult( @@ -226,7 +229,7 @@ PackagePlainTextSelectAdRequest(absl::string_view input_json_str, std::optional enable_debug_info, absl::string_view protected_app_signals_json, std::optional enable_unlimited_egress, - bool enforce_kanon) { + std::optional enforce_kanon) { rapidjson::Document input_json = ParseRequestInputJson(input_json_str); auto select_ad_request = std::make_unique(); ProtectedAuctionInput protected_auction_input = GetProtectedAuctionInput( @@ -244,7 +247,7 @@ PackagePlainTextSelectAdRequest(absl::string_view input_json_str, keyset); } } else { - google::protobuf::Map buyer_map_proto = + google::protobuf::Map buyer_map_proto = GetBuyerInputMap(client_type, &input_json, protected_app_signals_json); // Encode buyer map. absl::StatusOr> @@ -280,7 +283,8 @@ std::string PackagePlainTextSelectAdRequestToJson( absl::string_view input_json_str, ClientType client_type, const HpkeKeyset& keyset, std::optional enable_debug_reporting, std::optional enable_debug_info, - std::optional enable_unlimited_egress, bool enforce_kanon) { + std::optional enable_unlimited_egress, + std::optional enforce_kanon) { auto req = std::move( PackagePlainTextSelectAdRequest(input_json_str, client_type, keyset, enable_debug_reporting, enable_debug_info, diff --git a/tools/secure_invoke/payload_generator/payload_packaging.h b/tools/secure_invoke/payload_generator/payload_packaging.h index 406e1a22..0f353cd0 100644 --- a/tools/secure_invoke/payload_generator/payload_packaging.h +++ b/tools/secure_invoke/payload_generator/payload_packaging.h @@ -130,7 +130,7 @@ PackagePlainTextSelectAdRequest( std::optional enable_debug_info = std::nullopt, absl::string_view protected_app_signals_json = "", std::optional enable_unlimited_egress = std::nullopt, - bool enforce_kanon = false); + std::optional enforce_kanon = std::nullopt); // This method returns a SelectAdRequest json for testing B&A servers in // "test_mode" using the PackagePlainTextSelectAdRequest method. @@ -140,7 +140,7 @@ std::string PackagePlainTextSelectAdRequestToJson( std::optional enable_debug_reporting = std::nullopt, std::optional enable_debug_info = std::nullopt, std::optional enable_unlimited_egress = std::nullopt, - bool enforce_kanon = false); + std::optional enforce_kanon = std::nullopt); } // namespace privacy_sandbox::bidding_auction_servers #endif // TOOLS_PAYLOAD_GENERATOR_PAYLOAD_PACKAGING_H_ diff --git a/tools/secure_invoke/payload_generator/payload_packaging_utils.cc b/tools/secure_invoke/payload_generator/payload_packaging_utils.cc index 52dac820..709750b1 100644 --- a/tools/secure_invoke/payload_generator/payload_packaging_utils.cc +++ b/tools/secure_invoke/payload_generator/payload_packaging_utils.cc @@ -77,13 +77,15 @@ PackagePayload(const ProtectedAuctionInput& protected_auction_input, absl::StatusOr> PackageBuyerInputsForApp( - const google::protobuf::Map& buyer_inputs) { + const google::protobuf::Map& + buyer_inputs) { return GetProtoEncodedBuyerInputs(buyer_inputs); } absl::StatusOr> PackageBuyerInputsForBrowser( - const google::protobuf::Map& buyer_inputs) { + const google::protobuf::Map& + buyer_inputs) { return GetEncodedBuyerInputMap(buyer_inputs); } diff --git a/tools/secure_invoke/payload_generator/payload_packaging_utils.h b/tools/secure_invoke/payload_generator/payload_packaging_utils.h index 275fe658..b3d33e95 100644 --- a/tools/secure_invoke/payload_generator/payload_packaging_utils.h +++ b/tools/secure_invoke/payload_generator/payload_packaging_utils.h @@ -66,14 +66,16 @@ PackagePayload(const ProtectedAuctionInput& protected_auction_input, // for creating requests from a browser source. absl::StatusOr> PackageBuyerInputsForBrowser( - const google::protobuf::Map& buyer_inputs); + const google::protobuf::Map& + buyer_inputs); // Returns a map of IG owners -> GZIP compressed proto binary Buyer Inputs. // The output from this method can be used to populate the buyer_input map in // ProtectedAuctionInput for mimicking requests from android. absl::StatusOr> PackageBuyerInputsForApp( - const google::protobuf::Map& buyer_inputs); + const google::protobuf::Map& + buyer_inputs); // Returns a decrypted, decompressed and decoded proto response // for response strings generated for a browser or android source. diff --git a/tools/secure_invoke/payload_generator/payload_packaging_utils_test.cc b/tools/secure_invoke/payload_generator/payload_packaging_utils_test.cc index 516c1a08..f881729d 100644 --- a/tools/secure_invoke/payload_generator/payload_packaging_utils_test.cc +++ b/tools/secure_invoke/payload_generator/payload_packaging_utils_test.cc @@ -100,9 +100,9 @@ TEST(PackagePayloadForBrowserTest, GeneratesAValidBrowserPayload) { // by PackageBuyerInputsForBrowser method is correctly parsed in the // SelectAdReactor. TEST(PackageBuyerInputsForBrowserTest, GeneratesAValidBuyerInputMap) { - BuyerInput expected = MakeARandomBuyerInput(); + BuyerInputForBidding expected = MakeARandomBuyerInputForBidding(); std::string owner = MakeARandomString(); - google::protobuf::Map buyer_inputs; + google::protobuf::Map buyer_inputs; buyer_inputs.insert({owner, expected}); absl::StatusOr> output = PackageBuyerInputsForBrowser(buyer_inputs); @@ -114,14 +114,21 @@ TEST(PackageBuyerInputsForBrowserTest, GeneratesAValidBuyerInputMap) { const auto& buyer_input_it = output->find(owner); ASSERT_NE(buyer_input_it, output->end()); ErrorAccumulator error_accumulator; - absl::StatusOr actual = + BuyerInputForBidding actual = DecodeBuyerInput(owner, buyer_input_it->second, error_accumulator); - ASSERT_TRUE(actual.ok()) << actual.status(); + + // We have to manually set this field because even though only prev_wins is + // passed into the SFE request object, the SFE reads the field and sets *both* + // prev_wins and prev_wins_ms when decoding. + expected.mutable_interest_groups(0) + ->mutable_browser_signals() + ->set_prev_wins_ms( + actual.interest_groups(0).browser_signals().prev_wins_ms()); google::protobuf::util::MessageDifferencer diff; std::string difference; diff.ReportDifferencesToString(&difference); - EXPECT_TRUE(diff.Compare(*actual, expected)) << difference; + EXPECT_TRUE(diff.Compare(actual, expected)) << difference; } // This test follows the exact steps for encoding and encryption for the @@ -142,7 +149,8 @@ TEST(UnpackageBrowserAuctionResultTest, GeneratesAValidResponse) { // Encode. auto encoded_data = Encode( input, expected.bidding_groups(), expected.update_groups(), - /*error=*/std::nullopt, [](const grpc::Status& status) {}, nonce); + /*error=*/std::nullopt, [](const grpc::Status& status) {}, + /*per_adtech_paapi_contributions_limit=*/100, nonce); ASSERT_TRUE(encoded_data.ok()) << encoded_data.status(); // Compress. @@ -217,9 +225,9 @@ TEST(PackagePayloadForAppTest, GeneratesAValidAppPayload) { } TEST(PackageBuyerInputsForAppTest, GeneratesAValidBuyerInputMap) { - BuyerInput expected = MakeARandomBuyerInput(); + BuyerInputForBidding expected = MakeARandomBuyerInputForBidding(); std::string owner = MakeARandomString(); - google::protobuf::Map buyer_inputs; + google::protobuf::Map buyer_inputs; buyer_inputs.insert({owner, expected}); absl::StatusOr> output = PackageBuyerInputsForApp(buyer_inputs); @@ -233,7 +241,7 @@ TEST(PackageBuyerInputsForAppTest, GeneratesAValidBuyerInputMap) { auto decompressed_buyer_input = GzipDecompress(buyer_input_it->second); ASSERT_TRUE(decompressed_buyer_input.ok()) << decompressed_buyer_input.status(); - BuyerInput actual; + BuyerInputForBidding actual; ASSERT_TRUE(actual.ParseFromArray(decompressed_buyer_input->data(), decompressed_buyer_input->size())); diff --git a/tools/secure_invoke/secure_invoke.cc b/tools/secure_invoke/secure_invoke.cc index 59640961..a5f24dc3 100644 --- a/tools/secure_invoke/secure_invoke.cc +++ b/tools/secure_invoke/secure_invoke.cc @@ -97,7 +97,7 @@ int main(int argc, char** argv) { absl::GetFlag(FLAGS_enable_debug_info); std::optional enable_unlimited_egress = absl::GetFlag(FLAGS_enable_unlimited_egress); - const bool enforce_kanon = absl::GetFlag(FLAGS_enforce_kanon); + std::optional enforce_kanon = absl::GetFlag(FLAGS_enforce_kanon); if (op == "encrypt") { if (target_service == kSfe) { diff --git a/tools/secure_invoke/secure_invoke_lib.cc b/tools/secure_invoke/secure_invoke_lib.cc index 339e8d4b..bd698320 100644 --- a/tools/secure_invoke/secure_invoke_lib.cc +++ b/tools/secure_invoke/secure_invoke_lib.cc @@ -135,7 +135,8 @@ absl::Status InvokeSellerFrontEndWithRawRequest( const RequestOptions& request_options, ClientType client_type, const HpkeKeyset& keyset, std::optional enable_debug_reporting, std::optional enable_debug_info, - std::optional enable_unlimited_egress, bool enforce_kanon, + std::optional enable_unlimited_egress, + std::optional enforce_kanon, absl::AnyInvocable) &&> on_done) { // Validate input if (request_options.host_addr.empty()) { @@ -279,7 +280,7 @@ absl::Status SendRequestToSfe(ClientType client_type, const HpkeKeyset& keyset, std::optional enable_debug_reporting, std::optional enable_debug_info, std::optional enable_unlimited_egress, - bool enforce_kanon) { + std::optional enforce_kanon) { std::string raw_select_ad_request_json = absl::GetFlag(FLAGS_json_input_str); if (raw_select_ad_request_json.empty()) { raw_select_ad_request_json = LoadFile(absl::GetFlag(FLAGS_input_file)); diff --git a/tools/secure_invoke/secure_invoke_lib.h b/tools/secure_invoke/secure_invoke_lib.h index 153457d9..5af75066 100644 --- a/tools/secure_invoke/secure_invoke_lib.h +++ b/tools/secure_invoke/secure_invoke_lib.h @@ -42,7 +42,7 @@ absl::Status SendRequestToSfe(ClientType client_type, const HpkeKeyset& keyset, std::optional enable_debug_reporting, std::optional enable_debug_info, std::optional enable_unlimited_egress, - bool enforce_kanon); + std::optional enforce_kanon); // Sends a request to BFE. The parameters used for the request are retrieved // from absl flags that are used to run the script. diff --git a/tools/secure_invoke/secure_invoke_lib_test.cc b/tools/secure_invoke/secure_invoke_lib_test.cc index 50ad2b28..f027e860 100644 --- a/tools/secure_invoke/secure_invoke_lib_test.cc +++ b/tools/secure_invoke/secure_invoke_lib_test.cc @@ -35,6 +35,8 @@ #include "services/common/test/utils/ohttp_utils.h" #include "services/common/test/utils/service_utils.h" #include "services/common/test/utils/test_init.h" +#include "src/concurrent/event_engine_executor.h" +#include "src/core/lib/event_engine/default_event_engine.h" #include "tools/secure_invoke/flags.h" using ::privacy_sandbox::bidding_auction_servers::HpkeKeyset; @@ -71,6 +73,33 @@ constexpr char kSampleGetBidRequest[] = R"JSON({ "seller" : "https://sellerorigin.com" })JSON"; +constexpr char kSampleGetBidRequestUsingBuyerInputForBidding[] = R"JSON({ + "auctionSignals" : "{\"maxFloorCpmUsdMicros\":\"6250\"}", + "buyerInputForBidding" : { + "interestGroups" : [ + { + "biddingSignalsKeys" : [ + "1j473877681" + ], + "browserSignals" : { + "bidCount" : "17", + "joinCount" : "3", + "prevWins" : "[]", + "prevWinsMs" : "[]" + }, + "name" : "nike shoe", + "userBiddingSignals" : "[[]]" + } + ] + }, + "buyerSignals" : "[[[[\"Na6y7V9RU0A\",[-0.006591797,0.052001953]]]]]", + "logContext" : { + "generationId" : "hardcoded-uuid" + }, + "publisherName" : "example.com", + "seller" : "https://sellerorigin.com" +})JSON"; + constexpr char kSampleComponentGetBidRequest[] = R"JSON({ "auctionSignals" : "{\"maxFloorCpmUsdMicros\":\"6250\"}", "buyerInput" : { @@ -98,6 +127,35 @@ constexpr char kSampleComponentGetBidRequest[] = R"JSON({ "top_level_seller": "https://top-level-seller.com" })JSON"; +constexpr char kSampleComponentGetBidRequestUsingBuyerInputForBidding[] = + R"JSON({ + "auctionSignals" : "{\"maxFloorCpmUsdMicros\":\"6250\"}", + "buyerInputForBidding" : { + "interestGroups" : [ + { + "biddingSignalsKeys" : [ + "1j473877681" + ], + "browserSignals" : { + "bidCount" : "17", + "joinCount" : "3", + "prevWins" : "[]", + "prevWinsMs" : "[]" + }, + "name" : "nike shoe", + "userBiddingSignals" : "[[]]" + } + ] + }, + "buyerSignals" : "[[[[\"Na6y7V9RU0A\",[-0.006591797,0.052001953]]]]]", + "logContext" : { + "generationId" : "hardcoded-uuid" + }, + "publisherName" : "example.com", + "seller" : "https://sellerorigin.com", + "top_level_seller": "https://top-level-seller.com" +})JSON"; + void SetUpOptionFlags(int port) { absl::SetFlag(&FLAGS_host_addr, absl::StrFormat("localhost:%d", port)); absl::SetFlag(&FLAGS_client_ip, kClientIp); @@ -167,10 +225,16 @@ class SecureInvokeLib : public testing::Test { }; TEST_F(SecureInvokeLib, RequestToBfeNeedsClientIp) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); absl::SetFlag(&FLAGS_host_addr, @@ -180,10 +244,16 @@ TEST_F(SecureInvokeLib, RequestToBfeNeedsClientIp) { } TEST_F(SecureInvokeLib, RequestToBfeNeedsServerAddress) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); EXPECT_DEATH(auto unused = SendRequestToBfe(default_keyset_, false), @@ -191,10 +261,16 @@ TEST_F(SecureInvokeLib, RequestToBfeNeedsServerAddress) { } TEST_F(SecureInvokeLib, RequestToBfeNeedsUserAgent) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); absl::SetFlag(&FLAGS_host_addr, @@ -207,10 +283,16 @@ TEST_F(SecureInvokeLib, RequestToBfeNeedsUserAgent) { } TEST_F(SecureInvokeLib, RequestToBfeNeedsAcceptLanguage) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); absl::SetFlag(&FLAGS_host_addr, @@ -223,10 +305,16 @@ TEST_F(SecureInvokeLib, RequestToBfeNeedsAcceptLanguage) { } TEST_F(SecureInvokeLib, RequestToBfeReturnsAResponse) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); std::unique_ptr stub = @@ -241,11 +329,44 @@ TEST_F(SecureInvokeLib, RequestToBfeReturnsAResponse) { EXPECT_TRUE(status.ok()) << status; } +TEST_F(SecureInvokeLib, + RequestToBfeReturnsAResponse_UsingBuyerInputForBidding) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); + BuyerFrontEndService buyer_front_end_service{ + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; + + auto start_service_result = StartLocalService(&buyer_front_end_service); + std::unique_ptr stub = + CreateServiceStub(start_service_result.port); + SetUpOptionFlags(start_service_result.port); + absl::SetFlag(&FLAGS_json_input_str, + kSampleGetBidRequestUsingBuyerInputForBidding); + + // Verifies that the encrypted request makes it to BFE and the response comes + // back. Empty response is expected because trusted bidding signals for IG + // are empty. + auto status = SendRequestToBfe(default_keyset_, false, std::move(stub)); + EXPECT_TRUE(status.ok()) << status; +} + TEST_F(SecureInvokeLib, RequestToBfeReturnsAResponseWithDebugReportingEnabled) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); bool enable_debug_reporting = true; @@ -263,10 +384,16 @@ TEST_F(SecureInvokeLib, RequestToBfeReturnsAResponseWithDebugReportingEnabled) { } TEST_F(SecureInvokeLib, IncludesTopLevelSellerInBfeInput) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); bool enable_debug_reporting = true; @@ -283,11 +410,46 @@ TEST_F(SecureInvokeLib, IncludesTopLevelSellerInBfeInput) { EXPECT_TRUE(status.ok()) << status; } +TEST_F(SecureInvokeLib, + IncludesTopLevelSellerInBfeInput_UsingBuyerInputForBidding) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); + BuyerFrontEndService buyer_front_end_service{ + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; + + auto start_service_result = StartLocalService(&buyer_front_end_service); + bool enable_debug_reporting = true; + std::unique_ptr stub = + CreateServiceStub(start_service_result.port); + SetUpOptionFlags(start_service_result.port); + absl::SetFlag(&FLAGS_json_input_str, + kSampleComponentGetBidRequestUsingBuyerInputForBidding); + + // Verifies that the encrypted request makes it to BFE and the response comes + // back. Empty response is expected because trusted bidding signals for IG + // are empty. + auto status = SendRequestToBfe(default_keyset_, enable_debug_reporting, + std::move(stub)); + EXPECT_TRUE(status.ok()) << status; +} + TEST_F(SecureInvokeLib, RequestToBfeNeedsValidKey) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); std::unique_ptr stub = @@ -306,10 +468,16 @@ TEST_F(SecureInvokeLib, RequestToBfeNeedsValidKey) { } TEST_F(SecureInvokeLib, UsesKeyForBfeEncryption) { + auto executor = server_common::EventEngineExecutor( + grpc_event_engine::experimental::GetDefaultEventEngine()); BuyerFrontEndService buyer_front_end_service{ - std::move(bidding_signals_provider_), bidding_service_client_config_, - std::move(key_fetcher_manager_), std::move(crypto_client_), - std::move(kv_async_client_), get_bids_config_}; + std::move(bidding_signals_provider_), + bidding_service_client_config_, + std::move(key_fetcher_manager_), + std::move(crypto_client_), + std::move(kv_async_client_), + get_bids_config_, + executor}; auto start_service_result = StartLocalService(&buyer_front_end_service); std::unique_ptr stub = diff --git a/version.txt b/version.txt index 28446a5e..1163055e 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -4.6.0 \ No newline at end of file +4.7.0 \ No newline at end of file