Conversation
Some VDAFs now take max_measurement as a parameter instead of the bit width of measurements.
| pretty_assertions = "1.4.1" | ||
| # Disable default features so that individual workspace crates can choose to re-enable them | ||
| prio = { version = "0.18.1-alpha.2", default-features = false, features = ["experimental"] } | ||
| prio = { git = "https://github.com/divviup/libprio-rs", rev = "215cedb99f8b0d7395a5f885af53cd0398c878f3", default-features = false } |
There was a problem hiding this comment.
This should be version = "0.18.1-alpha.3" once we release that.
|
We still should do a mass rename of "prep", "prepare" to "verifier", "verify". In this PR, I only did the ones that are more or less forced by updating the |
|
I just merged a change to libprio-rs to remove the L2FixedPointVec stuff. So that will break the Janus build unless we remove all the stuff gated on feature |
Nope, that won't work. We'll have to delete L2FixedPointVec here. Ah, well, at least it should be overwhelmingly deletions, which are easy to review. |
| let mut helper_prepare_transitions = Vec::new(); | ||
|
|
||
| // Shard inputs into input shares, and initialize the initial PrepareTransitions. | ||
| println!("measurement: {measurement:?}"); |
There was a problem hiding this comment.
We probably want to be rid of this before merging
| println!("measurement: {measurement:?}"); |
There was a problem hiding this comment.
Oops, well spotted. Rather rude of me to keep adding commits while I have this marked as ready to review...
jcjones
left a comment
There was a problem hiding this comment.
This is a big'un. Please check me on that with_verify_init_fn one - that might be important.
Thanks for taking on this big :oof: of a changeset!
| )) | ||
| }, | ||
| ); | ||
| let $vdaf = ::prio::vdaf::dummy::Vdaf::new(1).with_verify_next_fn(|_| { |
There was a problem hiding this comment.
This looks like the same construction as aggregator.rs:1041 but it's not, and I think this one is wrong.
So here, for both FakeFailsPrepInit and FakeFailsPrepStep you call with_verify_next_fn -- which makes them functionally identical. By comparison, in aggregator.rs they're split up -- the Init step calls with_verify_init_fn while the Step calls next:
VdafInstance::FakeFailsPrepInit => VdafOps::Fake(Arc::new(
dummy::Vdaf::new(1).with_verify_init_fn(|_| -> Result<(), VdafError> {
Err(VdafError::Uncategorized(
"FakeFailsPrepInit failed at prep_init".to_string(),
))
}),
)),
#[cfg(feature = "test-util")]
VdafInstance::FakeFailsPrepStep => {
VdafOps::Fake(Arc::new(dummy::Vdaf::new(1).with_verify_next_fn(
|_| -> Result<VerifyTransition<dummy::Vdaf, 0, 16>, VdafError> {Being not a VDAF expert I'm not sure what the consequences are, but it was odd to me that we called the same thing in two different arms here, and then I remembered the other file.
There was a problem hiding this comment.
You are correct, this was a copy-pasta error on my part. It didn't cause test failures because we just check for something failing with VdafError::Uncategorized, I think.
| dp_strategy: _, | ||
| max_measurement, .. | ||
| } => metrics | ||
| .aggregated_report_share_dimension_histogram |
There was a problem hiding this comment.
I guess all these change the metrics, but they'll all change together at least.
Do we need to file a ticket to update any alerts?
There was a problem hiding this comment.
I do not think so. We define an alert:
- alert: janus_report_aggregation_rate
expr: |-
sum(rate(
janus_aggregated_report_share_vdaf_dimension_count[1h]
)) by (namespace)
< on () group_left ()
max(alert_threshold_janus_report_aggregation_rate)
labels:
severity: "CRITICAL"
annotations:
summary: "Report aggregation rate low"
description: "Reports are not being aggregated at the expected rate in
namespace {{ $labels.namespace }}."
But that histogram will still exist, with the same bucket boundaries. However I now think that my math is wrong: the previous math was doing bits * length to get a sense of the total bit size of the measurement. max_measurement isn't the same thing because it doesn't account for vector length. I'm not sure if we should use max_measurement * length or log2(max_measurement) * length, though. The latter is consistent with the dimension we recorded before, but the former is consistent with what we do for Prio3Sum. IIRC our goal here is to bucket report share counts by the size of the measurement, so I think it'd be nice to be consistent about doing so in units of bits.
| PingPongError::CodecPrepMessage(_) => ( | ||
| PingPongError::CodecVerifierMessage(_) => ( | ||
| format!("Couldn't decode {peer_role} prepare message"), | ||
| format!("{peer_role}_prep_message_decode_failure"), |
There was a problem hiding this comment.
| format!("{peer_role}_prep_message_decode_failure"), | |
| format!("{peer_role}_verify_message_decode_failure"), |
| // Bucket written reports by the number of bits their | ||
| // representation uses |
There was a problem hiding this comment.
This description is a bit clearer:
| // Bucket written reports by the number of bits their | |
| // representation uses | |
| // Bucket written reports by the size of their | |
| // encoded measurement in field elements |
| .aggregated_report_share_dimension_histogram | ||
| .record( | ||
| *max_measurement, | ||
| max_measurement.next_power_of_two().ilog2() as u64, |
There was a problem hiding this comment.
In libprio we do max_measurement.ilog2() + 1. I think these would differ on powers of two.
There was a problem hiding this comment.
I agree that aligning with what libprio does is right, but what about the case where max_measurement is a power of 2? Then the +1 is unnecessary, right?
There was a problem hiding this comment.
Taking 8 as an example, 8.ilog2() is 3 (as 2^3 is 8), so 8.ilog2() + 1 is 4, which is what we want since 8 is 0b1000. The +1 is necessary, but it's a constant offset, as ilog2() and counting bits happen to have the same boundary behavior. With this PR's code, we'd compute 8.next_power_of_two(), which is 8, then take the ilog2()` of that, which is 3.
max_measurement.ilog2() + 1 also works with non-powers of two: for 7, the floored base two log is 2, so we get 3, and for 9, the floored base two log is 3, so we get 4.
| // Each histogram bucket can contain up to | ||
| // Field128::modulus(), so 128 bits | ||
| Prio3Histogram { length, .. } => metrics | ||
| .aggregated_report_share_dimension_histogram | ||
| .record( | ||
| u64::try_from(*length).unwrap_or(u64::MAX), | ||
| *length as u64 * 128, |
There was a problem hiding this comment.
This is incorrect, each measurement is a one-hot vector, so there's no factor of 128.
There was a problem hiding this comment.
OK, I put it back to u64::try_from(*length).unwrap_or(u64::MAX)
| .saturating_mul( | ||
| u64::try_from(*length).unwrap_or(u64::MAX), | ||
| ), | ||
| (length * (max_measurement.ilog2() as usize) + 1) |
There was a problem hiding this comment.
The parentheses are misplaced here
| (length * (max_measurement.ilog2() as usize) + 1) | |
| (length * (max_measurement.ilog2() as usize + 1)) |
To implement
draft-ietf-ppm-dap-17+, we needdraft-irtf-cfrg-vdaf-18, which means we need a newprio.0.18.1-alpha.3is the current release candidate (divviup/libprio-rs#1391). It has all the major expected changes for the next Prio release, so now is the time to bite off the messy changes and update.Mostly this is straightforward stuff, renaming variables and fields to match name changes in
prio. Some nuances are:bitsparameter and instead usemax_measurement(this is because VDAF/prionow use tighter range checks)bad_clientintegration test is re-enabled now that Janus andprioboth use the samerandprio. This also means we can stop enabling featureexperimental.fixedandcfg_ifdependenciesfpvec_bounded_l2featurePrio3SumVec::max_measurement. These are going to be harmonized in the specification with Widen types used in VDAF config structs ietf-wg-ppm/draft-ietf-ppm-dap#777, and then we'll make it all make sense across taskprov, Janus, divviup-api and the various task representations we have.--bitssince no VDAF has that parameter.Part of #4402