diff --git a/Cargo.lock b/Cargo.lock index c5df8e7..663b8b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -264,6 +264,12 @@ version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64ct" version = "1.6.0" @@ -298,6 +304,7 @@ dependencies = [ "random_color", "serde", "serde_json", + "serde_with 3.8.1", "sha2 0.10.8", "time 0.1.45", ] @@ -803,7 +810,7 @@ dependencies = [ "secp256k1", "serde", "serde_json", - "serde_with", + "serde_with 2.3.3", "sha2 0.10.8", "sha3", "signature", @@ -1177,6 +1184,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.5", + "serde", ] [[package]] @@ -1995,7 +2003,25 @@ dependencies = [ "indexmap 1.9.3", "serde", "serde_json", - "serde_with_macros", + "serde_with_macros 2.3.3", + "time 0.3.36", +] + +[[package]] +name = "serde_with" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros 3.8.1", "time 0.3.36", ] @@ -2011,6 +2037,18 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "serde_with_macros" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "sha-1" version = "0.9.8" @@ -2078,6 +2116,7 @@ dependencies = [ "clap", "serde", "serde_json", + "serde_with 3.8.1", "tokio", "websockets", ] diff --git a/crates/bft-json-crdt/Cargo.toml b/crates/bft-json-crdt/Cargo.toml index 75f9d82..6519417 100644 --- a/crates/bft-json-crdt/Cargo.toml +++ b/crates/bft-json-crdt/Cargo.toml @@ -22,6 +22,7 @@ rand = "0.8.5" random_color = "0.6.1" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.85" +serde_with = "3.8.1" sha2 = "0.10.6" [dev-dependencies] diff --git a/crates/bft-json-crdt/src/json_crdt.rs b/crates/bft-json-crdt/src/json_crdt.rs index b8d5eac..abcfc30 100644 --- a/crates/bft-json-crdt/src/json_crdt.rs +++ b/crates/bft-json-crdt/src/json_crdt.rs @@ -17,6 +17,8 @@ use fastcrypto::{ traits::{KeyPair, ToFromBytes}, // Verifier, }; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, Bytes}; /// Anything that can be nested in a JSON CRDT pub trait CrdtNode: CrdtNodeFromValue + Hashable + Clone { @@ -106,15 +108,18 @@ pub struct BaseCrdt { } /// An [`Op`] with a few bits of extra metadata -#[derive(Clone)] +#[serde_as] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct SignedOp { // Note that this can be different from the author of the inner op as the inner op could have been created // by a different person author: AuthorId, /// Signed hash using priv key of author. Effectively [`OpID`] Use this as the ID to figure out what has been delivered already + #[serde_as(as = "Bytes")] pub signed_digest: SignedDigest, pub inner: Op, /// List of causal dependencies + #[serde_as(as = "Vec")] pub depends_on: Vec, } @@ -244,7 +249,7 @@ impl BaseCrdt { } /// An enum representing a JSON value -#[derive(Clone, Debug, PartialEq)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum JsonValue { Null, Bool(bool), diff --git a/crates/bft-json-crdt/src/op.rs b/crates/bft-json-crdt/src/op.rs index b217da3..9c28070 100644 --- a/crates/bft-json-crdt/src/op.rs +++ b/crates/bft-json-crdt/src/op.rs @@ -81,7 +81,7 @@ pub fn parse_field(path: Vec) -> Option { } /// Represents a single node in a CRDT -#[derive(Clone, Serialize, Deserialize)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct Op where T: CrdtNode, diff --git a/side-node/Cargo.toml b/side-node/Cargo.toml index 0fb2df3..e92005b 100644 --- a/side-node/Cargo.toml +++ b/side-node/Cargo.toml @@ -14,6 +14,7 @@ bft-crdt-derive = { path = "../crates/bft-json-crdt/bft-crdt-derive" } # serde_cbor = "0.11.2" # move to this once we need to pack things in CBOR serde = { version = "1.0", features = ["derive"] } serde_json = "1.0.117" +serde_with = "3.8.1" [features] default = ["bft", "logging-list", "logging-json"] diff --git a/side-node/src/websocket/mod.rs b/side-node/src/websocket/mod.rs index c85edfb..736ada7 100644 --- a/side-node/src/websocket/mod.rs +++ b/side-node/src/websocket/mod.rs @@ -1,4 +1,5 @@ use bft_crdt_derive::add_crdt_fields; +use bft_json_crdt::json_crdt::SignedOp; use bft_json_crdt::keypair::KeyPair; use bft_json_crdt::{ json_crdt::{BaseCrdt, CrdtNode, IntoCrdtNode}, @@ -15,27 +16,38 @@ pub(crate) async fn start() -> Result<(), websockets::WebSocketError> { println!("connecting to websocket at ws://127.0.0.1:8080/"); let mut ws = WebSocket::connect("ws://127.0.0.1:8080/").await?; - // generate a placeholder transaction - let json = generate_transaction().unwrap(); - // set up a new BFT-CRDT let keys = make_keypair(); let mut bft_crdt = BaseCrdt::::new(&keys); println!("Generated a new CRDT with public key: {}", keys.public()); + // generate a placeholder transaction + let transaction = generate_transaction(); + + let json = convert_to_json(&transaction).unwrap(); + // next job is to keep adding to this guy - let _a = bft_crdt.doc.list.insert(ROOT_ID, 'a').sign(&keys); + let signed_op = bft_crdt.doc.list.insert(ROOT_ID, json.clone()).sign(&keys); + println!("SignedOp before send is: {:?}", signed_op); let mut interval = every_two_seconds(); loop { interval.tick().await; - println!("Sending: {}", json); - ws.send_text(json.clone()).await?; + println!("Sending: {:?}", signed_op); + ws.send_text(serde_json::to_string(&signed_op).unwrap()) + .await?; let msg = ws.receive().await?; println!("Received: {:?}", msg); + + // deserialize the received Frame into a string + let msg = msg.into_text().unwrap().0; + + // deserialize the message into a Transaction struct + let operation: SignedOp = serde_json::from_str(&msg).unwrap(); + // TODO: bft_crdt.apply() changes in here when we receive socket input from other nodes - bft_crdt.apply(msg); + bft_crdt.apply(operation); println!("New crdt state is: {}", bft_crdt.doc.view()) } } @@ -59,15 +71,17 @@ struct Transaction { amount: f64, } -fn generate_transaction() -> serde_json::Result { - let transaction = Transaction { +fn generate_transaction() -> Transaction { + Transaction { from: "Alice".to_string(), to: "Bob".to_string(), amount: 100.0, path: vec![], id: [0; ED25519_PUBLIC_KEY_LENGTH], - }; + } +} +fn convert_to_json(transaction: &Transaction) -> serde_json::Result { let json = serde_json::to_string(&transaction).unwrap(); Ok(json) }