From 1d76127d3383ad35d4444db891fe3b8dff75d20b Mon Sep 17 00:00:00 2001 From: mixmix Date: Mon, 5 May 2025 12:18:44 +1200 Subject: [PATCH 1/4] WIP - add basic goset encode/decode --- src/feed_id.rs | 2 + src/go_set/go_set_claim.rs | 117 +++++++++++++++++++++++++++++++++++-- src/go_set/go_set_xor.rs | 2 + src/go_set/mod.rs | 19 +++++- src/lib.rs | 1 + 5 files changed, 135 insertions(+), 6 deletions(-) diff --git a/src/feed_id.rs b/src/feed_id.rs index 14fb413..97926d2 100644 --- a/src/feed_id.rs +++ b/src/feed_id.rs @@ -7,6 +7,8 @@ use std::cmp::Ordering; pub struct FeedId(pub [u8; 32]); impl FeedId { + pub const LENGTH: usize = 32; + pub fn encode(&self) -> [u8; 32] { self.0.clone() } diff --git a/src/go_set/go_set_claim.rs b/src/go_set/go_set_claim.rs index 1e24da9..25a2b39 100644 --- a/src/go_set/go_set_claim.rs +++ b/src/go_set/go_set_claim.rs @@ -3,6 +3,7 @@ use crate::go_set::{GOSet, GOSetXor}; use crate::Error; +#[derive(Debug)] pub struct GOSetClaim { lowest_feed_id: FeedId, highest_feed_id: FeedId, @@ -11,10 +12,118 @@ pub struct GOSetClaim { } impl GOSetClaim { - pub fn encode_go_set(go_set: &GOSet) -> [u8; 105] { - todo!() + const GOSET_DMX: [u8; 7] = [0, 1, 2, 3, 4, 5, 6]; + // TODO: calculate actual DMX + // ``` + // GOSET_DMX_MATERIAL = "tinySSB-0.1 GOset 1" + // GOSET_DMX = first 7 bytes of SHA256(GOSET_DMX_MATERIAL) + // ``` + + pub fn encode(go_set: &GOSet) -> [u8; 105] { + // NOTE: we're gonna want to make a range of claims + // 1. how do we deal with the concept of a "subset" of a claim? + // 2. where does the logic live about comparing a claim with a goset? + // 3. what's the algorithm for the claim-dance? + + // [DMX (7B) | 'c' (1 byte) | lowest FeedId (32 bytes) | highest FeedId (32 bytes) | XOR (32 bytes) | cnt (1 byte) ] + let mut claim = [0; 105]; + + let mut offset: usize = 0; + + // DMX + for (i, byte) in Self::GOSET_DMX.into_iter().enumerate() { + claim[offset + i] = byte; + } + offset += Self::GOSET_DMX.len(); + + // c + let byte = "c".as_bytes()[0]; + claim[offset] = byte; + offset += 1; + + // lowest FeedId + let lowest_feed_id_bytes = go_set.lowest_feed_id().unwrap().encode(); + for (i, byte) in lowest_feed_id_bytes.into_iter().enumerate() { + claim[offset + i] = byte; + } + offset += lowest_feed_id_bytes.len(); + + // highest FeedId + let highest_feed_id_bytes = go_set.highest_feed_id().unwrap().encode(); + for (i, byte) in highest_feed_id_bytes.into_iter().enumerate() { + claim[offset + i] = byte; + } + offset += highest_feed_id_bytes.len(); + + let xor = go_set.xor().encode(); + for (i, byte) in xor.into_iter().enumerate() { + claim[offset + i] = byte; + } + offset += xor.len(); + + let count = go_set.count(); + claim[offset] = count; + + claim + } + + pub fn decode(bytes: &[u8; 105]) -> Result { + let mut offset: usize = 0; + // TODO: check DMX + offset += Self::GOSET_DMX.len(); + + // TODO: check type: c + offset += 1; + + let mut lowest_feed_id_bytes = [0; FeedId::LENGTH]; + for i in 0..FeedId::LENGTH { + lowest_feed_id_bytes[i] = bytes[offset + i]; + } + offset += FeedId::LENGTH; + + let mut highest_feed_id_bytes = [0; FeedId::LENGTH]; + for i in 0..FeedId::LENGTH { + highest_feed_id_bytes[i] = bytes[offset + i]; + } + offset += FeedId::LENGTH; + + let mut xor_bytes = [0; GOSetXor::LENGTH]; + for i in 0..GOSetXor::LENGTH { + xor_bytes[i] = bytes[offset + i]; + } + offset += GOSetXor::LENGTH; + + let count = bytes[offset]; + + Ok(GOSetClaim { + lowest_feed_id: FeedId(lowest_feed_id_bytes), + highest_feed_id: FeedId(highest_feed_id_bytes), + xor: GOSetXor(xor_bytes), + count: count, + }) } - pub fn decode(bytes: &[u8]) -> Result { - todo!() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn claim_round_trip() { + let feed_a = FeedId([255; 32]); + let feed_b = FeedId([1; 32]); + let feed_c = FeedId([2; 32]); + + let go_set = GOSet::new(&[feed_a, feed_b, feed_c]); + let claim = GOSetClaim::encode(&go_set); + println!("encoded claim: {:?}", claim); + + let result = GOSetClaim::decode(&claim).unwrap(); + println!("decoded claim: {:?}", result); + + assert_eq!(&result.lowest_feed_id, go_set.lowest_feed_id().unwrap()); + assert_eq!(&result.highest_feed_id, go_set.highest_feed_id().unwrap()); + assert_eq!(result.xor, go_set.xor()); + assert_eq!(result.count, go_set.count()); } } diff --git a/src/go_set/go_set_xor.rs b/src/go_set/go_set_xor.rs index e994e68..5fc2aaa 100644 --- a/src/go_set/go_set_xor.rs +++ b/src/go_set/go_set_xor.rs @@ -2,6 +2,8 @@ pub struct GOSetXor(pub [u8; 32]); impl GOSetXor { + pub const LENGTH: usize = 32; + pub fn encode(&self) -> [u8; 32] { self.0.clone() } diff --git a/src/go_set/mod.rs b/src/go_set/mod.rs index 1603c80..f1b821d 100644 --- a/src/go_set/mod.rs +++ b/src/go_set/mod.rs @@ -19,21 +19,27 @@ impl GOSet { feed_ids: new_feed_ids, } } + pub fn count(&self) -> u8 { - todo!() + // NOTE: u8 is the max currently supported by the spec + // WARNING: this panics if it doesn't fit u8 + self.feed_ids.len().try_into().unwrap() } + pub fn xor(&self) -> GOSetXor { let mut xor: [u8; 32] = [0; 32]; for feed_id in self.feed_ids.iter() { for i in 0..32 { - xor[i] = xor[i] ^ feed_id.0[i]; + xor[i] ^= feed_id.0[i]; } } GOSetXor(xor) } + pub fn highest_feed_id(&self) -> Option<&FeedId> { self.feed_ids.last() } + pub fn lowest_feed_id(&self) -> Option<&FeedId> { self.feed_ids.first() } @@ -43,6 +49,15 @@ impl GOSet { mod tests { use super::*; + #[test] + fn count() { + let feed_a = FeedId([255; 32]); + let feed_b = FeedId([1; 32]); + + let go_set = GOSet::new(&[feed_a, feed_b]); + assert_eq!(go_set.count(), 2) + } + #[test] fn xor() { let feed_a = FeedId([255; 32]); diff --git a/src/lib.rs b/src/lib.rs index 6fec252..c7de5cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub mod go_set; use go_set::GOSetClaim; +#[derive(Debug)] pub enum Error {} enum WirePacket { From 8e62176d245bcaf7cd83860274c19e4280760daf Mon Sep 17 00:00:00 2001 From: mixmix Date: Mon, 5 May 2025 13:45:21 +1200 Subject: [PATCH 2/4] refactor go_set_claim, add dmx+type tests --- src/go_set/go_set_claim.rs | 148 ++++++++++++++++++++++--------------- src/lib.rs | 14 ++-- 2 files changed, 96 insertions(+), 66 deletions(-) diff --git a/src/go_set/go_set_claim.rs b/src/go_set/go_set_claim.rs index 25a2b39..a62e8f4 100644 --- a/src/go_set/go_set_claim.rs +++ b/src/go_set/go_set_claim.rs @@ -3,6 +3,12 @@ use crate::go_set::{GOSet, GOSetXor}; use crate::Error; +#[derive(Debug, PartialEq)] +enum GOSetClaimError { + InvlalidDMX, + InvalidTypeCode, +} + #[derive(Debug)] pub struct GOSetClaim { lowest_feed_id: FeedId, @@ -28,78 +34,70 @@ impl GOSetClaim { // [DMX (7B) | 'c' (1 byte) | lowest FeedId (32 bytes) | highest FeedId (32 bytes) | XOR (32 bytes) | cnt (1 byte) ] let mut claim = [0; 105]; - let mut offset: usize = 0; - - // DMX - for (i, byte) in Self::GOSET_DMX.into_iter().enumerate() { - claim[offset + i] = byte; - } - offset += Self::GOSET_DMX.len(); - - // c - let byte = "c".as_bytes()[0]; - claim[offset] = byte; - offset += 1; - - // lowest FeedId - let lowest_feed_id_bytes = go_set.lowest_feed_id().unwrap().encode(); - for (i, byte) in lowest_feed_id_bytes.into_iter().enumerate() { - claim[offset + i] = byte; - } - offset += lowest_feed_id_bytes.len(); + let dmx = Self::GOSET_DMX; + let type_code: [u8; 1] = [b'c']; + let lowest_feed_id = go_set.lowest_feed_id().unwrap().encode(); + let highest_feed_id = go_set.highest_feed_id().unwrap().encode(); + let xor = go_set.xor().encode(); + let count: [u8; 1] = [go_set.count()]; - // highest FeedId - let highest_feed_id_bytes = go_set.highest_feed_id().unwrap().encode(); - for (i, byte) in highest_feed_id_bytes.into_iter().enumerate() { - claim[offset + i] = byte; - } - offset += highest_feed_id_bytes.len(); + let chunks: [&[u8]; 6] = [ + &dmx, + &type_code, + &lowest_feed_id, + &highest_feed_id, + &xor, + &count, + ]; - let xor = go_set.xor().encode(); - for (i, byte) in xor.into_iter().enumerate() { - claim[offset + i] = byte; + let mut offset: usize = 0; + for chunk in chunks { + let len = chunk.len(); + claim[offset..offset + len].copy_from_slice(chunk); + offset += len; } - offset += xor.len(); - - let count = go_set.count(); - claim[offset] = count; claim } - pub fn decode(bytes: &[u8; 105]) -> Result { - let mut offset: usize = 0; - // TODO: check DMX - offset += Self::GOSET_DMX.len(); + pub fn decode(bytes: &[u8; 105]) -> Result { + let mut dmx = [0; Self::GOSET_DMX.len()]; + let mut type_code = [0; 1]; + let mut lowest_feed_id = [0; FeedId::LENGTH]; + let mut highest_feed_id = [0; FeedId::LENGTH]; + let mut xor = [0; GOSetXor::LENGTH]; + let mut count = [0; 1]; + + let chunks: [&mut [u8]; 6] = [ + &mut dmx, + &mut type_code, + &mut lowest_feed_id, + &mut highest_feed_id, + &mut xor, + &mut count, + ]; - // TODO: check type: c - offset += 1; + let mut offset: usize = 0; - let mut lowest_feed_id_bytes = [0; FeedId::LENGTH]; - for i in 0..FeedId::LENGTH { - lowest_feed_id_bytes[i] = bytes[offset + i]; + for chunk in chunks { + let len = chunk.len(); + chunk.copy_from_slice(&bytes[offset..offset + len]); + offset += len; } - offset += FeedId::LENGTH; - let mut highest_feed_id_bytes = [0; FeedId::LENGTH]; - for i in 0..FeedId::LENGTH { - highest_feed_id_bytes[i] = bytes[offset + i]; + if !dmx.eq(&Self::GOSET_DMX) { + return Err(GOSetClaimError::InvlalidDMX); } - offset += FeedId::LENGTH; - let mut xor_bytes = [0; GOSetXor::LENGTH]; - for i in 0..GOSetXor::LENGTH { - xor_bytes[i] = bytes[offset + i]; + if !type_code.eq(&[b'c']) { + return Err(GOSetClaimError::InvalidTypeCode); } - offset += GOSetXor::LENGTH; - - let count = bytes[offset]; Ok(GOSetClaim { - lowest_feed_id: FeedId(lowest_feed_id_bytes), - highest_feed_id: FeedId(highest_feed_id_bytes), - xor: GOSetXor(xor_bytes), - count: count, + lowest_feed_id: FeedId(lowest_feed_id), + highest_feed_id: FeedId(highest_feed_id), + xor: GOSetXor(xor), + count: count[0], }) } } @@ -113,17 +111,49 @@ mod tests { let feed_a = FeedId([255; 32]); let feed_b = FeedId([1; 32]); let feed_c = FeedId([2; 32]); - let go_set = GOSet::new(&[feed_a, feed_b, feed_c]); + let claim = GOSetClaim::encode(&go_set); - println!("encoded claim: {:?}", claim); + println!("encoded claim:\n {:?}", claim); let result = GOSetClaim::decode(&claim).unwrap(); - println!("decoded claim: {:?}", result); + println!("decoded claim:\n {:?}", result); assert_eq!(&result.lowest_feed_id, go_set.lowest_feed_id().unwrap()); assert_eq!(&result.highest_feed_id, go_set.highest_feed_id().unwrap()); assert_eq!(result.xor, go_set.xor()); assert_eq!(result.count, go_set.count()); } + + #[test] + fn claim_wrong_dmx() { + let feed_a = FeedId([255; 32]); + let go_set = GOSet::new(&[feed_a]); + + let mut claim = GOSetClaim::encode(&go_set); + let wrong_dmx = [6; 7]; + claim[0..7].copy_from_slice(&wrong_dmx); + println!("claim with incorrect dmx:\n {:?}", claim); + + match GOSetClaim::decode(&claim) { + Err(GOSetClaimError::InvlalidDMX) => {} // passed + _ => panic!("Expected InvalidDMX error"), + } + } + + #[test] + fn claim_wrong_type_code() { + let feed_a = FeedId([255; 32]); + let go_set = GOSet::new(&[feed_a]); + + let mut claim = GOSetClaim::encode(&go_set); + let wrong_type_code = [b'W']; + claim[7..8].copy_from_slice(&wrong_type_code); + println!("claim with incorrect type_code:\n {:?}", claim); + + match GOSetClaim::decode(&claim) { + Err(GOSetClaimError::InvalidTypeCode) => {} // passed + _ => panic!("Expected InvalidTypeCode error"), + } + } } diff --git a/src/lib.rs b/src/lib.rs index c7de5cd..0840f58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,20 +1,20 @@ pub mod feed_id; pub mod go_set; -use go_set::GOSetClaim; +// use go_set::GOSetClaim; #[derive(Debug)] pub enum Error {} -enum WirePacket { - // Dunno about this. Might be bytes not sure - Replication(GOSetClaim), - Log(), -} +// enum WirePacket { +// // Dunno about this. Might be bytes not sure +// Replication(GOSetClaim), +// Log(), +// } #[cfg(test)] mod tests { - use super::*; + // use super::*; use bipf_rs::bipf::{Bipf, decode}; #[test] From 62065d3870ba855033195dd9d48f19c21530630f Mon Sep 17 00:00:00 2001 From: mixmix Date: Mon, 5 May 2025 13:53:01 +1200 Subject: [PATCH 3/4] typo fix --- src/go_set/go_set_claim.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/go_set/go_set_claim.rs b/src/go_set/go_set_claim.rs index a62e8f4..7181dee 100644 --- a/src/go_set/go_set_claim.rs +++ b/src/go_set/go_set_claim.rs @@ -5,7 +5,7 @@ use crate::Error; #[derive(Debug, PartialEq)] enum GOSetClaimError { - InvlalidDMX, + InvalidDMX, InvalidTypeCode, } @@ -86,7 +86,7 @@ impl GOSetClaim { } if !dmx.eq(&Self::GOSET_DMX) { - return Err(GOSetClaimError::InvlalidDMX); + return Err(GOSetClaimError::InvalidDMX); } if !type_code.eq(&[b'c']) { @@ -136,7 +136,7 @@ mod tests { println!("claim with incorrect dmx:\n {:?}", claim); match GOSetClaim::decode(&claim) { - Err(GOSetClaimError::InvlalidDMX) => {} // passed + Err(GOSetClaimError::InvalidDMX) => {} // passed _ => panic!("Expected InvalidDMX error"), } } From b2b97128c98457897dc5bf05b85b3cbb7d15eace Mon Sep 17 00:00:00 2001 From: Piet Geursen Date: Mon, 5 May 2025 21:25:54 +1200 Subject: [PATCH 4/4] make some suggestions --- src/go_set/go_set_claim.rs | 58 +++++++++++++++++++-------------- src/go_set/mod.rs | 67 +++++++++++++++++++++++++++++++++----- 2 files changed, 93 insertions(+), 32 deletions(-) diff --git a/src/go_set/go_set_claim.rs b/src/go_set/go_set_claim.rs index 7181dee..46baf15 100644 --- a/src/go_set/go_set_claim.rs +++ b/src/go_set/go_set_claim.rs @@ -1,20 +1,18 @@ use crate::feed_id::FeedId; -use crate::go_set::{GOSet, GOSetXor}; - -use crate::Error; +use crate::go_set::GOSetXor; #[derive(Debug, PartialEq)] -enum GOSetClaimError { +pub enum GOSetClaimError { InvalidDMX, InvalidTypeCode, } #[derive(Debug)] pub struct GOSetClaim { - lowest_feed_id: FeedId, - highest_feed_id: FeedId, - xor: GOSetXor, - count: u8, + pub(crate) lowest_feed_id: FeedId, + pub(crate) highest_feed_id: FeedId, + pub(crate) xor: GOSetXor, + pub(crate) count: u8, } impl GOSetClaim { @@ -25,21 +23,29 @@ impl GOSetClaim { // GOSET_DMX = first 7 bytes of SHA256(GOSET_DMX_MATERIAL) // ``` - pub fn encode(go_set: &GOSet) -> [u8; 105] { + pub fn encode(&self) -> [u8; 105] { // NOTE: we're gonna want to make a range of claims // 1. how do we deal with the concept of a "subset" of a claim? + // PG: I think that GoSets should implement methods that let you create new sets from + // themselves. So GoSet could have a `bisect(&self) -> GoSet` method. If bisect is the + // right word // 2. where does the logic live about comparing a claim with a goset? + // PG: I implemented PartialEq on GoSet where the right hand side is GoSetClaim. // 3. what's the algorithm for the claim-dance? + // PG: That's gonna live in some new type we haven't made yet + // + // PG: I think that if GoSet.lowest_feed_id returns an option then GoSetClaim + // lowest_feed_id should also be an option. Same for highest obvs. // [DMX (7B) | 'c' (1 byte) | lowest FeedId (32 bytes) | highest FeedId (32 bytes) | XOR (32 bytes) | cnt (1 byte) ] let mut claim = [0; 105]; let dmx = Self::GOSET_DMX; let type_code: [u8; 1] = [b'c']; - let lowest_feed_id = go_set.lowest_feed_id().unwrap().encode(); - let highest_feed_id = go_set.highest_feed_id().unwrap().encode(); - let xor = go_set.xor().encode(); - let count: [u8; 1] = [go_set.count()]; + let lowest_feed_id = self.lowest_feed_id.encode(); + let highest_feed_id = self.highest_feed_id.encode(); + let xor = self.xor.encode(); + let count: [u8; 1] = [self.count]; let chunks: [&[u8]; 6] = [ &dmx, @@ -105,18 +111,20 @@ impl GOSetClaim { #[cfg(test)] mod tests { use super::*; + use crate::go_set::GOSet; #[test] fn claim_round_trip() { let feed_a = FeedId([255; 32]); let feed_b = FeedId([1; 32]); let feed_c = FeedId([2; 32]); - let go_set = GOSet::new(&[feed_a, feed_b, feed_c]); + let go_set = GOSet::new(&[feed_a, feed_b, feed_c]).unwrap(); - let claim = GOSetClaim::encode(&go_set); + let claim = go_set.create_claim().unwrap(); + let encoded_claim = claim.encode(); println!("encoded claim:\n {:?}", claim); - let result = GOSetClaim::decode(&claim).unwrap(); + let result = GOSetClaim::decode(&encoded_claim).unwrap(); println!("decoded claim:\n {:?}", result); assert_eq!(&result.lowest_feed_id, go_set.lowest_feed_id().unwrap()); @@ -128,14 +136,15 @@ mod tests { #[test] fn claim_wrong_dmx() { let feed_a = FeedId([255; 32]); - let go_set = GOSet::new(&[feed_a]); + let go_set = GOSet::new(&[feed_a]).unwrap(); - let mut claim = GOSetClaim::encode(&go_set); + let claim = go_set.create_claim().unwrap(); + let mut encoded_claim = claim.encode(); let wrong_dmx = [6; 7]; - claim[0..7].copy_from_slice(&wrong_dmx); + encoded_claim[0..7].copy_from_slice(&wrong_dmx); println!("claim with incorrect dmx:\n {:?}", claim); - match GOSetClaim::decode(&claim) { + match GOSetClaim::decode(&encoded_claim) { Err(GOSetClaimError::InvalidDMX) => {} // passed _ => panic!("Expected InvalidDMX error"), } @@ -144,14 +153,15 @@ mod tests { #[test] fn claim_wrong_type_code() { let feed_a = FeedId([255; 32]); - let go_set = GOSet::new(&[feed_a]); + let go_set = GOSet::new(&[feed_a]).unwrap(); - let mut claim = GOSetClaim::encode(&go_set); + let claim = go_set.create_claim().unwrap(); + let mut encoded_claim = claim.encode(); let wrong_type_code = [b'W']; - claim[7..8].copy_from_slice(&wrong_type_code); + encoded_claim[7..8].copy_from_slice(&wrong_type_code); println!("claim with incorrect type_code:\n {:?}", claim); - match GOSetClaim::decode(&claim) { + match GOSetClaim::decode(&encoded_claim) { Err(GOSetClaimError::InvalidTypeCode) => {} // passed _ => panic!("Expected InvalidTypeCode error"), } diff --git a/src/go_set/mod.rs b/src/go_set/mod.rs index f1b821d..18105d9 100644 --- a/src/go_set/mod.rs +++ b/src/go_set/mod.rs @@ -6,23 +6,32 @@ use crate::feed_id::FeedId; pub use go_set_claim::GOSetClaim; pub use go_set_xor::GOSetXor; +#[derive(Debug)] +pub enum Error { + TooManyFeedIds, +} + pub struct GOSet { feed_ids: Vec, } impl GOSet { - pub fn new(feed_ids: &[FeedId]) -> Self { + // PG: I made this return a result because it's better to ensure the GoSet is valid at + // construction time than blow up later on. If you've got a GoSet instance it's valid. + pub fn new(feed_ids: &[FeedId]) -> Result { + if feed_ids.len() > u8::MAX as usize { + return Err(Error::TooManyFeedIds); + } let mut new_feed_ids = Vec::from(feed_ids); // TODO: discuss from() magic with Piet new_feed_ids.sort(); - Self { + Ok(Self { feed_ids: new_feed_ids, - } + }) } pub fn count(&self) -> u8 { // NOTE: u8 is the max currently supported by the spec - // WARNING: this panics if it doesn't fit u8 self.feed_ids.len().try_into().unwrap() } @@ -43,6 +52,48 @@ impl GOSet { pub fn lowest_feed_id(&self) -> Option<&FeedId> { self.feed_ids.first() } + + pub fn create_claim(&self) -> Option { + match ( + self.lowest_feed_id().cloned(), + self.highest_feed_id().cloned(), + ) { + (Some(lowest_feed_id), Some(highest_feed_id)) => { + let xor = self.xor(); + let count = self.count(); + + Some(GOSetClaim { + highest_feed_id, + lowest_feed_id, + xor, + count, + }) + } + _ => None, + } + } +} + +impl PartialEq for GOSet { + fn eq(&self, other: &GOSetClaim) -> bool { + // TODO: If we change the GoSetClaim feeds to be options then we'll need to update the + // comparison here. + self.xor() == other.xor + && self.highest_feed_id() == Some(&other.highest_feed_id) + && self.lowest_feed_id() == Some(&other.lowest_feed_id) + && self.count() == other.count + } +} + +impl PartialEq for GOSetClaim { + fn eq(&self, other: &GOSet) -> bool { + // TODO: If we change the GoSetClaim feeds to be options then we'll need to update the + // comparison here. + self.xor == other.xor() + && Some(&self.highest_feed_id) == other.highest_feed_id() + && Some(&self.lowest_feed_id) == other.lowest_feed_id() + && self.count == other.count() + } } #[cfg(test)] @@ -54,7 +105,7 @@ mod tests { let feed_a = FeedId([255; 32]); let feed_b = FeedId([1; 32]); - let go_set = GOSet::new(&[feed_a, feed_b]); + let go_set = GOSet::new(&[feed_a, feed_b]).unwrap(); assert_eq!(go_set.count(), 2) } @@ -63,7 +114,7 @@ mod tests { let feed_a = FeedId([255; 32]); let feed_b = FeedId([1; 32]); - let go_set = GOSet::new(&[feed_a, feed_b]); + let go_set = GOSet::new(&[feed_a, feed_b]).unwrap(); assert_eq!(go_set.xor().encode(), [254; 32]); } @@ -73,7 +124,7 @@ mod tests { let feed_b = FeedId([2; 32]); let feed_c = FeedId([3; 32]); - let go_set = GOSet::new(&[feed_b, feed_a, feed_c]); + let go_set = GOSet::new(&[feed_b, feed_a, feed_c]).unwrap(); assert_eq!(go_set.highest_feed_id().unwrap(), &feed_c); } @@ -84,7 +135,7 @@ mod tests { let feed_b = FeedId([2; 32]); let feed_c = FeedId([3; 32]); - let go_set = GOSet::new(&[feed_b, feed_a, feed_c]); + let go_set = GOSet::new(&[feed_b, feed_a, feed_c]).unwrap(); assert_eq!(go_set.lowest_feed_id().unwrap(), &feed_a); }