diff --git a/Cargo.lock b/Cargo.lock index 9cd3cab..bea8a53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -365,8 +365,10 @@ dependencies = [ name = "dwn" version = "0.0.2" dependencies = [ + "base64", "cid 0.11.0", "iana-media-types", + "libipld-cbor", "libipld-core", "libipld-pb", "multihash-codetable", @@ -771,6 +773,17 @@ version = "0.2.151" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +[[package]] +name = "libipld-cbor" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77d98c9d1747aa5eef1cf099cd648c3fd2d235249f5fed07522aaebc348e423b" +dependencies = [ + "byteorder", + "libipld-core", + "thiserror", +] + [[package]] name = "libipld-core" version = "0.16.0" diff --git a/dwn-server/src/lib.rs b/dwn-server/src/lib.rs index 14cbfd7..54ef765 100644 --- a/dwn-server/src/lib.rs +++ b/dwn-server/src/lib.rs @@ -7,7 +7,7 @@ use axum::{ Json, Router, }; use dwn::{ - features::FeatureDetection, + features::{FeatureDetection, Record}, request::{Message, Method, RecordIdGenerator, RequestBody}, response::{MessageResult, ResponseBody, Status}, }; @@ -75,8 +75,8 @@ async fn post_handler(body: Json) -> Response { fn process_message(message: &Message) -> Result> { span!(tracing::Level::INFO, "message", ?message); + // Validate record_id { - // Validate record_id let generator = RecordIdGenerator::try_from(&message.descriptor)?; let cid = generator.generate_cid()?; @@ -95,13 +95,8 @@ fn process_message(message: &Message) -> Result { - // TODO: Validate data_format - } - None => { - return Err("Message has data but dataFormat is None".into()); - } + if message.descriptor.data_format.is_none() { + return Err("Message has data but dataFormat is None".into()); }; } @@ -113,8 +108,9 @@ fn process_message(message: &Message) -> Result, } + +#[derive(Debug, Deserialize, Serialize)] +pub enum Protocol { + ProtocolsConfigure, + ProtocolsQuery, +} + +impl Display for Protocol { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + serde_json::to_string(self).unwrap().fmt(f) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub enum Record { + RecordsCommit, + RecordsDelete, + RecordsQuery, + RecordsWrite, +} + +impl Display for Record { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + serde_json::to_string(self).unwrap().fmt(f) + } +} + +#[derive(Debug, Deserialize, Serialize)] +pub enum Permission { + PermissionsGrant, + PermissionsRevoke, +} + +impl Display for Permission { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + serde_json::to_string(self).unwrap().fmt(f) + } +} diff --git a/dwn/src/request.rs b/dwn/src/request.rs index e6f53a0..1e0dfa2 100644 --- a/dwn/src/request.rs +++ b/dwn/src/request.rs @@ -1,9 +1,9 @@ -use std::fmt::Display; - +use base64::Engine; pub use iana_media_types as media_types; -// use libipld_core::{codec::Codec, ipld::Ipld}; -// use libipld_pb::DagPbCodec; +use libipld_cbor::DagCborCodec; use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fmt::Display; use crate::util::cid_from_bytes; @@ -22,27 +22,47 @@ pub struct Message { } pub struct MessageBuilder { - pub data: Option, + pub data: Option, pub descriptor: DescriptorBuilder, } impl MessageBuilder { pub fn build(&self) -> Result> { + let data = self.data.as_ref().map(|d| d.to_base64url()); + let descriptor = self.descriptor.build(self.data.as_ref())?; + let record_id = self.generate_record_id()?; + Ok(Message { - record_id: self.generate_record_id()?, - data: self.data.clone(), // TODO: bas64 encode data - descriptor: self.descriptor.build()?, + data, + descriptor, + record_id, }) } pub fn generate_record_id(&self) -> Result> { - let descriptor = self.descriptor.build()?; + let descriptor = self.descriptor.build(self.data.as_ref())?; let generator = RecordIdGenerator::try_from(&descriptor)?; let record_id = generator.generate_cid()?; Ok(record_id) } } +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] +pub enum Data { + Json(Value), +} + +impl Data { + pub fn to_base64url(&self) -> String { + match self { + Data::Json(value) => { + base64::engine::general_purpose::URL_SAFE.encode(value.to_string()) + } + } + } +} + #[derive(Serialize)] pub struct RecordIdGenerator { #[serde(rename = "descriptorCid")] @@ -53,7 +73,7 @@ impl RecordIdGenerator { /// Generates the CID of this struct after DAG-CBOR serialization. pub fn generate_cid(&self) -> Result> { let bytes = serde_ipld_dagcbor::to_vec(self)?; - let cid = cid_from_bytes(&bytes); + let cid = cid_from_bytes(DagCborCodec.into(), &bytes); Ok(cid.to_string()) } } @@ -63,7 +83,7 @@ impl TryFrom<&Descriptor> for RecordIdGenerator { fn try_from(descriptor: &Descriptor) -> Result { let serialized = serde_ipld_dagcbor::to_vec(descriptor)?; - let descriptor_cid = cid_from_bytes(&serialized).to_string(); + let descriptor_cid = cid_from_bytes(DagCborCodec.into(), &serialized).to_string(); Ok(RecordIdGenerator { descriptor_cid }) } } @@ -75,7 +95,7 @@ pub struct Descriptor { #[serde(rename = "dataCid", skip_serializing_if = "Option::is_none")] pub data_cid: Option, #[serde(rename = "dataFormat", skip_serializing_if = "Option::is_none")] - pub data_format: Option, + pub data_format: Option, } #[derive(Clone, Debug, Deserialize, Serialize)] @@ -92,21 +112,16 @@ pub enum Interface { #[derive(Clone, Debug, Deserialize, Serialize)] pub enum Method { + Commit, + Configure, + Delete, FeatureDetectionRead, - - RecordsRead, - RecordsQuery, - RecordsWrite, - RecordsCommit, - RecordsDelete, - - ProtocolsConfigure, - ProtocolsQuery, - - PermissionsRequest, - PermissionsGrant, - PermissionsRevoke, - PermissionsQuery, + Grant, + Query, + Read, + Request, + Revoke, + Write, } impl Display for Method { @@ -115,12 +130,16 @@ impl Display for Method { } } +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(untagged)] pub enum DataFormat { /// JSON Web Token formatted Verifiable Credential + #[serde(rename = "application/vc+jwt")] VcJWT, /// JSON-LD formatted Verifiable Credential + #[serde(rename = "application/vc+ldp")] VcLDP, - IanaMediaType(media_types::MediaType), + MediaType(media_types::MediaType), } impl Display for DataFormat { @@ -131,11 +150,7 @@ impl Display for DataFormat { impl From<&DataFormat> for String { fn from(data_format: &DataFormat) -> Self { - match data_format { - DataFormat::VcJWT => "application/vc+jwt".to_string(), - DataFormat::VcLDP => "application/vc+ldp".to_string(), - DataFormat::IanaMediaType(media_type) => media_type.to_string(), - } + serde_json::to_string(data_format).unwrap() } } @@ -146,14 +161,16 @@ pub struct DescriptorBuilder { } impl DescriptorBuilder { - pub fn build(&self) -> Result> { - let data_format = self.data_format.as_ref().map(|f| f.to_string()); + pub fn build(&self, data: Option<&Data>) -> Result> { + let data_cid = data.map(|d| { + "".to_string() // TODO: Generate CID + }); Ok(Descriptor { interface: self.interface.clone(), method: self.method.clone(), - data_cid: None, // TODO: Generate data_cid - data_format, + data_cid, + data_format: self.data_format.clone(), }) } } diff --git a/dwn/src/util.rs b/dwn/src/util.rs index 8a4875a..f713518 100644 --- a/dwn/src/util.rs +++ b/dwn/src/util.rs @@ -1,9 +1,8 @@ use cid::Cid; +use libipld_cbor::DagCborCodec; use multihash_codetable::{Code, MultihashDigest}; -const RAW: u64 = 0x55; - -pub fn cid_from_bytes(bytes: &[u8]) -> Cid { +pub fn cid_from_bytes(codec: u64, bytes: &[u8]) -> Cid { let hash = Code::Sha2_256.digest(bytes); - Cid::new_v1(RAW, hash) + Cid::new_v1(codec, hash) }