|
|
@@ -1,9 +1,6 @@
|
|
|
/*! A module for the protocol for the user to submit one or more
|
|
|
reports.
|
|
|
|
|
|
-The user's credential must be in a "pending" state. (If it is not, they
|
|
|
-should run report_init first.)
|
|
|
-
|
|
|
The user may submit up to 3 reports at a time, but the protocol will
|
|
|
only succeed if the user's reports do not conflict with any pending
|
|
|
reports (i.e., the user may not submit a report for a bridge if they
|
|
|
@@ -28,6 +25,13 @@ The user presents their current Lox credential:
|
|
|
- false_reports: blinded
|
|
|
- pending: revealed
|
|
|
|
|
|
+and a Resolve credential (which may have all fields 0):
|
|
|
+- pending: revealed to be the same as in the above Lox credential
|
|
|
+- new_false_reports: revealed
|
|
|
+- bridge_0_pending: revealed
|
|
|
+- bridge_1_pending: revealed
|
|
|
+- bridge_2_pending: revealed
|
|
|
+
|
|
|
and a new Lox credential to be issued:
|
|
|
|
|
|
- id: jointly chosen by the user and BA
|
|
|
@@ -44,18 +48,22 @@ and a new Lox credential to be issued:
|
|
|
- false_reports: blinded, but proved in ZK that it's new_false_reports
|
|
|
more than the value in the Lox credential above
|
|
|
- pending: revealed to be the same as the pending value in the Lox
|
|
|
- credential above
|
|
|
+ credential above if that value is 0 OR a new, random pending value
|
|
|
+ from the server with the low byte 0
|
|
|
|
|
|
*/
|
|
|
#[cfg(feature = "bridgeauth")]
|
|
|
use super::super::dup_filter::SeenType;
|
|
|
use super::super::{
|
|
|
- bridge_table::MAX_BRIDGES_PER_BUCKET, report_table::MAX_FALSE_REPORTS, scalar_u32, Scalar, G,
|
|
|
+ bridge_table::MAX_BRIDGES_PER_BUCKET,
|
|
|
+ get_H,
|
|
|
+ report_table::{CountryCode, MAX_FALSE_REPORTS},
|
|
|
+ scalar_u32, Scalar, G,
|
|
|
};
|
|
|
#[cfg(feature = "bridgeauth")]
|
|
|
-use super::super::{report_table::ReportStatus, BridgeAuth, BridgeLine};
|
|
|
+use super::super::{BridgeAuth, BridgeLine};
|
|
|
use super::errors::CredentialError;
|
|
|
-use crate::lox_creds::Lox;
|
|
|
+use crate::lox_creds::{Lox, Resolve};
|
|
|
use cmz::*;
|
|
|
use group::Group;
|
|
|
use rand::{CryptoRng, RngCore};
|
|
|
@@ -63,36 +71,46 @@ use sha2::Sha512;
|
|
|
|
|
|
const SESSION_ID: &[u8] = b"report_submit";
|
|
|
|
|
|
-muCMZProtocol! { report_submit<max_false_reports, new_false_reports>,
|
|
|
- L: Lox { id: R, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H, false_reports: H, pending: R },
|
|
|
- N: Lox { id: J, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H, false_reports: H, pending: I },
|
|
|
+muCMZProtocol! { report_submit<max_false_reports, bucket>,
|
|
|
+ [ L: Lox { id: R, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H, false_reports: H, pending: R },
|
|
|
+ R: Resolve { pending: R, new_false_reports: R, bridge_0_pending: R, bridge_1_pending: R, bridge_2_pending: R } ],
|
|
|
+ N: Lox { id: J, bucket: H, trust_level: H, level_since: H, invites_remaining: H, blockages: H, false_reports: H, pending: S },
|
|
|
+ L.bucket = bucket,
|
|
|
N.bucket = L.bucket,
|
|
|
N.trust_level = L.trust_level,
|
|
|
N.level_since = L.level_since,
|
|
|
N.invites_remaining = L.invites_remaining,
|
|
|
N.blockages = L.blockages,
|
|
|
- (0..=max_false_reports).contains(L.false_reports + new_false_reports),
|
|
|
- N.false_reports = L.false_reports + new_false_reports,
|
|
|
+ (0..=max_false_reports).contains(L.false_reports + R.new_false_reports),
|
|
|
+ N.false_reports = L.false_reports + R.new_false_reports,
|
|
|
}
|
|
|
|
|
|
pub fn request(
|
|
|
rng: &mut (impl CryptoRng + RngCore),
|
|
|
L: Lox,
|
|
|
- new_false_reports: u32, // 0, 1, 2, or 3
|
|
|
+ R: Resolve,
|
|
|
+ cc: CountryCode,
|
|
|
bridges_being_reported: [bool; MAX_BRIDGES_PER_BUCKET],
|
|
|
) -> Result<
|
|
|
(
|
|
|
(report_submit::Request, report_submit::ClientState),
|
|
|
+ CountryCode,
|
|
|
+ G,
|
|
|
[bool; MAX_BRIDGES_PER_BUCKET],
|
|
|
),
|
|
|
CredentialError,
|
|
|
> {
|
|
|
cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
|
|
|
|
|
|
- // Ensure the credential can be correctly shown; that is, the
|
|
|
- // pending value is not 0
|
|
|
- if L.pending.is_some_and(|p| p == Scalar::ZERO) {
|
|
|
- return Err(CredentialError::CredentialNotPending);
|
|
|
+ // For simplicity, always have a resolve credential, and let user
|
|
|
+ // have empty one if not already pending
|
|
|
+ // TODO: We might want this to implemented the other way (where the
|
|
|
+ // logic is different) in the future. I'm prioritizing simplicity of
|
|
|
+ // the code and not repeating myself rather than most
|
|
|
+ // straightforward logic.
|
|
|
+
|
|
|
+ if L.pending != R.pending {
|
|
|
+ return Err(CredentialError::CredentialMismatch);
|
|
|
}
|
|
|
|
|
|
let false_reports: u32 = match scalar_u32(&L.false_reports.unwrap()) {
|
|
|
@@ -104,6 +122,15 @@ pub fn request(
|
|
|
))
|
|
|
}
|
|
|
};
|
|
|
+ let new_false_reports: u32 = match scalar_u32(&R.new_false_reports.unwrap()) {
|
|
|
+ Some(v) => v,
|
|
|
+ None => {
|
|
|
+ return Err(CredentialError::InvalidField(
|
|
|
+ String::from("new_false_reports"),
|
|
|
+ String::from("could not be converted to u32"),
|
|
|
+ ))
|
|
|
+ }
|
|
|
+ };
|
|
|
if false_reports + new_false_reports > MAX_FALSE_REPORTS {
|
|
|
return Err(CredentialError::InvalidField(
|
|
|
String::from("false_reports"),
|
|
|
@@ -125,6 +152,8 @@ pub fn request(
|
|
|
return Err(CredentialError::NoBridgesReported);
|
|
|
}
|
|
|
|
|
|
+ let D = L.bucket.unwrap() * get_H();
|
|
|
+
|
|
|
let mut N = Lox::using_pubkey(L.get_pubkey());
|
|
|
N.bucket = L.bucket;
|
|
|
N.trust_level = L.trust_level;
|
|
|
@@ -132,14 +161,13 @@ pub fn request(
|
|
|
N.invites_remaining = L.invites_remaining;
|
|
|
N.blockages = L.blockages;
|
|
|
N.false_reports = Some(Scalar::from(false_reports + new_false_reports));
|
|
|
- N.pending = L.pending;
|
|
|
|
|
|
let params = report_submit::Params {
|
|
|
max_false_reports: MAX_FALSE_REPORTS.into(),
|
|
|
- new_false_reports: new_false_reports.into(),
|
|
|
+ bucket: L.bucket.unwrap(),
|
|
|
};
|
|
|
- match report_submit::prepare(rng, SESSION_ID, &L, N, ¶ms) {
|
|
|
- Ok(req_state) => Ok((req_state, bridges_being_reported)),
|
|
|
+ match report_submit::prepare(rng, SESSION_ID, &L, &R, N, ¶ms) {
|
|
|
+ Ok(req_state) => Ok((req_state, cc, D, bridges_being_reported)),
|
|
|
Err(e) => Err(CredentialError::CMZError(e)),
|
|
|
}
|
|
|
}
|
|
|
@@ -149,130 +177,146 @@ impl BridgeAuth {
|
|
|
pub fn handle_report_submit(
|
|
|
&mut self,
|
|
|
req: report_submit::Request,
|
|
|
+ cc: CountryCode,
|
|
|
+ D: G,
|
|
|
bridges_being_reported: [bool; MAX_BRIDGES_PER_BUCKET],
|
|
|
) -> Result<report_submit::Reply, CredentialError> {
|
|
|
let mut rng = rand::thread_rng();
|
|
|
let reqbytes = req.as_bytes();
|
|
|
let recvreq = report_submit::Request::try_from(&reqbytes[..]).unwrap();
|
|
|
|
|
|
- let mut bridges_with_old_reports = Vec::<[u8; 20]>::new();
|
|
|
+ let (bucket, bridges) = match self.bridge_table.get_bucket_and_bridges(D) {
|
|
|
+ Ok((bucket, bridges)) => (bucket, bridges),
|
|
|
+ Err(_) => {
|
|
|
+ return Err(CredentialError::InvalidField(
|
|
|
+ String::from("D"),
|
|
|
+ String::from("Submitted D does not match an existing bucket"),
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut bridges_with_new_reports = Vec::<[u8; 20]>::new();
|
|
|
|
|
|
match report_submit::handle(
|
|
|
&mut rng,
|
|
|
SESSION_ID,
|
|
|
recvreq,
|
|
|
- |L: &mut Lox, N: &mut Lox| {
|
|
|
+ |L: &mut Lox, R: &mut Resolve, N: &mut Lox| {
|
|
|
L.set_privkey(&self.lox_priv);
|
|
|
+ R.set_privkey(&self.resolve_priv);
|
|
|
N.set_privkey(&self.lox_priv);
|
|
|
|
|
|
- let bucket = match self
|
|
|
- .report_table
|
|
|
- .get_user_bucket(L.pending.unwrap().clone())
|
|
|
- {
|
|
|
- Some(bucket) => bucket,
|
|
|
- None => {
|
|
|
- return Err(CMZError::RevealAttrMissing(
|
|
|
- "pending",
|
|
|
- "Pending value is incorrect",
|
|
|
- ));
|
|
|
- }
|
|
|
- };
|
|
|
+ if L.pending != R.pending {
|
|
|
+ return Err(CMZError::RevealAttrMissing(
|
|
|
+ "pending",
|
|
|
+ "Lox credential pending value does not match resolve credential pending value"
|
|
|
+ ));
|
|
|
+ }
|
|
|
|
|
|
- let bridges = match self.bridge_table.get_bridge_hashed_fingerprints(bucket) {
|
|
|
- Ok(bridges) => bridges,
|
|
|
- Err(_) => {
|
|
|
- return Err(CMZError::RevealAttrMissing(
|
|
|
- "bucket",
|
|
|
- "Bucket value is incorrect",
|
|
|
- ));
|
|
|
+ let pending = if L.pending == Some(Scalar::ZERO) {
|
|
|
+ // Get random scalar with low bits 0
|
|
|
+ let mut rng = rand::rngs::OsRng;
|
|
|
+ let p = Scalar::random(&mut rng);
|
|
|
+ let mut p_bytes = *p.as_bytes();
|
|
|
+ p_bytes[0] = 0u8;
|
|
|
+ Scalar::from_bytes_mod_order(p_bytes)
|
|
|
+ } else {
|
|
|
+ match self.report_table.get_user_info(L.pending.unwrap()) {
|
|
|
+ Some(user_info) => {
|
|
|
+ // Check that submitted data matches previously submitted data
|
|
|
+ if user_info.bucket * get_H() != D {
|
|
|
+ return Err(CMZError::RevealAttrMissing(
|
|
|
+ "D",
|
|
|
+ "D does not equal bucket * H",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ if user_info.cc != cc {
|
|
|
+ return Err(CMZError::RevealAttrMissing(
|
|
|
+ "cc",
|
|
|
+ "cc does not equal previously used cc",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+ if user_info.current_pending_value != L.pending.unwrap() {
|
|
|
+ return Err(CMZError::RevealAttrMissing(
|
|
|
+ "pending",
|
|
|
+ "incorrect pending value",
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ L.pending.unwrap()
|
|
|
+ }
|
|
|
+ None => {
|
|
|
+ return Err(CMZError::RevealAttrMissing(
|
|
|
+ "pending",
|
|
|
+ "incorrect pending value",
|
|
|
+ ));
|
|
|
+ }
|
|
|
}
|
|
|
};
|
|
|
|
|
|
// Check that user indicated at least one non-default bridge to
|
|
|
// submit a report for
|
|
|
- let mut reporting_a_bridge = false;
|
|
|
+ let bridges_pending = [R.bridge_0_pending, R.bridge_1_pending, R.bridge_2_pending];
|
|
|
for i in 0..MAX_BRIDGES_PER_BUCKET {
|
|
|
if bridges_being_reported[i] {
|
|
|
- if bridges[i] == BridgeLine::default().get_hashed_fingerprint() {
|
|
|
+ if bridges_pending[i] != Some(Scalar::ZERO) {
|
|
|
+ return Err(CMZError::RevealAttrMissing(
|
|
|
+ "bridges_being_reported",
|
|
|
+ "Report submitted for a bridge that already has a pending report",
|
|
|
+ ));
|
|
|
+ } else if bridges[i] == BridgeLine::default().get_hashed_fingerprint() {
|
|
|
return Err(CMZError::RevealAttrMissing(
|
|
|
"bridges_being_reported",
|
|
|
"Report submitted for a bridge that does not exist",
|
|
|
));
|
|
|
+ } else {
|
|
|
+ bridges_with_new_reports.push(bridges[i]);
|
|
|
}
|
|
|
- reporting_a_bridge = true;
|
|
|
}
|
|
|
}
|
|
|
- if !reporting_a_bridge {
|
|
|
+ if bridges_with_new_reports.len() == 0 {
|
|
|
return Err(CMZError::RevealAttrMissing(
|
|
|
"bridges_being_reported",
|
|
|
"No bridges are being reported",
|
|
|
));
|
|
|
}
|
|
|
|
|
|
- let mut new_false_reports: u32 = 0;
|
|
|
-
|
|
|
- for i in 0..MAX_BRIDGES_PER_BUCKET {
|
|
|
- let status = self
|
|
|
- .report_table
|
|
|
- .get_report_status(&bridges[i], L.pending.unwrap().clone());
|
|
|
-
|
|
|
- // Check for old reports
|
|
|
- if status == ReportStatus::NotBlocked
|
|
|
- || status == ReportStatus::Blocked
|
|
|
- || status == ReportStatus::Timeout
|
|
|
- {
|
|
|
- // We have an old report that will need to be cleaned up afterwards
|
|
|
- bridges_with_old_reports.push(bridges[i]);
|
|
|
- }
|
|
|
-
|
|
|
- if status == ReportStatus::Pending && bridges_being_reported[i] {
|
|
|
- // TODO: Fix this error
|
|
|
- return Err(CMZError::RevealAttrMissing(
|
|
|
- "pending",
|
|
|
- "Attempted to submit a report while a pending report exists",
|
|
|
- ));
|
|
|
- } else if status == ReportStatus::NotBlocked {
|
|
|
- // Report allowed for this bridge, but we have to add to false_reports
|
|
|
- new_false_reports += 1;
|
|
|
- }
|
|
|
- // Implicitly, other cases are fine
|
|
|
- }
|
|
|
-
|
|
|
- // Add the reports to the table
|
|
|
- for i in 0..MAX_BRIDGES_PER_BUCKET {
|
|
|
- if bridges_being_reported[i] {
|
|
|
- self.report_table
|
|
|
- .process_new_report(bridges[i], L.pending.unwrap());
|
|
|
- }
|
|
|
- }
|
|
|
+ N.pending = Some(pending);
|
|
|
|
|
|
- N.pending = L.pending;
|
|
|
Ok(report_submit::Params {
|
|
|
max_false_reports: MAX_FALSE_REPORTS.into(),
|
|
|
- new_false_reports: new_false_reports.into(),
|
|
|
+ bucket: bucket,
|
|
|
})
|
|
|
},
|
|
|
- |L: &Lox, _N: &Lox| {
|
|
|
+ |L: &Lox, _R: &Resolve, _N: &Lox| {
|
|
|
if self.id_filter.filter(&L.id.unwrap()) == SeenType::Seen {
|
|
|
return Err(CMZError::RevealAttrMissing("id", ""));
|
|
|
}
|
|
|
Ok(())
|
|
|
},
|
|
|
) {
|
|
|
- Ok((response, (L_issuer, _N_issuer))) => {
|
|
|
- // Only if everything went okay, remove old reports from this user
|
|
|
- if self
|
|
|
- .report_table
|
|
|
- .remove_old_reports(&bridges_with_old_reports, L_issuer.pending.unwrap())
|
|
|
- {
|
|
|
- Ok(response)
|
|
|
- } else {
|
|
|
- // TODO: Fix this error message/type
|
|
|
- Err(CredentialError::InvalidField(
|
|
|
- "pending".to_string(),
|
|
|
- "Failed to remove old reports".to_string(),
|
|
|
- ))
|
|
|
+ Ok((response, (_L_issuer, _R_issuer, N_issuer))) => {
|
|
|
+ // Ensure correct user data in table
|
|
|
+ match self.report_table.get_user_info(N_issuer.pending.unwrap()) {
|
|
|
+ Some(_user_info) => {
|
|
|
+ // If everything went okay, remove any existing resolve
|
|
|
+ // credential for this user
|
|
|
+ self.report_table
|
|
|
+ .set_user_resolve_cred(N_issuer.pending.unwrap(), None);
|
|
|
+ }
|
|
|
+ None => {
|
|
|
+ // Add new user's data
|
|
|
+ self.report_table
|
|
|
+ .add_user(N_issuer.pending.unwrap(), bucket, cc);
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
+ // Add new reports to the table
|
|
|
+ for bridge in bridges_with_new_reports {
|
|
|
+ self.report_table
|
|
|
+ .process_new_report(bridge, N_issuer.pending.unwrap());
|
|
|
+ }
|
|
|
+ Ok(response)
|
|
|
}
|
|
|
Err(e) => Err(CredentialError::CMZError(e)),
|
|
|
}
|
|
|
@@ -298,7 +342,7 @@ mod tests {
|
|
|
use crate::proto::*;
|
|
|
use crate::{
|
|
|
report_table::{CountryCode, ReportStatus},
|
|
|
- BridgeLine, Scalar, MAX_BRIDGES_PER_BUCKET,
|
|
|
+ Scalar,
|
|
|
};
|
|
|
use std::time::SystemTime;
|
|
|
|
|
|
@@ -313,71 +357,88 @@ mod tests {
|
|
|
th.verify_lox(&lox_cred);
|
|
|
assert_eq!(lox_cred.pending.unwrap(), Scalar::ZERO);
|
|
|
|
|
|
- // Enter pending state
|
|
|
- lox_cred = th.report_init(rng, lox_cred.clone(), cc);
|
|
|
- th.verify_lox(&lox_cred);
|
|
|
- let pending_1 = lox_cred.pending.unwrap();
|
|
|
- assert!(pending_1 != Scalar::ZERO);
|
|
|
-
|
|
|
- // Figure out which bridge is non-default
|
|
|
+ // Get bridges
|
|
|
let (bucket, _reach_cred) = th.get_bucket(lox_cred.clone());
|
|
|
- let mut bridges_to_report = [false; MAX_BRIDGES_PER_BUCKET];
|
|
|
- for i in 0..bucket.len() {
|
|
|
- if bucket[i].get_hashed_fingerprint() != BridgeLine::default().get_hashed_fingerprint()
|
|
|
- {
|
|
|
- bridges_to_report[i] = true;
|
|
|
- }
|
|
|
- }
|
|
|
|
|
|
- // Only bridge 0 should be non-default
|
|
|
- assert_eq!(bridges_to_report, [true, false, false]);
|
|
|
+ // Check we start in non-pending state
|
|
|
+ assert_eq!(lox_cred.pending, Some(Scalar::ZERO));
|
|
|
+
|
|
|
+ // Scalar with low byte 0
|
|
|
+ let scalar = Scalar::random(rng);
|
|
|
+ let mut scalar_bytes = scalar.as_bytes().clone();
|
|
|
+ scalar_bytes[0] = 0u8;
|
|
|
+ let scalar = Scalar::from_bytes_mod_order(scalar_bytes);
|
|
|
+ assert_eq!(scalar.as_bytes()[0], 0u8);
|
|
|
|
|
|
// Only bridge 0 exists. We should not be able to submit a
|
|
|
// report for bridge 1 or 2.
|
|
|
- let report_submit_request =
|
|
|
- report_submit::request(rng, lox_cred.clone(), 0, [false, true, false]);
|
|
|
- let ((rs_request, _rs_client_state), bridges_being_reported_server) =
|
|
|
+ let mut resolve_cred = th.ba.get_resolve_cred_for_new_session(rng);
|
|
|
+
|
|
|
+ assert_eq!(resolve_cred.pending, Some(Scalar::ZERO));
|
|
|
+
|
|
|
+ let report_submit_request = report_submit::request(
|
|
|
+ rng,
|
|
|
+ lox_cred.clone(),
|
|
|
+ resolve_cred,
|
|
|
+ cc,
|
|
|
+ [false, true, false],
|
|
|
+ );
|
|
|
+ let ((rs_request, _rs_client_state), cc_server, D, bridges_being_reported_server) =
|
|
|
report_submit_request.unwrap();
|
|
|
- let report_submit_response = th
|
|
|
- .ba
|
|
|
- .handle_report_submit(rs_request, bridges_being_reported_server);
|
|
|
+ let report_submit_response =
|
|
|
+ th.ba
|
|
|
+ .handle_report_submit(rs_request, cc_server, D, bridges_being_reported_server);
|
|
|
assert!(report_submit_response.is_err());
|
|
|
|
|
|
+ assert_eq!(lox_cred.pending, Some(Scalar::ZERO));
|
|
|
+
|
|
|
// Submit a report for bridge 0
|
|
|
- lox_cred = th.report_submit(rng, lox_cred.clone(), 0, [true, false, false]);
|
|
|
+ lox_cred = th.report_submit(rng, lox_cred.clone(), None, cc, [true, false, false]);
|
|
|
th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(pending_1, lox_cred.pending.unwrap());
|
|
|
+ let pending_1 = lox_cred.pending.unwrap();
|
|
|
+ assert_eq!(lox_cred.pending.unwrap().as_bytes()[0], 0u8);
|
|
|
|
|
|
// Get report status
|
|
|
- let report_statuses;
|
|
|
- (lox_cred, report_statuses) = th.report_status(rng, lox_cred.clone());
|
|
|
+ (lox_cred, resolve_cred) = th.report_status(rng, lox_cred.clone());
|
|
|
th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(pending_1, lox_cred.pending.unwrap());
|
|
|
- assert_eq!(
|
|
|
- report_statuses,
|
|
|
- [
|
|
|
- ReportStatus::Pending,
|
|
|
- ReportStatus::NoReport,
|
|
|
- ReportStatus::NoReport
|
|
|
- ]
|
|
|
- );
|
|
|
+ let pending_2 = lox_cred.pending.unwrap();
|
|
|
+ assert_eq!(pending_1 + Scalar::ONE, pending_2);
|
|
|
+ assert_eq!(resolve_cred.pending.unwrap(), pending_2);
|
|
|
+ assert_eq!(resolve_cred.new_false_reports, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_0_pending, Some(Scalar::ONE));
|
|
|
+ assert_eq!(resolve_cred.bridge_1_pending, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_2_pending, Some(Scalar::ZERO));
|
|
|
+
|
|
|
+ // Resolve (stay in pending state because we have a pending report)
|
|
|
+ lox_cred = th.report_resolve(rng, lox_cred.clone(), resolve_cred.clone());
|
|
|
+ th.verify_lox(&lox_cred);
|
|
|
+ assert_eq!(lox_cred.pending.unwrap(), pending_2);
|
|
|
|
|
|
- // We should be unable to resolve pending state due to pending
|
|
|
- // report
|
|
|
- let report_resolve_request = report_resolve::request(rng, lox_cred.clone(), 0);
|
|
|
- let (rr_request, _rr_client_state) = report_resolve_request.unwrap();
|
|
|
- let report_resolve_response = th.ba.handle_report_resolve(rr_request);
|
|
|
- assert!(report_resolve_response.is_err());
|
|
|
+ // Get report status
|
|
|
+ (lox_cred, resolve_cred) = th.report_status(rng, lox_cred.clone());
|
|
|
+ th.verify_lox(&lox_cred);
|
|
|
+ let pending_3 = lox_cred.pending.unwrap();
|
|
|
+ assert_eq!(pending_2 + Scalar::ONE, pending_3);
|
|
|
+ assert_eq!(resolve_cred.pending.unwrap(), pending_3);
|
|
|
+ assert_eq!(resolve_cred.new_false_reports, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_0_pending, Some(Scalar::ONE));
|
|
|
+ assert_eq!(resolve_cred.bridge_1_pending, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_2_pending, Some(Scalar::ZERO));
|
|
|
|
|
|
// We should be unable to submit another report for bridge 0 due
|
|
|
// to pending report for bridge 0
|
|
|
- let report_submit_request =
|
|
|
- report_submit::request(rng, lox_cred.clone(), 0, [true, false, false]);
|
|
|
- let ((rs_request, _rs_client_state), bridges_being_reported_server) =
|
|
|
+ let report_submit_request = report_submit::request(
|
|
|
+ rng,
|
|
|
+ lox_cred.clone(),
|
|
|
+ resolve_cred.clone(),
|
|
|
+ cc,
|
|
|
+ [true, false, false],
|
|
|
+ );
|
|
|
+ let ((rs_request, _rs_client_state), cc_server, D, bridges_being_reported_server) =
|
|
|
report_submit_request.unwrap();
|
|
|
- let report_submit_response = th
|
|
|
- .ba
|
|
|
- .handle_report_submit(rs_request, bridges_being_reported_server);
|
|
|
+ let report_submit_response =
|
|
|
+ th.ba
|
|
|
+ .handle_report_submit(rs_request, cc_server, D, bridges_being_reported_server);
|
|
|
assert!(report_submit_response.is_err());
|
|
|
|
|
|
// Let the report time out
|
|
|
@@ -387,10 +448,27 @@ mod tests {
|
|
|
SystemTime::now(),
|
|
|
);
|
|
|
|
|
|
+ // Get report status
|
|
|
+ (lox_cred, resolve_cred) = th.report_status(rng, lox_cred.clone());
|
|
|
+ th.verify_lox(&lox_cred);
|
|
|
+ let pending_4 = lox_cred.pending.unwrap();
|
|
|
+ assert_eq!(pending_3 + Scalar::ONE, pending_4);
|
|
|
+ assert_eq!(resolve_cred.pending.unwrap(), pending_4);
|
|
|
+ assert_eq!(resolve_cred.new_false_reports, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_0_pending, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_1_pending, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_2_pending, Some(Scalar::ZERO));
|
|
|
+
|
|
|
// Submit another report for bridge 0
|
|
|
- lox_cred = th.report_submit(rng, lox_cred.clone(), 0, [true, false, false]);
|
|
|
+ lox_cred = th.report_submit(
|
|
|
+ rng,
|
|
|
+ lox_cred.clone(),
|
|
|
+ Some(resolve_cred.clone()),
|
|
|
+ cc,
|
|
|
+ [true, false, false],
|
|
|
+ );
|
|
|
th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(pending_1, lox_cred.pending.unwrap());
|
|
|
+ assert_eq!(pending_4, lox_cred.pending.unwrap());
|
|
|
|
|
|
// Bridge was not blocked
|
|
|
th.ba.report_table.process_scan_result(
|
|
|
@@ -400,37 +478,27 @@ mod tests {
|
|
|
);
|
|
|
|
|
|
// Get report status
|
|
|
- let report_statuses;
|
|
|
- (lox_cred, report_statuses) = th.report_status(rng, lox_cred.clone());
|
|
|
+ (lox_cred, resolve_cred) = th.report_status(rng, lox_cred.clone());
|
|
|
th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(pending_1, lox_cred.pending.unwrap());
|
|
|
- assert_eq!(
|
|
|
- report_statuses,
|
|
|
- [
|
|
|
- ReportStatus::NotBlocked,
|
|
|
- ReportStatus::NoReport,
|
|
|
- ReportStatus::NoReport
|
|
|
- ]
|
|
|
- );
|
|
|
+ let pending_5 = lox_cred.pending.unwrap();
|
|
|
+ assert_eq!(pending_4 + Scalar::ONE, pending_5);
|
|
|
+ assert_eq!(resolve_cred.pending.unwrap(), pending_5);
|
|
|
+ assert_eq!(resolve_cred.new_false_reports, Some(Scalar::ONE));
|
|
|
+ assert_eq!(resolve_cred.bridge_0_pending, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_1_pending, Some(Scalar::ZERO));
|
|
|
+ assert_eq!(resolve_cred.bridge_2_pending, Some(Scalar::ZERO));
|
|
|
|
|
|
// Submit a new report. We need to increase false_reports.
|
|
|
- lox_cred = th.report_submit(rng, lox_cred.clone(), 1, [true, false, false]);
|
|
|
+ lox_cred = th.report_submit(
|
|
|
+ rng,
|
|
|
+ lox_cred.clone(),
|
|
|
+ Some(resolve_cred.clone()),
|
|
|
+ cc,
|
|
|
+ [true, false, false],
|
|
|
+ );
|
|
|
th.verify_lox(&lox_cred);
|
|
|
assert_eq!(Scalar::ONE, lox_cred.false_reports.unwrap());
|
|
|
|
|
|
- // Get report status
|
|
|
- let report_statuses;
|
|
|
- (lox_cred, report_statuses) = th.report_status(rng, lox_cred.clone());
|
|
|
- th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(
|
|
|
- report_statuses,
|
|
|
- [
|
|
|
- ReportStatus::Pending,
|
|
|
- ReportStatus::NoReport,
|
|
|
- ReportStatus::NoReport
|
|
|
- ]
|
|
|
- );
|
|
|
-
|
|
|
// Time out
|
|
|
th.ba.report_table.mark_old_reports_simulated_time(
|
|
|
&bucket[0].get_hashed_fingerprint(),
|
|
|
@@ -438,10 +506,12 @@ mod tests {
|
|
|
SystemTime::now(),
|
|
|
);
|
|
|
|
|
|
- // Resolve so we can trust promote
|
|
|
- lox_cred = th.report_resolve(rng, lox_cred.clone(), 0);
|
|
|
- assert_eq!(Scalar::ZERO, lox_cred.pending.unwrap());
|
|
|
- assert_eq!(Scalar::ONE, lox_cred.false_reports.unwrap());
|
|
|
+ // Get status
|
|
|
+ (lox_cred, resolve_cred) = th.report_status(rng, lox_cred.clone());
|
|
|
+
|
|
|
+ // Resolve so we can trust promote (exit pending state)
|
|
|
+ lox_cred = th.report_resolve(rng, lox_cred.clone(), resolve_cred.clone());
|
|
|
+ assert_eq!(lox_cred.pending, Some(Scalar::ZERO));
|
|
|
|
|
|
// Promote/migrate to level 1
|
|
|
let mig_cred = th.trust_promotion(rng, lox_cred.clone());
|
|
|
@@ -453,28 +523,11 @@ mod tests {
|
|
|
// Get our new bucket
|
|
|
let (bucket, _reach_cred) = th.get_bucket(lox_cred.clone());
|
|
|
|
|
|
- // Try to submit reports for bridges 1 and 2 (fails because
|
|
|
- // we are not in pending state)
|
|
|
- let report_submit_request =
|
|
|
- report_submit::request(rng, lox_cred.clone(), 0, [false, true, true]);
|
|
|
- assert!(report_submit_request.is_err());
|
|
|
-
|
|
|
- // Enter pending state
|
|
|
- lox_cred = th.report_init(rng, lox_cred.clone(), cc);
|
|
|
-
|
|
|
// Submit reports for bridges 0 and 2
|
|
|
- lox_cred = th.report_submit(rng, lox_cred.clone(), 0, [true, false, true]);
|
|
|
-
|
|
|
- // Try to submit reports for bridges 1 and 2 (fails because
|
|
|
- // there is a pending report for bridge 2)
|
|
|
- let report_submit_request =
|
|
|
- report_submit::request(rng, lox_cred.clone(), 0, [false, true, true]);
|
|
|
- let ((rs_request, _rs_client_state), bridges_being_reported_server) =
|
|
|
- report_submit_request.unwrap();
|
|
|
- let report_submit_response = th
|
|
|
- .ba
|
|
|
- .handle_report_submit(rs_request, bridges_being_reported_server);
|
|
|
- assert!(report_submit_response.is_err());
|
|
|
+ lox_cred = th.report_submit(rng, lox_cred.clone(), None, cc, [true, false, true]);
|
|
|
+ // Pending should be new random scalar with low byte 0
|
|
|
+ assert!(lox_cred.pending.unwrap() != pending_1);
|
|
|
+ assert_eq!(lox_cred.pending.unwrap().as_bytes()[0], 0u8);
|
|
|
|
|
|
// Bridge 2 is not blocked
|
|
|
th.ba.report_table.process_scan_result(
|
|
|
@@ -483,34 +536,32 @@ mod tests {
|
|
|
ReportStatus::NotBlocked,
|
|
|
);
|
|
|
|
|
|
- // Get report statuses
|
|
|
- let report_statuses;
|
|
|
- (lox_cred, report_statuses) = th.report_status(rng, lox_cred.clone());
|
|
|
- th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(
|
|
|
- report_statuses,
|
|
|
- [
|
|
|
- ReportStatus::Pending,
|
|
|
- ReportStatus::NoReport,
|
|
|
- ReportStatus::NotBlocked
|
|
|
- ]
|
|
|
+ // Get status
|
|
|
+ (lox_cred, resolve_cred) = th.report_status(rng, lox_cred.clone());
|
|
|
+
|
|
|
+ // Try to submit reports for a different country (should not work)
|
|
|
+ let report_submit_request = report_submit::request(
|
|
|
+ rng,
|
|
|
+ lox_cred.clone(),
|
|
|
+ resolve_cred.clone(),
|
|
|
+ CountryCode::BY,
|
|
|
+ [false, true, false],
|
|
|
);
|
|
|
+ let ((rs_request, _rs_client_state), cc_server, D, bridges_being_reported_server) =
|
|
|
+ report_submit_request.unwrap();
|
|
|
+ let report_submit_response =
|
|
|
+ th.ba
|
|
|
+ .handle_report_submit(rs_request, cc_server, D, bridges_being_reported_server);
|
|
|
+ assert!(report_submit_response.is_err());
|
|
|
|
|
|
// Submit reports for bridges 1 and 2 (must increase
|
|
|
// false_reports by 1)
|
|
|
- lox_cred = th.report_submit(rng, lox_cred.clone(), 1, [false, true, true]);
|
|
|
-
|
|
|
- // Get report statuses
|
|
|
- let report_statuses;
|
|
|
- (lox_cred, report_statuses) = th.report_status(rng, lox_cred.clone());
|
|
|
- th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(
|
|
|
- report_statuses,
|
|
|
- [
|
|
|
- ReportStatus::Pending,
|
|
|
- ReportStatus::Pending,
|
|
|
- ReportStatus::Pending
|
|
|
- ]
|
|
|
+ lox_cred = th.report_submit(
|
|
|
+ rng,
|
|
|
+ lox_cred.clone(),
|
|
|
+ Some(resolve_cred),
|
|
|
+ cc,
|
|
|
+ [false, true, true],
|
|
|
);
|
|
|
|
|
|
// Two of them are accessible, and one is blocked
|
|
|
@@ -531,38 +582,46 @@ mod tests {
|
|
|
ReportStatus::NotBlocked,
|
|
|
);
|
|
|
|
|
|
+ // Level up
|
|
|
+ lox_cred = th.level_up(rng, lox_cred.clone());
|
|
|
+
|
|
|
// Get report statuses
|
|
|
- let report_statuses;
|
|
|
- (lox_cred, report_statuses) = th.report_status(rng, lox_cred.clone());
|
|
|
- th.verify_lox(&lox_cred);
|
|
|
- assert_eq!(
|
|
|
- report_statuses,
|
|
|
- [
|
|
|
- ReportStatus::Blocked,
|
|
|
- ReportStatus::NotBlocked,
|
|
|
- ReportStatus::NotBlocked
|
|
|
- ]
|
|
|
- );
|
|
|
+ (lox_cred, resolve_cred) = th.report_status(rng, lox_cred.clone());
|
|
|
|
|
|
// Can't submit more reports because we've already submitted too
|
|
|
// many false reports
|
|
|
- let report_submit_request =
|
|
|
- report_submit::request(rng, lox_cred.clone(), 2, [false, true, true]);
|
|
|
+ let report_submit_request = report_submit::request(
|
|
|
+ rng,
|
|
|
+ lox_cred.clone(),
|
|
|
+ resolve_cred.clone(),
|
|
|
+ cc,
|
|
|
+ [false, true, true],
|
|
|
+ );
|
|
|
assert!(report_submit_request.is_err());
|
|
|
|
|
|
- // Can't just lie about the number of false reports
|
|
|
- let report_submit_request =
|
|
|
- report_submit::request(rng, lox_cred.clone(), 0, [false, true, true]);
|
|
|
- let ((rs_request, _rs_client_state), bridges_being_reported_server) =
|
|
|
- report_submit_request.unwrap();
|
|
|
- let report_submit_response = th
|
|
|
- .ba
|
|
|
- .handle_report_submit(rs_request, bridges_being_reported_server);
|
|
|
- assert!(report_submit_response.is_err());
|
|
|
-
|
|
|
// Resolve
|
|
|
- let lox_cred = th.report_resolve(rng, lox_cred.clone(), 2);
|
|
|
+ lox_cred = th.report_resolve(rng, lox_cred.clone(), resolve_cred.clone());
|
|
|
th.verify_lox(&lox_cred);
|
|
|
assert_eq!(Scalar::ZERO, lox_cred.pending.unwrap());
|
|
|
+
|
|
|
+ // Invite a friend
|
|
|
+ let invitation;
|
|
|
+ (invitation, lox_cred) = th.issue_invite(rng, lox_cred.clone());
|
|
|
+ let redeem_invite_request =
|
|
|
+ redeem_invite::request(rng, invitation, th.ba.lox_pub.clone(), th.ba.today());
|
|
|
+ let (redeem_invite_request, redeem_invite_client_state) = redeem_invite_request.unwrap();
|
|
|
+ let redeem_invite_response = th.ba.handle_redeem_invite(redeem_invite_request);
|
|
|
+ assert!(
|
|
|
+ redeem_invite_response.is_ok(),
|
|
|
+ "Redeem Invite response from server should succeed"
|
|
|
+ );
|
|
|
+ let response = redeem_invite_response.unwrap();
|
|
|
+ let r_cred = redeem_invite::handle_response(redeem_invite_client_state, response);
|
|
|
+ assert!(r_cred.is_ok(), "Handle response should succeed");
|
|
|
+ let friend_cred = r_cred.unwrap();
|
|
|
+ th.verify_lox(&friend_cred);
|
|
|
+
|
|
|
+ // Invited friend inherits false_reports
|
|
|
+ assert_eq!(lox_cred.false_reports, friend_cred.false_reports);
|
|
|
}
|
|
|
}
|