diff --git a/Cargo.lock b/Cargo.lock index 52c283d..b6304dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -310,6 +310,7 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445" name = "bft-crdt-derive" version = "0.1.0" dependencies = [ + "indexmap 2.2.6", "proc-macro-crate", "proc-macro2", "quote", @@ -323,6 +324,7 @@ dependencies = [ "bft-crdt-derive", "colored", "fastcrypto", + "indexmap 2.2.6", "itertools", "rand 0.8.5", "random_color", @@ -1905,6 +1907,7 @@ version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" dependencies = [ + "indexmap 2.2.6", "itoa", "ryu", "serde", @@ -2056,6 +2059,7 @@ dependencies = [ "dirs", "ezsockets", "fastcrypto", + "indexmap 2.2.6", "serde", "serde_json", "serde_with 3.8.1", diff --git a/crates/bft-json-crdt/Cargo.toml b/crates/bft-json-crdt/Cargo.toml index 6519417..5dd682e 100644 --- a/crates/bft-json-crdt/Cargo.toml +++ b/crates/bft-json-crdt/Cargo.toml @@ -17,15 +17,16 @@ bft = [] bft-crdt-derive = { path = "bft-crdt-derive" } colored = "2.0.0" fastcrypto = "0.1.8" +indexmap = { version = "2.2.6", features = ["serde"] } itertools = "0.10.5" rand = "0.8.5" random_color = "0.6.1" serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.85" +serde_json = { version = "1.0.85", features = ["preserve_order"] } serde_with = "3.8.1" sha2 = "0.10.6" [dev-dependencies] -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0.85" time = "0.1" +serde = { version = "1.0", features = ["derive"] } +serde_json = { version = "1.0.85", features = ["preserve_order"] } diff --git a/crates/bft-json-crdt/bft-crdt-derive/Cargo.toml b/crates/bft-json-crdt/bft-crdt-derive/Cargo.toml index de8821c..152a39d 100644 --- a/crates/bft-json-crdt/bft-crdt-derive/Cargo.toml +++ b/crates/bft-json-crdt/bft-crdt-derive/Cargo.toml @@ -8,6 +8,7 @@ publish = false proc-macro = true [dependencies] +indexmap = { version = "2.2.6", features = ["serde"] } proc-macro2 = "1.0.47" proc-macro-crate = "1.2.1" quote = "1.0.21" diff --git a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs index f76813a..656cb77 100644 --- a/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs +++ b/crates/bft-json-crdt/bft-crdt-derive/src/lib.rs @@ -6,13 +6,12 @@ use syn::{ parse::{self, Parser}, parse_macro_input, spanned::Spanned, - Data, DeriveInput, Field, Fields, ItemStruct, LitStr, Type + Data, DeriveInput, Field, Fields, ItemStruct, LitStr, Type, }; /// Helper to get tokenstream representing the parent crate fn get_crate_name() -> TokenStream { - let cr8 = crate_name("bft-json-crdt") - .unwrap_or(FoundCrate::Itself); + let cr8 = crate_name("bft-json-crdt").unwrap_or(FoundCrate::Itself); match cr8 { FoundCrate::Itself => quote! { ::bft_json_crdt }, FoundCrate::Name(name) => { @@ -106,7 +105,7 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream { }) } else { Err(format!("failed to convert {:?} -> {}", value, #ident_str.to_string())) - } + } } } @@ -116,7 +115,7 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream { #(fields.push(format!("{}", #ident_strings.to_string()));)* write!(f, "{{ {:?} }}", fields.join(", ")) } - } + } impl #impl_generics #crate_name::json_crdt::CrdtNode for #ident #ty_generics #where_clause { fn apply(&mut self, op: #crate_name::op::Op<#crate_name::json_crdt::JsonValue>) -> #crate_name::json_crdt::OpState { @@ -128,7 +127,7 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream { } if self.path.len() == op.path.len() { - return #crate_name::json_crdt::OpState::ErrApplyOnStruct; + return #crate_name::json_crdt::OpState::ErrApplyOnStruct; } else { let idx = self.path.len(); if let #crate_name::op::PathSegment::Field(path_seg) = &op.path[idx] { @@ -139,12 +138,12 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream { _ => {}, }; }; - return #crate_name::json_crdt::OpState::ErrPathMismatch + return #crate_name::json_crdt::OpState::ErrPathMismatch } } fn view(&self) -> #crate_name::json_crdt::JsonValue { - let mut view_map = std::collections::HashMap::new(); + let mut view_map = indexmap::IndexMap::new(); #(view_map.insert(#ident_strings.to_string(), self.#ident_literals.view().into());)* #crate_name::json_crdt::JsonValue::Object(view_map) } @@ -173,7 +172,7 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream { fn debug_view(&self, _indent: usize) -> String { "".to_string() } - } + } }; // Hand the output tokens back to the compiler diff --git a/crates/bft-json-crdt/src/json_crdt.rs b/crates/bft-json-crdt/src/json_crdt.rs index b2cb62c..77527fd 100644 --- a/crates/bft-json-crdt/src/json_crdt.rs +++ b/crates/bft-json-crdt/src/json_crdt.rs @@ -17,6 +17,7 @@ use fastcrypto::{ traits::{KeyPair, ToFromBytes}, // Verifier, }; +use indexmap::IndexMap; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, Bytes}; @@ -256,7 +257,7 @@ pub enum JsonValue { Number(f64), String(String), Array(Vec), - Object(HashMap), + Object(IndexMap), } impl Display for JsonValue { diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index 9abf227..c6886a2 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -24,6 +24,7 @@ tracing = "0.1.32" tracing-subscriber = "0.3.9" toml = "0.8.14" url = "2.2.2" +indexmap = { version = "2.2.6", features = ["serde"] } [dev-dependencies] uuid = { version = "1.8.0", features = ["v4"] } diff --git a/side-node/src/node.rs b/side-node/src/node.rs index 1326d06..35eee2d 100644 --- a/side-node/src/node.rs +++ b/side-node/src/node.rs @@ -39,14 +39,14 @@ impl SideNode { let transaction = utils::fake_transaction_json(stdin); let json = serde_json::to_value(transaction).unwrap(); let signed_op = self.add_transaction_local(json); - println!("STDIN: {}", shappy(signed_op.clone())); + println!("STDIN: {}", utils::shappy(signed_op.clone())); self.send_to_network(signed_op).await; } Err(_) => {} // ignore empty channel errors in this PoC } match self.incoming_receiver.try_recv() { Ok(incoming) => { - println!("INCOMING"); + println!("INCOMING: {}", utils::shappy(incoming.clone())); self.handle_incoming(incoming); } Err(_) => {} // ignore empty channel errors in this PoC @@ -60,7 +60,6 @@ impl SideNode { } pub fn handle_incoming(&mut self, incoming: SignedOp) { - println!("handle_incoming: {}", shappy(incoming.clone())); self.crdt.apply(incoming); // self.trace_crdt(); } @@ -95,8 +94,3 @@ impl SideNode { self.crdt.doc.view_sha() } } - -fn shappy(op: SignedOp) -> String { - let b = serde_json::to_string(&op).unwrap().into_bytes(); - sha256::digest(b).to_string() -} diff --git a/side-node/src/utils.rs b/side-node/src/utils.rs index 4bd2818..17fb4cb 100644 --- a/side-node/src/utils.rs +++ b/side-node/src/utils.rs @@ -1,5 +1,6 @@ use std::path::PathBuf; +use bft_json_crdt::json_crdt::SignedOp; use serde_json::{json, Value}; pub(crate) const KEY_FILE: &str = "keys.pem"; @@ -31,3 +32,13 @@ pub fn fake_transaction_json(from: String) -> Value { "amount": 1 }) } + +pub fn shappy(op: SignedOp) -> String { + let b = serde_json::to_string(&op).unwrap().into_bytes(); + sha256::digest(b).to_string() +} + +pub fn shassy(text: String) -> String { + let b = text.into_bytes(); + sha256::digest(b).to_string() +} diff --git a/side-node/src/websocket.rs b/side-node/src/websocket.rs index 46db012..de6ee5d 100644 --- a/side-node/src/websocket.rs +++ b/side-node/src/websocket.rs @@ -3,6 +3,8 @@ use bft_json_crdt::json_crdt::SignedOp; use ezsockets::ClientConfig; use tokio::sync::mpsc; +use crate::utils; + pub struct WebSocketClient { incoming_sender: mpsc::Sender, handle: ezsockets::Client, @@ -39,7 +41,16 @@ impl ezsockets::ClientExt for WebSocketClient { /// When we receive a text message, apply the crdt operation contained in it to our /// local crdt. async fn on_text(&mut self, text: String) -> Result<(), ezsockets::Error> { + let string_sha = utils::shassy(text.clone()); + println!("received text, sha: {string_sha}"); let incoming: bft_json_crdt::json_crdt::SignedOp = serde_json::from_str(&text).unwrap(); + let object_sha = utils::shappy(incoming.clone()); + println!("deserialized: {}", object_sha); + if string_sha != object_sha { + println!("SHA mismatch: {string_sha} != {object_sha}"); + println!("text: {text}"); + println!("incoming: {incoming:?}"); + } self.incoming_sender.send(incoming).await?; Ok(()) }