578 lines
20 KiB
Rust
578 lines
20 KiB
Rust
#![allow(non_snake_case)]
|
|
|
|
use crate::{bridge_verification_info::BridgeVerificationInfo, *};
|
|
use lox_library::{
|
|
bridge_table::{self, BridgeLine},
|
|
cred::Lox,
|
|
proto::*,
|
|
scalar_u32, BridgeAuth, BridgeDb,
|
|
};
|
|
|
|
use base64::{engine::general_purpose, Engine as _};
|
|
use curve25519_dalek::Scalar;
|
|
use rand::RngCore;
|
|
use std::collections::{BTreeMap, HashSet};
|
|
use x25519_dalek::{PublicKey, StaticSecret};
|
|
|
|
struct TestHarness {
|
|
bdb: BridgeDb,
|
|
pub ba: BridgeAuth,
|
|
}
|
|
|
|
impl TestHarness {
|
|
fn new() -> Self {
|
|
TestHarness::new_buckets(5, 5)
|
|
}
|
|
|
|
fn new_buckets(num_buckets: u16, hot_spare: u16) -> Self {
|
|
// Create a BridegDb
|
|
let mut bdb = BridgeDb::new();
|
|
// Create a BridgeAuth
|
|
let mut ba = BridgeAuth::new(bdb.pubkey);
|
|
|
|
// Make 3 x num_buckets open invitation bridges, in sets of 3
|
|
for _ in 0..num_buckets {
|
|
let bucket = [random(), random(), random()];
|
|
let _ = ba.add_openinv_bridges(bucket, &mut bdb);
|
|
}
|
|
// Add hot_spare more hot spare buckets
|
|
for _ in 0..hot_spare {
|
|
let bucket = [random(), random(), random()];
|
|
let _ = ba.add_spare_bucket(bucket, &mut bdb);
|
|
}
|
|
// Create the encrypted bridge table
|
|
ba.enc_bridge_table();
|
|
|
|
Self { bdb, ba }
|
|
}
|
|
|
|
fn advance_days(&mut self, days: u16) {
|
|
self.ba.advance_days(days);
|
|
}
|
|
|
|
fn get_new_credential(&mut self) -> Lox {
|
|
let inv = self.bdb.invite().unwrap();
|
|
let (req, state) = open_invite::request(&inv);
|
|
let resp = self.ba.handle_open_invite(req).unwrap();
|
|
let (cred, _bridgeline) =
|
|
open_invite::handle_response(state, resp, &self.ba.lox_pub).unwrap();
|
|
cred
|
|
}
|
|
|
|
fn level_up(&mut self, cred: &Lox) -> Lox {
|
|
let current_level = scalar_u32(&cred.trust_level).unwrap();
|
|
if current_level == 0 {
|
|
self.advance_days(trust_promotion::UNTRUSTED_INTERVAL.try_into().unwrap());
|
|
let (promreq, promstate) =
|
|
trust_promotion::request(cred, &self.ba.lox_pub, self.ba.today()).unwrap();
|
|
let promresp = self.ba.handle_trust_promotion(promreq).unwrap();
|
|
let migcred = trust_promotion::handle_response(promstate, promresp).unwrap();
|
|
let (migreq, migstate) =
|
|
migration::request(cred, &migcred, &self.ba.lox_pub, &self.ba.migration_pub)
|
|
.unwrap();
|
|
let migresp = self.ba.handle_migration(migreq).unwrap();
|
|
let new_cred = migration::handle_response(migstate, migresp, &self.ba.lox_pub).unwrap();
|
|
new_cred
|
|
} else {
|
|
self.advance_days(
|
|
level_up::LEVEL_INTERVAL[usize::try_from(current_level).unwrap()]
|
|
.try_into()
|
|
.unwrap(),
|
|
);
|
|
let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
|
|
let encbuckets = self.ba.enc_bridge_table();
|
|
let bucket =
|
|
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
|
.unwrap();
|
|
let reachcred = bucket.1.unwrap();
|
|
let (lvreq, lvstate) = level_up::request(
|
|
cred,
|
|
&reachcred,
|
|
&self.ba.lox_pub,
|
|
&self.ba.reachability_pub,
|
|
self.ba.today(),
|
|
)
|
|
.unwrap();
|
|
let lvresp = self.ba.handle_level_up(lvreq).unwrap();
|
|
let new_cred = level_up::handle_response(lvstate, lvresp, &self.ba.lox_pub).unwrap();
|
|
new_cred
|
|
}
|
|
}
|
|
|
|
fn get_bucket(&mut self, cred: &Lox) -> [BridgeLine; bridge_table::MAX_BRIDGES_PER_BUCKET] {
|
|
let (id, key) = bridge_table::from_scalar(cred.bucket).unwrap();
|
|
let encbuckets = self.ba.enc_bridge_table();
|
|
let bucket =
|
|
bridge_table::BridgeTable::decrypt_bucket(id, &key, encbuckets.get(&id).unwrap())
|
|
.unwrap();
|
|
bucket.0
|
|
}
|
|
}
|
|
|
|
pub fn random() -> BridgeLine {
|
|
let mut rng = rand::thread_rng();
|
|
let mut res: BridgeLine = BridgeLine::default();
|
|
// Pick a random 4-byte address
|
|
let mut addr: [u8; 4] = [0; 4];
|
|
rng.fill_bytes(&mut addr);
|
|
// If the leading byte is 224 or more, that's not a valid IPv4
|
|
// address. Choose an IPv6 address instead (but don't worry too
|
|
// much about it being well formed).
|
|
if addr[0] >= 224 {
|
|
rng.fill_bytes(&mut res.addr);
|
|
} else {
|
|
// Store an IPv4 address as a v4-mapped IPv6 address
|
|
res.addr[10] = 255;
|
|
res.addr[11] = 255;
|
|
res.addr[12..16].copy_from_slice(&addr);
|
|
};
|
|
let ports: [u16; 4] = [443, 4433, 8080, 43079];
|
|
let portidx = (rng.next_u32() % 4) as usize;
|
|
res.port = ports[portidx];
|
|
res.uid_fingerprint = rng.next_u64();
|
|
rng.fill_bytes(&mut res.fingerprint);
|
|
let mut cert: [u8; 52] = [0; 52];
|
|
rng.fill_bytes(&mut cert);
|
|
let infostr: String = format!(
|
|
"obfs4 cert={}, iat-mode=0",
|
|
general_purpose::STANDARD_NO_PAD.encode(cert)
|
|
);
|
|
res.info[..infostr.len()].copy_from_slice(infostr.as_bytes());
|
|
res
|
|
}
|
|
|
|
#[test]
|
|
fn test_negative_reports() {
|
|
let mut th = TestHarness::new();
|
|
|
|
// Get new level 1 credential
|
|
let cred = th.get_new_credential();
|
|
let cred = th.level_up(&cred);
|
|
|
|
let bridges = th.get_bucket(&cred);
|
|
|
|
// Create BridgeVerificationInfo for each bridge
|
|
let mut buckets = HashSet::<Scalar>::new();
|
|
buckets.insert(cred.bucket);
|
|
let bridge_info_1 = BridgeVerificationInfo {
|
|
bridge_line: bridges[0],
|
|
buckets: buckets.clone(),
|
|
pubkey: None,
|
|
};
|
|
let bridge_info_2 = BridgeVerificationInfo {
|
|
bridge_line: bridges[1],
|
|
buckets: buckets.clone(),
|
|
pubkey: None,
|
|
};
|
|
let bridge_info_3 = BridgeVerificationInfo {
|
|
bridge_line: bridges[2],
|
|
buckets: buckets.clone(),
|
|
pubkey: None,
|
|
};
|
|
|
|
// Create reports
|
|
let report_1 =
|
|
NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox);
|
|
let report_2 =
|
|
NegativeReport::from_lox_bucket(bridges[1].fingerprint, cred.bucket, "ru".to_string());
|
|
let report_3 =
|
|
NegativeReport::from_lox_credential(bridges[2].fingerprint, &cred, "ru".to_string());
|
|
|
|
// Backdated reports
|
|
let date = get_date();
|
|
let mut rng = rand::thread_rng();
|
|
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
let report_4 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
|
|
&bridges[0],
|
|
date - 1,
|
|
nonce,
|
|
)),
|
|
"ru".to_string(),
|
|
date - 1,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
let report_5 = NegativeReport::new(
|
|
bridges[1].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
|
|
&bridges[1],
|
|
date - 2,
|
|
nonce,
|
|
)),
|
|
"ru".to_string(),
|
|
date - 2,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
let report_6 = NegativeReport::new(
|
|
bridges[2].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
|
|
&bridges[2],
|
|
date - 3,
|
|
nonce,
|
|
)),
|
|
"ru".to_string(),
|
|
date - 3,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Verify reports
|
|
assert!(report_1.verify(&bridge_info_1));
|
|
assert!(report_2.verify(&bridge_info_2));
|
|
assert!(report_3.verify(&bridge_info_3));
|
|
assert!(report_4.verify(&bridge_info_1));
|
|
assert!(report_5.verify(&bridge_info_2));
|
|
assert!(report_6.verify(&bridge_info_3));
|
|
|
|
// Check that deserialization fails under invalid conditions
|
|
|
|
// Date in the future
|
|
let mut invalid_report_1 =
|
|
NegativeReport::from_bridgeline(bridges[0], "ru".to_string(), BridgeDistributor::Lox)
|
|
.to_serializable_report();
|
|
invalid_report_1.date = invalid_report_1.date + 2;
|
|
|
|
// Date too far in past
|
|
let mut invalid_report_2 =
|
|
NegativeReport::from_bridgeline(bridges[1], "ru".to_string(), BridgeDistributor::Lox)
|
|
.to_serializable_report();
|
|
invalid_report_2.date = invalid_report_2.date - MAX_BACKDATE - 1;
|
|
|
|
// Invalid country code
|
|
let invalid_report_3 =
|
|
NegativeReport::from_bridgeline(bridges[2], "xx".to_string(), BridgeDistributor::Lox)
|
|
.to_serializable_report();
|
|
|
|
assert!(invalid_report_1.to_report().is_err());
|
|
assert!(invalid_report_2.to_report().is_err());
|
|
assert!(invalid_report_3.to_report().is_err());
|
|
|
|
// Check that verification fails with incorrect data
|
|
|
|
let date = get_date();
|
|
let mut rng = rand::thread_rng();
|
|
|
|
// Incorrect BridgeLine hash
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
let invalid_report_4 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
|
|
&BridgeLine::default(),
|
|
date,
|
|
nonce,
|
|
)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Incorrect bucket hash
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
let invalid_report_5 = NegativeReport::new(
|
|
bridges[1].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&Scalar::ZERO, date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
assert!(!invalid_report_4.verify(&bridge_info_1));
|
|
assert!(!invalid_report_5.verify(&bridge_info_2));
|
|
|
|
// Test that reports with duplicate nonces are rejected
|
|
// (Also test encryption and decryption.)
|
|
|
|
// Open test database
|
|
let db: Db = sled::open("test_db_nr").unwrap();
|
|
|
|
// Delete all data in test DB
|
|
db.clear().unwrap();
|
|
assert!(!db.contains_key("nrs-to-process").unwrap());
|
|
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
|
|
// A valid report
|
|
let valid_report_1 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
let valid_report_1_copy_1 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
let valid_report_1_copy_2 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Report which reuses this nonce
|
|
let invalid_report_1 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// This is the same report
|
|
assert_eq!(valid_report_1, invalid_report_1);
|
|
|
|
// Report which reuses this nonce for a different bridge
|
|
let invalid_report_2 = NegativeReport::new(
|
|
bridges[1].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[1], date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Report which uses this nonce but on a different day
|
|
let valid_report_2 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(
|
|
&bridges[0],
|
|
date - 1,
|
|
nonce,
|
|
)),
|
|
"ru".to_string(),
|
|
date - 1,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Report with different nonce
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
|
|
let valid_report_3 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBridgeLine(HashOfBridgeLine::new(&bridges[0], date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
let map_key_1 = format!(
|
|
"{}_{}_{}",
|
|
array_bytes::bytes2hex("", valid_report_1.fingerprint),
|
|
"ru".to_string(),
|
|
date
|
|
);
|
|
|
|
// Generate key for today
|
|
let secret = StaticSecret::random_from_rng(&mut rng);
|
|
let public = PublicKey::from(&secret);
|
|
let secret_yesterday = StaticSecret::random_from_rng(&mut rng);
|
|
let public_yesterday = PublicKey::from(&secret_yesterday);
|
|
assert!(!db.contains_key("nr-keys").unwrap());
|
|
|
|
// Fail to add to database because we can't decrypt
|
|
handle_encrypted_negative_report(&db, valid_report_1_copy_1.encrypt(&public));
|
|
assert!(!db.contains_key("nrs-to-process").unwrap());
|
|
|
|
// Store yesterday's key but not today's
|
|
let mut nr_keys = BTreeMap::<u32, StaticSecret>::new();
|
|
nr_keys.insert(date - 1, secret_yesterday);
|
|
db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
|
|
.unwrap();
|
|
|
|
// Fail to add to database because we still can't decrypt
|
|
handle_encrypted_negative_report(&db, valid_report_1_copy_2.encrypt(&public));
|
|
assert!(!db.contains_key("nrs-to-process").unwrap());
|
|
|
|
// Store today's key
|
|
nr_keys.insert(date, secret);
|
|
db.insert("nr-keys", bincode::serialize(&nr_keys).unwrap())
|
|
.unwrap();
|
|
|
|
handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
|
|
assert_eq!(negative_reports.len(), 1);
|
|
|
|
handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
|
|
assert_eq!(negative_reports.len(), 1);
|
|
|
|
let map_key_2 = format!(
|
|
"{}_{}_{}",
|
|
array_bytes::bytes2hex("", invalid_report_2.fingerprint),
|
|
"ru".to_string(),
|
|
date
|
|
);
|
|
handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
assert!(!nrs_to_process.contains_key(&map_key_2));
|
|
|
|
let map_key_3 = format!(
|
|
"{}_{}_{}",
|
|
array_bytes::bytes2hex("", valid_report_2.fingerprint),
|
|
"ru".to_string(),
|
|
date - 1
|
|
);
|
|
handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
|
|
assert_eq!(negative_reports.len(), 1);
|
|
|
|
handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
|
|
assert_eq!(negative_reports.len(), 2);
|
|
|
|
// Same tests, but use hash of bucket
|
|
|
|
// Delete all data in test DB
|
|
db.clear().unwrap();
|
|
assert!(!db.contains_key("nrs-to-process").unwrap());
|
|
|
|
// Re-generate keys and save in database
|
|
let public = new_negative_report_key(&db, date).unwrap();
|
|
let public_yesterday = new_negative_report_key(&db, date - 1).unwrap();
|
|
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
|
|
// A valid report
|
|
let valid_report_1 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Report which reuses this nonce
|
|
let invalid_report_1 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// This is the same report
|
|
assert_eq!(valid_report_1, invalid_report_1);
|
|
|
|
// Report which reuses this nonce for a different bridge
|
|
let invalid_report_2 = NegativeReport::new(
|
|
bridges[1].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Report which uses this nonce but on a different day
|
|
let valid_report_2 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date - 1, nonce)),
|
|
"ru".to_string(),
|
|
date - 1,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
// Report with different nonce
|
|
let mut nonce = [0; 32];
|
|
rng.fill_bytes(&mut nonce);
|
|
|
|
let valid_report_3 = NegativeReport::new(
|
|
bridges[0].fingerprint,
|
|
ProofOfBridgeKnowledge::HashOfBucket(HashOfBucket::new(&cred.bucket, date, nonce)),
|
|
"ru".to_string(),
|
|
date,
|
|
nonce,
|
|
BridgeDistributor::Lox,
|
|
);
|
|
|
|
let map_key_1 = format!(
|
|
"{}_{}_{}",
|
|
array_bytes::bytes2hex("", valid_report_1.fingerprint),
|
|
"ru".to_string(),
|
|
date
|
|
);
|
|
handle_encrypted_negative_report(&db, valid_report_1.encrypt(&public));
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
|
|
assert_eq!(negative_reports.len(), 1);
|
|
|
|
handle_encrypted_negative_report(&db, invalid_report_1.encrypt(&public)); // no change
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
|
|
assert_eq!(negative_reports.len(), 1);
|
|
|
|
let map_key_2 = format!(
|
|
"{}_{}_{}",
|
|
array_bytes::bytes2hex("", invalid_report_2.fingerprint),
|
|
"ru".to_string(),
|
|
date
|
|
);
|
|
handle_encrypted_negative_report(&db, invalid_report_2.encrypt(&public)); // no change
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
assert!(!nrs_to_process.contains_key(&map_key_2));
|
|
|
|
let map_key_3 = format!(
|
|
"{}_{}_{}",
|
|
array_bytes::bytes2hex("", valid_report_2.fingerprint),
|
|
"ru".to_string(),
|
|
date - 1
|
|
);
|
|
handle_encrypted_negative_report(&db, valid_report_2.encrypt(&public_yesterday));
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_3).unwrap();
|
|
assert_eq!(negative_reports.len(), 1);
|
|
|
|
handle_encrypted_negative_report(&db, valid_report_3.encrypt(&public));
|
|
let nrs_to_process: BTreeMap<String, Vec<SerializableNegativeReport>> =
|
|
bincode::deserialize(&db.get("nrs-to-process").unwrap().unwrap()).unwrap();
|
|
let negative_reports = nrs_to_process.get(&map_key_1).unwrap();
|
|
assert_eq!(negative_reports.len(), 2);
|
|
}
|