lib: Membership proofs [failing]

This commit is contained in:
Davie Li 2024-04-24 18:22:38 +00:00
parent 8275508d8c
commit a57de247d4

View file

@ -3,10 +3,11 @@ use std::collections::HashMap;
use ed25519_dalek as dalek; use ed25519_dalek as dalek;
use ed25519_dalek::Verifier; use ed25519_dalek::Verifier;
use rand::prelude::random; use rand::prelude::random;
use rs_merkle::algorithms::Sha256 as Sha256Algorithm; // Imports from sha2 crate
use rs_merkle::{MerkleProof, MerkleTree}; use rs_merkle::{MerkleProof, MerkleTree};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
pub use rs_merkle::algorithms::Sha256 as DefaultHasher; // Imports from sha2 crate
/// TODO: Use hashes instead of public addresses. /// TODO: Use hashes instead of public addresses.
/// TODO: Don't forget document endiannes. /// TODO: Don't forget document endiannes.
/// TODO: Allow doubly signed obligations or implement abstract interfaces for Cert (see below). /// TODO: Allow doubly signed obligations or implement abstract interfaces for Cert (see below).
@ -67,23 +68,63 @@ fn pseudo_anon_id(a: Address) -> PseudoAnonID {
} }
pub struct Members { pub struct Members {
data: MerkleTree<Sha256Algorithm>, pub data: MerkleTree<DefaultHasher>,
indicies: HashMap<PseudoAnonID, usize>, indicies: HashMap<PseudoAnonID, usize>,
} }
pub type MembershipProof = MerkleProof<Sha256Algorithm>; pub struct Member {
pub id: PseudoAnonID,
index: usize,
}
pub struct MembershipProof {
subjects: Vec<Member>,
root: [u8; 32],
hashes: MerkleProof<DefaultHasher>,
}
impl MembershipProof {
pub fn verify(&self) -> bool {
self.hashes.verify(
self.root,
&self.subjects.iter().map(|x| x.index).collect::<Vec<_>>(),
&self.subjects.iter().map(|x| x.id).collect::<Vec<_>>(),
self.subjects.len(),
)
}
}
impl Members { impl Members {
pub fn add(mut self, m: Address) { pub fn add(mut self, m: Address) {
self.data.insert(m).commit() self.data.insert(m).commit()
} }
pub fn proof(&self, members: &[PseudoAnonID]) -> MembershipProof { pub fn proof(&self, subjects: Vec<PseudoAnonID>) -> Result<MembershipProof, &'static str> {
self.data.proof( if (1..subjects.len()).any(|i| subjects[i..].contains(&subjects[i - 1])) {
&members return Err("Duplicate subjects");
}
let (subjects, indices) = {
let mut tosort = subjects
.iter() .iter()
.map(|x| *self.indicies.get(x).unwrap()) .map(|x| Member {
.collect::<Vec<_>>(), id: *x,
) index: *self.indicies.get(x).unwrap(),
})
.collect::<Vec<_>>();
tosort.sort_by_key(|x| x.index);
let indices: Vec<usize> = Vec::from_iter(tosort.iter().map(|x| x.index));
(tosort, indices)
};
Ok(MembershipProof {
subjects: subjects,
hashes: self.data.proof(&indices),
root: self.data.root().unwrap(),
})
}
pub fn index(&self, member: PseudoAnonID) -> Option<usize> {
match self.indicies.get(&member) {
Some(i) => Some(*i),
None => None,
}
} }
} }
@ -117,6 +158,12 @@ mod tests {
use ed25519_dalek::Signer; use ed25519_dalek::Signer;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use rand::seq::IteratorRandom; use rand::seq::IteratorRandom;
use std::collections::HashSet;
#[allow(dead_code)]
pub fn make_address() -> Address {
random()
}
fn sk_gen() -> (dalek::SigningKey, [u8; dalek::SECRET_KEY_LENGTH], Address) { fn sk_gen() -> (dalek::SigningKey, [u8; dalek::SECRET_KEY_LENGTH], Address) {
let skobj = dalek::SigningKey::from_bytes(&random()); let skobj = dalek::SigningKey::from_bytes(&random());
@ -177,13 +224,70 @@ mod tests {
v v
}); });
static MEMBERS: Lazy<Members> = Lazy::new(|| Members { static MEMBERS: Lazy<Members> = Lazy::new(|| {
data: MerkleTree::<Sha256Algorithm>::from_leaves( let tree = MerkleTree::<DefaultHasher>::from_leaves(
&ADDRESSES.keys().map(|x| x.clone()).collect::<Vec<_>>(), &ADDRESSES.keys().cloned().collect::<Vec<PseudoAnonID>>(),
), );
indicies: HashMap::from_iter(ADDRESSES.keys().enumerate().map(|(i, x)| (x.clone(), i))), let mut indices: HashMap<PseudoAnonID, usize> = HashMap::with_capacity(tree.leaves_len());
tree.leaves()
.unwrap()
.iter()
.enumerate()
.for_each(|(i, x)| {
indices.insert(*x, i);
});
Members {
data: tree,
indicies: indices,
}
}); });
fn rand_member() -> Option<PseudoAnonID> {
MEMBERS
.indicies
.keys()
.choose(&mut rand::thread_rng())
.cloned()
}
fn rand_unique_member_gen() -> Box<impl FnMut() -> PseudoAnonID> {
let mut cache: HashSet<PseudoAnonID> = HashSet::new();
// note: will go in endless loop if the cache >= set of members
Box::new(move || loop {
let rmem = rand_member();
if cache.get(&rmem.unwrap()).is_some() {
continue;
}
cache.insert(rmem.unwrap());
break rmem.unwrap();
})
}
fn sort_by_indices(m: Vec<PseudoAnonID>) -> Vec<PseudoAnonID> {
let mut res = m.clone();
res.sort_by_cached_key(|x| MEMBERS.indicies.get(x));
res
}
fn rand_members(n: usize) -> Vec<PseudoAnonID> {
let l = if n > ADDRESSES.len() {
ADDRESSES.len()
} else {
n
};
let mut f = rand_unique_member_gen();
sort_by_indices(std::iter::from_fn(|| Some(f())).take(l).collect())
}
fn rand_obligation() -> Obligation {
let mut r = rand_unique_member_gen();
Obligation {
from: r(),
to: r(),
value: random(),
}
}
#[test] #[test]
fn serialize_obligation() { fn serialize_obligation() {
assert_eq!( assert_eq!(
@ -213,19 +317,7 @@ mod tests {
#[test] #[test]
fn membership_cert_new() { fn membership_cert_new() {
fn rand_member() -> PseudoAnonID { let o = rand_obligation();
assert_ne!(ADDRESSES.len(), 0);
*MEMBERS
.indicies
.keys()
.choose(&mut rand::thread_rng())
.unwrap()
}
let o = Obligation {
from: rand_member(),
to: rand_member(),
value: 1000,
};
let _c = Cert::new( let _c = Cert::new(
o, o,
AuthCert::new( AuthCert::new(
@ -238,8 +330,14 @@ mod tests {
.into(), .into(),
) )
.expect("Auth failed"), .expect("Auth failed"),
MEMBERS.proof(&[o.from, o.to]), MEMBERS.proof([o.from, o.to].to_vec()).unwrap(),
); );
assert!(true) }
// FIXME: Failing test - some weirdnes in rs_merkle or indexes fiddling.
#[test]
#[ignore]
fn test_merkle() {
assert!(MEMBERS.proof(rand_members(2)).unwrap().verify());
} }
} }