-
Notifications
You must be signed in to change notification settings - Fork 173
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Port flexible_event_privacy.py to TypeScript (#917)
This will allow us to incorporate this information into the header validator. Co-authored-by: Charlie Harrison <[email protected]>
- Loading branch information
1 parent
e99fb81
commit bd0512d
Showing
8 changed files
with
728 additions
and
234 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
dist | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
Setup: | ||
|
||
```sh | ||
npm install && npm run tsc | ||
``` | ||
|
||
Examples: | ||
|
||
```sh | ||
npm run main -- -w 1,2,3 -b 4,5,6 | ||
``` | ||
|
||
```sh | ||
npm run main -- -f /path/to/source_registration.json | ||
``` |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
const commandLineArgs = require('command-line-args') | ||
const fs = require('fs') | ||
|
||
import {Config, DefaultConfig, PerTriggerDataConfig, SourceType} from './privacy' | ||
|
||
function commaSeparatedInts(str: string): number[] { | ||
return str.split(',').map(v => Number(v)) | ||
} | ||
|
||
function parseSourceType(str: string): SourceType { | ||
if (!(str in DefaultConfig)) { | ||
throw 'unknown source type' | ||
} | ||
return str as SourceType | ||
} | ||
|
||
function getNumWindowsFromJson(defaultVal: number, windows?: object): number { | ||
if (typeof windows === 'undefined') { | ||
return defaultVal | ||
} | ||
|
||
if (typeof windows !== 'object') { | ||
throw 'event_report_windows must be an object' | ||
} | ||
|
||
const endTimes = windows['end_times'] | ||
if (!Array.isArray(endTimes)) { | ||
throw 'end_times must be an array' | ||
} | ||
|
||
return endTimes.length | ||
} | ||
|
||
function getConfig(json: object, sourceType: SourceType): Config { | ||
const defaultMaxReports = DefaultConfig[sourceType].maxEventLevelReports | ||
const defaultWindows = defaultMaxReports | ||
|
||
let maxEventLevelReports = json['max_event_level_reports'] | ||
if (typeof maxEventLevelReports === 'undefined') { | ||
maxEventLevelReports = defaultMaxReports | ||
} else if (typeof maxEventLevelReports !== 'number') { | ||
throw 'max_event_level_reports must be a number' | ||
} | ||
|
||
const topLevelNumWindows = getNumWindowsFromJson(defaultWindows, json['event_report_windows']) | ||
|
||
const triggerSpecs = json['trigger_specs'] | ||
if (!Array.isArray(triggerSpecs)) { | ||
throw 'trigger_specs must be an array' | ||
} | ||
|
||
const perTriggerDataConfigs: PerTriggerDataConfig[] = [] | ||
triggerSpecs.forEach((spec: object) => { | ||
const triggerData = spec['trigger_data'] | ||
if (!Array.isArray(triggerData)) { | ||
throw 'trigger_data must be an array' | ||
} | ||
|
||
const numDataTypes = triggerData.length | ||
|
||
const numWindows = getNumWindowsFromJson(topLevelNumWindows, spec['event_report_windows']) | ||
|
||
// Technically this can be larger, but we will always be constrained | ||
// by `max_event_level_reports`. | ||
let numBuckets = maxEventLevelReports | ||
const summaryBuckets = spec['summary_buckets'] | ||
if (typeof summaryBuckets !== 'undefined') { | ||
if (!Array.isArray(summaryBuckets)) { | ||
throw 'summary_buckets must be an array' | ||
} | ||
numBuckets = summaryBuckets.length | ||
} | ||
|
||
for (let i = 0; i < numDataTypes; i++) { | ||
perTriggerDataConfigs.push(new PerTriggerDataConfig(numWindows, numBuckets)) | ||
} | ||
}); | ||
|
||
return new Config(maxEventLevelReports, perTriggerDataConfigs) | ||
} | ||
|
||
const optionDefs = [ | ||
{ | ||
name: 'max_event_level_reports', | ||
alias: 'm', | ||
type: Number, | ||
defaultValue: 20, | ||
}, | ||
{ | ||
name: 'epsilon', | ||
alias: 'e', | ||
type: Number, | ||
defaultValue: 14, | ||
}, | ||
{ | ||
name: 'source_type', | ||
alias: 't', | ||
type: parseSourceType, | ||
defaultValue: SourceType.Navigation, | ||
}, | ||
{ | ||
name: 'windows', | ||
alias: 'w', | ||
type: commaSeparatedInts, | ||
}, | ||
{ | ||
name: 'buckets', | ||
alias: 'b', | ||
type: commaSeparatedInts, | ||
}, | ||
{ | ||
name: 'json_file', | ||
alias: 'f', | ||
type: String, | ||
}, | ||
] | ||
|
||
const options = commandLineArgs(optionDefs) | ||
|
||
let config: Config | ||
if ('json_file' in options) { | ||
const json = JSON.parse(fs.readFileSync(options.json_file, {encoding: 'utf8'})) | ||
config = getConfig(json, options.source_type) | ||
} else { | ||
if (options.windows.length != options.buckets.length) { | ||
throw 'windows and buckets must have same length' | ||
} | ||
config = new Config( | ||
options.max_event_level_reports, | ||
options.windows.map((w, i) => new PerTriggerDataConfig(w, options.buckets[i])), | ||
) | ||
} | ||
|
||
const out = config.computeConfigData(options.epsilon, options.source_type) | ||
|
||
console.log(`Number of possible different output states: ${out.numStates}`) | ||
console.log(`Information gain: ${out.infoGain.toFixed(2)} bits`) | ||
console.log(`Flip percent: ${(100 * out.flipProb).toFixed(5)}%`) | ||
|
||
if (out.excessive) { | ||
const e = out.excessive | ||
console.log( | ||
`WARNING: info gain > ${e.infoGainDefault.toFixed(2)} for ${options.source_type} sources. Would require a ${(100 * | ||
e.newFlipProb).toFixed(5)}% flip chance (effective epsilon = ${e.newEps.toFixed(3)}) to resolve.`) | ||
} |
Oops, something went wrong.