Skip to content

Commit ff5a4d4

Browse files
committed
Add tests for quorum satisfaction logic
1 parent ea358f6 commit ff5a4d4

2 files changed

Lines changed: 132 additions & 32 deletions

File tree

src/lib.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,20 @@
1313
//! # Usage
1414
//!
1515
//! ```rust
16-
//! use sigsum::{verify, Hash, Policy, PublicKey, SigsumSignature};
16+
//! # use std::error::Error;
17+
//! use sigsum::{verify, Hash, PolicyBuilder, PublicKey, SigsumSignature};
1718
//! use hex_literal::hex;
1819
//!
1920
//! let data = b"Hello, Sigsum!";
2021
//! let signers: Vec<PublicKey> =
2122
//! vec![hex!("8a9b8b9f45a826b541c1477861a458c7b30ef1cc9600c515d593c4e2f72f375e").into()];
22-
//! let policy = Policy::new_k_of_n(
23-
//! vec![hex!("4644af2abd40f4895a003bca350f9d5912ab301a49c77f13e5b6d905c20a5fe6").into()],
24-
//! vec![hex!("4a921b7caef58ae670cdc11ef4184f1c058f7b9259a9107a969f69fa54aa496f").into()],
25-
//! 1,
26-
//! );
23+
//! let mut policy = PolicyBuilder::new();
24+
//! policy
25+
//! .add_log(hex!("4644af2abd40f4895a003bca350f9d5912ab301a49c77f13e5b6d905c20a5fe6").into(), None)?
26+
//! .add_witness(String::from("mywitness"), hex!("4a921b7caef58ae670cdc11ef4184f1c058f7b9259a9107a969f69fa54aa496f").into(), None)?
27+
//! .set_quorum(String::from("mywitness"))?;
28+
//! let policy = policy
29+
//! .build();
2730
//! let signature = SigsumSignature::from_ascii("version=2
2831
//! log=4e89cc51651f0d95f3c6127c15e1a42e3ddf7046c5b17b752689c402e773bb4d
2932
//! leaf=2ca612aaa355c19a0cc7ebaacb04723b97e873df4dbadd0f97a2e00a13d8f76a 1a5fcc2b05fb6ca66dbc7e62bcc7934fb0ebf49bba501c285222550a67e590fa3237b338ba59d2327800788b85ff9e80ea88c71157519974c70d10c825e65401
@@ -43,7 +46,7 @@
4346
//! node_hash=e8bb977d7ae35a4b7e591ded5e3d7fad0afee0b958d6309a52f48fe46c679c36
4447
//! ")?;
4548
//! assert!(verify(&Hash::new(data), signature, signers, &policy).is_ok());
46-
//! # Ok::<(), sigsum::ParseAsciiError>(())
49+
//! # Ok::<(), Box<dyn Error>>(())
4750
//! ```
4851
//!
4952
//! # Contributing
@@ -66,6 +69,6 @@ mod verify;
6669
pub use crypto::{Hash, PublicKey, Signature};
6770
pub use io::ascii::ParseAsciiError;
6871
pub use log::{InclusionProof, Leaf, Protoleaf, SignedTreeHead, WitnessCosignature};
69-
pub use policy::Policy;
72+
pub use policy::{Policy, PolicyBuilder};
7073
pub use sigsumsig::SigsumSignature;
7174
pub use verify::verify;

src/policy/mod.rs

Lines changed: 121 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ impl Policy {
8181
})
8282
}
8383

84-
/// Checks whether a set of signatures by all `keyhashes` would satisfy the quorum of this
85-
/// policy.
84+
/// satisfies_quorum_policy checks whether a set of signatures by all keys corresponding to`keyhashes`
85+
/// would satisfy the quorum of this policy.
8686
/// This function does not check that those keyhashes actually signed anything.
8787
pub fn satisfies_quorum_policy(&self, keyhashes: &[Hash]) -> bool {
8888
match &self.quorum {
@@ -150,6 +150,7 @@ pub enum PolicyError {
150150
QuorumAlreadySet,
151151
}
152152

153+
#[derive(Debug)]
153154
pub struct PolicyBuilder(Policy);
154155

155156
impl Default for PolicyBuilder {
@@ -167,21 +168,21 @@ impl PolicyBuilder {
167168
quorum: None,
168169
})
169170
}
170-
pub fn add_log(&mut self, key: PublicKey, url: Option<String>) -> Result<(), PolicyError> {
171+
pub fn add_log(&mut self, key: PublicKey, url: Option<String>) -> Result<&mut Self, PolicyError> {
171172
let keyhash = Hash::new(&key);
172173
if self.0.logs.contains_key(&keyhash) {
173174
return Err(PolicyError::DuplicateLogKey(key));
174175
}
175176
self.0.logs.insert(keyhash, Entity(key, url));
176-
Ok(())
177+
Ok(self)
177178
}
178179

179180
pub fn add_witness(
180181
&mut self,
181182
name: String,
182183
key: PublicKey,
183184
url: Option<String>,
184-
) -> Result<(), PolicyError> {
185+
) -> Result<&mut Self, PolicyError> {
185186
let keyhash = Hash::new(&key);
186187
if self.0.witnesses.contains_key(&keyhash) {
187188
return Err(PolicyError::DuplicateWitnessKey(key));
@@ -191,15 +192,15 @@ impl PolicyBuilder {
191192
}
192193
self.0.witnesses.insert(keyhash.clone(), Entity(key, url));
193194
self.0.quorums.insert(name, Quorum::Witness(keyhash));
194-
Ok(())
195+
Ok(self)
195196
}
196197

197198
pub fn add_group(
198199
&mut self,
199200
name: String,
200201
k: usize,
201202
members: Vec<String>,
202-
) -> Result<(), PolicyError> {
203+
) -> Result<&mut Self, PolicyError> {
203204
if self.0.quorums.contains_key(&name) {
204205
return Err(PolicyError::DuplicateName(name));
205206
}
@@ -209,18 +210,18 @@ impl PolicyBuilder {
209210
}
210211
}
211212
self.0.quorums.insert(name, Quorum::Group { k, members });
212-
Ok(())
213+
Ok(self)
213214
}
214215

215-
pub fn set_quorum(&mut self, name: String) -> Result<(), PolicyError> {
216+
pub fn set_quorum(&mut self, name: String) -> Result<&mut Self, PolicyError> {
216217
if self.0.quorum.is_some() {
217218
return Err(PolicyError::QuorumAlreadySet);
218219
}
219220
if !self.0.quorums.contains_key(&name) {
220221
return Err(PolicyError::UnknownName(name));
221222
}
222223
self.0.quorum = Some(name);
223-
Ok(())
224+
Ok(self)
224225
}
225226

226227
pub fn build(self) -> Policy {
@@ -354,8 +355,8 @@ mod tests {
354355
hex!("ec5681da2b676ab81df2daea3254cd8c4a5149318a62ae3bec6b4e80504b3b24").into();
355356
let mut builder = PolicyBuilder::new();
356357
builder.add_log(key.clone(), None).unwrap();
357-
let res = builder.add_log(key.clone(), Some("https://example.org".into()));
358-
assert_eq!(Err(PolicyError::DuplicateLogKey(key)), res);
358+
let res = builder.add_log(key.clone(), Some("https://example.org".into())).unwrap_err();
359+
assert_eq!(PolicyError::DuplicateLogKey(key), res);
359360
insta::assert_debug_snapshot!(builder.build());
360361
}
361362

@@ -371,8 +372,8 @@ mod tests {
371372
"mywitness2".into(),
372373
key.clone(),
373374
Some("https://example.com".into()),
374-
);
375-
assert_eq!(Err(PolicyError::DuplicateWitnessKey(key)), res);
375+
).unwrap_err();
376+
assert_eq!(PolicyError::DuplicateWitnessKey(key), res);
376377
insta::assert_debug_snapshot!(builder.build());
377378
}
378379

@@ -386,28 +387,28 @@ mod tests {
386387
let mut builder = PolicyBuilder::new();
387388
builder.add_witness(name.clone(), key1, None).unwrap();
388389

389-
let res1 = builder.add_group(name.clone(), 1, vec!["foo".into()]);
390-
assert_eq!(Err(PolicyError::DuplicateName(name.clone())), res1);
390+
let res1 = builder.add_group(name.clone(), 1, vec!["foo".into()]).unwrap_err();
391+
assert_eq!(PolicyError::DuplicateName(name.clone()), res1);
391392

392-
let res2 = builder.add_witness(name.clone(), key2, None);
393-
assert_eq!(Err(PolicyError::DuplicateName(name)), res2);
393+
let res2 = builder.add_witness(name.clone(), key2, None).unwrap_err();
394+
assert_eq!(PolicyError::DuplicateName(name), res2);
394395

395396
insta::assert_debug_snapshot!(builder.build());
396397
}
397398

398399
#[test]
399400
fn uplicate_unknown_member_name() {
400401
let mut builder = PolicyBuilder::new();
401-
let res = builder.add_group("mygroup".into(), 1, vec!["mywitness".into()]);
402-
assert_eq!(Err(PolicyError::UnknownName("mywitness".into())), res);
402+
let res = builder.add_group("mygroup".into(), 1, vec!["mywitness".into()]).unwrap_err();
403+
assert_eq!(PolicyError::UnknownName("mywitness".into()), res);
403404
insta::assert_debug_snapshot!(builder.build());
404405
}
405406

406407
#[test]
407408
fn unknown_quorum_name() {
408409
let mut builder = PolicyBuilder::new();
409-
let res = builder.set_quorum("mywitness".into());
410-
assert_eq!(Err(PolicyError::UnknownName("mywitness".into())), res);
410+
let res = builder.set_quorum("mywitness".into()).unwrap_err();
411+
assert_eq!(PolicyError::UnknownName("mywitness".into()), res);
411412
insta::assert_debug_snapshot!(builder.build());
412413
}
413414

@@ -425,8 +426,104 @@ mod tests {
425426
.add_witness("mywitness2".into(), key2, None)
426427
.unwrap();
427428
builder.set_quorum("mywitness1".into()).unwrap();
428-
let res = builder.set_quorum("mywitness2".into());
429-
assert_eq!(Err(PolicyError::QuorumAlreadySet), res);
429+
let res = builder.set_quorum("mywitness2".into()).unwrap_err();
430+
assert_eq!(PolicyError::QuorumAlreadySet, res);
430431
assert_eq!(Some("mywitness1".into()), builder.build().quorum);
431432
}
433+
434+
#[test]
435+
fn witness_quorum_satisfied() {
436+
let key1: PublicKey =
437+
hex!("ec5681da2b676ab81df2daea3254cd8c4a5149318a62ae3bec6b4e80504b3b24").into();
438+
let mut builder = PolicyBuilder::new();
439+
builder
440+
.add_witness("mywitness1".into(), key1.clone(), None).unwrap()
441+
.set_quorum("mywitness1".into()).unwrap();
442+
let policy = builder.build();
443+
444+
assert!(policy.satisfies_quorum_policy(&[Hash::new(key1)]));
445+
}
446+
447+
#[test]
448+
fn one_of_two_quorum_satisfied() {
449+
let key1: PublicKey =
450+
hex!("ec5681da2b676ab81df2daea3254cd8c4a5149318a62ae3bec6b4e80504b3b24").into();
451+
let key2: PublicKey =
452+
hex!("d9440882ae2bd57076d4da2e7a12d4b26e137d56116419a69f8d6969709ed747").into();
453+
let mut builder = PolicyBuilder::new();
454+
builder
455+
.add_witness("mywitness1".into(), key1.clone(), None)
456+
.unwrap()
457+
.add_witness("mywitness2".into(), key2, None)
458+
.unwrap()
459+
.add_group(String::from("myquorum"), 1, vec!["mywitness1".to_string(), "mywitness2".to_string()]).unwrap()
460+
.set_quorum("myquorum".into()).unwrap();
461+
let policy = builder.build();
462+
463+
assert!(policy.satisfies_quorum_policy(&[Hash::new(key1)]));
464+
}
465+
466+
#[test]
467+
fn complex_quorum_satisfied() {
468+
let key1: PublicKey =
469+
hex!("ec5681da2b676ab81df2daea3254cd8c4a5149318a62ae3bec6b4e80504b3b24").into();
470+
let key2: PublicKey =
471+
hex!("d9440882ae2bd57076d4da2e7a12d4b26e137d56116419a69f8d6969709ed747").into();
472+
let key3: PublicKey =
473+
hex!("ac5681da2b676ab81df2daea3254cd8c4a5149318a62ae3bec6b4e80504b3b24").into();
474+
let key4: PublicKey =
475+
hex!("b9440882ae2bd57076d4da2e7a12d4b26e137d56116419a69f8d6969709ed747").into();
476+
let key5: PublicKey =
477+
hex!("aa5681da2b676ab81df2daea3254cd8c4a5149318a62ae3bec6b4e80504b3b24").into();
478+
let key6: PublicKey =
479+
hex!("bc440882ae2bd57076d4da2e7a12d4b26e137d56116419a69f8d6969709ed747").into();
480+
let key7: PublicKey =
481+
hex!("bc440882ae2bd57076d4da2e7a12d4b26e137d56116419a69f8d6969709ed74a").into();
482+
483+
let mut builder = PolicyBuilder::new();
484+
485+
builder
486+
.add_witness("mywitness1".into(), key1.clone(), None)
487+
.unwrap()
488+
.add_witness("mywitness2".into(), key2.clone(), None)
489+
.unwrap()
490+
.add_group(String::from("group1"), 1, vec!["mywitness1".to_string(), "mywitness2".to_string()]).unwrap();
491+
492+
builder
493+
.add_witness("mywitness3".into(), key3.clone(), None)
494+
.unwrap()
495+
.add_witness("mywitness4".into(), key4.clone(), None)
496+
.unwrap()
497+
.add_group(String::from("group2"), 2, vec!["mywitness3".to_string(), "mywitness4".to_string()]).unwrap();
498+
499+
builder
500+
.add_witness("mywitness5".into(), key5.clone(), None)
501+
.unwrap()
502+
.add_witness("mywitness6".into(), key6.clone(), None)
503+
.unwrap()
504+
.add_witness("mywitness7".into(), key7.clone(), None)
505+
.unwrap()
506+
.add_group(String::from("group3"), 2, vec!["mywitness5".to_string(), "mywitness6".to_string(), "mywitness7".to_string()]).unwrap();
507+
508+
builder
509+
.add_group(String::from("finalgroup"), 2, vec!["group1".to_string(), "group2".to_string(), "group3".to_string()]).unwrap()
510+
.set_quorum(String::from("finalgroup")).unwrap();
511+
512+
let policy = builder.build();
513+
514+
assert!(policy.satisfies_quorum_policy(&[Hash::new(&key1), Hash::new(&key3), Hash::new(&key4)]));
515+
516+
assert!(!policy.satisfies_quorum_policy(&[Hash::new(&key1), Hash::new(&key3), Hash::new(&key5)]));
517+
518+
assert!(policy.satisfies_quorum_policy(&[Hash::new(&key1), Hash::new(&key3), Hash::new(&key5), Hash::new(&key6)]));
519+
520+
assert!(!policy.satisfies_quorum_policy(&[Hash::new(&key3), Hash::new(&key4)]));
521+
522+
assert!(policy.satisfies_quorum_policy(&[Hash::new(&key3), Hash::new(&key4), Hash::new(&key5), Hash::new(&key7)]));
523+
524+
assert!(policy.satisfies_quorum_policy(&[Hash::new(&key1), Hash::new(&key3), Hash::new(&key4), Hash::new(&key7), Hash::new(&key5)]));
525+
526+
assert!(!policy.satisfies_quorum_policy(&[Hash::new(&key3), Hash::new(&key4), Hash::new(&key6)]));
527+
}
528+
432529
}

0 commit comments

Comments
 (0)