Skip to content

Commit

Permalink
FLEDGE: Annotate bids with checkable currencies.
Browse files Browse the repository at this point in the history
This let auction configuration state which currencies they expect each buyer to use, and buyers to state which ones they are using, so if they disagree on what currencies they use they get an error rather than a financial disagreement.

For transition reasons, the checking only happens if both specify currencies.

This CL does not cover the reporting phase, which will be in follow up CL, along with requested features involving how seller currency conversion interacts with reporting, due to size.

Context for this work is WICG/turtledove#166

Change-Id: I366f9021598bc2dd35b21abec809464c115f257d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4354633
Reviewed-by: Daniel Cheng <[email protected]>
Commit-Queue: Maks Orlovich <[email protected]>
Reviewed-by: Russ Hamilton <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1125366}
  • Loading branch information
Maks Orlovich authored and Chromium LUCI CQ committed Apr 3, 2023
1 parent f04e1bb commit cddfc06
Show file tree
Hide file tree
Showing 41 changed files with 1,698 additions and 293 deletions.
27 changes: 27 additions & 0 deletions content/browser/interest_group/auction_runner.cc
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,33 @@ void AuctionRunner::ResolvedBuyerTimeoutsPromise(
NotifyPromiseResolved(auction_id.get(), config);
}

void AuctionRunner::ResolvedBuyerCurrenciesPromise(
blink::mojom::AuctionAdConfigAuctionIdPtr auction_id,
const blink::AuctionConfig::BuyerCurrencies& buyer_currencies) {
if (state_ == State::kFailed) {
return;
}

blink::AuctionConfig* config =
LookupAuction(*owned_auction_config_, auction_id);
if (!config) {
mojo::ReportBadMessage(
"Invalid auction ID in ResolvedBuyerCurrenciesPromise");
return;
}

if (!config->non_shared_params.buyer_currencies.is_promise()) {
mojo::ReportBadMessage(
"ResolvedBuyerCurrenciesPromise updating non-promise");
return;
}

config->non_shared_params.buyer_currencies =
blink::AuctionConfig::MaybePromiseBuyerCurrencies::FromValue(
buyer_currencies);
NotifyPromiseResolved(auction_id.get(), config);
}

void AuctionRunner::ResolvedDirectFromSellerSignalsPromise(
blink::mojom::AuctionAdConfigAuctionIdPtr auction_id,
const absl::optional<blink::DirectFromSellerSignals>&
Expand Down
3 changes: 3 additions & 0 deletions content/browser/interest_group/auction_runner.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ class CONTENT_EXPORT AuctionRunner : public blink::mojom::AbortableAdAuction {
blink::mojom::AuctionAdConfigAuctionIdPtr auction_id,
blink::mojom::AuctionAdConfigBuyerTimeoutField field,
const blink::AuctionConfig::BuyerTimeouts& buyer_timeouts) override;
void ResolvedBuyerCurrenciesPromise(
blink::mojom::AuctionAdConfigAuctionIdPtr auction_id,
const blink::AuctionConfig::BuyerCurrencies& buyer_currencies) override;
void ResolvedDirectFromSellerSignalsPromise(
blink::mojom::AuctionAdConfigAuctionIdPtr auction_id,
const absl::optional<blink::DirectFromSellerSignals>&
Expand Down
419 changes: 336 additions & 83 deletions content/browser/interest_group/auction_runner_unittest.cc

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -323,12 +323,14 @@ class MockSellerWorklet : public auction_worklet::mojom::SellerWorklet {

void ScoreAd(const std::string& ad_metadata_json,
double bid,
const std::string& bid_currency,
const blink::AuctionConfig::NonSharedParams&
auction_ad_config_non_shared_params,
const absl::optional<GURL>& direct_from_seller_seller_signals,
const absl::optional<GURL>& direct_from_seller_auction_signals,
auction_worklet::mojom::ComponentAuctionOtherSellerPtr
browser_signals_other_seller,
const absl::optional<std::string>& component_expect_bid_currency,
const url::Origin& browser_signal_interest_group_owner,
const GURL& browser_signal_render_url,
const std::vector<GURL>& browser_signal_ad_components,
Expand Down
82 changes: 69 additions & 13 deletions content/browser/interest_group/interest_group_auction.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
#include "services/network/public/mojom/url_loader_factory.mojom-forward.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/interest_group/ad_auction_constants.h"
#include "third_party/blink/public/common/interest_group/ad_auction_currencies.h"
#include "third_party/blink/public/common/interest_group/ad_display_size_utils.h"
#include "third_party/blink/public/common/interest_group/auction_config.h"
#include "third_party/blink/public/common/interest_group/interest_group.h"
Expand Down Expand Up @@ -338,6 +339,27 @@ absl::optional<base::TimeDelta> PerBuyerCumulativeTimeout(
buyer, auction_config.non_shared_params.buyer_cumulative_timeouts);
}

std::string PerBuyerCurrency(const url::Origin& buyer,
const blink::AuctionConfig& auction_config) {
const blink::AuctionConfig::MaybePromiseBuyerCurrencies& buyer_currencies =
auction_config.non_shared_params.buyer_currencies;
DCHECK(!buyer_currencies.is_promise());
const auto& per_buyer_currencies =
buyer_currencies.value().per_buyer_currencies;
if (per_buyer_currencies.has_value()) {
auto it = per_buyer_currencies->find(buyer);
if (it != per_buyer_currencies->end()) {
return it->second;
}
}
const auto& all_buyers_currency =
buyer_currencies.value().all_buyers_currency;
if (all_buyers_currency.has_value()) {
return all_buyers_currency.value();
}
return blink::kUnspecifiedAdCurrency;
}

} // namespace

InterestGroupAuction::BidState::BidState() = default;
Expand Down Expand Up @@ -398,6 +420,7 @@ InterestGroupAuction::Bid::Bid(
BidRole bid_role,
std::string ad_metadata,
double bid,
std::string bid_currency,
absl::optional<double> ad_cost,
blink::AdDescriptor ad_descriptor,
std::vector<blink::AdDescriptor> ad_component_descriptors,
Expand All @@ -410,6 +433,7 @@ InterestGroupAuction::Bid::Bid(
: bid_role(bid_role),
ad_metadata(std::move(ad_metadata)),
bid(bid),
bid_currency(std::move(bid_currency)),
ad_cost(std::move(ad_cost)),
ad_descriptor(std::move(ad_descriptor)),
ad_component_descriptors(std::move(ad_component_descriptors)),
Expand Down Expand Up @@ -1000,6 +1024,7 @@ class InterestGroupAuction::BuyerHelper
GetPerBuyerSignals(*auction_->config_,
bid_state->bidder->interest_group.owner),
PerBuyerTimeout(owner_, *auction_->config_),
PerBuyerCurrency(owner_, *auction_->config_),
GetDirectFromSellerPerBuyerSignals(
url_builder, bid_state->bidder->interest_group.owner),
GetDirectFromSellerAuctionSignals(url_builder));
Expand Down Expand Up @@ -1382,6 +1407,15 @@ class InterestGroupAuction::BuyerHelper
return nullptr;
}

if (!blink::IsValidOrUnspecifiedAdCurrencyCode(mojo_bid->bid_currency) ||
!blink::VerifyAdCurrencyCode(
PerBuyerCurrency(owner_, *auction_->config_),
mojo_bid->bid_currency)) {
generate_bid_client_receiver_set_.ReportBadMessage(
"Invalid bid currency");
return nullptr;
}

const blink::InterestGroup& interest_group =
bid_state.bidder->interest_group;
const blink::InterestGroup::Ad* matching_ad = FindMatchingAd(
Expand Down Expand Up @@ -1441,7 +1475,8 @@ class InterestGroupAuction::BuyerHelper
}

return std::make_unique<Bid>(
bid_role, std::move(mojo_bid->ad), mojo_bid->bid, mojo_bid->ad_cost,
bid_role, std::move(mojo_bid->ad), mojo_bid->bid,
std::move(mojo_bid->bid_currency), mojo_bid->ad_cost,
std::move(mojo_bid->ad_descriptor), std::move(ad_component_descriptors),
std::move(mojo_bid->modeling_signals), mojo_bid->bid_duration,
bidding_signals_data_version, matching_ad, &bid_state, auction_);
Expand Down Expand Up @@ -2727,6 +2762,8 @@ InterestGroupAuction::CreateBidFromComponentAuctionWinner(
bid_role, modified_bid_params->ad,
modified_bid_params->has_bid ? modified_bid_params->bid
: component_bid->bid,
modified_bid_params->has_bid ? modified_bid_params->bid_currency
: std::string(),
component_bid->ad_cost, component_bid->ad_descriptor,
component_bid->ad_component_descriptors, component_bid->modeling_signals,
component_bid->bid_duration, component_bid->bidding_signals_data_version,
Expand Down Expand Up @@ -2778,13 +2815,16 @@ void InterestGroupAuction::ScoreBidIfReady(std::unique_ptr<Bid> bid) {
DCHECK(url_builder); // Should be ready by now.
seller_worklet_handle_->AuthorizeSubresourceUrls(*url_builder);
seller_worklet_handle_->GetSellerWorklet()->ScoreAd(
bid_raw->ad_metadata, bid_raw->bid, config_->non_shared_params,
GetDirectFromSellerSellerSignals(url_builder),
bid_raw->ad_metadata, bid_raw->bid, bid_raw->bid_currency,
config_->non_shared_params, GetDirectFromSellerSellerSignals(url_builder),
GetDirectFromSellerAuctionSignals(url_builder),
GetOtherSellerParam(*bid_raw), bid_raw->interest_group->owner,
bid_raw->ad_descriptor.url, bid_raw->GetAdComponentUrls(),
bid_raw->bid_duration.InMilliseconds(), SellerTimeout(), bid_trace_id,
std::move(score_ad_remote));
GetOtherSellerParam(*bid_raw),
parent_ ? absl::make_optional<std::string>(
PerBuyerCurrency(config_->seller, *parent_->config_))
: absl::nullopt,
bid_raw->interest_group->owner, bid_raw->ad_descriptor.url,
bid_raw->GetAdComponentUrls(), bid_raw->bid_duration.InMilliseconds(),
SellerTimeout(), bid_trace_id, std::move(score_ad_remote));
}

bool InterestGroupAuction::ValidateScoreBidCompleteResult(
Expand Down Expand Up @@ -2820,13 +2860,29 @@ bool InterestGroupAuction::ValidateScoreBidCompleteResult(
"Invalid component_auction_modified_bid_params");
return false;
}
// If a component seller modified the bid, the new bid must also be valid.
// If a component seller modified the bid, the new bid must also be valid,
// as should its currency.
if (component_auction_modified_bid_params &&
component_auction_modified_bid_params->has_bid &&
!IsValidBid(component_auction_modified_bid_params->bid)) {
score_ad_receivers_.ReportBadMessage(
"Invalid component_auction_modified_bid_params bid");
return false;
component_auction_modified_bid_params->has_bid) {
if (!IsValidBid(component_auction_modified_bid_params->bid)) {
score_ad_receivers_.ReportBadMessage(
"Invalid component_auction_modified_bid_params bid");
return false;
}

if (!blink::IsValidOrUnspecifiedAdCurrencyCode(
component_auction_modified_bid_params->bid_currency) ||
(config_->non_shared_params.seller_currency &&
!blink::VerifyAdCurrencyCode(
config_->non_shared_params.seller_currency.value(),
component_auction_modified_bid_params->bid_currency)) ||
!blink::VerifyAdCurrencyCode(
PerBuyerCurrency(config_->seller, *parent_->config_),
component_auction_modified_bid_params->bid_currency)) {
score_ad_receivers_.ReportBadMessage(
"Invalid component_auction_modified_bid_params bid_currency");
return false;
}
}
}
return true;
Expand Down
2 changes: 2 additions & 0 deletions content/browser/interest_group/interest_group_auction.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ class CONTENT_EXPORT InterestGroupAuction
Bid(BidRole bid_role,
std::string ad_metadata,
double bid,
std::string bid_currency,
absl::optional<double> ad_cost,
blink::AdDescriptor ad_descriptor,
std::vector<blink::AdDescriptor> ad_component_descriptors,
Expand Down Expand Up @@ -292,6 +293,7 @@ class CONTENT_EXPORT InterestGroupAuction
// auction_worklet::mojom::BidderWorkletBid.
const std::string ad_metadata;
const double bid;
const std::string bid_currency;
const absl::optional<double> ad_cost;
const blink::AdDescriptor ad_descriptor;
const std::vector<blink::AdDescriptor> ad_component_descriptors;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ class InterestGroupAuctionReporterTest
CreateSellerWinningBidInfo(&component_auction_config);
component_seller_winning_bid_info_->component_auction_modified_bid_params =
auction_worklet::mojom::ComponentAuctionModifiedBidParams::New(
/*ad=*/"null", /*bid=*/0, /*has_bid=*/false);
/*ad=*/"null", /*bid=*/0, /*bid_currency=*/"", /*has_bid=*/false);
}

void SetUpReporterAndStart() {
Expand Down
65 changes: 61 additions & 4 deletions content/browser/interest_group/interest_group_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4170,8 +4170,7 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
"perBuyerCumulativeTimeouts buyer 'https://invalid^&' for "
"AuctionAdConfig "
"with seller 'https://test.com' must be \"*\" (wildcard) or a valid "
"https "
"origin.",
"https origin.",
RunAuctionAndWait(R"({
seller: 'https://test.com',
decisionLogicUrl: 'https://test.com',
Expand All @@ -4180,6 +4179,59 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
WaitForAccessObserved({});
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
RunAdAuctionInvalidPerBuyerCurrenciesOrigin) {
ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
AttachInterestGroupObserver();

EXPECT_EQ(
"TypeError: Failed to execute 'runAdAuction' on 'Navigator': "
"perBuyerCurrencies buyer 'https://invalid^&' for "
"AuctionAdConfig "
"with seller 'https://test.com' must be \"*\" (wildcard) or a valid "
"https origin.",
RunAuctionAndWait(R"({
seller: 'https://test.com',
decisionLogicUrl: 'https://test.com',
perBuyerCurrencies: {'https://invalid^&': 'USD'}
})"));
WaitForAccessObserved({});
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
RunAdAuctionInvalidPerBuyerCurrenciesCurrency) {
ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
AttachInterestGroupObserver();

EXPECT_EQ(
"TypeError: Failed to execute 'runAdAuction' on 'Navigator':"
" perBuyerCurrencies currency 'usd' for AuctionAdConfig with seller"
" 'https://test.com' must be a 3-letter uppercase currency code.",
RunAuctionAndWait(R"({
seller: 'https://test.com',
decisionLogicUrl: 'https://test.com',
perBuyerCurrencies: {'*': 'usd'}
})"));
WaitForAccessObserved({});
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
RunAdAuctionInvalidSellerCurrency) {
ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
AttachInterestGroupObserver();

EXPECT_EQ(
"TypeError: Failed to execute 'runAdAuction' on 'Navigator':"
" sellerCurrency 'usd' for AuctionAdConfig with seller"
" 'https://test.com' must be a 3-letter uppercase currency code.",
RunAuctionAndWait(R"({
seller: 'https://test.com',
decisionLogicUrl: 'https://test.com',
sellerCurrency: 'usd'
})"));
WaitForAccessObserved({});
}

IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
RunAdAuctionInvalidPerBuyerGroupLimitsValue) {
ASSERT_TRUE(NavigateToURL(shell(), https_server_->GetURL("a.test", "/echo")));
Expand Down Expand Up @@ -8641,7 +8693,8 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest, ValidateWorkletParameters) {
perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}},
perBuyerTimeouts: {$4: 110, $5: 120, '*': 150},
perBuyerCumulativeTimeouts: {$4: 13000, $5: 14000, '*': 16000},
perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}}
perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}},
perBuyerCurrencies: {$4: 'USD', $5: 'CAD', '*': 'EUR'}
});
})())",
seller_origin, seller_script_url,
Expand Down Expand Up @@ -8771,7 +8824,8 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
perBuyerSignals: {$4: {signalsForBuyer: 1}, $5: {signalsForBuyer: 2}},
perBuyerTimeouts: {$4: 110, $5: 120, '*': 150},
perBuyerCumulativeTimeouts: {$4: 13000, $5: 14000, '*': 16000},
perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}}
perBuyerPrioritySignals: {$4: {foo: 1}, '*': {BaR: -2}},
perBuyerCurrencies: {$4: 'USD', $5: 'CAD', '*': 'EUR'}
});
})())",
seller_origin, seller_script_url,
Expand Down Expand Up @@ -8949,6 +9003,7 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
perBuyerTimeouts: maybePromise({$8: 110, '*': 150}),
perBuyerCumulativeTimeouts: maybePromise({$8: 11100, '*': 15100}),
perBuyerPrioritySignals: {'*': {foo: 3}},
perBuyerCurrencies: {'*': 'MXN', $5: 'CAD'},
componentAuctions: [{
seller: $5,
decisionLogicUrl: $6,
Expand All @@ -8962,6 +9017,8 @@ IN_PROC_BROWSER_TEST_F(InterestGroupBrowserTest,
perBuyerTimeouts: maybePromise({$8: 200}),
perBuyerCumulativeTimeouts: maybePromise({$8: 20100}),
perBuyerPrioritySignals: {$8: {bar: 1}, '*': {BaZ: -2}},
perBuyerCurrencies: maybePromise({$8: 'USD'}),
sellerCurrency: 'CAD',
}],
});
})())",
Expand Down
10 changes: 7 additions & 3 deletions content/browser/interest_group/mock_auction_process_manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ void MockBidderWorklet::FinishGenerateBid(
const absl::optional<std::string>& auction_signals_json,
const absl::optional<std::string>& per_buyer_signals_json,
const absl::optional<base::TimeDelta> per_buyer_timeout,
const std::string& per_buyer_currency,
const absl::optional<GURL>& direct_from_seller_per_buyer_signals,
const absl::optional<GURL>& direct_from_seller_auction_signals) {
// per_buyer_timeout passed to GenerateBid() should not be empty, because
Expand Down Expand Up @@ -190,6 +191,7 @@ void MockBidderWorklet::SetBiddingLatency(base::TimeDelta delta) {

void MockBidderWorklet::InvokeGenerateBidCallback(
absl::optional<double> bid,
const std::string& bid_currency,
const blink::AdDescriptor& ad_descriptor,
auction_worklet::mojom::BidderWorkletKAnonEnforcedBidPtr mojo_kanon_bid,
absl::optional<std::vector<blink::AdDescriptor>> ad_component_descriptors,
Expand Down Expand Up @@ -228,9 +230,9 @@ void MockBidderWorklet::InvokeGenerateBidCallback(

generate_bid_client_->OnGenerateBidComplete(
auction_worklet::mojom::BidderWorkletBid::New(
"ad", *bid, /*ad_cost=*/absl::nullopt, std::move(ad_descriptor),
ad_component_descriptors, /*modeling_signals=*/absl::nullopt,
duration),
"ad", *bid, bid_currency, /*ad_cost=*/absl::nullopt,
std::move(ad_descriptor), ad_component_descriptors,
/*modeling_signals=*/absl::nullopt, duration),
/*kanon_bid=*/std::move(mojo_kanon_bid),
bidding_signals_data_version.value_or(0),
bidding_signals_data_version.has_value(), debug_loss_report_url,
Expand Down Expand Up @@ -308,12 +310,14 @@ MockSellerWorklet::~MockSellerWorklet() {
void MockSellerWorklet::ScoreAd(
const std::string& ad_metadata_json,
double bid,
const std::string& bid_currency,
const blink::AuctionConfig::NonSharedParams&
auction_ad_config_non_shared_params,
const absl::optional<GURL>& direct_from_seller_seller_signals,
const absl::optional<GURL>& direct_from_seller_auction_signals,
auction_worklet::mojom::ComponentAuctionOtherSellerPtr
browser_signals_other_seller,
const absl::optional<std::string>& component_expect_bid_currency,
const url::Origin& browser_signal_interest_group_owner,
const GURL& browser_signal_render_url,
const std::vector<GURL>& browser_signal_ad_components,
Expand Down

0 comments on commit cddfc06

Please sign in to comment.