Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: #1090 #1091 DID:SOV DIDDoc resolves without a service/endpoint and expands shorthand verkey notation for publicKeyBase58 #1105

Merged
merged 21 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

8 changes: 4 additions & 4 deletions aries/aries_vcx/src/handlers/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,17 @@ macro_rules! matches_opt_thread_id {
#[rustfmt::skip] // This macro results in some false positives and formatting makes it harder to read
macro_rules! get_attach_as_string {
($attachments:expr) => {{
let __attach = $attachments.get(0).as_ref().map(|a| &a.data.content);
let __attach = $attachments.first().as_ref().map(|a| &a.data.content);
let err_fn = |attach: Option<&messages::decorators::attachment::Attachment>| {
Err(AriesVcxError::from_msg(
AriesVcxErrorKind::SerializationError,
format!("Attachment is not base 64 encoded JSON: {:?}", attach),
))
};

let Some(messages::decorators::attachment::AttachmentType::Base64(encoded_attach)) = __attach else { return err_fn($attachments.get(0)); };
let Ok(bytes) = base64::engine::Engine::decode(&base64::engine::general_purpose::STANDARD, &encoded_attach) else { return err_fn($attachments.get(0)); };
let Ok(attach_string) = String::from_utf8(bytes) else { return err_fn($attachments.get(0)); };
let Some(messages::decorators::attachment::AttachmentType::Base64(encoded_attach)) = __attach else { return err_fn($attachments.first()); };
let Ok(bytes) = base64::engine::Engine::decode(&base64::engine::general_purpose::STANDARD, &encoded_attach) else { return err_fn($attachments.first()); };
let Ok(attach_string) = String::from_utf8(bytes) else { return err_fn($attachments.first()); };

attach_string
}};
Expand Down
1 change: 1 addition & 0 deletions did_core/did_methods/did_resolver_sov/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ chrono = { version = "0.4.24", default-features = false }
thiserror = "1.0.40"
url = "2.3.1"
log = "0.4.16"
bs58 = "0.5.0"

[dev-dependencies]
mockall = "0.13.0"
Expand Down
120 changes: 93 additions & 27 deletions did_core/did_methods/did_resolver_sov/src/resolution/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,28 @@ fn unix_to_datetime(posix_timestamp: i64) -> Option<DateTime<Utc>> {
DateTime::from_timestamp(posix_timestamp, 0)
}

fn expand_abbreviated_verkey(nym: &str, verkey: &str) -> Result<String, DidSovError> {
if let Some(stripped_verkey) = verkey.strip_prefix('~') {
let mut decoded_nym = bs58::decode(nym).into_vec().map_err(|e| {
DidSovError::ParsingError(ParsingErrorSource::LedgerResponseParsingError(format!(
"Failed to decode did from base58: {} (error: {})",
nym, e
)))
})?;
let decoded_stripped_verkey = bs58::decode(stripped_verkey).into_vec().map_err(|e| {
DidSovError::ParsingError(ParsingErrorSource::LedgerResponseParsingError(format!(
"Failed to decode verkey from base58: {} (error: {})",
stripped_verkey, e
)))
})?;
decoded_nym.extend(&decoded_stripped_verkey);

Ok(bs58::encode(decoded_nym).into_string())
} else {
Ok(verkey.to_string())
}
}

pub(super) fn is_valid_sovrin_did_id(id: &str) -> bool {
if id.len() < 21 || id.len() > 22 {
return false;
Expand All @@ -69,50 +91,71 @@ pub(super) async fn ledger_response_to_ddo(
log::info!("ledger_response_to_ddo >> did: {did}, verkey: {verkey}, resp: {resp}");
let (service_id, ddo_id) = prepare_ids(did)?;

let service_data = get_data_from_response(resp)?;
log::info!("ledger_response_to_ddo >> service_data: {service_data:?}");
let endpoint: EndpointDidSov = serde_json::from_value(service_data["endpoint"].clone())?;

let txn_time = get_txn_time_from_response(resp)?;
let datetime = unix_to_datetime(txn_time);

let service_types: Vec<ServiceType> = endpoint
.types
.into_iter()
.map(|t| match t {
DidSovServiceType::Endpoint => ServiceType::AIP1,
DidSovServiceType::DidCommunication => ServiceType::DIDCommV1,
DidSovServiceType::DIDComm => ServiceType::DIDCommV2,
DidSovServiceType::Unknown => ServiceType::Other("Unknown".to_string()),
})
.collect();
let service = Service::new(
service_id,
endpoint.endpoint,
OneOrList::List(service_types),
Default::default(),
);
let service_data = match get_data_from_response(resp) {
Ok(data) => data,
Err(e) => {
log::warn!("Failed to get service data: {}", e);
serde_json::Value::Null
}
};

let mut ddo = DidDocument::new(ddo_id.clone());

let mut services = Vec::new();

if !service_data.is_null() {
let endpoint: EndpointDidSov = serde_json::from_value(service_data["endpoint"].clone())?;
gmulhearn marked this conversation as resolved.
Show resolved Hide resolved

let service_types: Vec<ServiceType> = endpoint
.types
.into_iter()
.map(|t| match t {
DidSovServiceType::Endpoint => ServiceType::AIP1,
DidSovServiceType::DidCommunication => ServiceType::DIDCommV1,
DidSovServiceType::DIDComm => ServiceType::DIDCommV2,
DidSovServiceType::Unknown => ServiceType::Other("Unknown".to_string()),
})
.collect();
let service = Service::new(
service_id,
endpoint.endpoint,
OneOrList::List(service_types),
Default::default(),
);
services.push(service);
}

ddo.set_service(services);

let txn_time_result = get_txn_time_from_response(resp);
let datetime = match txn_time_result {
Ok(txn_time) => unix_to_datetime(txn_time),
Err(e) => {
log::warn!("Failed to parse txnTime: {}", e);
None
}
};

let expanded_verkey = expand_abbreviated_verkey(ddo_id.id(), &verkey)?;

// TODO: Use multibase instead of base58
let verification_method = VerificationMethod::builder()
.id(DidUrl::parse("#1".to_string())?)
.controller(did.to_string().try_into()?)
.verification_method_type(VerificationMethodType::Ed25519VerificationKey2018)
.public_key(PublicKeyField::Base58 {
public_key_base58: verkey,
public_key_base58: expanded_verkey,
})
.build();

let mut ddo = DidDocument::new(ddo_id);
ddo.add_service(service);
ddo.add_verification_method(verification_method);
ddo.add_key_agreement_ref(DidUrl::parse("#1".to_string())?);

let ddo_metadata = {
let mut metadata_builder = DidDocumentMetadata::builder().deactivated(false);
if let Some(datetime) = datetime {
metadata_builder = metadata_builder.updated(datetime);
};
}
metadata_builder.build()
};

Expand Down Expand Up @@ -216,4 +259,27 @@ mod tests {
panic!("Unexpected public key type");
}
}

#[test]
fn test_expand_abbreviated_verkey_with_abbreviation() {
let nym = "7Sqc3ne5NfUVxMTrHahxz3";
let abbreviated_verkey = "~DczaFTexiEYv5abkEUZeZt";
let expected_full_verkey = "4WkksEAXsewRbDYDz66aTdjtVF2LBxbqEMyF2WEjTBKk";

assert_eq!(
expand_abbreviated_verkey(nym, abbreviated_verkey).unwrap(),
expected_full_verkey
);
}

#[test]
fn test_expand_abbreviated_verkey_without_abbreviation() {
let nym = "123456789abcdefghi";
let full_verkey = "123456789abcdefghixyz123";

assert_eq!(
expand_abbreviated_verkey(nym, full_verkey).unwrap(),
full_verkey
);
}
}
34 changes: 33 additions & 1 deletion did_core/did_methods/did_resolver_sov/tests/resolution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::{thread, time::Duration};

use aries_vcx::common::ledger::{service_didsov::EndpointDidSov, transactions::write_endpoint};
use aries_vcx_ledger::ledger::base_ledger::IndyLedgerWrite;
use aries_vcx_wallet::wallet::base_wallet::BaseWallet;
use aries_vcx_wallet::wallet::base_wallet::{did_wallet::DidWallet, BaseWallet};
use did_resolver::{
did_doc::schema::service::typed::ServiceType,
did_parser_nom::Did,
Expand Down Expand Up @@ -58,3 +58,35 @@ async fn test_error_handling_during_resolution() {

assert!(result.is_err());
}

#[tokio::test]
async fn write_new_nym_and_get_did_doc() {
let profile = build_setup_profile().await;
let did_data = profile
.wallet
.create_and_store_my_did(None, None)
.await
.unwrap();

profile
.ledger_write
.publish_nym(
&profile.wallet,
&profile.institution_did,
&did_data.did().parse().unwrap(),
Some(did_data.verkey()),
None,
None,
)
.await
.unwrap();

let resolver = DidSovResolver::new(profile.ledger_read);
let did = format!("did:sov:{}", did_data.did());

let DidResolutionOutput { did_document, .. } = resolver
.resolve(&Did::parse(did.clone()).unwrap(), &())
.await
.unwrap();
assert_eq!(did_document.id().to_string(), did);
}
Loading