Skip to content

Commit

Permalink
Refactor privacy_math to do randomized response
Browse files Browse the repository at this point in the history
This CL does a few things:
1. Moves most randomized response logic out of the storage delegate
2. Moves RandomizedResponseData into privacy_math
3. Computes channel capacity at the same time as doing randomized
   response. This causes a bit of extra work in the case where channel
   capacity is exceeded, but it simplifies the interface and may end up
   being needed down the line anyway in case we stop unconditionally
   failing when channel capacity is exceeded.
4. Augments the k-combination helper function to work at extreme values
   which required some variables to migrate `int` to `int64_t`.

Note that the storage delegate still needs to know how to compute the
randomized response rate, as it is needed for computing on-the-fly
`randomized_trigger_rate` when that information is not stored in the db.
This path can likely be removed when we are confident all sources in the
db properly store this.

A follow-up CL will perform randomized response on inputs configured
using TriggerSpecs.

Change-Id: I865cf5a14e5211af45b043a62b5f3d94e31b0380
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4982908
Commit-Queue: Charlie Harrison <csharrison@chromium.org>
Reviewed-by: Andrew Paseltiner <apaseltiner@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1216415}
  • Loading branch information
csharrison authored and Chromium LUCI CQ committed Oct 27, 2023
1 parent 65c207d commit eb8169b
Show file tree
Hide file tree
Showing 10 changed files with 437 additions and 471 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,49 +4,19 @@

#include "content/browser/attribution_reporting/attribution_storage_delegate.h"

#include <utility>
#include <vector>

#include "base/check.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "components/attribution_reporting/source_type.mojom.h"
#include "content/browser/attribution_reporting/attribution_config.h"
#include "content/browser/attribution_reporting/attribution_reporting.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace content {

namespace {
using ::attribution_reporting::mojom::SourceType;

using RandomizedResponse =
::content::AttributionStorageDelegate::RandomizedResponse;
using RandomizedResponseData =
::content::AttributionStorageDelegate::RandomizedResponseData;
} // namespace

RandomizedResponseData::RandomizedResponseData(double rate,
RandomizedResponse response)
: rate_(rate), response_(std::move(response)) {
DCHECK_GE(rate_, 0);
DCHECK_LE(rate_, 1);
}

RandomizedResponseData::~RandomizedResponseData() = default;

RandomizedResponseData::RandomizedResponseData(const RandomizedResponseData&) =
default;

RandomizedResponseData& RandomizedResponseData::operator=(
const RandomizedResponseData&) = default;

RandomizedResponseData::RandomizedResponseData(RandomizedResponseData&&) =
default;

RandomizedResponseData& RandomizedResponseData::operator=(
RandomizedResponseData&&) = default;

AttributionStorageDelegate::AttributionStorageDelegate(
const AttributionConfig& config)
: config_(config) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
#include "content/browser/attribution_reporting/attribution_reporting.mojom-forward.h"
#include "content/browser/attribution_reporting/privacy_math.h"
#include "content/common/content_export.h"
#include "third_party/abseil-cpp/absl/types/optional.h"

namespace attribution_reporting {
class EventReportWindows;
Expand All @@ -36,7 +35,6 @@ namespace content {

class AttributionReport;
class AttributionTrigger;
class StoredSource;

// Storage delegate that can supplied to extend basic attribution storage
// functionality like annotating reports. Users and subclasses must NOT assume
Expand All @@ -50,34 +48,6 @@ class CONTENT_EXPORT AttributionStorageDelegate {
base::TimeDelta max;
};


// Corresponds to `StoredSource::AttributionLogic` as follows:
// `absl::nullopt` -> `StoredSource::AttributionLogic::kTruthfully`
// empty vector -> `StoredSource::AttributionLogic::kNever`
// non-empty vector -> `StoredSource::AttributionLogic::kFalsely`
using RandomizedResponse = absl::optional<std::vector<FakeEventLevelReport>>;

class CONTENT_EXPORT RandomizedResponseData {
public:
RandomizedResponseData(double rate, RandomizedResponse);

~RandomizedResponseData();

RandomizedResponseData(const RandomizedResponseData&);
RandomizedResponseData& operator=(const RandomizedResponseData&);

RandomizedResponseData(RandomizedResponseData&&);
RandomizedResponseData& operator=(RandomizedResponseData&&);

double rate() const { return rate_; }

const RandomizedResponse& response() const { return response_; }

private:
double rate_;
RandomizedResponse response_;
};

struct ExceedsChannelCapacityLimit {};

struct NullAggregatableReport {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include <utility>
#include <vector>

#include "base/check_op.h"
#include "base/feature_list.h"
#include "base/memory/ptr_util.h"
#include "base/rand_util.h"
Expand Down Expand Up @@ -41,15 +40,6 @@ namespace {
using ::attribution_reporting::EventReportWindows;
using ::attribution_reporting::mojom::SourceType;

// The max possible number of state combinations given a valid input.
constexpr int64_t kMaxNumCombinations = 4191844505805495;

bool GenerateWithRate(double r) {
DCHECK_GE(r, 0);
DCHECK_LE(r, 1);
return base::RandDouble() < r;
}

std::vector<AttributionStorageDelegate::NullAggregatableReport>
GetNullAggregatableReportsForLookback(
const AttributionTrigger& trigger,
Expand Down Expand Up @@ -210,122 +200,35 @@ double AttributionStorageDelegateImpl::GetRandomizedResponseRate(
int max_event_level_reports) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return content::GetRandomizedResponseRate(
GetNumStates(source_type, event_report_windows, max_event_level_reports),
GetNumStates(
attribution_reporting::DefaultTriggerDataCardinality(source_type),
event_report_windows, max_event_level_reports),
config_.event_level_limit.randomized_response_epsilon);
}

int64_t AttributionStorageDelegateImpl::GetNumStates(
SourceType source_type,
const EventReportWindows& event_report_windows,
int max_event_level_reports) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return GetNumberOfStarsAndBarsSequences(
/*num_stars=*/max_event_level_reports,
/*num_bars=*/attribution_reporting::DefaultTriggerDataCardinality(
source_type) *
event_report_windows.end_times().size());
}

AttributionStorageDelegate::GetRandomizedResponseResult
AttributionStorageDelegateImpl::GetRandomizedResponse(
SourceType source_type,
const EventReportWindows& event_report_windows,
int max_event_level_reports,
base::Time source_time) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
RandomizedResponseData response = DoRandomizedResponse(
attribution_reporting::DefaultTriggerDataCardinality(source_type),
event_report_windows, max_event_level_reports,
config_.event_level_limit.randomized_response_epsilon);

const int64_t num_states =
GetNumStates(source_type, event_report_windows, max_event_level_reports);

const double rate = content::GetRandomizedResponseRate(
num_states, config_.event_level_limit.randomized_response_epsilon);

const double capacity = ComputeChannelCapacity(num_states, rate);

if (capacity > GetMaxChannelCapacity(source_type)) {
if (response.channel_capacity() > GetMaxChannelCapacity(source_type)) {
return base::unexpected(ExceedsChannelCapacityLimit());
}

switch (noise_mode_) {
case AttributionNoiseMode::kDefault: {
return RandomizedResponseData(
rate, GenerateWithRate(rate)
? absl::make_optional(GetRandomFakeReports(
source_type, event_report_windows,
max_event_level_reports, source_time, num_states))
: absl::nullopt);
}
case AttributionNoiseMode::kDefault:
return response;
case AttributionNoiseMode::kNone:
return RandomizedResponseData(rate, absl::nullopt);
}
}

std::vector<FakeEventLevelReport>
AttributionStorageDelegateImpl::GetRandomFakeReports(
SourceType source_type,
const EventReportWindows& event_report_windows,
int max_event_level_reports,
base::Time source_time,
int64_t num_states) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(noise_mode_, AttributionNoiseMode::kDefault);

DCHECK_EQ(num_states, GetNumStates(source_type, event_report_windows,
max_event_level_reports));

const int64_t sequence_index =
static_cast<int64_t>(base::RandGenerator(num_states));
DCHECK_GE(sequence_index, 0);
DCHECK_LE(sequence_index, kMaxNumCombinations);

return GetFakeReportsForSequenceIndex(source_type, event_report_windows,
max_event_level_reports, source_time,
sequence_index);
}

std::vector<FakeEventLevelReport>
AttributionStorageDelegateImpl::GetFakeReportsForSequenceIndex(
SourceType source_type,
const EventReportWindows& event_report_windows,
int max_event_level_reports,
base::Time source_time,
int64_t random_stars_and_bars_sequence_index) const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK_EQ(noise_mode_, AttributionNoiseMode::kDefault);

const int trigger_data_cardinality =
attribution_reporting::DefaultTriggerDataCardinality(source_type);

const std::vector<int> bars_preceding_each_star =
GetBarsPrecedingEachStar(GetStarIndices(
/*num_stars=*/max_event_level_reports,
/*num_bars=*/trigger_data_cardinality *
event_report_windows.end_times().size(),
/*sequence_index=*/random_stars_and_bars_sequence_index));

std::vector<FakeEventLevelReport> fake_reports;

// an output state is uniquely determined by an ordering of c stars and w*d
// bars, where:
// w = the number of reporting windows
// c = the maximum number of reports for a source
// d = the trigger data cardinality for a source
for (int num_bars : bars_preceding_each_star) {
if (num_bars == 0) {
continue;
}

auto result = std::div(num_bars - 1, trigger_data_cardinality);

const int trigger_data = result.rem;
DCHECK_GE(trigger_data, 0);
DCHECK_LT(trigger_data, trigger_data_cardinality);

fake_reports.push_back({.trigger_data = static_cast<uint64_t>(trigger_data),
.window_index = result.quot});
return RandomizedResponseData(response.rate(),
response.channel_capacity(), absl::nullopt);
}
DCHECK_LE(fake_reports.size(), static_cast<size_t>(max_event_level_reports));
return fake_reports;
}

std::vector<AttributionStorageDelegate::NullAggregatableReport>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class TriggerVerification;
namespace content {

struct AttributionConfig;
struct FakeEventLevelReport;

enum class AttributionNoiseMode {
// Various aspects of the API are subject to noise:
Expand Down Expand Up @@ -104,38 +103,6 @@ class CONTENT_EXPORT AttributionStorageDelegateImpl
base::Time trigger_time,
absl::optional<base::Time> attributed_source_time) const override;

// Exposed for testing.
int64_t GetNumStates(attribution_reporting::mojom::SourceType,
const attribution_reporting::EventReportWindows&,
int max_event_level_reports) const;

// Generates fake reports using a random "stars and bars" sequence index of a
// possible output of the API.
//
// Exposed for testing.
std::vector<FakeEventLevelReport> GetRandomFakeReports(
attribution_reporting::mojom::SourceType,
const attribution_reporting::EventReportWindows&,
int max_event_level_reports,
base::Time source_time,
int64_t num_states) const;

// Generates fake reports from the "stars and bars" sequence index of a
// possible output of the API. This output is determined by the following
// algorithm:
// 1. Find all stars before the first bar. These stars represent suppressed
// reports.
// 2. For all other stars, count the number of bars that precede them. Each
// star represents a report where the reporting window and trigger data is
// uniquely determined by that number.
//
// Exposed for testing.
std::vector<FakeEventLevelReport> GetFakeReportsForSequenceIndex(
attribution_reporting::mojom::SourceType,
const attribution_reporting::EventReportWindows&,
int max_event_level_reports,
base::Time source_time,
int64_t random_stars_and_bars_sequence_index) const;

private:
AttributionStorageDelegateImpl(AttributionNoiseMode noise_mode,
Expand Down

0 comments on commit eb8169b

Please sign in to comment.