hash + verify events
This commit is contained in:
parent
0daa4be975
commit
324b1c7d73
6 changed files with 218 additions and 30 deletions
90
Cargo.lock
generated
90
Cargo.lock
generated
|
@ -2,6 +2,21 @@
|
|||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "aho-corasick"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.21.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
|
@ -17,6 +32,19 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "canonical_json"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f89083fd014d71c47a718d7f4ac050864dac8587668dbe90baf9e261064c5710"
|
||||
dependencies = [
|
||||
"hex",
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -80,11 +108,13 @@ dependencies = [
|
|||
name = "dag-resolve"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"canonical_json",
|
||||
"ed25519",
|
||||
"ed25519-dalek",
|
||||
"hex",
|
||||
"rand",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"thiserror",
|
||||
]
|
||||
|
@ -167,12 +197,24 @@ version = "0.4.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
|
@ -243,6 +285,35 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-automata",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-automata"
|
||||
version = "0.4.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5bb987efffd3c6d0d8f5f89510bb458559eab11e4f869acb20bf845e016259cd"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr",
|
||||
"regex-syntax",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.0"
|
||||
|
@ -252,6 +323,12 @@ dependencies = [
|
|||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.21"
|
||||
|
@ -278,6 +355,17 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.113"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69801b70b1c3dac963ecb03a364ba0ceda9cf60c71cfe475e99864759c8b8a79"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
|
|
|
@ -6,10 +6,12 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
base64 = "0.21.7"
|
||||
canonical_json = "0.5.0"
|
||||
ed25519 = "2.2.3"
|
||||
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
|
||||
hex = "0.4.3"
|
||||
rand = "0.8.5"
|
||||
serde = { version = "1.0.196", features = ["derive"] }
|
||||
serde_json = "1.0.113"
|
||||
sha2 = "0.10.8"
|
||||
thiserror = "1.0.56"
|
||||
|
|
85
src/atoms.rs
85
src/atoms.rs
|
@ -1,11 +1,13 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
use ed25519_dalek::{Signer, Verifier};
|
||||
use rand::{distributions::Alphanumeric, Rng};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use base64::{Engine, engine::general_purpose::URL_SAFE_NO_PAD as b64engine};
|
||||
|
||||
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct RoomId(String);
|
||||
|
||||
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
|
||||
pub struct EventId(String);
|
||||
|
||||
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
|
@ -17,6 +19,34 @@ pub struct ActorSecret(ed25519::ComponentBytes);
|
|||
#[derive(Clone, PartialEq, Eq)]
|
||||
pub struct ActorSignature(ed25519::Signature);
|
||||
|
||||
/* TODO: implement other keypair methods, for extensibility
|
||||
|
||||
enum ActorIdType {
|
||||
Debug(String),
|
||||
Ed25519(ed25519::ComponentBytes),
|
||||
PostQuantum(Something),
|
||||
}
|
||||
|
||||
enum ActorSecretType {
|
||||
Debug(String),
|
||||
Ed25519(ed25519::ComponentBytes),
|
||||
PostQuantum(Something),
|
||||
}
|
||||
|
||||
enum ActorSignatureType {
|
||||
Debug(String),
|
||||
Ed25519(ed25519::Signature),
|
||||
PostQuantum(Something),
|
||||
}
|
||||
|
||||
// unsure how to format this
|
||||
enum EventHash {
|
||||
Debug(String),
|
||||
Sha2_256(bytes),
|
||||
Sha3_512(bytes),
|
||||
}
|
||||
*/
|
||||
|
||||
impl RoomId {
|
||||
// maybe use the initial create event hash/id as the room id?
|
||||
// ie. !foobar refers to the room while $foobar refers to the event
|
||||
|
@ -63,26 +93,36 @@ impl ActorSecret {
|
|||
}
|
||||
|
||||
impl EventId {
|
||||
// TEMP: will use hashes
|
||||
pub fn random() -> Self {
|
||||
let rng = rand::thread_rng()
|
||||
.sample_iter(Alphanumeric)
|
||||
.map(char::from)
|
||||
.take(12)
|
||||
.collect();
|
||||
Self(rng)
|
||||
pub fn from_hash(data: &[u8]) -> Self {
|
||||
use sha2::Digest;
|
||||
let hash = sha2::Sha256::digest(data);
|
||||
Self(format!("{}", b64engine.encode(&hash)))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ActorId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "^{}", hex::encode(&self.0))
|
||||
write!(f, "^{}", b64engine.encode(&self.0))
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ActorId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
Debug::fmt(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Serialize for ActorId {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer {
|
||||
serializer.serialize_str(&format!("{}", self))
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for ActorSignature {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "ActorSignature {{ {} }}", hex::encode(&self.0.to_bytes()))
|
||||
write!(f, "ActorSignature {{ {} }}", b64engine.encode(&self.0.to_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,3 +150,24 @@ macro_rules! impl_display {
|
|||
|
||||
impl_display!(EventId, "${}");
|
||||
impl_display!(RoomId, "!{}");
|
||||
|
||||
impl Serialize for ActorSignature {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer {
|
||||
serializer.serialize_str(&b64engine.encode(&self.0.to_bytes()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for ActorSignature {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de> {
|
||||
#[derive(Deserialize)]
|
||||
struct Wrapper(String);
|
||||
let wrapped = Wrapper::deserialize(deserializer)?;
|
||||
let bytes = b64engine.decode(wrapped.0).unwrap();
|
||||
let sig = ed25519_dalek::Signature::from_slice(&bytes).unwrap();
|
||||
Ok(ActorSignature(sig))
|
||||
}
|
||||
}
|
||||
|
|
55
src/event.rs
55
src/event.rs
|
@ -1,5 +1,7 @@
|
|||
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)]
|
||||
/// An event, is the basic building block/envolope containing all data.
|
||||
|
@ -11,24 +13,37 @@ pub struct Event<T> {
|
|||
pub signature: ActorSignature,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct EventBuilder<T> {
|
||||
content: CoreContent<T>,
|
||||
references: Vec<EventId>,
|
||||
author: ActorId,
|
||||
#[serde(skip)]
|
||||
author_secret: ActorSecret,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct EventBuilderHasId<T> {
|
||||
id: EventId,
|
||||
content: CoreContent<T>,
|
||||
references: Vec<EventId>,
|
||||
author: ActorId,
|
||||
#[serde(skip)]
|
||||
author_secret: ActorSecret,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct EventVerify<T> {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
id: Option<EventId>,
|
||||
content: CoreContent<T>,
|
||||
references: Vec<EventId>,
|
||||
author: ActorId,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
signature: Option<ActorSignature>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// This defines content of an event as either a builtin event or application-specific event.
|
||||
pub enum CoreContent<T> {
|
||||
Create(CreateContent),
|
||||
|
@ -56,19 +71,37 @@ pub enum CoreContent<T> {
|
|||
*/
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
/// Every room has exactly one immutable create event as the single root of the event dag.
|
||||
pub struct CreateContent {
|
||||
pub resolver: String,
|
||||
}
|
||||
|
||||
impl<T> Event<T> {
|
||||
impl<T: Debug + Serialize + Clone> Event<T> {
|
||||
pub fn builder(content: CoreContent<T>, actor: &ActorSecret) -> EventBuilder<T> {
|
||||
EventBuilder::new(content, actor)
|
||||
}
|
||||
|
||||
pub fn verify(&self) -> Result<(), ()> {
|
||||
let mut shallow = EventVerify {
|
||||
id: None,
|
||||
content: self.content.clone(),
|
||||
references: self.references.clone(),
|
||||
author: self.author.clone(),
|
||||
signature: None,
|
||||
};
|
||||
let value = serde_json::to_value(&shallow).unwrap();
|
||||
let data = canonical_json::to_string(&value).unwrap();
|
||||
assert_eq!(EventId::from_hash(data.as_bytes()), self.id);
|
||||
shallow.id = Some(self.id.clone());
|
||||
let value = serde_json::to_value(&shallow).unwrap();
|
||||
let data = dbg!(canonical_json::to_string(&value).unwrap());
|
||||
self.author.verify(data.as_bytes(), &self.signature).unwrap();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> EventBuilder<T> {
|
||||
impl<T: Serialize> EventBuilder<T> {
|
||||
pub fn new(content: CoreContent<T>, actor: &ActorSecret) -> Self {
|
||||
EventBuilder {
|
||||
content,
|
||||
|
@ -87,8 +120,10 @@ impl<T> EventBuilder<T> {
|
|||
}
|
||||
|
||||
pub fn then_hash(self) -> EventBuilderHasId<T> {
|
||||
let value = serde_json::to_value(&self).unwrap();
|
||||
let data = canonical_json::to_string(&value).unwrap();
|
||||
EventBuilderHasId {
|
||||
id: EventId::random(),
|
||||
id: EventId::from_hash(data.as_bytes()),
|
||||
content: self.content,
|
||||
references: self.references,
|
||||
author: self.author,
|
||||
|
@ -97,14 +132,16 @@ impl<T> EventBuilder<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> EventBuilderHasId<T> {
|
||||
impl<T: Serialize> EventBuilderHasId<T> {
|
||||
pub fn and_sign(self) -> Event<T> {
|
||||
let value = serde_json::to_value(&self).unwrap();
|
||||
let data = canonical_json::to_string(&value).unwrap();
|
||||
Event {
|
||||
id: self.id,
|
||||
content: self.content,
|
||||
references: self.references,
|
||||
author: self.author,
|
||||
signature: self.author_secret.sign(&[0x00, 0x01, 0x02, 0x03]),
|
||||
signature: self.author_secret.sign(data.as_bytes()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
event::{CoreContent, Event}, resolver::Resolver
|
||||
};
|
||||
|
@ -26,7 +28,7 @@ impl PremadeResolver {
|
|||
/// A basic key-value store
|
||||
pub struct KVResolver;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum KVEventContent {
|
||||
Set(String, String),
|
||||
}
|
||||
|
|
10
src/room.rs
10
src/room.rs
|
@ -1,3 +1,5 @@
|
|||
use serde::Serialize;
|
||||
|
||||
use crate::{
|
||||
atoms::{ActorSecret, EventId},
|
||||
event::{CoreContent, CreateContent, Event},
|
||||
|
@ -12,7 +14,7 @@ pub struct Room<E, S> {
|
|||
pub resolver: Box<dyn Resolver<Type = E, State = S>>,
|
||||
}
|
||||
|
||||
impl<T: Debug, S> Room<T, S> {
|
||||
impl<T: Debug + Serialize + Clone, S> Room<T, S> {
|
||||
pub fn new(
|
||||
resolver_name: impl Into<String>,
|
||||
resolver: Box<dyn Resolver<Type = T, State = S>>,
|
||||
|
@ -59,11 +61,7 @@ impl<T: Debug, S> Room<T, S> {
|
|||
}
|
||||
|
||||
pub fn append_event(&mut self, event: Event<T>) {
|
||||
// let parents: Vec<_> = self
|
||||
// .events
|
||||
// .iter()
|
||||
// .filter(|parent| event.references.contains(&parent.id))
|
||||
// .collect();
|
||||
event.verify().expect("event failed verification");
|
||||
let event_id = event.id.clone();
|
||||
self.events.push(event);
|
||||
self.heads = vec![event_id];
|
||||
|
|
Loading…
Reference in a new issue