Skip to content

Commit

Permalink
refactor for updated event API
Browse files Browse the repository at this point in the history
  • Loading branch information
DougAnderson444 committed Feb 13, 2024
1 parent 638b626 commit 9444f53
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 49 deletions.
2 changes: 1 addition & 1 deletion crates/delano-wit-ui/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ package = "delano:wit-ui"
# It's not documented, but you can use `package.metadata.component.bindings` to
# set additional derive attributes for the generated bindings:
[package.metadata.component.bindings]
derives = ["serde::Serialize", "serde::Deserialize"]
# derives = ["serde::Serialize", "serde::Deserialize"]

[package.metadata.component.dependencies]

Expand Down
121 changes: 80 additions & 41 deletions crates/delano-wit-ui/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
//! used to format and serialize the data as it's passed around.
//!
//! WIT interface types are kebab case, and all types must be serializable and deserializable.
use self::attributes::{AttributeKOV, Hint};

use super::*;

use base64ct::{Base64UrlUnpadded, Encoding};
use chrono::prelude::*;
use delano_keys::publish::PublishingKey;
use delano_events::{Context, Provables};
use delano_keys::publish::{IssuerKey, OfferedPreimages, PublishingKey};
use delanocreds::Attribute;
use serde::{Deserialize, Serialize};

Expand Down Expand Up @@ -45,7 +45,7 @@ pub(crate) struct State {
/// The offer
pub(crate) offer: Option<String>,
/// The proof, if any
pub(crate) proof: Option<String>,
pub(crate) proof: Option<Loaded>,
}

impl State {
Expand Down Expand Up @@ -93,9 +93,11 @@ impl State {
pub(crate) fn with_proof(mut self) -> Self {
let proof = match self.proof() {
Ok(proof) => proof,
Err(e) => Some(e),
Err(e) => {
println!("Error generating proof: {}", e);
return self;
}
};
println!("Proof: {:?}", proof);
self.proof = proof;
self
}
Expand Down Expand Up @@ -139,17 +141,11 @@ impl State {
let Ok(issuer_key) = wallet::actions::issuer_public() else {
return Err("Issuer public key failed".to_string());
};
let publish_key = PublishingKey::default()
.with_attributes(
&self
.builder
.entries
.iter()
.map(|a| a.iter().map(|a| a.into_bytes()).collect::<Vec<Vec<u8>>>())
.collect::<Vec<Vec<Vec<u8>>>>(),
)
.with_issuer_key(&issuer_key)
.cid();
let publish_key = PublishingKey::new(
&delano_keys::publish::OfferedPreimages(&self.builder.entries),
&delano_keys::publish::IssuerKey(&issuer_key),
)
.cid();

let offered = offer
.to_urlsafe()
Expand All @@ -172,14 +168,15 @@ impl State {
}

/// Generate proof from this State if there is an offer loaded.
fn proof(&self) -> Result<Option<String>, String> {
fn proof(&self) -> Result<Option<Loaded>, String> {
match &self.loaded {
Loaded::Offer { cred, .. } => {
let accepted = wallet::actions::accept(&cred)?;
let cred = self.builder.extend(accepted)?;
let proof_package = self.builder.proof_package(&cred)?;
match proof_package.verify() {
Ok(true) => Ok(Some(proof_package.to_urlsafe().map_err(|e| e.to_string())?)),
// TODO: move url fn to with_proof fn
Ok(true) => Ok(Some(proof_package)),
Ok(false) => Err("That proof is invalid!".to_string()),
Err(e) => Err(format!("Verify function failed: {}", e)),
}
Expand All @@ -192,6 +189,37 @@ impl State {
self.builder = builder;
self
}

/// Publish the proof to the network by emiting a serialized message of the Key and Provables
pub(crate) fn publish_proof(self) -> Self {
// TODO: Handle failures better
let Some(Loaded::Proof(ref provables)) = self.proof else {
return self;
};
let Ok(issuer_key) = wallet::actions::issuer_public() else {
return self;
};
// Emit key-value pair.
// The key is a delano-keys::PublishingKey using only the first Entry of attributes in the
// offered credential (entry zero).
// The value is proof with provables
let publishables = delano_events::Publishables::new(
PublishingKey::new(
&OfferedPreimages(&self.builder.entries[0]),
&IssuerKey(&issuer_key),
),
provables.clone(),
);
// serde_json and base64 encode the publishables
// We do this instead of string because JavaScript doesn't handle string from Uint8Array well.
let serde_publishables = serde_json::to_vec(&publishables).unwrap_or_default();
let base64_publishables = base64ct::Base64Url::encode_string(&serde_publishables);

let message_data = Context::Message(base64_publishables.to_string());
let message = serde_json::to_string(&message_data).unwrap_or_default();
wurbo_in::emit(&message);
self
}
}

impl StructObject for State {
Expand All @@ -208,7 +236,10 @@ impl StructObject for State {
None => Some(Value::from("No offer generated")),
},
"proof" => match self.proof {
Some(ref proof) => Some(Value::from(proof.clone())),
Some(ref proof) => match proof.to_urlsafe() {
Ok(urlsafe_proof) => Some(Value::from(urlsafe_proof)),
Err(e) => Some(Value::from(e.to_string())),
},
None => Some(Value::from("No proof generated")),
},
"history" => Some(Value::from_serializable(&self.history.clone())),
Expand Down Expand Up @@ -259,18 +290,7 @@ pub enum Loaded {
/// The Hints
hints: Vec<Vec<AttributeKOV>>,
},
Proof {
/// The proof byte vector
proof: Vec<u8>,
/// The selected Entry attributes, as CID bytes, in the right sequence.
selected: Vec<Vec<Vec<u8>>>,
/// The selected Entry Attributes are hashes, so we can provide the preimage here. The UI
/// can then compare these preimage hints to the selected values to verify they match.
preimages: Vec<Vec<AttributeKOV>>,
/// We will also include the Issuer's Public Parameters from the Credential, so the holder
/// can verify against the Issuer's public key.
issuer_public: Vec<u8>,
},
Proof(Provables<AttributeKOV>),
#[default]
None,
}
Expand All @@ -288,12 +308,12 @@ impl Loaded {
// 3) TODO: The user should also check the Issuer's public key included in the proof
// against the Issuer's public key they have on file / find online (web resolve).
match self {
Self::Proof {
Self::Proof(Provables::<AttributeKOV> {
selected,
preimages,
selected_preimages: preimages,
proof,
issuer_public,
} => {
}) => {
// Step 1) Hash preimages & compare to selected
// Iterate through each preimage converting them into delanocreds::Attribute,
// then compare them to the selected.
Expand All @@ -303,14 +323,18 @@ impl Loaded {
.zip(selected.iter())
.all(|(preimage, selected)| {
// Convert the preimage into a delanocreds::Attribute
let preimage = preimage
.iter()
.map(|kov| Attribute::from(kov.to_string()).to_bytes())
.collect::<Vec<Vec<u8>>>();

// Hash the preimage
// Compare the preimage hash to the selected
preimage == *selected
*selected
== preimage
.iter()
.map(|kov| {
Attribute::from(
AttributeKOV::try_from(kov.clone()).unwrap_or_default(),
)
.to_bytes()
})
.collect::<Vec<Vec<u8>>>()
});

// Step 2) Verify the proof using wallet::actions::verify
Expand Down Expand Up @@ -358,7 +382,22 @@ impl StructObject for Loaded {
_ => None,
},
"preimages" => match self {
Self::Proof { preimages, .. } => Some(Value::from(preimages.clone())),
Self::Proof(Provables::<AttributeKOV> {
selected_preimages: preimages,
..
}) => {
let de_preimages: Vec<Vec<AttributeKOV>> = preimages
.iter()
.map(|entry| {
entry
.iter()
.map(|a| AttributeKOV::try_from(a.clone()).unwrap_or_default())
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();

Some(Value::from(de_preimages.clone()))
}
_ => None,
},
"verified" => match self.verify() {
Expand Down
14 changes: 14 additions & 0 deletions crates/delano-wit-ui/src/attributes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use super::*;

use delanocreds::CBORCodec;
use serde::{Deserialize, Serialize};
use std::ops::Deref;

Expand Down Expand Up @@ -115,9 +116,14 @@ pub struct AttributeKOV {
pub value: AttributeValue,
/// Selected, whether the user has selected this attribute for inclusion in the credential
/// proof
/// Skip in serialization
#[serde(skip)]
/// Default to true
pub selected: bool,
}

impl CBORCodec for AttributeKOV {}

impl Default for AttributeKOV {
fn default() -> Self {
Self {
Expand Down Expand Up @@ -201,6 +207,14 @@ impl From<&AttributeKOV> for delanocreds::Attribute {
}
}

/// Uses serde to deserialize from bytes.Uses default if deserialization fails.
impl From<Vec<u8>> for AttributeKOV {
fn from(bytes: Vec<u8>) -> Self {
// Deserialize from bytes using CBORCodec
Self::from_bytes(&bytes).unwrap_or_default()
}
}

/// Hints are only the key and operator values
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Hint {
Expand Down
37 changes: 34 additions & 3 deletions crates/delano-wit-ui/src/bindings.rs

Large diffs are not rendered by default.

9 changes: 5 additions & 4 deletions crates/delano-wit-ui/src/credential.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
use super::*;
use crate::attributes::AttributeKOV;
use delano_events::Provables;
use delanocreds::{CBORCodec, Credential, Nonce};

/// The Credential Struct
Expand Down Expand Up @@ -180,12 +181,12 @@ impl CredentialStruct {

let cred_struct = Credential::from_bytes(cred).map_err(|e| e.to_string())?;

Ok(crate::api::Loaded::Proof {
Ok(api::Loaded::Proof(Provables {
proof,
selected: selected_entries,
preimages,
issuer_public: cred_struct.issuer_public.to_bytes().unwrap_or_default(),
})
selected: selected_entries,
selected_preimages: preimages,
}))
}
}

Expand Down
5 changes: 5 additions & 0 deletions crates/delano-wit-ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ impl From<&context_types::Context> for StructContext {
StructContext::from(api::State::from_latest().with_proof())
.with_target(OUTPUT_HTML.to_string())
}
context_types::Context::Publishproof => {
// emit publish event
StructContext::from(api::State::from_latest().publish_proof())
.with_target(OUTPUT_HTML.to_string())
}
}
}
}
Expand Down

0 comments on commit 9444f53

Please sign in to comment.