basic cli for testing things

This commit is contained in:
tezlm 2024-02-09 16:40:03 -08:00
parent 575d9a353e
commit fa07875cca
Signed by: tezlm
GPG key ID: 649733FCD94AFBBA
8 changed files with 681 additions and 78 deletions

383
Cargo.lock generated
View file

@ -2,6 +2,33 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "ahash"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01"
dependencies = [
"cfg-if",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "1.1.2"
@ -11,6 +38,84 @@ dependencies = [
"memchr",
]
[[package]]
name = "allocator-api2"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
[[package]]
name = "anstream"
version = "0.6.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2e1ebcb11de5c03c67de28a7df593d32191b44939c482e97702baaaa6ab6a5"
dependencies = [
"anstyle",
"anstyle-parse",
"anstyle-query",
"anstyle-wincon",
"colorchoice",
"utf8parse",
]
[[package]]
name = "anstyle"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc"
[[package]]
name = "anstyle-parse"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c"
dependencies = [
"utf8parse",
]
[[package]]
name = "anstyle-query"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648"
dependencies = [
"windows-sys",
]
[[package]]
name = "anstyle-wincon"
version = "3.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cd54b81ec8d6180e24654d0b371ad22fc3dd083b6ff8ba325b72e00c87660a7"
dependencies = [
"anstyle",
"windows-sys",
]
[[package]]
name = "anyhow"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "080e9890a082662b09c1ad45f567faeeb47f22b5fb23895fbe1e651e718e25ca"
dependencies = [
"backtrace",
]
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "base64"
version = "0.21.7"
@ -23,6 +128,12 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
[[package]]
name = "bitflags"
version = "2.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
[[package]]
name = "block-buffer"
version = "0.10.4"
@ -45,12 +156,67 @@ dependencies = [
"thiserror",
]
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80c21025abd42669a92efc996ef13cfb2c5c627858421ea58d5c3b331a6c134f"
dependencies = [
"clap_builder",
"clap_derive",
]
[[package]]
name = "clap_builder"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "458bf1f341769dfcf849846f65dffdf9146daa56bcd2a47cb4e1de9915567c99"
dependencies = [
"anstream",
"anstyle",
"clap_lex",
"strsim",
]
[[package]]
name = "clap_derive"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "clap_lex"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce"
[[package]]
name = "colorchoice"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "const-oid"
version = "0.9.6"
@ -108,11 +274,14 @@ dependencies = [
name = "dag-resolve"
version = "0.1.0"
dependencies = [
"anyhow",
"base64",
"canonical_json",
"clap",
"ed25519",
"ed25519-dalek",
"rand",
"rusqlite",
"serde",
"serde_json",
"sha2",
@ -164,6 +333,18 @@ dependencies = [
"zeroize",
]
[[package]]
name = "fallible-iterator"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
[[package]]
name = "fallible-streaming-iterator"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
[[package]]
name = "fiat-crypto"
version = "0.2.6"
@ -191,6 +372,37 @@ dependencies = [
"wasi",
]
[[package]]
name = "gimli"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
[[package]]
name = "hashbrown"
version = "0.14.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
dependencies = [
"ahash",
"allocator-api2",
]
[[package]]
name = "hashlink"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
dependencies = [
"hashbrown",
]
[[package]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "hex"
version = "0.4.3"
@ -209,12 +421,47 @@ version = "0.2.153"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
[[package]]
name = "libsqlite3-sys"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716"
dependencies = [
"cc",
"pkg-config",
"vcpkg",
]
[[package]]
name = "memchr"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149"
[[package]]
name = "miniz_oxide"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.32.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441"
dependencies = [
"memchr",
]
[[package]]
name = "once_cell"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "pkcs8"
version = "0.10.2"
@ -225,6 +472,12 @@ dependencies = [
"spki",
]
[[package]]
name = "pkg-config"
version = "0.3.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2900ede94e305130c13ddd391e0ab7cbaeb783945ae07a279c268cb05109c6cb"
[[package]]
name = "platforms"
version = "3.3.0"
@ -314,6 +567,26 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "rusqlite"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d"
dependencies = [
"bitflags",
"fallible-iterator",
"fallible-streaming-iterator",
"hashlink",
"libsqlite3-sys",
"smallvec",
]
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc_version"
version = "0.4.0"
@ -386,6 +659,12 @@ dependencies = [
"rand_core",
]
[[package]]
name = "smallvec"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7"
[[package]]
name = "spki"
version = "0.7.3"
@ -396,6 +675,12 @@ dependencies = [
"der",
]
[[package]]
name = "strsim"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01"
[[package]]
name = "subtle"
version = "2.5.0"
@ -445,6 +730,18 @@ version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "version_check"
version = "0.9.4"
@ -457,6 +754,92 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_gnullvm",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea"
[[package]]
name = "windows_aarch64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef"
[[package]]
name = "windows_i686_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313"
[[package]]
name = "windows_i686_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a"
[[package]]
name = "windows_x86_64_gnu"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd"
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e"
[[package]]
name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "zerocopy"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "zeroize"
version = "1.7.0"

View file

@ -1,16 +1,20 @@
[package]
name = "dag-resolve"
description = "experiment in directed acyclic graphs and reducers"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = { version = "1.0.79", features = ["backtrace"] }
base64 = "0.21.7"
canonical_json = "0.5.0"
clap = { version = "4.5.0", features = ["derive"] }
ed25519 = "2.2.3"
ed25519-dalek = { version = "2.1.1", features = ["rand_core"] }
rand = "0.8.5"
rusqlite = { version = "0.30.0", features = ["bundled"] }
serde = { version = "1.0.196", features = ["derive"] }
serde_json = "1.0.113"
sha2 = "0.10.8"

144
src/bin/dag.rs Normal file
View file

@ -0,0 +1,144 @@
use std::{
path::{Path, PathBuf},
str::FromStr,
};
use clap::Parser;
use dag_resolve::{
actor::{ActorId, ActorSecret},
event::{CoreContent, HashType, SignatureType},
resolvers::kv::{KVEventContent, KVResolver, KVState},
room::Room,
};
use rusqlite::{Connection, OpenFlags};
#[derive(Parser)]
#[clap(version, about)]
enum Cli {
/// Initialize a new repository
Init { repo: PathBuf },
/// Set a key in a repository
Set {
repo: PathBuf,
key: String,
value: String,
},
}
type Result<T> = std::result::Result<T, anyhow::Error>;
struct State {
db: Connection,
room: Room<KVEventContent, KVState>,
secret: ActorSecret,
}
impl State {
fn create_event(&mut self, content: CoreContent<KVEventContent>) -> Result<()> {
let event = self.room.create_event(content, &self.secret)?;
self.db.execute(
"INSERT INTO events (id, json) VALUES (?, ?)",
(event.id().to_string(), serde_json::to_string(&event)?),
)?;
Ok(())
}
}
fn get_repo(path: &Path, create: bool) -> Result<State> {
match (create, path.try_exists()?) {
(true, true) => panic!("repo already exists"),
(false, false) => panic!("repo does not exist"),
(true, false) => {
// create new repo
let db = rusqlite::Connection::open(path)?;
db.execute_batch(
r#"
CREATE TABLE IF NOT EXISTS config (
key TEXT PRIMARY KEY,
value ANY
);
CREATE TABLE IF NOT EXISTS events (
id TEXT PRIMARY KEY,
json TEXT
);
"#,
)?;
let (actor, secret) = ActorId::new(SignatureType::Ed25519);
let resolver = Box::new(KVResolver);
let room = Room::builder()
.with_resolver(resolver)
.with_hasher(HashType::Sha256)
.with_signer(SignatureType::Ed25519)
.create(&secret)?;
db.execute(
"INSERT INTO config VALUES (?, ?)",
("actor_id", actor.to_string()),
)?;
db.execute(
"INSERT INTO config VALUES (?, ?)",
("actor_secret", serde_json::to_string(&secret)?),
)?;
for event in &room.events {
db.execute(
"INSERT INTO events (id, json) VALUES (?, ?)",
(event.id().to_string(), serde_json::to_string(&event)?),
)?;
}
Ok(State { db, room, secret })
}
(false, true) => {
// restore repo
let db = rusqlite::Connection::open(path)?;
let actor: ActorId =
db.query_row("SELECT value FROM config WHERE key='actor_id'", [], |row| {
row.get(0).map(|s: String| s.parse())
})??;
let secret: ActorSecret = db.query_row(
"SELECT value FROM config WHERE key='actor_secret'",
[],
|row| row.get(0).map(|s: String| serde_json::from_str(&s)),
)??;
dbg!(&actor, &secret);
let mut q = db.prepare("SELECT json FROM events")?;
let mut rows = q.query([])?;
let mut room: Option<Room<KVEventContent, KVState>> = None;
while let Some(row) = rows.next()? {
let s: String = dbg!(row.get(0)?);
let ev = dbg!(serde_json::from_str(&s)?);
match &mut room {
Some(r) => {
r.append_event(ev)?;
},
None => {
let resolver = Box::new(KVResolver);
let r = Room::from_root(resolver, ev)?;
room = Some(r);
},
}
}
drop(rows);
drop(q);
let room = room.unwrap();
dbg!(&room);
Ok(State { db, room, secret })
}
}
}
fn main() -> Result<()> {
let cli = Cli::parse();
match cli {
Cli::Init { repo } => {
get_repo(&repo, true)?;
},
Cli::Set { repo, key, value } => {
let mut state = get_repo(&repo, false)?;
state.create_event(CoreContent::Custom(KVEventContent::Set(key, value)))?;
dbg!(&state.room);
dbg!(&state.room.get_state());
}
};
Ok(())
}

View file

@ -1,31 +0,0 @@
use dag_resolve::{
actor::ActorId,
event::{CoreContent, HashType, SignatureType},
resolvers::kv::{KVEventContent, KVResolver},
room::Room,
};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (_actor, secret) = ActorId::new(SignatureType::Ed25519);
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())),
&secret,
)?;
room.create_event(
CoreContent::Custom(KVEventContent::Set("bar".into(), "banana".into())),
&secret,
)?;
room.create_event(
CoreContent::Custom(KVEventContent::Set("baz".into(), "carrot".into())),
&secret,
)?;
dbg!(&room);
dbg!(&room.get_state());
Ok(())
}

View file

@ -4,13 +4,15 @@ use rand::{distributions::Alphanumeric, Rng};
use serde::{Deserialize, Serialize};
use std::fmt::{Debug, Display};
use std::result::Result as StdResult;
use std::str::FromStr;
use crate::Error;
use crate::{event::SignatureType, Result};
#[derive(Hash, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ActorId(ActorIdData);
#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq)]
pub struct ActorSecret(ActorSecretData);
#[derive(Clone, PartialEq, Eq)]
@ -162,7 +164,30 @@ impl Display for ActorId {
ActorIdData::Ed25519(d) => (0x01, d.as_ref()),
};
let data: Vec<u8> = [prefix].into_iter().chain(bytes.to_vec()).collect();
write!(f, "${}", b64engine.encode(data))
write!(f, "^{}", b64engine.encode(data))
}
}
impl FromStr for ActorId {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let s = s.strip_prefix('^').ok_or(Error::InvalidSigil)?;
let bytes = b64engine.decode(s)?;
match bytes.first() {
Some(0x00) => {
let id = String::from_utf8(bytes[1..].to_vec())?;
Ok(ActorId(ActorIdData::Debug(id)))
}
Some(0x01) => Ok(ActorId(ActorIdData::Ed25519(
bytes[1..].try_into().map_err(|_| Error::InvalidLength {
expected: 32,
got: bytes.len() - 1,
})?,
))),
Some(_) => unimplemented!("unsupported signature type"),
None => panic!("missing type byte"),
}
}
}
@ -171,7 +196,7 @@ impl Serialize for ActorId {
where
S: serde::Serializer,
{
serializer.serialize_str(&format!("^{}", self))
serializer.serialize_str(&self.to_string())
}
}
@ -183,30 +208,65 @@ impl<'de> Deserialize<'de> for ActorId {
#[derive(Deserialize)]
struct Wrapper(String);
let wrapped = Wrapper::deserialize(deserializer)?;
let bytes = b64engine
.decode(wrapped.0)
.map_err(serde::de::Error::custom)?;
wrapped.0.parse().map_err(serde::de::Error::custom)
}
}
impl Debug for ActorSecret {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "ActorSecret {{ <redacted> }}")
}
}
impl Serialize for ActorSecret {
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
where
S: serde::Serializer,
{
let (prefix, bytes) = match &self.0 {
ActorSecretData::Debug(d) => (0x00, d.as_bytes().to_vec()),
ActorSecretData::Ed25519(d) => (0x01, d.to_vec()),
};
let data: Vec<u8> = [prefix].into_iter().chain(bytes).collect();
if serializer.is_human_readable() {
serializer.serialize_str(&b64engine.encode(data))
} else {
serializer.serialize_bytes(&data)
}
}
}
impl<'de> Deserialize<'de> for ActorSecret {
fn deserialize<D>(deserializer: D) -> StdResult<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum Wrapper {
Blobby(Vec<u8>),
Stringy(String),
}
let bytes = match Wrapper::deserialize(deserializer)? {
Wrapper::Blobby(b) => b,
Wrapper::Stringy(s) => b64engine.decode(s).map_err(serde::de::Error::custom)?,
};
match bytes.first() {
Some(0x00) => {
let id =
String::from_utf8(bytes[1..].to_vec()).map_err(serde::de::Error::custom)?; // TODO: Error::invalid_data
Ok(ActorId(ActorIdData::Debug(id)))
let sig =
String::from_utf8(bytes[1..].to_vec()).map_err(serde::de::Error::custom)?;
Ok(ActorSecret(ActorSecretData::Debug(sig)))
}
Some(0x01) => {
let key = bytes[1..].try_into().map_err(serde::de::Error::custom)?;
Ok(ActorSecret(ActorSecretData::Ed25519(key)))
}
Some(0x01) => Ok(ActorId(ActorIdData::Ed25519(
bytes[1..].try_into().map_err(serde::de::Error::custom)?, // TODO: Error::invalid_length
))),
Some(_) => unimplemented!("unsupported signature type"),
None => panic!("missing type byte"),
}
}
}
// impl Debug for ActorSecret {
// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// write!(f, "ActorSecret {{ <redacted> }}")
// }
// }
impl Debug for ActorSignature {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let (prefix, bytes) = match &self.0 {
@ -228,7 +288,11 @@ impl Serialize for ActorSignature {
ActorSignatureData::Ed25519(d) => (0x01, d.to_bytes().to_vec()),
};
let data: Vec<u8> = [prefix].into_iter().chain(bytes).collect();
serializer.serialize_bytes(&data)
if serializer.is_human_readable() {
serializer.serialize_str(&b64engine.encode(data))
} else {
serializer.serialize_bytes(&data)
}
}
}
@ -237,12 +301,16 @@ impl<'de> Deserialize<'de> for ActorSignature {
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct Wrapper(String);
let wrapped = Wrapper::deserialize(deserializer)?;
let bytes = b64engine
.decode(wrapped.0)
.map_err(serde::de::Error::custom)?;
#[derive(Debug, Deserialize)]
#[serde(untagged)]
enum Wrapper {
Blobby(Vec<u8>),
Stringy(String),
}
let bytes = match Wrapper::deserialize(deserializer)? {
Wrapper::Blobby(b) => b,
Wrapper::Stringy(s) => b64engine.decode(s).map_err(serde::de::Error::custom)?,
};
match bytes.first() {
Some(0x00) => {
let sig =

View file

@ -113,7 +113,7 @@ impl<T: Debug + Serialize + Clone> Event<T> {
EventBuilder::new(content, actor)
}
pub fn verify<S>(&self, room: &Room<T, S>) -> Result<()> {
pub fn verify(&self) -> Result<()> {
let mut shallow = EventVerify {
id: None,
content: self.content.clone(),
@ -121,10 +121,6 @@ impl<T: Debug + Serialize + Clone> Event<T> {
author: self.author.clone(),
signature: None,
};
let room_config = match &room.get_root().content {
CoreContent::Create(c) => c,
_ => unreachable!("the root should always be a create event"),
};
// verify id hash
let value = serde_json::to_value(&shallow)?;
@ -136,18 +132,37 @@ impl<T: Debug + Serialize + Clone> Event<T> {
got: calculated_hash,
});
}
if room_config.hasher != self.id.get_type() {
return Err(Error::MismatchedHasher {
expected: room_config.hasher,
got: self.id.get_type(),
});
}
shallow.id = Some(self.id.clone());
// verify signature
let value = serde_json::to_value(&shallow)?;
let data = dbg!(canonical_json::to_string(&value)?);
self.author.verify(data.as_bytes(), &self.signature)?;
if self.author.get_type() != self.signature.get_type() {
return Err(Error::MismatchedSigner {
expected: self.author.get_type(),
got: self.signature.get_type(),
});
}
Ok(())
}
pub fn verify_room<S>(&self, room: &Room<T, S>) -> Result<()> {
self.verify()?;
let room_config = match &room.get_root().content {
CoreContent::Create(c) => c,
_ => unreachable!("the root should always be a create event"),
};
if room_config.hasher != self.id.get_type() {
return Err(Error::MismatchedHasher {
expected: room_config.hasher,
got: self.id.get_type(),
});
}
if room_config.signer != self.author.get_type()
|| room_config.signer != self.signature.get_type()
{
@ -288,7 +303,7 @@ impl TryFrom<&str> for EventId {
type Error = Error;
fn try_from(value: &str) -> Result<Self> {
Ok(EventId::from_str(value)?)
EventId::from_str(value)
}
}

View file

@ -41,7 +41,7 @@ pub fn sort<T: Debug + Serialize + Clone>(
.into_iter()
.partition(|candidate| references.iter().any(|(_, child)| child == candidate.id()));
unsorted = new_unsorted;
children.sort_by(|a, b| tiebreak(a, b).then_with(|| a.id().cmp(&b.id())));
children.sort_by(|a, b| tiebreak(a, b).then_with(|| a.id().cmp(b.id())));
heads.extend(children);
}
assert!(unsorted.is_empty());

View file

@ -41,6 +41,17 @@ impl<T: Debug + Serialize + Clone, S> Room<T, S> {
})
}
pub fn from_root(
resolver: Box<dyn Resolver<T, S>>,
event: Event<T>,
) -> Result<Self> {
Ok(Self {
heads: vec![event.id().clone()],
events: vec![event],
resolver,
})
}
pub fn get_root(&self) -> &Event<T> {
self.events
.first()
@ -58,20 +69,23 @@ impl<T: Debug + Serialize + Clone, S> Room<T, S> {
resolver.resolve(&sorted)
}
pub fn create_event(&mut self, event_content: CoreContent<T>, secret: &ActorSecret) -> Result<()> {
pub fn create_event(&mut self, event_content: CoreContent<T>, secret: &ActorSecret) -> Result<&Event<T>> {
let event = Event::builder(event_content, secret)
.with_references(std::mem::take(&mut self.heads))
.then_hash()?
.and_sign()?;
self.append_event(event);
Ok(())
self.append_event(event)
}
pub fn append_event(&mut self, event: Event<T>) {
event.verify(self).expect("event failed verification");
let event_id = event.id().clone();
pub fn append_event(&mut self, event: Event<T>) -> Result<&Event<T>> {
event.verify_room(self).expect("event failed verification");
if self.events.iter().any(|p| p == &event) {
panic!("already exists");
}
self.events.push(event);
self.heads = vec![event_id];
let event_ref = self.events.last().unwrap();
self.heads = vec![event_ref.id().clone()];
Ok(event_ref)
}
}
@ -81,14 +95,20 @@ pub struct RoomBuilder<T, S> {
signer: Option<SignatureType>,
}
impl<T: Clone + Debug + Serialize, S> RoomBuilder<T, S> {
pub fn new() -> Self {
impl<T: Clone + Debug + Serialize, S> Default for RoomBuilder<T, S> {
fn default() -> Self {
Self {
resolver: None,
hasher: None,
signer: None,
}
}
}
impl<T: Clone + Debug + Serialize, S> RoomBuilder<T, S> {
pub fn new() -> Self {
Self::default()
}
pub fn with_resolver(mut self, resolver: Box<dyn Resolver<T, S>>) -> Self {
self.resolver = Some(resolver);