From 8433c36e37ef4b9b522a2baa3c5379c899edcec3 Mon Sep 17 00:00:00 2001 From: Craig Bester Date: Mon, 30 Aug 2021 21:15:29 +0200 Subject: [PATCH 1/6] Add NetworkName Use a newtype wrapper for network names to enforce spec compliance. Fix potential bug with ClientMap theoretically having duplicate network clients when custom explorer URLs are set. --- identity-account/src/account/builder.rs | 5 +- identity-iota/src/did/url/iota_did.rs | 2 +- identity-iota/src/tangle/client_map.rs | 15 +- identity-iota/src/tangle/mod.rs | 2 +- identity-iota/src/tangle/network.rs | 219 ++++++++++++++++-------- 5 files changed, 163 insertions(+), 80 deletions(-) diff --git a/identity-account/src/account/builder.rs b/identity-account/src/account/builder.rs index b9a384e503..bb7c507333 100644 --- a/identity-account/src/account/builder.rs +++ b/identity-account/src/account/builder.rs @@ -4,6 +4,7 @@ use hashbrown::HashMap; use identity_iota::tangle::ClientBuilder; use identity_iota::tangle::Network; +use identity_iota::tangle::NetworkName; #[cfg(feature = "stronghold")] use std::path::PathBuf; #[cfg(feature = "stronghold")] @@ -35,7 +36,7 @@ pub enum AccountStorage { pub struct AccountBuilder { config: Config, storage: AccountStorage, - clients: Option>, + clients: Option>, } impl AccountBuilder { @@ -80,7 +81,7 @@ impl AccountBuilder { self .clients .get_or_insert_with(HashMap::new) - .insert(network.clone(), f(ClientBuilder::new().network(network))); + .insert(network.name(), f(ClientBuilder::new().network(network))); self } diff --git a/identity-iota/src/did/url/iota_did.rs b/identity-iota/src/did/url/iota_did.rs index 984948efb7..018de577bb 100644 --- a/identity-iota/src/did/url/iota_did.rs +++ b/identity-iota/src/did/url/iota_did.rs @@ -248,7 +248,7 @@ impl IotaDID { /// Returns the Tangle `network` of the `DID`, if it is valid. pub fn network(&self) -> Result { - Network::from_name(self.network_str()) + Network::from_name(self.network_str().to_owned()) } /// Returns the Tangle `network` name of the `DID`. diff --git a/identity-iota/src/tangle/client_map.rs b/identity-iota/src/tangle/client_map.rs index 0a311df3bc..50037734d3 100644 --- a/identity-iota/src/tangle/client_map.rs +++ b/identity-iota/src/tangle/client_map.rs @@ -1,9 +1,10 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use dashmap::DashMap; use std::sync::Arc; +use dashmap::DashMap; + use crate::chain::DocumentChain; use crate::did::DocumentDiff; use crate::did::IotaDID; @@ -13,10 +14,11 @@ use crate::tangle::Client; use crate::tangle::ClientBuilder; use crate::tangle::MessageId; use crate::tangle::Network; +use crate::tangle::NetworkName; use crate::tangle::Receipt; use crate::tangle::TangleResolve; -type State = DashMap>; +type State = DashMap>; #[derive(Debug)] pub struct ClientMap { @@ -31,7 +33,7 @@ impl ClientMap { pub fn from_client(client: Client) -> Self { let data: State = State::new(); - data.insert(client.network.clone(), Arc::new(client)); + data.insert(client.network.name(), Arc::new(client)); Self { data } } @@ -49,7 +51,7 @@ impl ClientMap { } pub fn insert(&self, client: Client) { - self.data.insert(client.network.clone(), Arc::new(client)); + self.data.insert(client.network.name(), Arc::new(client)); } pub async fn publish_document(&self, document: &IotaDocument) -> Result { @@ -81,13 +83,14 @@ impl ClientMap { } pub async fn client(&self, network: Network) -> Result> { - if let Some(client) = self.data.get(&network) { + let network_name = network.name(); + if let Some(client) = self.data.get(&network_name) { return Ok(Arc::clone(&client)); } let client: Arc = Client::from_network(network.clone()).await.map(Arc::new)?; - self.data.insert(network, Arc::clone(&client)); + self.data.insert(network_name, Arc::clone(&client)); Ok(client) } diff --git a/identity-iota/src/tangle/mod.rs b/identity-iota/src/tangle/mod.rs index 69fec7a746..716b1e6a83 100644 --- a/identity-iota/src/tangle/mod.rs +++ b/identity-iota/src/tangle/mod.rs @@ -16,7 +16,7 @@ pub use self::message_ext::MessageExt; pub use self::message_ext::MessageIdExt; pub use self::message_ext::TryFromMessage; pub use self::message_index::MessageIndex; -pub use self::network::Network; +pub use self::network::{Network, NetworkName}; pub use self::receipt::Receipt; pub use self::traits::TangleRef; pub use self::traits::TangleResolve; diff --git a/identity-iota/src/tangle/network.rs b/identity-iota/src/tangle/network.rs index 43f7679a66..7b1e00cd19 100644 --- a/identity-iota/src/tangle/network.rs +++ b/identity-iota/src/tangle/network.rs @@ -1,6 +1,9 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use core::convert::TryFrom; +use core::fmt::{Display, Formatter}; +use core::ops::Deref; use std::borrow::Cow; use identity_core::common::Url; @@ -8,8 +11,8 @@ use identity_core::common::Url; use crate::did::IotaDID; use crate::error::{Error, Result}; -const MAIN_NETWORK_NAME: &str = "main"; -const TEST_NETWORK_NAME: &str = "test"; +const NETWORK_NAME_MAIN: &str = "main"; +const NETWORK_NAME_TEST: &str = "test"; lazy_static! { static ref EXPLORER_MAIN: Url = Url::parse("https://explorer.iota.org/mainnet").unwrap(); @@ -19,58 +22,48 @@ lazy_static! { } /// The Tangle network to use ([`Mainnet`][Network::Mainnet] or [`Testnet`][Network::Testnet]). -#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] pub enum Network { #[serde(rename = "main")] Mainnet, #[serde(rename = "test")] Testnet, Other { - name: String, + name: NetworkName, explorer_url: Option, }, } impl Network { - /// Parses the provided string to a [Network]. + /// Parses the provided string to a [`Network`]. + /// + /// The names `"test"` and `"main"` will be mapped to the well-known [`Testnet`][Network::Testnet] + /// and [`Mainnet`][Network::Mainnet] networks respectively. Other inputs will return an instance + /// of [`Other`][Network::Other] if the name is valid. /// - /// The inputs `"test"` and `"main"` will be mapped to the well-known [Testnet][Network::Testnet] - /// and [Mainnet][Network::Mainnet] variants, respectively. - /// Other inputs will return an instance of [Other][Network::Other]. + /// Network names must comply with the IOTA DID Method spec, that is: be non-empty, at most + /// 6 characters long, and only include alphanumeric characters `0-9` and `a-z`. /// - /// Note that the empty string is not a valid network name, and that names have to be compliant - /// with the IOTA DID Method spec, that is, be at most 6 characters long and only include - /// characters `0-9` or `a-z`. - pub fn from_name(string: &str) -> Result { - match string { - "" => Err(Error::InvalidNetworkName("name cannot be the empty string")), - TEST_NETWORK_NAME => Ok(Self::Testnet), - MAIN_NETWORK_NAME => Ok(Self::Mainnet), - other => { - Self::check_name_compliance(other)?; + /// See [`NetworkName`]. + pub fn from_name(name: S) -> Result + where + // Allow String, &'static str, Cow<'static, str> + S: AsRef + Into>, + { + match name.as_ref() { + NETWORK_NAME_TEST => Ok(Self::Testnet), + NETWORK_NAME_MAIN => Ok(Self::Mainnet), + _ => { + // Accept any other valid string - validation is performed by NetworkName + let network_name: NetworkName = NetworkName::try_from_name(name)?; Ok(Self::Other { - name: other.to_owned(), + name: network_name, explorer_url: None, }) } } } - /// Checks if a string is a spec-compliant network name. - fn check_name_compliance(string: &str) -> Result<()> { - if string.len() > 6 { - return Err(Error::InvalidNetworkName("name cannot exceed 6 characters")); - }; - - if !string.chars().all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit()) { - return Err(Error::InvalidNetworkName( - "name may only contain characters `0-9` and `a-z`", - )); - } - - Ok(()) - } - /// Sets the explorer url if `self` is an `Other` variant. /// /// The `Url` needs to be a valid base url, i.e. `url.cannot_be_a_base()` @@ -92,12 +85,12 @@ impl Network { did.network() } - /// Returns true if this network is the same network as the DID. + /// Returns true if this network is the same network as specified in the DID. pub fn matches_did(self, did: &IotaDID) -> bool { - did.network_str() == self.name() + did.network_str() == self.name().deref() } - /// Returns the default node URL of the Tangle network. + /// Returns the default node [`Url`] of the Tangle network. pub fn default_node_url(&self) -> Option<&'static Url> { match self { Self::Mainnet => Some(&*NODE_MAIN), @@ -106,33 +99,32 @@ impl Network { } } - /// Returns the web explorer URL of the Tangle network. - pub fn explorer_url(&self) -> Result { + /// Returns the web explorer [`Url`] of the Tangle network. + pub fn explorer_url(&self) -> Option<&Url> { match self { - Self::Mainnet => Ok(EXPLORER_MAIN.clone()), - Self::Testnet => Ok(EXPLORER_TEST.clone()), - Self::Other { - explorer_url: Some(url), - .. - } => Ok(url.clone()), - _ => Err(Error::NoExplorerURLSet), + Self::Mainnet => Some(&EXPLORER_MAIN), + Self::Testnet => Some(&EXPLORER_TEST), + Self::Other { explorer_url, .. } => explorer_url.as_ref(), } } - /// Returns the web explorer URL of the given `message`. + /// Returns the web explorer URL of the given `message_id`. pub fn message_url(&self, message_id: &str) -> Result { - let mut url = self.explorer_url()?; - // unwrap is safe, the explorer URL is always a valid base URL - url.path_segments_mut().unwrap().push("message").push(message_id); + let mut url = self.explorer_url().ok_or(Error::NoExplorerURLSet)?.clone(); + url + .path_segments_mut() + .map_err(|_| Error::InvalidExplorerURL)? + .push("message") + .push(message_id); Ok(url) } - /// Returns the name of the network. - pub fn name(&self) -> Cow<'static, str> { + /// Returns the [`NetworkName`] of the network. + pub fn name(&self) -> NetworkName { match self { - Self::Mainnet => Cow::Borrowed(MAIN_NETWORK_NAME), - Self::Testnet => Cow::Borrowed(TEST_NETWORK_NAME), - Self::Other { name, .. } => Cow::Owned(name.clone()), + Self::Mainnet => NetworkName(Cow::from(NETWORK_NAME_MAIN)), + Self::Testnet => NetworkName(Cow::from(NETWORK_NAME_TEST)), + Self::Other { name, .. } => name.clone(), } } } @@ -144,40 +136,126 @@ impl Default for Network { } } +/// Network name compliant with the IOTA DID method specification: +/// https://github.com/iotaledger/identity.rs/blob/dev/documentation/docs/specs/iota_did_method_spec.md +#[derive(Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)] +#[repr(transparent)] +pub struct NetworkName(Cow<'static, str>); + +impl NetworkName { + /// Creates a new [`NetworkName`] if the name passes validation. + pub fn try_from_name(name: T) -> Result + where + T: Into>, + { + let name_cow: Cow<'static, str> = name.into(); + Self::validate_network_name(&name_cow)?; + Ok(Self(name_cow)) + } + + /// Validates whether a string is a spec-compliant IOTA DID [`NetworkName`]. + pub fn validate_network_name(name: &str) -> Result<()> { + if name.is_empty() { + return Err(Error::InvalidNetworkName("network name must not be empty")); + } + + if name.len() > 6 { + return Err(Error::InvalidNetworkName("network name cannot exceed 6 characters")); + }; + + if !name.chars().all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit()) { + return Err(Error::InvalidNetworkName( + "network name must only contain characters `0-9` and `a-z`", + )); + } + + Ok(()) + } +} + +impl Deref for NetworkName { + type Target = Cow<'static, str>; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl TryFrom<&'static str> for NetworkName { + type Error = Error; + + fn try_from(name: &'static str) -> Result { + Self::try_from_name(Cow::Borrowed(name)) + } +} + +impl TryFrom for NetworkName { + type Error = Error; + + fn try_from(name: String) -> Result { + Self::try_from_name(Cow::Owned(name)) + } +} + +impl Display for NetworkName { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + f.write_str(self.as_ref()) + } +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn test_from_name_standard_networks() { + assert_eq!(Network::from_name(NETWORK_NAME_TEST).unwrap(), Network::Testnet); + assert_eq!(Network::from_name(NETWORK_NAME_MAIN).unwrap(), Network::Mainnet); + } + + #[test] + fn test_from_name_types() { + let static_str = "custom"; + assert!(Network::from_name(static_str).is_ok()); + + let string = static_str.to_owned(); + assert!(Network::from_name(string.clone()).is_ok()); + + let cow_owned = Cow::Owned(string); + assert!(Network::from_name(cow_owned).is_ok()); + + let cow_borrowed = Cow::Borrowed(static_str); + assert!(Network::from_name(cow_borrowed).is_ok()); + } + #[test] fn test_from_name() { - assert_eq!(Network::from_name("test").unwrap(), Network::Testnet); - assert_eq!(Network::from_name("main").unwrap(), Network::Mainnet); assert_eq!( Network::from_name("6chars").unwrap(), Network::Other { - name: "6chars".to_owned(), - explorer_url: None + name: NetworkName::try_from("6chars").unwrap(), + explorer_url: None, } ); assert!(matches!( Network::from_name("7seven7").unwrap_err(), - Error::InvalidNetworkName("name cannot exceed 6 characters") + Error::InvalidNetworkName("network name cannot exceed 6 characters") )); assert!(matches!( Network::from_name("täst").unwrap_err(), - Error::InvalidNetworkName("name may only contain characters `0-9` and `a-z`") + Error::InvalidNetworkName("network name must only contain characters `0-9` and `a-z`") )); assert!(matches!( Network::from_name(" ").unwrap_err(), - Error::InvalidNetworkName("name may only contain characters `0-9` and `a-z`") + Error::InvalidNetworkName("network name must only contain characters `0-9` and `a-z`") )); assert!(matches!( Network::from_name("").unwrap_err(), - Error::InvalidNetworkName("name cannot be the empty string") + Error::InvalidNetworkName("network name must not be empty") )); } @@ -194,13 +272,16 @@ mod tests { #[test] fn test_explorer_url() { - let testnet = Network::Testnet; + let mainnet = Network::Mainnet; + assert!(mainnet.explorer_url().is_some()); + assert_eq!(mainnet.explorer_url().unwrap().as_str(), EXPLORER_MAIN.as_str()); - assert!(testnet.explorer_url().is_ok()); + let testnet = Network::Testnet; + assert!(testnet.explorer_url().is_some()); + assert_eq!(testnet.explorer_url().unwrap().as_str(), EXPLORER_TEST.as_str()); let mut other = Network::from_name("atoi").unwrap(); - - assert!(matches!(other.explorer_url().unwrap_err(), Error::NoExplorerURLSet)); + assert!(other.explorer_url().is_none()); // Try setting a `cannot_be_a_base` url. assert!(matches!( @@ -211,9 +292,7 @@ mod tests { )); let url = Url::parse("https://explorer.iota.org/testnet").unwrap(); - assert!(other.set_explorer_url(url.clone()).is_ok()); - - assert_eq!(other.explorer_url().unwrap(), url); + assert_eq!(other.explorer_url().unwrap().clone(), url); } } From eec2f4e9da01fc715cb40712ab35a5bfcc06854b Mon Sep 17 00:00:00 2001 From: Craig Bester Date: Mon, 30 Aug 2021 21:52:08 +0200 Subject: [PATCH 2/6] Update IotaDID to validate network name Rename `with_network` to `new_with_network`. --- .../src/did/doc/iota_verification_method.rs | 2 +- identity-iota/src/did/url/iota_did.rs | 75 ++++++++++++------- identity-iota/src/tangle/network.rs | 2 +- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/identity-iota/src/did/doc/iota_verification_method.rs b/identity-iota/src/did/doc/iota_verification_method.rs index ab8e453425..73814ddee5 100644 --- a/identity-iota/src/did/doc/iota_verification_method.rs +++ b/identity-iota/src/did/doc/iota_verification_method.rs @@ -78,7 +78,7 @@ impl IotaVerificationMethod { F: Into>, { let key: &[u8] = keypair.public().as_ref(); - let did: IotaDID = IotaDID::with_network(key, network)?; + let did: IotaDID = IotaDID::new_with_network(key, network)?; Self::from_did(did, keypair, fragment) } diff --git a/identity-iota/src/did/url/iota_did.rs b/identity-iota/src/did/url/iota_did.rs index 018de577bb..9b45f6a9a9 100644 --- a/identity-iota/src/did/url/iota_did.rs +++ b/identity-iota/src/did/url/iota_did.rs @@ -7,8 +7,10 @@ use core::fmt::Display; use core::fmt::Formatter; use core::fmt::Result as FmtResult; use core::str::FromStr; + use crypto::hashes::blake2b::Blake2b256; use crypto::hashes::Digest; + use identity_core::utils::decode_b58; use identity_core::utils::encode_b58; use identity_did::did::Error as DIDError; @@ -18,6 +20,7 @@ use crate::did::Segments; use crate::error::Error; use crate::error::Result; use crate::tangle::Network; +use crate::tangle::NetworkName; // The hash size of BLAKE2b-256 (32-bytes) const BLAKE2B_256_LEN: usize = 32; @@ -41,11 +44,11 @@ impl IotaDID { /// The default Tangle network (`"main"`). pub const DEFAULT_NETWORK: &'static str = "main"; - /// Converts a borrowed `DID` to an `IotaDID`. + /// Converts a borrowed `DID` to an [`IotaDID`]. /// /// # Errors /// - /// Returns `Err` if the input is not a valid `IotaDID`. + /// Returns `Err` if the input is not a valid [`IotaDID`]. pub fn try_from_borrowed(did: &CoreDID) -> Result<&Self> { Self::check_validity(did)?; @@ -53,18 +56,18 @@ impl IotaDID { Ok(unsafe { Self::new_unchecked_ref(did) }) } - /// Converts an owned `DID` to an `IotaDID`. + /// Converts an owned `DID` to an [`IotaDID`]. /// /// # Errors /// - /// Returns `Err` if the input is not a valid `IotaDID`. + /// Returns `Err` if the input is not a valid [`IotaDID`]. pub fn try_from_owned(did: CoreDID) -> Result { Self::check_validity(&did)?; Ok(Self(Self::normalize(did))) } - /// Converts a `DID` reference to an `IotaDID` reference without performing + /// Converts a `DID` reference to an [`IotaDID`] reference without performing /// validation checks. /// /// # Safety @@ -75,30 +78,32 @@ impl IotaDID { &*(did as *const CoreDID as *const IotaDID) } - /// Parses an `IotaDID` from the given `input`. + /// Parses an [`IotaDID`] from the given `input`. /// /// # Errors /// - /// Returns `Err` if the input is not a valid `IotaDID`. + /// Returns `Err` if the input is not a valid [`IotaDID`]. pub fn parse(input: impl AsRef) -> Result { CoreDID::parse(input).map_err(Into::into).and_then(Self::try_from_owned) } - /// Creates a new `IotaDID` with a tag derived from the given `public` key. + /// Creates a new [`IotaDID`] with a tag derived from the given `public` key. /// /// # Errors /// - /// Returns `Err` if the input does not form a valid `IotaDID`. + /// Returns `Err` if the input does not form a valid [`IotaDID`]. pub fn new(public: &[u8]) -> Result { try_did!(public) } - /// Creates a new `IotaDID` from the given `public` key and `network`. + /// Creates a new [`IotaDID`] from the given `public` key and `network`. /// /// # Errors /// - /// Returns `Err` if the input does not form a valid `IotaDID`. - pub fn with_network(public: &[u8], network: &str) -> Result { + /// Returns `Err` if the input does not form a valid [`IotaDID`] or the `network` is invalid. + /// See [`NetworkName`] for validation requirements. + pub fn new_with_network(public: &[u8], network: &str) -> Result { + NetworkName::validate_network_name(network)?; try_did!(public, network) } @@ -143,7 +148,7 @@ impl IotaDID { /// /// # Errors /// - /// Returns `Err` if the input is not a valid `IotaDID`. + /// Returns `Err` if the input is not a valid [`IotaDID`]. pub fn check_method(did: &CoreDID) -> Result<()> { if did.method() != Self::METHOD { Err(Error::InvalidDID(DIDError::InvalidMethodName)) @@ -152,11 +157,11 @@ impl IotaDID { } } - /// Checks if the given `DID` has a valid `IotaDID` `method_id`. + /// Checks if the given `DID` has a valid [`IotaDID`] `method_id`. /// /// # Errors /// - /// Returns `Err` if the input is not a valid `IotaDID`. + /// Returns `Err` if the input is not a valid [`IotaDID`]. pub fn check_method_id(did: &CoreDID) -> Result<()> { let segments: Vec<&str> = did.method_id().split(':').collect(); @@ -175,21 +180,33 @@ impl IotaDID { } } - /// Checks if the given `DID` is valid according to the `IotaDID` method + /// Checks if the given `DID` has a valid [`IotaDID`] network name, e.g. "main", "test". + /// + /// # Errors + /// + /// Returns `Err` if the input is not a valid [`IotaDID`]. + /// See [`NetworkName`] for validation requirements. + pub fn check_network(did: &CoreDID) -> Result<()> { + let network_name = Segments(did.method_id()).network(); + NetworkName::validate_network_name(network_name) + } + + /// Checks if the given `DID` is valid according to the [`IotaDID`] method /// specification. /// /// # Errors /// - /// Returns `Err` if the input is not a valid `IotaDID`. + /// Returns `Err` if the input is not a valid [`IotaDID`]. pub fn check_validity(did: &CoreDID) -> Result<()> { Self::check_method(did)?; Self::check_method_id(did)?; + Self::check_network(did)?; Ok(()) } /// Returns a `bool` indicating if the given `DID` is valid according to the - /// `IotaDID` method specification. + /// [`IotaDID`] method specification. pub fn is_valid(did: &CoreDID) -> bool { Self::check_validity(did).is_ok() } @@ -358,10 +375,10 @@ mod tests { assert!(IotaDID::parse(format!("did:iota:test:{}?somequery=somevalue", TAG)).is_ok()); assert!(IotaDID::parse(format!("did:iota:test:{}?somequery=somevalue#fragment", TAG)).is_ok()); - assert!(IotaDID::parse(format!("did:iota:rainbow:{}", TAG)).is_ok()); - assert!(IotaDID::parse(format!("did:iota:rainbow:{}#fragment", TAG)).is_ok()); - assert!(IotaDID::parse(format!("did:iota:rainbow:{}?somequery=somevalue", TAG)).is_ok()); - assert!(IotaDID::parse(format!("did:iota:rainbow:{}?somequery=somevalue#fragment", TAG)).is_ok()); + assert!(IotaDID::parse(format!("did:iota:custom:{}", TAG)).is_ok()); + assert!(IotaDID::parse(format!("did:iota:custom:{}#fragment", TAG)).is_ok()); + assert!(IotaDID::parse(format!("did:iota:custom:{}?somequery=somevalue", TAG)).is_ok()); + assert!(IotaDID::parse(format!("did:iota:custom:{}?somequery=somevalue#fragment", TAG)).is_ok()); } #[test] @@ -370,13 +387,17 @@ mod tests { assert!(IotaDID::parse("did:foo::").is_err()); // An empty DID method is invalid. assert!(IotaDID::parse("did:::").is_err()); - assert!(IotaDID::parse(format!("did::rainbow:shard-1:{}", TAG)).is_err()); + assert!(IotaDID::parse(format!("did::main:{}", TAG)).is_err()); // A non-"iota" DID method is invalid. assert!(IotaDID::parse("did:iota---::").is_err()); // An empty `iota-specific-idstring` is invalid. assert!(IotaDID::parse("did:iota:").is_err()); // Too many components is invalid. - assert!(IotaDID::parse(format!("did:iota:rainbow:shard-1:random:{}", TAG)).is_err()); + assert!(IotaDID::parse(format!("did:iota:custom:shard-1:random:{}", TAG)).is_err()); + // Explicit empty network name is invalid (omitting it is still fine) + assert!(IotaDID::parse(format!("did:iota::{}", TAG)).is_err()); + // Invalid network name is invalid. + assert!(IotaDID::parse(format!("did:iota:Invalid-Network:{}", TAG)).is_err()); } #[test] @@ -407,8 +428,8 @@ mod tests { let did: IotaDID = format!("did:iota:{}", key).parse().unwrap(); assert_eq!(did.network_str(), "main"); - let did: IotaDID = format!("did:iota:rainbow:{}", key).parse().unwrap(); - assert_eq!(did.network_str(), "rainbow"); + let did: IotaDID = format!("did:iota:custom:{}", key).parse().unwrap(); + assert_eq!(did.network_str(), "custom"); } #[test] @@ -440,7 +461,7 @@ mod tests { #[test] fn test_with_network() { let key: KeyPair = KeyPair::new_ed25519().unwrap(); - let did: IotaDID = IotaDID::with_network(key.public().as_ref(), "foo").unwrap(); + let did: IotaDID = IotaDID::new_with_network(key.public().as_ref(), "foo").unwrap(); let tag: String = IotaDID::encode_key(key.public().as_ref()); assert_eq!(did.tag(), tag); diff --git a/identity-iota/src/tangle/network.rs b/identity-iota/src/tangle/network.rs index 7b1e00cd19..c837d7f1da 100644 --- a/identity-iota/src/tangle/network.rs +++ b/identity-iota/src/tangle/network.rs @@ -265,7 +265,7 @@ mod tests { assert!(Network::matches_did(Network::Mainnet, &did)); assert!(!Network::matches_did(Network::Testnet, &did)); - let did: IotaDID = IotaDID::with_network(b"", "test").unwrap(); + let did: IotaDID = IotaDID::new_with_network(b"", "test").unwrap(); assert!(Network::matches_did(Network::Testnet, &did)); assert!(!Network::matches_did(Network::Mainnet, &did)); } From 505c27fffecc29c6e119b523e20e47ad94067908 Mon Sep 17 00:00:00 2001 From: Craig Bester Date: Mon, 30 Aug 2021 23:41:35 +0200 Subject: [PATCH 3/6] Fix Wasm bindings Add `Network::name_str` to avoid cloning the `NetworkName`. --- bindings/wasm/docs/api-reference.md | 12 ++++++----- bindings/wasm/src/did/wasm_document.rs | 2 +- bindings/wasm/src/tangle/network.rs | 23 +++++++++------------- identity-iota/src/tangle/client_builder.rs | 2 +- identity-iota/src/tangle/network.rs | 11 ++++++++++- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 6ef77be97b..692f385678 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -1492,11 +1492,11 @@ Deserializes a `KeyPair` object from a JSON object. * [Network](#Network) * _instance_ * [.defaultNodeURL](#Network+defaultNodeURL) ⇒ string \| undefined - * [.explorerURL](#Network+explorerURL) ⇒ string + * [.explorerURL](#Network+explorerURL) ⇒ string \| undefined * [.messageURL(message_id)](#Network+messageURL) ⇒ string * [.toString()](#Network+toString) ⇒ string * _static_ - * [.from_name(string)](#Network.from_name) ⇒ [Network](#Network) + * [.from_name(name)](#Network.from_name) ⇒ [Network](#Network) * [.mainnet()](#Network.mainnet) ⇒ [Network](#Network) * [.testnet()](#Network.testnet) ⇒ [Network](#Network) @@ -1508,7 +1508,7 @@ Returns the node URL of the Tangle network. **Kind**: instance property of [Network](#Network) -### network.explorerURL ⇒ string +### network.explorerURL ⇒ string \| undefined Returns the web explorer URL of the Tangle network. **Kind**: instance property of [Network](#Network) @@ -1529,12 +1529,14 @@ Returns the web explorer URL of the given `message`. **Kind**: instance method of [Network](#Network) -### Network.from\_name(string) ⇒ [Network](#Network) +### Network.from\_name(name) ⇒ [Network](#Network) +Parses the provided string to a [`WasmNetwork`]. + **Kind**: static method of [Network](#Network) | Param | Type | | --- | --- | -| string | string | +| name | string | diff --git a/bindings/wasm/src/did/wasm_document.rs b/bindings/wasm/src/did/wasm_document.rs index c76b081b7f..981fc867fa 100644 --- a/bindings/wasm/src/did/wasm_document.rs +++ b/bindings/wasm/src/did/wasm_document.rs @@ -67,7 +67,7 @@ impl WasmDocument { let public: &PublicKey = keypair.0.public(); let did: IotaDID = if let Some(network) = network.as_deref() { - IotaDID::with_network(public.as_ref(), network).wasm_result()? + IotaDID::new_with_network(public.as_ref(), network).wasm_result()? } else { IotaDID::new(public.as_ref()).wasm_result()? }; diff --git a/bindings/wasm/src/tangle/network.rs b/bindings/wasm/src/tangle/network.rs index 516d8d4816..0c5edb72ef 100644 --- a/bindings/wasm/src/tangle/network.rs +++ b/bindings/wasm/src/tangle/network.rs @@ -2,10 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 use identity::iota::Network as IotaNetwork; - use wasm_bindgen::prelude::*; -use crate::error::wasm_error; +use crate::error::{Result, WasmResult}; #[wasm_bindgen(js_name = Network)] #[derive(Clone, Debug)] @@ -13,10 +12,10 @@ pub struct WasmNetwork(IotaNetwork); #[wasm_bindgen(js_class = Network)] impl WasmNetwork { + /// Parses the provided string to a [`WasmNetwork`]. #[wasm_bindgen] - pub fn from_name(string: &str) -> Result { - let network = IotaNetwork::from_name(string).map_err(wasm_error)?; - Ok(Self(network)) + pub fn from_name(name: String) -> Result { + IotaNetwork::from_name(name).map(Self).wasm_result() } #[wasm_bindgen] @@ -37,24 +36,20 @@ impl WasmNetwork { /// Returns the web explorer URL of the Tangle network. #[wasm_bindgen(getter = explorerURL)] - pub fn explorer_url(&self) -> Result { - self.0.explorer_url().map(|url| url.to_string()).map_err(wasm_error) + pub fn explorer_url(&self) -> Option { + self.0.explorer_url().map(ToString::to_string) } /// Returns the web explorer URL of the given `message`. #[wasm_bindgen(js_name = messageURL)] - pub fn message_url(&self, message_id: &str) -> Result { - self - .0 - .message_url(message_id) - .map(|url| url.to_string()) - .map_err(wasm_error) + pub fn message_url(&self, message_id: &str) -> Result { + self.0.message_url(message_id).map(|url| url.to_string()).wasm_result() } #[allow(clippy::inherent_to_string, clippy::wrong_self_convention)] #[wasm_bindgen(js_name = toString)] pub fn to_string(&self) -> String { - self.0.name().into() + self.0.name_str().to_owned() } } diff --git a/identity-iota/src/tangle/client_builder.rs b/identity-iota/src/tangle/client_builder.rs index 90f4a60aca..cc9cf9f623 100644 --- a/identity-iota/src/tangle/client_builder.rs +++ b/identity-iota/src/tangle/client_builder.rs @@ -31,7 +31,7 @@ impl ClientBuilder { /// Sets the IOTA Tangle network. pub fn network(mut self, network: Network) -> Self { - self.builder = self.builder.with_network(&network.name()); + self.builder = self.builder.with_network(network.name_str()); self.network = network; self } diff --git a/identity-iota/src/tangle/network.rs b/identity-iota/src/tangle/network.rs index c837d7f1da..cf8b908a51 100644 --- a/identity-iota/src/tangle/network.rs +++ b/identity-iota/src/tangle/network.rs @@ -87,7 +87,7 @@ impl Network { /// Returns true if this network is the same network as specified in the DID. pub fn matches_did(self, did: &IotaDID) -> bool { - did.network_str() == self.name().deref() + did.network_str() == self.name_str() } /// Returns the default node [`Url`] of the Tangle network. @@ -127,6 +127,15 @@ impl Network { Self::Other { name, .. } => name.clone(), } } + + /// Returns the name of the network. + pub fn name_str(&self) -> &str { + match self { + Self::Mainnet => NETWORK_NAME_MAIN, + Self::Testnet => NETWORK_NAME_TEST, + Self::Other { name, .. } => name.as_ref(), + } + } } impl Default for Network { From 5e07726b213279f156fad0043414d83bc0c11496 Mon Sep 17 00:00:00 2001 From: Craig Bester Date: Wed, 1 Sep 2021 14:19:27 +0200 Subject: [PATCH 4/6] Remove did! macro The did! macro was unused and just converted the errors from try_did! into panics. Add network validation tests for Account::create_identity. --- identity-account/tests/commands.rs | 37 ++++++++++++++++++++++++++- identity-iota/src/did/macros.rs | 26 +++++-------------- identity-iota/src/did/url/iota_did.rs | 4 +-- identity/src/lib.rs | 3 --- 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/identity-account/tests/commands.rs b/identity-account/tests/commands.rs index b789ce5a38..b95b2fc7a6 100644 --- a/identity-account/tests/commands.rs +++ b/identity-account/tests/commands.rs @@ -7,12 +7,13 @@ use identity_account::error::Error; use identity_account::error::Result; use identity_account::events::Command; use identity_account::events::CommandError; -use identity_account::identity::IdentityId; use identity_account::identity::IdentitySnapshot; use identity_account::identity::TinyMethod; +use identity_account::identity::{IdentityCreate, IdentityId}; use identity_account::storage::MemStore; use identity_account::types::Generation; use identity_core::common::UnixTimestamp; +use identity_core::crypto::KeyType; use identity_did::verification::MethodType; async fn new_account() -> Result { @@ -78,6 +79,40 @@ async fn test_create_identity_invalid_method() -> Result<()> { Ok(()) } +#[tokio::test] +async fn test_create_identity_network() -> Result<()> { + let account: Account = new_account().await?; + + // Create an identity with a valid network string + let create_identity: IdentityCreate = IdentityCreate::new().network("test").key_type(KeyType::Ed25519); + let snapshot: IdentitySnapshot = account.create_identity(create_identity).await?; + + // Ensure the identity creation was successful + assert!(snapshot.identity().did().is_some()); + assert!(snapshot.identity().authentication().is_ok()); + + Ok(()) +} + +#[tokio::test] +async fn test_create_identity_invalid_network() -> Result<()> { + let account: Account = new_account().await?; + + // Attempt to create an identity with an invalid network string + let create_identity: IdentityCreate = IdentityCreate::new() + .network("Invalid=Network!") + .key_type(KeyType::Ed25519); + let result = account.create_identity(create_identity).await; + + // Ensure an `InvalidNetworkName` error is thrown + assert!(matches!( + result.unwrap_err(), + Error::IotaError(identity_iota::Error::InvalidNetworkName(_)), + )); + + Ok(()) +} + #[tokio::test] async fn test_create_identity_already_exists() -> Result<()> { let account: Account = new_account().await?; diff --git a/identity-iota/src/did/macros.rs b/identity-iota/src/did/macros.rs index 835a73d2cd..a43a65406b 100644 --- a/identity-iota/src/did/macros.rs +++ b/identity-iota/src/did/macros.rs @@ -3,41 +3,27 @@ /// Creates a new IOTA DID from a `public` key and optional `network`. /// -/// # Panics +/// # Errors /// -/// Panics if the DID format is not valid. +/// Errors if the [`IotaDID`][crate::did::IotaDID] is invalid. /// /// # Example /// /// ``` -/// # use identity_iota::did; +/// # use identity_iota::try_did; /// # -/// let did = did!(b"public-key"); +/// let did = try_did!(b"public-key").unwrap(); /// assert_eq!(did.as_str(), "did:iota:2xQiiGHDq5gCi1H7utY1ni7cf65fTay3G11S4xKp1vkS"); /// -/// let did = did!(b"public-key", "com"); +/// let did = try_did!(b"public-key", "com").unwrap(); /// assert_eq!( /// did.as_str(), /// "did:iota:com:2xQiiGHDq5gCi1H7utY1ni7cf65fTay3G11S4xKp1vkS" /// ); /// ``` #[macro_export] -macro_rules! did { - // Defining explicit branches rather than `$($tt:tt)+` gives much better docs. - ($public:expr, $network:expr) => { - $crate::try_did!($public, $network).unwrap() - }; - ($public:expr, $network:expr) => { - $crate::try_did!($public, $network).unwrap() - }; - ($public:expr) => { - $crate::try_did!($public).unwrap() - }; -} - -/// A fallible version of the [did] macro. -#[macro_export] macro_rules! try_did { + // Defining explicit branches rather than `$($tt:tt)+` gives much better docs. ($public:expr, $network:expr) => { $crate::did::IotaDID::parse(format!( "{}:{}:{}:{}", diff --git a/identity-iota/src/did/url/iota_did.rs b/identity-iota/src/did/url/iota_did.rs index 9b45f6a9a9..1e0a095a09 100644 --- a/identity-iota/src/did/url/iota_did.rs +++ b/identity-iota/src/did/url/iota_did.rs @@ -110,8 +110,8 @@ impl IotaDID { #[doc(hidden)] pub fn from_components(public: &[u8], network: Option<&str>) -> Result { match network { - Some(network) => try_did!(public, network), - None => try_did!(public), + Some(network) => Self::new_with_network(public, network), + None => Self::new(public), } } diff --git a/identity/src/lib.rs b/identity/src/lib.rs index 995fd2af3f..0ed3c016ee 100644 --- a/identity/src/lib.rs +++ b/identity/src/lib.rs @@ -76,9 +76,6 @@ pub mod iota { pub use identity_iota::error::*; pub use identity_iota::tangle::*; - #[doc(inline)] - pub use identity_iota::did; - #[doc(inline)] pub use identity_iota::try_did; } From 8ac076f6e6e200e80e23b9abed34c97b28c0a1e3 Mon Sep 17 00:00:00 2001 From: Craig Bester Date: Thu, 2 Sep 2021 14:50:59 +0200 Subject: [PATCH 5/6] Address comments Rename Network::from_name to Network::try_from_name Rename NetworkName::try_from_name to NetworkName::try_from Rename try_did! to try_construct_did! --- bindings/wasm/docs/api-reference.md | 6 ++-- .../wasm/examples/browser/private_tangle.js | 2 +- bindings/wasm/examples/node/private_tangle.js | 2 +- bindings/wasm/src/tangle/network.rs | 4 +-- examples/account/private_tangle.rs | 2 +- examples/low-level-api/private_tangle.rs | 2 +- identity-iota/src/did/macros.rs | 8 ++--- identity-iota/src/did/url/iota_did.rs | 6 ++-- identity-iota/src/tangle/network.rs | 34 +++++++++---------- identity/src/lib.rs | 2 +- 10 files changed, 34 insertions(+), 34 deletions(-) diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 692f385678..b8d78ac3f5 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -1496,7 +1496,7 @@ Deserializes a `KeyPair` object from a JSON object. * [.messageURL(message_id)](#Network+messageURL) ⇒ string * [.toString()](#Network+toString) ⇒ string * _static_ - * [.from_name(name)](#Network.from_name) ⇒ [Network](#Network) + * [.try_from_name(name)](#Network.try_from_name) ⇒ [Network](#Network) * [.mainnet()](#Network.mainnet) ⇒ [Network](#Network) * [.testnet()](#Network.testnet) ⇒ [Network](#Network) @@ -1527,9 +1527,9 @@ Returns the web explorer URL of the given `message`. ### network.toString() ⇒ string **Kind**: instance method of [Network](#Network) - + -### Network.from\_name(name) ⇒ [Network](#Network) +### Network.try\_from\_name(name) ⇒ [Network](#Network) Parses the provided string to a [`WasmNetwork`]. **Kind**: static method of [Network](#Network) diff --git a/bindings/wasm/examples/browser/private_tangle.js b/bindings/wasm/examples/browser/private_tangle.js index 79e8a21588..629103d79e 100644 --- a/bindings/wasm/examples/browser/private_tangle.js +++ b/bindings/wasm/examples/browser/private_tangle.js @@ -30,7 +30,7 @@ export async function createIdentityPrivateTangle(inBrowser = true, log = true) } // This is an arbitrarily defined network name - const network = Network.from_name(networkName); + const network = Network.try_from_name(networkName); // Create a DID Document (an identity). const { doc, key } = new Document(KeyType.Ed25519, network.toString()); diff --git a/bindings/wasm/examples/node/private_tangle.js b/bindings/wasm/examples/node/private_tangle.js index a148b43095..c1d670edb9 100644 --- a/bindings/wasm/examples/node/private_tangle.js +++ b/bindings/wasm/examples/node/private_tangle.js @@ -11,7 +11,7 @@ const { Client, Config, Document, KeyType, Network } = require('../../node/ident **/ async function createIdentityPrivateTangle() { // This is an arbitrarily defined network name - const network = Network.from_name("custom"); + const network = Network.try_from_name("custom"); // Create a DID Document (an identity). const { doc, key } = new Document(KeyType.Ed25519, network.toString()); diff --git a/bindings/wasm/src/tangle/network.rs b/bindings/wasm/src/tangle/network.rs index 0c5edb72ef..25f0ddedd2 100644 --- a/bindings/wasm/src/tangle/network.rs +++ b/bindings/wasm/src/tangle/network.rs @@ -14,8 +14,8 @@ pub struct WasmNetwork(IotaNetwork); impl WasmNetwork { /// Parses the provided string to a [`WasmNetwork`]. #[wasm_bindgen] - pub fn from_name(name: String) -> Result { - IotaNetwork::from_name(name).map(Self).wasm_result() + pub fn try_from_name(name: String) -> Result { + IotaNetwork::try_from_name(name).map(Self).wasm_result() } #[wasm_bindgen] diff --git a/examples/account/private_tangle.rs b/examples/account/private_tangle.rs index 6e4a22293e..d63c267930 100644 --- a/examples/account/private_tangle.rs +++ b/examples/account/private_tangle.rs @@ -23,7 +23,7 @@ async fn main() -> Result<()> { // This is an arbitrarily defined network name let network_name = "custom"; - let network = Network::from_name(network_name)?; + let network = Network::try_from_name(network_name)?; // Create a new Account with the default configuration let private_node_url = "http://127.0.0.1:14265/"; diff --git a/examples/low-level-api/private_tangle.rs b/examples/low-level-api/private_tangle.rs index 5d7990aaa9..22f81e71d3 100644 --- a/examples/low-level-api/private_tangle.rs +++ b/examples/low-level-api/private_tangle.rs @@ -19,7 +19,7 @@ use identity::prelude::*; pub async fn main() -> Result<()> { // This is an arbitrarily defined network name let network_name = "custom"; - let network = Network::from_name(network_name)?; + let network = Network::try_from_name(network_name)?; // Set the network and the URL that points to // the REST API of the locally running hornet node. diff --git a/identity-iota/src/did/macros.rs b/identity-iota/src/did/macros.rs index a43a65406b..e83909d9f2 100644 --- a/identity-iota/src/did/macros.rs +++ b/identity-iota/src/did/macros.rs @@ -10,19 +10,19 @@ /// # Example /// /// ``` -/// # use identity_iota::try_did; +/// # use identity_iota::try_construct_did; /// # -/// let did = try_did!(b"public-key").unwrap(); +/// let did = try_construct_did!(b"public-key").unwrap(); /// assert_eq!(did.as_str(), "did:iota:2xQiiGHDq5gCi1H7utY1ni7cf65fTay3G11S4xKp1vkS"); /// -/// let did = try_did!(b"public-key", "com").unwrap(); +/// let did = try_construct_did!(b"public-key", "com").unwrap(); /// assert_eq!( /// did.as_str(), /// "did:iota:com:2xQiiGHDq5gCi1H7utY1ni7cf65fTay3G11S4xKp1vkS" /// ); /// ``` #[macro_export] -macro_rules! try_did { +macro_rules! try_construct_did { // Defining explicit branches rather than `$($tt:tt)+` gives much better docs. ($public:expr, $network:expr) => { $crate::did::IotaDID::parse(format!( diff --git a/identity-iota/src/did/url/iota_did.rs b/identity-iota/src/did/url/iota_did.rs index 1e0a095a09..22e4e7ccb3 100644 --- a/identity-iota/src/did/url/iota_did.rs +++ b/identity-iota/src/did/url/iota_did.rs @@ -93,7 +93,7 @@ impl IotaDID { /// /// Returns `Err` if the input does not form a valid [`IotaDID`]. pub fn new(public: &[u8]) -> Result { - try_did!(public) + try_construct_did!(public) } /// Creates a new [`IotaDID`] from the given `public` key and `network`. @@ -104,7 +104,7 @@ impl IotaDID { /// See [`NetworkName`] for validation requirements. pub fn new_with_network(public: &[u8], network: &str) -> Result { NetworkName::validate_network_name(network)?; - try_did!(public, network) + try_construct_did!(public, network) } #[doc(hidden)] @@ -265,7 +265,7 @@ impl IotaDID { /// Returns the Tangle `network` of the `DID`, if it is valid. pub fn network(&self) -> Result { - Network::from_name(self.network_str().to_owned()) + Network::try_from_name(self.network_str().to_owned()) } /// Returns the Tangle `network` name of the `DID`. diff --git a/identity-iota/src/tangle/network.rs b/identity-iota/src/tangle/network.rs index cf8b908a51..b9c5042a36 100644 --- a/identity-iota/src/tangle/network.rs +++ b/identity-iota/src/tangle/network.rs @@ -45,7 +45,7 @@ impl Network { /// 6 characters long, and only include alphanumeric characters `0-9` and `a-z`. /// /// See [`NetworkName`]. - pub fn from_name(name: S) -> Result + pub fn try_from_name(name: S) -> Result where // Allow String, &'static str, Cow<'static, str> S: AsRef + Into>, @@ -55,7 +55,7 @@ impl Network { NETWORK_NAME_MAIN => Ok(Self::Mainnet), _ => { // Accept any other valid string - validation is performed by NetworkName - let network_name: NetworkName = NetworkName::try_from_name(name)?; + let network_name: NetworkName = NetworkName::try_from(name)?; Ok(Self::Other { name: network_name, explorer_url: None, @@ -153,7 +153,7 @@ pub struct NetworkName(Cow<'static, str>); impl NetworkName { /// Creates a new [`NetworkName`] if the name passes validation. - pub fn try_from_name(name: T) -> Result + pub fn try_from(name: T) -> Result where T: Into>, { @@ -194,7 +194,7 @@ impl TryFrom<&'static str> for NetworkName { type Error = Error; fn try_from(name: &'static str) -> Result { - Self::try_from_name(Cow::Borrowed(name)) + Self::try_from(Cow::Borrowed(name)) } } @@ -202,7 +202,7 @@ impl TryFrom for NetworkName { type Error = Error; fn try_from(name: String) -> Result { - Self::try_from_name(Cow::Owned(name)) + Self::try_from(Cow::Owned(name)) } } @@ -218,29 +218,29 @@ mod tests { #[test] fn test_from_name_standard_networks() { - assert_eq!(Network::from_name(NETWORK_NAME_TEST).unwrap(), Network::Testnet); - assert_eq!(Network::from_name(NETWORK_NAME_MAIN).unwrap(), Network::Mainnet); + assert_eq!(Network::try_from_name(NETWORK_NAME_TEST).unwrap(), Network::Testnet); + assert_eq!(Network::try_from_name(NETWORK_NAME_MAIN).unwrap(), Network::Mainnet); } #[test] fn test_from_name_types() { let static_str = "custom"; - assert!(Network::from_name(static_str).is_ok()); + assert!(Network::try_from_name(static_str).is_ok()); let string = static_str.to_owned(); - assert!(Network::from_name(string.clone()).is_ok()); + assert!(Network::try_from_name(string.clone()).is_ok()); let cow_owned = Cow::Owned(string); - assert!(Network::from_name(cow_owned).is_ok()); + assert!(Network::try_from_name(cow_owned).is_ok()); let cow_borrowed = Cow::Borrowed(static_str); - assert!(Network::from_name(cow_borrowed).is_ok()); + assert!(Network::try_from_name(cow_borrowed).is_ok()); } #[test] fn test_from_name() { assert_eq!( - Network::from_name("6chars").unwrap(), + Network::try_from_name("6chars").unwrap(), Network::Other { name: NetworkName::try_from("6chars").unwrap(), explorer_url: None, @@ -248,22 +248,22 @@ mod tests { ); assert!(matches!( - Network::from_name("7seven7").unwrap_err(), + Network::try_from_name("7seven7").unwrap_err(), Error::InvalidNetworkName("network name cannot exceed 6 characters") )); assert!(matches!( - Network::from_name("täst").unwrap_err(), + Network::try_from_name("täst").unwrap_err(), Error::InvalidNetworkName("network name must only contain characters `0-9` and `a-z`") )); assert!(matches!( - Network::from_name(" ").unwrap_err(), + Network::try_from_name(" ").unwrap_err(), Error::InvalidNetworkName("network name must only contain characters `0-9` and `a-z`") )); assert!(matches!( - Network::from_name("").unwrap_err(), + Network::try_from_name("").unwrap_err(), Error::InvalidNetworkName("network name must not be empty") )); } @@ -289,7 +289,7 @@ mod tests { assert!(testnet.explorer_url().is_some()); assert_eq!(testnet.explorer_url().unwrap().as_str(), EXPLORER_TEST.as_str()); - let mut other = Network::from_name("atoi").unwrap(); + let mut other = Network::try_from_name("atoi").unwrap(); assert!(other.explorer_url().is_none()); // Try setting a `cannot_be_a_base` url. diff --git a/identity/src/lib.rs b/identity/src/lib.rs index 0ed3c016ee..d230533626 100644 --- a/identity/src/lib.rs +++ b/identity/src/lib.rs @@ -77,7 +77,7 @@ pub mod iota { pub use identity_iota::tangle::*; #[doc(inline)] - pub use identity_iota::try_did; + pub use identity_iota::try_construct_did; } #[cfg(feature = "account")] From 7bceff99e3b502f204383742201fa29ec4d88070 Mon Sep 17 00:00:00 2001 From: Craig Bester Date: Thu, 2 Sep 2021 15:36:34 +0200 Subject: [PATCH 6/6] Fix compilation --- identity-account/tests/commands.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/identity-account/tests/commands.rs b/identity-account/tests/commands.rs index 0f0054344a..6da5436a7b 100644 --- a/identity-account/tests/commands.rs +++ b/identity-account/tests/commands.rs @@ -184,7 +184,7 @@ async fn test_create_identity_from_secret_key() -> Result<()> { async fn test_create_identity_from_invalid_secret_key() -> Result<()> { let account: Account = new_account().await?; - let secret_bytes: Box<[u8]> = Box::new(*[0; 33]); + let secret_bytes: Box<[u8]> = Box::new([0; 33]); let secret_key: SecretKey = SecretKey::from(secret_bytes); let id_create = IdentityCreate::new() @@ -358,7 +358,7 @@ async fn test_create_method_from_invalid_secret_key() -> Result<()> { account.process(identity, command, false).await?; - let secret_bytes: Box<[u8]> = Box::new(*[0; 33]); + let secret_bytes: Box<[u8]> = Box::new([0; 33]); let secret_key = SecretKey::from(secret_bytes); let command: Command = Command::create_method() @@ -387,7 +387,7 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { account.process(identity, command, false).await?; - let secret_bytes: Box<[u8]> = Box::new(*[0; 32]); + let secret_bytes: Box<[u8]> = Box::new([0; 32]); let secret_key = SecretKey::from(secret_bytes); let command: Command = Command::create_method()