Skip to content

Commit

Permalink
Fix: #1090 #1091 DID:SOV DIDDoc resolves without a service/endpoint a…
Browse files Browse the repository at this point in the history
…nd expands shorthand verkey notation for publicKeyBase58 (#1105)

* lli - Added Solution/Tests

Signed-off-by: lli <lli@anonyome.com>

* lli - merge issue 1090

Signed-off-by: lli <lli@anonyome.com>

* lli - Add Resolution Test back

Signed-off-by: lli <lli@anonyome.com>

* lli - Clippy fixes

Signed-off-by: lli <lli@anonyome.com>

* lli - Clippy fixes

Signed-off-by: lli <lli@anonyome.com>

* lli - Clippy fixes

Signed-off-by: lli <lli@anonyome.com>

* lli - Clippy fixes

Signed-off-by: lli <lli@anonyome.com>

* lli - Clippy fixes

Signed-off-by: lli <lli@anonyome.com>

* lli - Fix Expanding Verkey Fn

Signed-off-by: lli <lli@anonyome.com>

* lli - Fix unwrapping err

Signed-off-by: lli <lli@anonyome.com>

* lli - Remove unwraps

Signed-off-by: lli <lli@anonyome.com>

* lli - use ddo_id to retrieve nym

Signed-off-by: lli <lli@anonyome.com>

* Merge Conflicts

Signed-off-by: lli <lli@anonyome.com>

* Merge Conflicts

Signed-off-by: lli <lli@anonyome.com>

* Fixing Test

Signed-off-by: lli <lli@anonyome.com>

* formatting

Signed-off-by: lli <lli@anonyome.com>

* Fix tests

Signed-off-by: lli <lli@anonyome.com>

* Clippy

Signed-off-by: lli <lli@anonyome.com>

* Fix Endpoint Test

Signed-off-by: lli <lli@anonyome.com>

* Remove print

Signed-off-by: lli <lli@anonyome.com>

* Log response error

Signed-off-by: lli <lli@anonyome.com>

---------

Signed-off-by: lli <lli@anonyome.com>
  • Loading branch information
lukewli-anonyome authored Aug 7, 2024
1 parent db9d1d4 commit b664ce7
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 32 deletions.
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())?;

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);
}

0 comments on commit b664ce7

Please sign in to comment.