Skip to content

Commit

Permalink
add base64 data encoding
Browse files Browse the repository at this point in the history
  • Loading branch information
kayhhh committed Dec 18, 2023
1 parent 6786ff2 commit 3b1a2b4
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 54 deletions.
13 changes: 13 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 7 additions & 11 deletions dwn-server/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use axum::{
Json, Router,
};
use dwn::{
features::FeatureDetection,
features::{FeatureDetection, Record},
request::{Message, Method, RecordIdGenerator, RequestBody},
response::{MessageResult, ResponseBody, Status},
};
Expand Down Expand Up @@ -75,8 +75,8 @@ async fn post_handler(body: Json<RequestBody>) -> Response {
fn process_message(message: &Message) -> Result<MessageResult, Box<dyn std::error::Error>> {
span!(tracing::Level::INFO, "message", ?message);

// Validate record_id
{
// Validate record_id
let generator = RecordIdGenerator::try_from(&message.descriptor)?;
let cid = generator.generate_cid()?;

Expand All @@ -95,13 +95,8 @@ fn process_message(message: &Message) -> Result<MessageResult, Box<dyn std::erro
}
};

match &message.descriptor.data_format {
Some(_) => {
// 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());
};
}

Expand All @@ -113,8 +108,9 @@ fn process_message(message: &Message) -> Result<MessageResult, Box<dyn std::erro
let mut features = FeatureDetection::default();

features.interfaces.records = Some(BTreeMap::from_iter(vec![
(Method::RecordsRead.to_string(), true),
(Method::RecordsQuery.to_string(), true),
(Record::RecordsCommit.to_string(), true),
(Record::RecordsQuery.to_string(), true),
(Record::RecordsWrite.to_string(), true),
]));

let value = serde_json::to_value(features)?;
Expand Down
9 changes: 7 additions & 2 deletions dwn-server/tests/messages.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use dwn::{
features::FeatureDetection,
request::{DescriptorBuilder, Interface, Message, MessageBuilder, Method, RequestBody},
request::{
media_types::{Application, MediaType},
DataFormat, DescriptorBuilder, Interface, Message, MessageBuilder, Method, RequestBody,
},
response::ResponseBody,
};
use dwn_server::StartOptions;
Expand Down Expand Up @@ -139,7 +142,9 @@ async fn requires_data_descriptors() {
let mut msg = empty_message();
msg.data = Some("test data".to_string());
msg.descriptor.data_cid = Some("test data cid".to_string());
msg.descriptor.data_format = Some("test data format".to_string());
msg.descriptor.data_format = Some(DataFormat::MediaType(MediaType::Application(
Application::Json,
)));

let mut without_cid = msg.clone();
without_cid.descriptor.data_cid = None;
Expand Down
2 changes: 2 additions & 0 deletions dwn/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ repository.workspace = true
license.workspace = true

[dependencies]
base64 = "0.21.5"
cid = "0.11.0"
iana-media-types = "0.1.2"
libipld-cbor = "0.16.0"
libipld-core = "0.16.0"
libipld-pb = "0.16.0"
multihash-codetable = { version = "0.1.1", features = ["digest", "sha2"] }
Expand Down
40 changes: 39 additions & 1 deletion dwn/src/features.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::collections::BTreeMap;
use std::{collections::BTreeMap, fmt::Display};

use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -40,3 +40,41 @@ pub struct Messaging {
#[serde(skip_serializing_if = "Option::is_none")]
pub batching: Option<bool>,
}

#[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)
}
}
89 changes: 53 additions & 36 deletions dwn/src/request.rs
Original file line number Diff line number Diff line change
@@ -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;

Expand All @@ -22,27 +22,47 @@ pub struct Message {
}

pub struct MessageBuilder {
pub data: Option<String>,
pub data: Option<Data>,
pub descriptor: DescriptorBuilder,
}

impl MessageBuilder {
pub fn build(&self) -> Result<Message, Box<dyn std::error::Error>> {
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<String, Box<dyn std::error::Error>> {
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")]
Expand All @@ -53,7 +73,7 @@ impl RecordIdGenerator {
/// Generates the CID of this struct after DAG-CBOR serialization.
pub fn generate_cid(&self) -> Result<String, Box<dyn std::error::Error>> {
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())
}
}
Expand All @@ -63,7 +83,7 @@ impl TryFrom<&Descriptor> for RecordIdGenerator {

fn try_from(descriptor: &Descriptor) -> Result<Self, Self::Error> {
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 })
}
}
Expand All @@ -75,7 +95,7 @@ pub struct Descriptor {
#[serde(rename = "dataCid", skip_serializing_if = "Option::is_none")]
pub data_cid: Option<String>,
#[serde(rename = "dataFormat", skip_serializing_if = "Option::is_none")]
pub data_format: Option<String>,
pub data_format: Option<DataFormat>,
}

#[derive(Clone, Debug, Deserialize, Serialize)]
Expand All @@ -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 {
Expand All @@ -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 {
Expand All @@ -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()
}
}

Expand All @@ -146,14 +161,16 @@ pub struct DescriptorBuilder {
}

impl DescriptorBuilder {
pub fn build(&self) -> Result<Descriptor, Box<dyn std::error::Error>> {
let data_format = self.data_format.as_ref().map(|f| f.to_string());
pub fn build(&self, data: Option<&Data>) -> Result<Descriptor, Box<dyn std::error::Error>> {
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(),
})
}
}
7 changes: 3 additions & 4 deletions dwn/src/util.rs
Original file line number Diff line number Diff line change
@@ -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)
}

0 comments on commit 3b1a2b4

Please sign in to comment.