cleanup, preparing to make room more generic

This commit is contained in:
tezlm 2024-02-08 23:15:17 -08:00
parent 324b1c7d73
commit 81329e55ee
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
7 changed files with 169 additions and 71 deletions

View file

@ -1,4 +1,4 @@
use std::fmt::{Debug, Display};
use std::{fmt::{Debug, Display}, str::FromStr};
use ed25519_dalek::{Signer, Verifier};
use rand::{distributions::Alphanumeric, Rng};
use serde::{Deserialize, Serialize};
@ -8,7 +8,8 @@ use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD as b64engine};
pub struct RoomId(String);
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub struct EventId(String);
#[serde(try_from = "&str", into = "String")]
pub struct EventId(EventHash);
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ActorId(ed25519::ComponentBytes);
@ -19,33 +20,29 @@ pub struct ActorSecret(ed25519::ComponentBytes);
#[derive(Clone, PartialEq, Eq)]
pub struct ActorSignature(ed25519::Signature);
/* TODO: implement other keypair methods, for extensibility
// #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
// enum ActorIdType {
// Debug(String),
// Ed25519(ed25519::ComponentBytes),
// }
enum ActorIdType {
Debug(String),
Ed25519(ed25519::ComponentBytes),
PostQuantum(Something),
}
// #[derive(Debug, Clone, PartialEq, Eq)]
// enum ActorSecretType {
// Debug(String),
// Ed25519(ed25519::ComponentBytes),
// }
enum ActorSecretType {
Debug(String),
Ed25519(ed25519::ComponentBytes),
PostQuantum(Something),
}
// #[derive(Clone, PartialEq, Eq)]
// enum ActorSignatureType {
// Debug(String),
// Ed25519(ed25519::Signature),
// }
enum ActorSignatureType {
Debug(String),
Ed25519(ed25519::Signature),
PostQuantum(Something),
}
// unsure how to format this
#[derive(Debug, Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
enum EventHash {
Debug(String),
Sha2_256(bytes),
Sha3_512(bytes),
Sha2(Vec<u8>),
}
*/
impl RoomId {
// maybe use the initial create event hash/id as the room id?
@ -93,22 +90,74 @@ impl ActorSecret {
}
impl EventId {
/// creates a Sha2 event id
pub fn from_hash(data: &[u8]) -> Self {
use sha2::Digest;
let hash = sha2::Sha256::digest(data);
Self(format!("{}", b64engine.encode(&hash)))
Self(EventHash::Sha2(hash.to_vec()))
}
}
impl FromStr for EventId {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.strip_prefix("$").unwrap();
let data = b64engine.decode(s).unwrap();
let hash = match data.get(0) {
Some(0x00) => EventHash::Debug(String::from_utf8(data[1..].to_vec()).unwrap()),
Some(0x01) => {
let vec = data[1..].to_vec();
assert_ne!(vec.len(), 32);
EventHash::Sha2(vec)
},
Some(_) => unimplemented!(),
None => panic!("missing version byte"),
};
Ok(EventId(hash))
}
}
impl Display for EventId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (prefix, bytes) = match &self.0 {
EventHash::Debug(d) => (0x00, d.as_bytes()),
EventHash::Sha2(d) => (0x01, d.as_ref()),
};
let data: Vec<u8> = [prefix].into_iter().chain(bytes.to_vec()).collect();
write!(f, "${}", b64engine.encode(data))
}
}
impl From<EventId> for String {
fn from(value: EventId) -> Self {
value.to_string()
}
}
impl TryFrom<&str> for EventId {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
Ok(EventId::from_str(value).unwrap())
}
}
impl Debug for EventId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(self, f)
}
}
impl Debug for ActorId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "^{}", b64engine.encode(&self.0))
Display::fmt(self, f)
}
}
impl Display for ActorId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Debug::fmt(self, f)
write!(f, "^{}", b64engine.encode(&self.0))
}
}
@ -148,7 +197,6 @@ macro_rules! impl_display {
};
}
impl_display!(EventId, "${}");
impl_display!(RoomId, "!{}");
impl Serialize for ActorSignature {

View file

@ -1,4 +1,6 @@
use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {}

View file

@ -1,6 +1,5 @@
use crate::atoms::{ActorId, ActorSecret, ActorSignature, EventId};
use std::fmt::Debug;
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD as b64engine};
use serde::{Serialize, Deserialize};
#[derive(Debug)]
@ -75,6 +74,20 @@ pub enum CoreContent<T> {
/// Every room has exactly one immutable create event as the single root of the event dag.
pub struct CreateContent {
pub resolver: String,
pub hasher: HashType,
pub signer: SignatureType,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum HashType {
Debug = 0,
Sha256 = 1,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum SignatureType {
Debug = 0,
Ed25519 = 1,
}
impl<T: Debug + Serialize + Clone> Event<T> {

View file

@ -1,7 +1,7 @@
#![allow(dead_code)] // TODO: remove this later!
use crate::{
atoms::ActorId, event::CoreContent, resolvers::{KVEventContent, PremadeResolver}, room::Room
atoms::ActorId, event::{CoreContent, HashType, SignatureType}, resolvers::{KVEventContent, KVResolver}, room::Room
};
mod atoms;
@ -13,10 +13,12 @@ mod room;
fn main() {
let (_actor, secret) = ActorId::new_pair();
let resolver = match PremadeResolver::get("kv") {
PremadeResolver::KVResolver(resolver) => resolver,
};
let mut room = Room::new("kv", Box::new(resolver), &secret);
let resolver = Box::new(KVResolver);
let mut room = Room::builder()
.with_resolver(resolver)
.with_hasher(HashType::Sha256)
.with_signer(SignatureType::Ed25519)
.create(&secret);
room.create_event(CoreContent::Custom(KVEventContent::Set(
"foo".into(),
"apple".into(),

View file

@ -1,22 +1,22 @@
use crate::{atoms::EventId, event::Event};
use std::{cmp::Ordering, collections::HashSet, fmt::Debug};
///! small shards of code designed to resolve state
pub trait Resolver {
type Type;
type State;
/// small shards of code designed to resolve state
pub trait Resolver<Type, State> {
/// Given a set of ordered events, resolve the final state
fn resolve(&self, events: &[&Event<Self::Type>]) -> Self::State;
fn resolve(&self, events: &[&Event<Type>]) -> State;
/// Given two events, decide which one comes first
/// if Ordering::Equal is returned, the event id is used
fn tiebreak(
&self,
a: &Event<Self::Type>,
b: &Event<Self::Type>,
a: &Event<Type>,
b: &Event<Type>,
) -> Ordering;
/// TEMP: Get the name/id of this resolver
fn name(&self) -> &str;
}
/// topologically sort a list of events
@ -51,7 +51,7 @@ pub fn sort<T>(
sorted
}
impl<T, S> Debug for dyn Resolver<Type = T, State = S> {
impl<T, S> Debug for dyn Resolver<T, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,

View file

@ -7,23 +7,6 @@ use std::{cmp::Ordering, collections::BTreeMap};
///! contains premade resolvers, for testing
// type KVRoom = Room<KVEventContent, KVState>;
#[derive(Debug)]
#[non_exhaustive]
pub enum PremadeResolver {
KVResolver(KVResolver),
}
impl PremadeResolver {
pub fn get(name: &str) -> PremadeResolver {
match name {
"kv" => PremadeResolver::KVResolver(KVResolver),
_ => unimplemented!("resolver does not exist"),
}
}
}
#[derive(Debug)]
/// A basic key-value store
pub struct KVResolver;
@ -38,11 +21,8 @@ pub struct KVState {
entries: BTreeMap<String, String>,
}
impl Resolver for KVResolver {
type Type = KVEventContent;
type State = KVState;
fn resolve(&self, events: &[&Event<Self::Type>]) -> Self::State {
impl Resolver<KVEventContent, KVState> for KVResolver {
fn resolve(&self, events: &[&Event<KVEventContent>]) -> KVState {
dbg!(events);
let mut kv = BTreeMap::new();
for event in events {
@ -56,7 +36,11 @@ impl Resolver for KVResolver {
KVState { entries: kv }
}
fn tiebreak(&self, _a: &Event<Self::Type>, _b: &Event<Self::Type>) -> Ordering {
fn tiebreak(&self, _a: &Event<KVEventContent>, _b: &Event<KVEventContent>) -> Ordering {
Ordering::Equal
}
fn name(&self) -> &str {
"kv"
}
}

View file

@ -2,27 +2,35 @@ use serde::Serialize;
use crate::{
atoms::{ActorSecret, EventId},
event::{CoreContent, CreateContent, Event},
event::{CoreContent, CreateContent, Event, HashType, SignatureType},
resolver::{sort, Resolver},
};
use std::fmt::Debug;
#[derive(Debug)]
pub struct Room<E, S> {
pub events: Vec<Event<E>>,
pub struct Room<T, S> {
pub events: Vec<Event<T>>,
pub heads: Vec<EventId>,
pub resolver: Box<dyn Resolver<Type = E, State = S>>,
pub resolver: Box<dyn Resolver<T, S>>,
}
impl<T: Debug + Serialize + Clone, S> Room<T, S> {
pub fn builder() -> RoomBuilder<T, S> {
RoomBuilder::new()
}
pub fn new(
resolver_name: impl Into<String>,
resolver: Box<dyn Resolver<Type = T, State = S>>,
resolver: Box<dyn Resolver<T, S>>,
hasher: HashType,
signer: SignatureType,
secret: &ActorSecret,
) -> Self {
let base_event = Event::builder(
CoreContent::Create(CreateContent {
resolver: resolver_name.into(),
hasher,
signer,
}),
secret,
)
@ -42,7 +50,7 @@ impl<T: Debug + Serialize + Clone, S> Room<T, S> {
.expect("room always has a root event")
}
pub fn get_resolver(&self) -> &dyn Resolver<Type = T, State = S> {
pub fn get_resolver(&self) -> &dyn Resolver<T, S> {
self.resolver.as_ref()
}
@ -67,3 +75,44 @@ impl<T: Debug + Serialize + Clone, S> Room<T, S> {
self.heads = vec![event_id];
}
}
pub struct RoomBuilder<T, S> {
resolver: Option<Box<dyn Resolver<T, S>>>,
hasher: Option<HashType>,
signer: Option<SignatureType>,
}
impl<T: Clone + Debug + Serialize, S> RoomBuilder<T, S> {
pub fn new() -> Self {
Self {
resolver: None,
hasher: None,
signer: None,
}
}
pub fn with_resolver(mut self, resolver: Box<dyn Resolver<T, S>>) -> Self {
self.resolver = Some(resolver);
self
}
pub fn with_hasher(mut self, hash_type: HashType) -> Self {
self.hasher = Some(hash_type);
self
}
pub fn with_signer(mut self, signature_type: SignatureType) -> Self {
self.signer = Some(signature_type);
self
}
pub fn create(self, secret: &ActorSecret) -> Room<T, S> {
Room::<T, S>::new(
self.resolver.as_ref().unwrap().name().to_owned(),
self.resolver.unwrap(),
self.hasher.unwrap(),
self.signer.unwrap(),
secret,
)
}
}