cleanup, preparing to make room more generic
This commit is contained in:
parent
324b1c7d73
commit
81329e55ee
7 changed files with 169 additions and 71 deletions
102
src/atoms.rs
102
src/atoms.rs
|
@ -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 {
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
use thiserror::Error;
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {}
|
||||
|
|
15
src/event.rs
15
src/event.rs
|
@ -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> {
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -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(),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
61
src/room.rs
61
src/room.rs
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue