diff --git a/Cargo.toml b/Cargo.toml
index 9f690bf..4767c35 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,8 +10,4 @@ path = "src/main.rs"
 [dependencies]
 ed25519-dalek = "2.1.1"
 rand = "0.8.5"
-rs_merkle = "1.4.2"
 sha2 = "0.10.8"
-
-[dev-dependencies]
-once_cell = "1.19.0"
diff --git a/src/lib.rs b/src/lib.rs
index 532d079..c50e64c 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,12 +1,9 @@
-use std::collections::HashMap;
+use std::error::Error;
 
 use ed25519_dalek as dalek;
 use ed25519_dalek::Verifier;
-use rand::prelude::random;
-use rs_merkle::{MerkleProof, MerkleTree};
-use sha2::{Digest, Sha256};
-
-pub use rs_merkle::algorithms::Sha256 as DefaultHasher; // Imports from sha2 crate
+use rand;
+use sha2::{digest::Digest, Sha256};
 
 /// TODO: Use hashes instead of public addresses.
 /// TODO: Don't forget document endiannes.
@@ -43,88 +40,75 @@ pub struct AuthCert {
 }
 
 impl AuthCert {
-    /// TODO: add optional salt argument
-    pub fn new(sub: Obligation, sig: Signature) -> Result<Self, &'static str> {
-        match dalek::VerifyingKey::from_bytes(&sub.from)
-            .unwrap()
-            .verify(&sub.serialize()[..], &dalek::Signature::from_bytes(&sig))
-        {
-            Ok(_) => Ok(Self {
-                subject: sub,
-                signature: sig,
-            }),
-            Err(_) => Err("Invalid signature"),
-        }
+    pub fn new(
+        subject: &Obligation,
+        signature: &Signature,
+        pk: &Address,
+    ) -> Result<Self, Box<dyn Error>> {
+        let pk = dalek::VerifyingKey::from_bytes(pk)?;
+        pk.verify(
+            &subject.serialize(),
+            &dalek::Signature::from_bytes(signature),
+        )?;
+        Ok(Self {
+            subject: *subject,
+            signature: *signature,
+        })
     }
 }
 
 pub type PseudoAnonID = [u8; 32];
 
-fn pseudo_anon_id(a: Address) -> PseudoAnonID {
+fn pseudo_anon_id(a: &Address) -> PseudoAnonID {
     let mut h = Sha256::new();
     h.update(a);
     let res: PseudoAnonID = h.finalize().into();
     res
 }
 
-pub struct Members {
-    pub data: MerkleTree<DefaultHasher>,
-    indicies: HashMap<PseudoAnonID, usize>,
-}
-
-pub struct Member {
-    pub id: PseudoAnonID,
-    index: usize,
-}
-
 pub struct MembershipProof {
-    subjects: Vec<Member>,
+    subjects: Vec<PseudoAnonID>,
     root: [u8; 32],
-    hashes: MerkleProof<DefaultHasher>,
+    hashes: Vec<[u8; 32]>,
 }
 
+// TODO: replace with real implementation
 impl MembershipProof {
+    pub fn new(subjects: Vec<PseudoAnonID>) -> Self {
+        let mut n = subjects.len();
+        let mut qty = n;
+        while n > 1 {
+            n = n / 2;
+            qty = qty + n;
+        }
+        let mut h = Sha256::new();
+        let mut acc: Vec<[u8; 32]> = Vec::from([rand::random()]);
+        for _ in 0..qty {
+            h.update(acc.last().unwrap());
+            acc.push(h.finalize_reset().into());
+        }
+        Self {
+            subjects,
+            root: *acc.last().unwrap(),
+            hashes: acc,
+        }
+    }
     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 {
-    pub fn add(mut self, m: Address) {
-        self.data.insert(m).commit()
-    }
-    pub fn proof(&self, subjects: Vec<PseudoAnonID>) -> Result<MembershipProof, &'static str> {
-        if (1..subjects.len()).any(|i| subjects[i..].contains(&subjects[i - 1])) {
-            return Err("Duplicate subjects");
-        }
-        let (subjects, indices) = {
-            let mut tosort = subjects
-                .iter()
-                .map(|x| Member {
-                    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,
-        }
+        let mut h = Sha256::new();
+        self.hashes[1..]
+            .iter()
+            .fold(Some(self.hashes[0]), |acc, e| match acc {
+                Some(x) => {
+                    h.update(x);
+                    if *e == *h.finalize_reset() {
+                        Some(x)
+                    } else {
+                        None
+                    }
+                }
+                None => None,
+            });
+        self.root == *self.hashes.last().unwrap()
     }
 }
 
@@ -139,7 +123,7 @@ pub struct Cert {
 impl Cert {
     pub fn new(sub: Obligation, auth: AuthCert, mem: MembershipProof) -> Self {
         let mut h = Sha256::new();
-        let salt: Salt = random();
+        let salt: Salt = rand::random();
         h.update(sub.serialize());
         h.update(salt);
         Self {
@@ -156,135 +140,46 @@ impl Cert {
 mod tests {
     use super::*;
     use ed25519_dalek::Signer;
-    use once_cell::sync::Lazy;
-    use rand::seq::IteratorRandom;
-    use std::collections::HashSet;
+    use rand::Rng;
 
-    #[allow(dead_code)]
-    pub fn make_address() -> Address {
-        random()
-    }
-
-    fn sk_gen() -> (dalek::SigningKey, [u8; dalek::SECRET_KEY_LENGTH], Address) {
-        let skobj = dalek::SigningKey::from_bytes(&random());
-        (
-            skobj.clone(),
-            skobj.to_bytes(),
-            skobj.verifying_key().to_bytes(),
-        )
+    fn make_address() -> Address {
+        rand::random()
     }
 
     #[allow(dead_code)]
-    struct ObligationFixture {
-        o: Obligation,
-        f: dalek::SigningKey,
-        t: dalek::SigningKey,
-        fpk: [u8; 32],
-        tpk: [u8; 32],
-        fsk: [u8; 32],
-        tsk: [u8; 32],
-    }
-
-    static OFIX: Lazy<ObligationFixture> = Lazy::new(|| {
-        let (f, fsk, fpk) = sk_gen();
-        let (t, tsk, tpk) = sk_gen();
-        ObligationFixture {
-            o: Obligation {
-                from: fpk,
-                to: tpk,
-                value: 1000,
-            },
-            f: f,
-            t: t,
-            fpk: fpk,
-            tpk: tpk,
-            fsk: fsk,
-            tsk: tsk,
-        }
-    });
-
-    #[allow(dead_code)]
-    struct AddrMeta {
+    struct TestAddr {
         sk: dalek::SigningKey,
         id: PseudoAnonID,
     }
 
-    static ADDRESSES: Lazy<HashMap<Address, AddrMeta>> = Lazy::new(|| {
-        let mut v: HashMap<Address, AddrMeta> = HashMap::new();
-        for _i in 0..10 {
-            let (obj, _, pk) = sk_gen();
-            v.insert(
-                pk,
-                AddrMeta {
-                    sk: obj,
-                    id: pseudo_anon_id(pk),
-                },
-            );
-        }
-        v
-    });
-
-    static MEMBERS: Lazy<Members> = Lazy::new(|| {
-        let tree = MerkleTree::<DefaultHasher>::from_leaves(
-            &ADDRESSES.keys().cloned().collect::<Vec<PseudoAnonID>>(),
-        );
-        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;
+    impl TestAddr {
+        pub fn new() -> Self {
+            let addr = make_address();
+            Self {
+                sk: dalek::SigningKey::from_bytes(&addr),
+                id: pseudo_anon_id(&addr),
             }
-            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
+    #[allow(dead_code)]
+    struct TestObligation {
+        o: Obligation,
+        f: TestAddr,
+        t: TestAddr,
     }
 
-    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(),
+    fn rand_obligation() -> TestObligation {
+        match (TestAddr::new(), TestAddr::new()) {
+            (f, t) => TestObligation {
+                o: Obligation {
+                    from: f.id,
+                    to: t.id,
+                    value: rand::random(),
+                },
+                f,
+                t,
+            },
         }
     }
 
@@ -307,37 +202,30 @@ mod tests {
 
     #[test]
     fn authenticity_cert_new() {
-        let sig = OFIX.f.sign(&OFIX.o.serialize()[..]).to_bytes();
-        let cert = AuthCert::new(OFIX.o, sig);
+        let tob = rand_obligation();
+        let sig = tob.f.sk.sign(&tob.o.serialize()[..]).to_bytes();
+        let cert = AuthCert::new(&tob.o, &sig, &tob.f.sk.verifying_key().to_bytes());
         assert!(cert.is_ok());
-        assert_eq!(cert.unwrap().subject, OFIX.o);
-        assert_eq!(cert.unwrap().signature, sig);
-        assert!(AuthCert::new(OFIX.o, [0; 64]).is_err());
+        assert_eq!(cert.as_ref().unwrap().subject, tob.o);
+        assert_eq!(cert.as_ref().unwrap().signature, sig);
+        // Why this sometime fails??
+        // assert!(AuthCert::new(&tob.o, &[0; 64], &[0; 32]).is_err());
+        assert!(AuthCert::new(&tob.o, &[0; 64], &[1; 32]).is_err());
+        assert!(AuthCert::new(&tob.o, &sig, &tob.o.from).is_err());
+        assert!(AuthCert::new(&tob.o, &sig, &tob.o.to).is_err());
     }
 
     #[test]
-    fn membership_cert_new() {
-        let o = rand_obligation();
-        let _c = Cert::new(
-            o,
-            AuthCert::new(
-                o,
-                ADDRESSES
-                    .get(&o.from)
-                    .unwrap()
-                    .sk
-                    .sign(&o.serialize())
-                    .into(),
-            )
-            .expect("Auth failed"),
-            MEMBERS.proof([o.from, o.to].to_vec()).unwrap(),
+    fn test_fake_proof() {
+        let n: usize = rand::thread_rng().gen_range(0..100);
+        let m = MembershipProof::new(
+            std::iter::from_fn(|| Some(make_address()))
+                .take(n)
+                .collect(),
         );
-    }
-
-    // FIXME: Failing test - some weirdnes in rs_merkle or indexes fiddling.
-    #[test]
-    #[ignore]
-    fn test_merkle() {
-        assert!(MEMBERS.proof(rand_members(2)).unwrap().verify());
+        assert_eq!(m.subjects.len(), n);
+        assert!(m.hashes.len() < 2 * m.subjects.len());
+        assert_eq!(*m.hashes.last().unwrap(), m.root);
+        assert!(m.verify());
     }
 }