Fixed serializiation determinacy problems.

This commit is contained in:
Dave Hrycyszyn
2024-06-18 11:19:36 +01:00
parent 416d1ad88b
commit a4441af53a
9 changed files with 44 additions and 21 deletions

4
Cargo.lock generated
View File

@@ -310,6 +310,7 @@ checksum = "d86b93f97252c47b41663388e6d155714a9d0c398b99f1005cbc5f978b29f445"
name = "bft-crdt-derive" name = "bft-crdt-derive"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"indexmap 2.2.6",
"proc-macro-crate", "proc-macro-crate",
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -323,6 +324,7 @@ dependencies = [
"bft-crdt-derive", "bft-crdt-derive",
"colored", "colored",
"fastcrypto", "fastcrypto",
"indexmap 2.2.6",
"itertools", "itertools",
"rand 0.8.5", "rand 0.8.5",
"random_color", "random_color",
@@ -1905,6 +1907,7 @@ version = "1.0.117"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3"
dependencies = [ dependencies = [
"indexmap 2.2.6",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
@@ -2056,6 +2059,7 @@ dependencies = [
"dirs", "dirs",
"ezsockets", "ezsockets",
"fastcrypto", "fastcrypto",
"indexmap 2.2.6",
"serde", "serde",
"serde_json", "serde_json",
"serde_with 3.8.1", "serde_with 3.8.1",

View File

@@ -17,15 +17,16 @@ bft = []
bft-crdt-derive = { path = "bft-crdt-derive" } bft-crdt-derive = { path = "bft-crdt-derive" }
colored = "2.0.0" colored = "2.0.0"
fastcrypto = "0.1.8" fastcrypto = "0.1.8"
indexmap = { version = "2.2.6", features = ["serde"] }
itertools = "0.10.5" itertools = "0.10.5"
rand = "0.8.5" rand = "0.8.5"
random_color = "0.6.1" random_color = "0.6.1"
serde = { version = "1.0", features = ["derive"] } 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" serde_with = "3.8.1"
sha2 = "0.10.6" sha2 = "0.10.6"
[dev-dependencies] [dev-dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0.85"
time = "0.1" time = "0.1"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0.85", features = ["preserve_order"] }

View File

@@ -8,6 +8,7 @@ publish = false
proc-macro = true proc-macro = true
[dependencies] [dependencies]
indexmap = { version = "2.2.6", features = ["serde"] }
proc-macro2 = "1.0.47" proc-macro2 = "1.0.47"
proc-macro-crate = "1.2.1" proc-macro-crate = "1.2.1"
quote = "1.0.21" quote = "1.0.21"

View File

@@ -6,13 +6,12 @@ use syn::{
parse::{self, Parser}, parse::{self, Parser},
parse_macro_input, parse_macro_input,
spanned::Spanned, spanned::Spanned,
Data, DeriveInput, Field, Fields, ItemStruct, LitStr, Type Data, DeriveInput, Field, Fields, ItemStruct, LitStr, Type,
}; };
/// Helper to get tokenstream representing the parent crate /// Helper to get tokenstream representing the parent crate
fn get_crate_name() -> TokenStream { fn get_crate_name() -> TokenStream {
let cr8 = crate_name("bft-json-crdt") let cr8 = crate_name("bft-json-crdt").unwrap_or(FoundCrate::Itself);
.unwrap_or(FoundCrate::Itself);
match cr8 { match cr8 {
FoundCrate::Itself => quote! { ::bft_json_crdt }, FoundCrate::Itself => quote! { ::bft_json_crdt },
FoundCrate::Name(name) => { FoundCrate::Name(name) => {
@@ -144,7 +143,7 @@ pub fn derive_json_crdt(input: OgTokenStream) -> OgTokenStream {
} }
fn view(&self) -> #crate_name::json_crdt::JsonValue { 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());)* #(view_map.insert(#ident_strings.to_string(), self.#ident_literals.view().into());)*
#crate_name::json_crdt::JsonValue::Object(view_map) #crate_name::json_crdt::JsonValue::Object(view_map)
} }

View File

@@ -17,6 +17,7 @@ use fastcrypto::{
traits::{KeyPair, ToFromBytes}, traits::{KeyPair, ToFromBytes},
// Verifier, // Verifier,
}; };
use indexmap::IndexMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_with::{serde_as, Bytes}; use serde_with::{serde_as, Bytes};
@@ -256,7 +257,7 @@ pub enum JsonValue {
Number(f64), Number(f64),
String(String), String(String),
Array(Vec<JsonValue>), Array(Vec<JsonValue>),
Object(HashMap<String, JsonValue>), Object(IndexMap<String, JsonValue>),
} }
impl Display for JsonValue { impl Display for JsonValue {

View File

@@ -24,6 +24,7 @@ tracing = "0.1.32"
tracing-subscriber = "0.3.9" tracing-subscriber = "0.3.9"
toml = "0.8.14" toml = "0.8.14"
url = "2.2.2" url = "2.2.2"
indexmap = { version = "2.2.6", features = ["serde"] }
[dev-dependencies] [dev-dependencies]
uuid = { version = "1.8.0", features = ["v4"] } uuid = { version = "1.8.0", features = ["v4"] }

View File

@@ -39,14 +39,14 @@ impl SideNode {
let transaction = utils::fake_transaction_json(stdin); let transaction = utils::fake_transaction_json(stdin);
let json = serde_json::to_value(transaction).unwrap(); let json = serde_json::to_value(transaction).unwrap();
let signed_op = self.add_transaction_local(json); 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; self.send_to_network(signed_op).await;
} }
Err(_) => {} // ignore empty channel errors in this PoC Err(_) => {} // ignore empty channel errors in this PoC
} }
match self.incoming_receiver.try_recv() { match self.incoming_receiver.try_recv() {
Ok(incoming) => { Ok(incoming) => {
println!("INCOMING"); println!("INCOMING: {}", utils::shappy(incoming.clone()));
self.handle_incoming(incoming); self.handle_incoming(incoming);
} }
Err(_) => {} // ignore empty channel errors in this PoC Err(_) => {} // ignore empty channel errors in this PoC
@@ -60,7 +60,6 @@ impl SideNode {
} }
pub fn handle_incoming(&mut self, incoming: SignedOp) { pub fn handle_incoming(&mut self, incoming: SignedOp) {
println!("handle_incoming: {}", shappy(incoming.clone()));
self.crdt.apply(incoming); self.crdt.apply(incoming);
// self.trace_crdt(); // self.trace_crdt();
} }
@@ -95,8 +94,3 @@ impl SideNode {
self.crdt.doc.view_sha() 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()
}

View File

@@ -1,5 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use bft_json_crdt::json_crdt::SignedOp;
use serde_json::{json, Value}; use serde_json::{json, Value};
pub(crate) const KEY_FILE: &str = "keys.pem"; pub(crate) const KEY_FILE: &str = "keys.pem";
@@ -31,3 +32,13 @@ pub fn fake_transaction_json(from: String) -> Value {
"amount": 1 "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()
}

View File

@@ -3,6 +3,8 @@ use bft_json_crdt::json_crdt::SignedOp;
use ezsockets::ClientConfig; use ezsockets::ClientConfig;
use tokio::sync::mpsc; use tokio::sync::mpsc;
use crate::utils;
pub struct WebSocketClient { pub struct WebSocketClient {
incoming_sender: mpsc::Sender<SignedOp>, incoming_sender: mpsc::Sender<SignedOp>,
handle: ezsockets::Client<WebSocketClient>, handle: ezsockets::Client<WebSocketClient>,
@@ -39,7 +41,16 @@ impl ezsockets::ClientExt for WebSocketClient {
/// When we receive a text message, apply the crdt operation contained in it to our /// When we receive a text message, apply the crdt operation contained in it to our
/// local crdt. /// local crdt.
async fn on_text(&mut self, text: String) -> Result<(), ezsockets::Error> { 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 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?; self.incoming_sender.send(incoming).await?;
Ok(()) Ok(())
} }