Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/feed_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
169 changes: 159 additions & 10 deletions src/go_set/go_set_claim.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,169 @@
use crate::feed_id::FeedId;
use crate::go_set::{GOSet, GOSetXor};
use crate::go_set::GOSetXor;

use crate::Error;
#[derive(Debug, PartialEq)]
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 {
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)
// ```
Comment on lines +19 to +24
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔥 TODO


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 = 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,
&type_code,
&lowest_feed_id,
&highest_feed_id,
&xor,
&count,
];

let mut offset: usize = 0;
for chunk in chunks {
let len = chunk.len();
claim[offset..offset + len].copy_from_slice(chunk);
offset += len;
}

claim
}

pub fn decode(bytes: &[u8; 105]) -> Result<Self, GOSetClaimError> {
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,
];

let mut offset: usize = 0;

for chunk in chunks {
let len = chunk.len();
chunk.copy_from_slice(&bytes[offset..offset + len]);
offset += len;
}

if !dmx.eq(&Self::GOSET_DMX) {
return Err(GOSetClaimError::InvalidDMX);
}

if !type_code.eq(&[b'c']) {
return Err(GOSetClaimError::InvalidTypeCode);
}

Ok(GOSetClaim {
lowest_feed_id: FeedId(lowest_feed_id),
highest_feed_id: FeedId(highest_feed_id),
xor: GOSetXor(xor),
count: count[0],
})
}
pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
todo!()
}

#[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]).unwrap();

let claim = go_set.create_claim().unwrap();
let encoded_claim = claim.encode();
println!("encoded claim:\n {:?}", claim);

let result = GOSetClaim::decode(&encoded_claim).unwrap();
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]).unwrap();

let claim = go_set.create_claim().unwrap();
let mut encoded_claim = claim.encode();
let wrong_dmx = [6; 7];
encoded_claim[0..7].copy_from_slice(&wrong_dmx);
println!("claim with incorrect dmx:\n {:?}", claim);

match GOSetClaim::decode(&encoded_claim) {
Err(GOSetClaimError::InvalidDMX) => {} // passed
_ => panic!("Expected InvalidDMX error"),
}
}

#[test]
fn claim_wrong_type_code() {
let feed_a = FeedId([255; 32]);
let go_set = GOSet::new(&[feed_a]).unwrap();

let claim = go_set.create_claim().unwrap();
let mut encoded_claim = claim.encode();
let wrong_type_code = [b'W'];
encoded_claim[7..8].copy_from_slice(&wrong_type_code);
println!("claim with incorrect type_code:\n {:?}", claim);

match GOSetClaim::decode(&encoded_claim) {
Err(GOSetClaimError::InvalidTypeCode) => {} // passed
_ => panic!("Expected InvalidTypeCode error"),
}
}
}
2 changes: 2 additions & 0 deletions src/go_set/go_set_xor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down
82 changes: 74 additions & 8 deletions src/go_set/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,115 @@ 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<FeedId>,
}
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<Self, Error> {
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 {
todo!()
// NOTE: u8 is the max currently supported by the spec
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()
}

pub fn create_claim(&self) -> Option<GOSetClaim> {
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<GOSetClaim> 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<GOSet> 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)]
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]).unwrap();
assert_eq!(go_set.count(), 2)
}

#[test]
fn xor() {
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]);
}

Expand All @@ -58,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);
}
Expand All @@ -69,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);
}
Expand Down
15 changes: 8 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,19 +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]
Expand Down