Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
147 changes: 143 additions & 4 deletions src/go_set/go_set_claim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ use crate::go_set::{GOSet, GOSetXor};

use crate::Error;

#[derive(Debug, PartialEq)]
enum GOSetClaimError {
InvalidDMX,
InvalidTypeCode,
}

#[derive(Debug)]
pub struct GOSetClaim {
lowest_feed_id: FeedId,
highest_feed_id: FeedId,
Expand All @@ -11,10 +18,142 @@ 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)
// ```
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(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 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 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
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is the tidiest / most readable way I could write the pattern of concatenating / splitting.
I'm quite interested to know if there is a tidier was to concat/ split, as this feels... somewhat clunky (esp for concat).

ChatGPT suggested copy_from_slice which I was delighted to find. I was doing byte-wise copying before that!


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],
})
}
}

#[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:\n {:?}", claim);

let result = GOSetClaim::decode(&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());
}
pub fn decode(bytes: &[u8]) -> Result<Self, Error> {
todo!()

#[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::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]);

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"),
}
}
}
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
19 changes: 17 additions & 2 deletions src/go_set/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand All @@ -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]);
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