Skip to content

Commit

Permalink
Support multiple IOTA networks in the Resolver (#1304)
Browse files Browse the repository at this point in the history
  • Loading branch information
abdulmth authored Feb 21, 2024
1 parent 48696c6 commit 41105e5
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 4 deletions.
4 changes: 3 additions & 1 deletion identity_iota_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ rustdoc-args = ["--cfg", "docsrs"]
default = ["client", "iota-client", "revocation-bitmap", "send-sync-client-ext"]
# Exposes the IotaIdentityClient and IotaIdentityClientExt traits.
client = ["dep:async-trait", "iota-sdk"]
# Enbales the implementation of the extension traits on the iota-sdk's Client.
# Enables the implementation of the extension traits on the iota-sdk's Client.
iota-client = ["client", "iota-sdk/client", "iota-sdk/tls"]
# Enables revocation with `RevocationBitmap2022`.
revocation-bitmap = ["identity_credential/revocation-bitmap"]
# Adds Send bounds on the futures produces by the client extension traits.
send-sync-client-ext = []
# Disables the blanket implementation of `IotaIdentityClientExt`.
test = ["client"]
7 changes: 6 additions & 1 deletion identity_iota_core/src/client/identity_client.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Copyright 2020-2023 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use crate::block::protocol::ProtocolParameters;
#[cfg(feature = "test")]
use iota_sdk::client::Client;

use crate::block::address::Address;
use crate::block::output::feature::SenderFeature;
Expand All @@ -14,6 +15,7 @@ use crate::block::output::Feature;
use crate::block::output::OutputId;
use crate::block::output::RentStructure;
use crate::block::output::UnlockCondition;
use crate::block::protocol::ProtocolParameters;
use crate::Error;
use crate::IotaDID;
use crate::IotaDocument;
Expand Down Expand Up @@ -192,7 +194,10 @@ pub trait IotaIdentityClientExt: IotaIdentityClient {
}
}

#[cfg(not(feature = "test"))]
impl<T> IotaIdentityClientExt for T where T: IotaIdentityClient {}
#[cfg(feature = "test")]
impl IotaIdentityClientExt for Client {}

pub(super) async fn validate_network<T>(client: &T, did: &IotaDID) -> Result<()>
where
Expand Down
2 changes: 2 additions & 0 deletions identity_resolver/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ features = ["send-sync-client-ext", "iota-client"]
optional = true

[dev-dependencies]
identity_iota_core = { version = "=1.1.1", path = "../identity_iota_core", features = ["test"] }
iota-sdk = { version = "1.0.2" }
tokio = { version = "1.29.0", default-features = false, features = ["rt-multi-thread", "macros"] }

[features]
Expand Down
3 changes: 3 additions & 0 deletions identity_resolver/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,7 @@ pub enum ErrorCause {
/// The method that is unsupported.
method: String,
},
/// No client attached to the specific network.
#[error("none of the attached clients support the network {0}")]
UnsupportedNetwork(String),
}
118 changes: 116 additions & 2 deletions identity_resolver/src/resolution/resolver.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,12 +249,14 @@ impl<DOC: 'static> Resolver<DOC, SingleThreadedCommand<DOC>> {

#[cfg(feature = "iota")]
mod iota_handler {
use crate::ErrorCause;

use super::Resolver;
use identity_document::document::CoreDocument;
use identity_iota_core::IotaClientExt;
use identity_iota_core::IotaDID;
use identity_iota_core::IotaDocument;
use identity_iota_core::IotaIdentityClientExt;
use std::collections::HashMap;
use std::sync::Arc;

impl<DOC> Resolver<DOC>
Expand All @@ -266,7 +268,7 @@ mod iota_handler {
/// See also [`attach_handler`](Self::attach_handler).
pub fn attach_iota_handler<CLI>(&mut self, client: CLI)
where
CLI: IotaClientExt + Send + Sync + 'static,
CLI: IotaIdentityClientExt + Send + Sync + 'static,
{
let arc_client: Arc<CLI> = Arc::new(client);

Expand All @@ -277,6 +279,58 @@ mod iota_handler {

self.attach_handler(IotaDID::METHOD.to_owned(), handler);
}

/// Convenience method for attaching multiple handlers responsible for resolving IOTA DIDs
/// on multiple networks.
///
///
/// # Arguments
///
/// * `clients` - A collection of tuples where each tuple contains the name of the network name and its
/// corresponding client.
///
/// # Examples
///
/// ```ignore
/// // Assume `smr_client` and `iota_client` are instances IOTA clients `iota_sdk::client::Client`.
/// attach_multiple_iota_handlers(vec![("smr", smr_client), ("iota", iota_client)]);
/// ```
///
/// # See Also
/// - [`attach_handler`](Self::attach_handler).
///
/// # Note
///
/// - Using `attach_iota_handler` or `attach_handler` for the IOTA method would override all
/// previously added clients.
/// - This function does not validate the provided configuration. Ensure that the provided
/// network name corresponds with the client, possibly by using `client.network_name()`.
pub fn attach_multiple_iota_handlers<CLI, I>(&mut self, clients: I)
where
CLI: IotaIdentityClientExt + Send + Sync + 'static,
I: IntoIterator<Item = (&'static str, CLI)>,
{
let arc_clients = Arc::new(clients.into_iter().collect::<HashMap<&'static str, CLI>>());

let handler = move |did: IotaDID| {
let future_client = arc_clients.clone();
async move {
let did_network = did.network_str();
let client: &CLI =
future_client
.get(did_network)
.ok_or(crate::Error::new(ErrorCause::UnsupportedNetwork(
did_network.to_string(),
)))?;
client
.resolve_did(&did)
.await
.map_err(|err| crate::Error::new(ErrorCause::HandlerError { source: Box::new(err) }))
}
};

self.attach_handler(IotaDID::METHOD.to_owned(), handler);
}
}
}

Expand All @@ -301,3 +355,63 @@ where
.finish()
}
}

#[cfg(test)]
mod tests {
use identity_iota_core::block::output::AliasId;
use identity_iota_core::block::output::AliasOutput;
use identity_iota_core::block::output::OutputId;
use identity_iota_core::block::protocol::ProtocolParameters;
use identity_iota_core::IotaDID;
use identity_iota_core::IotaDocument;
use identity_iota_core::IotaIdentityClient;
use identity_iota_core::IotaIdentityClientExt;

use super::*;

struct DummyClient(IotaDocument);

#[async_trait::async_trait]
impl IotaIdentityClient for DummyClient {
async fn get_alias_output(&self, _id: AliasId) -> identity_iota_core::Result<(OutputId, AliasOutput)> {
unreachable!()
}
async fn get_protocol_parameters(&self) -> identity_iota_core::Result<ProtocolParameters> {
unreachable!()
}
}

#[async_trait::async_trait]
impl IotaIdentityClientExt for DummyClient {
async fn resolve_did(&self, did: &IotaDID) -> identity_iota_core::Result<IotaDocument> {
if self.0.id().as_str() == did.as_str() {
Ok(self.0.clone())
} else {
Err(identity_iota_core::Error::DIDResolutionError(
iota_sdk::client::error::Error::NoOutput(did.to_string()),
))
}
}
}

#[tokio::test]
async fn test_multiple_handlers() {
let did1 =
IotaDID::parse("did:iota:smr:0x0101010101010101010101010101010101010101010101010101010101010101").unwrap();
let document = IotaDocument::new_with_id(did1.clone());
let dummy_smr_client = DummyClient(document);

let did2 = IotaDID::parse("did:iota:0x0101010101010101010101010101010101010101010101010101010101010101").unwrap();
let document = IotaDocument::new_with_id(did2.clone());
let dummy_iota_client = DummyClient(document);

let mut resolver = Resolver::<IotaDocument>::new();
resolver.attach_multiple_iota_handlers(vec![("iota", dummy_iota_client), ("smr", dummy_smr_client)]);

let doc = resolver.resolve(&did1).await.unwrap();
assert_eq!(doc.id(), &did1);

let doc = resolver.resolve(&did2).await.unwrap();
assert_eq!(doc.id(), &did2);
}
}

0 comments on commit 41105e5

Please sign in to comment.