diff --git a/bindings/wasm/docs/api-reference.md b/bindings/wasm/docs/api-reference.md index 2665ea2bdf..b1e863d21d 100644 --- a/bindings/wasm/docs/api-reference.md +++ b/bindings/wasm/docs/api-reference.md @@ -114,10 +114,10 @@ See IVerifierOptions.

## Members
-
KeyType
-
DIDMessageEncoding
+
KeyType
+
MethodRelationship
Digest
@@ -3519,14 +3519,14 @@ Throws an error if any of the options are invalid. Creates a new `VerifierOptions` with default options. **Kind**: static method of [VerifierOptions](#VerifierOptions) - - -## KeyType -**Kind**: global variable ## DIDMessageEncoding **Kind**: global variable + + +## KeyType +**Kind**: global variable ## MethodRelationship diff --git a/bindings/wasm/src/account/wasm_account/account.rs b/bindings/wasm/src/account/wasm_account/account.rs index e7ed77c06d..d01a0e1d3e 100644 --- a/bindings/wasm/src/account/wasm_account/account.rs +++ b/bindings/wasm/src/account/wasm_account/account.rs @@ -1,7 +1,6 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use std::cell::Ref; use std::cell::RefCell; use std::rc::Rc; use std::sync::Arc; @@ -14,6 +13,7 @@ use identity::account::Storage; use identity::crypto::SetSignature; use identity::crypto::SignatureOptions; use identity::did::verifiable::VerifiableProperties; +use identity::iota::Client; use identity::iota::IotaDID; use identity::iota::IotaDocument; use js_sys::Promise; @@ -33,20 +33,21 @@ use crate::did::WasmResolvedDocument; use crate::error::Result; use crate::error::WasmResult; +pub(crate) type AccountRc = Account>; + /// An account manages one identity. /// /// It handles private keys, writing to storage and /// publishing to the Tangle. #[wasm_bindgen(js_name = Account)] -pub struct WasmAccount(pub(crate) Rc>); +pub struct WasmAccount(pub(crate) Rc>); #[wasm_bindgen(js_class = Account)] impl WasmAccount { /// Returns the {@link DID} of the managed identity. #[wasm_bindgen(js_name = did)] pub fn did(&self) -> WasmDID { - let account: Ref = self.0.borrow(); - WasmDID::from(account.did().clone()) + WasmDID::from(self.0.borrow().did().clone()) } /// Returns whether auto-publish is enabled. @@ -71,7 +72,7 @@ impl WasmAccount { /// Resolves the DID Document associated with this `Account` from the Tangle. #[wasm_bindgen(js_name = resolveIdentity)] pub fn resolve_identity(&self) -> PromiseResolvedDocument { - let account = self.0.clone(); + let account: Rc> = self.0.clone(); let promise: Promise = future_to_promise(async move { account @@ -95,13 +96,9 @@ impl WasmAccount { let did: IotaDID = self.0.borrow().did().to_owned(); let storage: Arc = Arc::clone(self.0.borrow().storage()); - // Drop account should release the DIDLease because we cannot take ownership of the Rc. - // Note that this will still fail if anyone else has a reference to the Account. - std::mem::drop(self.0); - future_to_promise(async move { // Create a new account since `delete_identity` consumes it. - let account: Result = AccountBuilder::new() + let account: Result = AccountBuilder::new() .storage(AccountStorage::Custom(storage)) .load_identity(did) .await @@ -246,8 +243,8 @@ impl WasmAccount { } } -impl From for WasmAccount { - fn from(account: Account) -> WasmAccount { +impl From for WasmAccount { + fn from(account: AccountRc) -> WasmAccount { WasmAccount(Rc::new(RefCell::new(account))) } } diff --git a/bindings/wasm/src/account/wasm_account/account_builder.rs b/bindings/wasm/src/account/wasm_account/account_builder.rs index 8d9b1c7571..7c587c88ab 100644 --- a/bindings/wasm/src/account/wasm_account/account_builder.rs +++ b/bindings/wasm/src/account/wasm_account/account_builder.rs @@ -8,6 +8,8 @@ use std::sync::Arc; use identity::account::AccountBuilder; use identity::account::AccountStorage; use identity::account::IdentitySetup; +use identity::iota::Client; +use identity::iota::IotaDID; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; @@ -24,6 +26,8 @@ use crate::error::WasmResult; use crate::tangle::Config; use crate::tangle::WasmClient; +type AccountBuilderRc = AccountBuilder>; + /// An [`Account`] builder for easy account configuration. /// /// To reduce memory usage, accounts created from the same builder share the same `Storage` @@ -33,14 +37,14 @@ use crate::tangle::WasmClient; /// This means a builder can be reconfigured in-between account creations, without affecting /// the configuration of previously built accounts. #[wasm_bindgen(js_name = AccountBuilder)] -pub struct WasmAccountBuilder(Rc>); +pub struct WasmAccountBuilder(Rc>); #[wasm_bindgen(js_class = AccountBuilder)] impl WasmAccountBuilder { /// Creates a new `AccountBuilder`. #[wasm_bindgen(constructor)] pub fn new(options: Option) -> Result { - let mut builder = AccountBuilder::new(); + let mut builder: AccountBuilderRc = AccountBuilderRc::new(); if let Some(builder_options) = options { if let Some(autopublish) = builder_options.autopublish() { @@ -53,7 +57,7 @@ impl WasmAccountBuilder { if let Some(mut config) = builder_options.clientConfig() { let client: WasmClient = WasmClient::from_config(&mut config)?; - builder = builder.client(Arc::new(client.client.as_ref().clone())); + builder = builder.client(client.client); }; if let Some(storage) = builder_options.storage() { @@ -68,13 +72,13 @@ impl WasmAccountBuilder { /// The identity must exist in the configured `Storage`. #[wasm_bindgen(js_name = loadIdentity)] pub fn load_identity(&mut self, did: &WasmDID) -> Result { - let builder = self.0.clone(); - let did = did.clone(); + let builder: Rc> = self.0.clone(); + let did: IotaDID = did.0.clone(); let promise: Promise = future_to_promise(async move { builder .as_ref() .borrow_mut() - .load_identity(did.0) + .load_identity(did) .await .map(WasmAccount::from) .map(Into::into) @@ -94,7 +98,7 @@ impl WasmAccountBuilder { pub fn create_identity(&mut self, identity_setup: Option) -> Result { let setup: IdentitySetup = identity_setup.map(IdentitySetup::from).unwrap_or_default(); - let builder: Rc> = self.0.clone(); + let builder: Rc> = self.0.clone(); let promise: Promise = future_to_promise(async move { builder .as_ref() diff --git a/bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs b/bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs index fb42230a01..a4d0f22705 100644 --- a/bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs +++ b/bindings/wasm/src/account/wasm_account/update/attach_method_relationships.rs @@ -5,17 +5,18 @@ use std::cell::RefCell; use std::cell::RefMut; use std::rc::Rc; -use identity::account::Account; use identity::account::AttachMethodRelationshipBuilder; use identity::account::IdentityUpdater; use identity::account::UpdateError::MissingRequiredField; use identity::core::OneOrMany; use identity::did::MethodRelationship; +use identity::iota::Client; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; +use crate::account::wasm_account::account::AccountRc; use crate::account::wasm_account::WasmAccount; use crate::common::PromiseVoid; use crate::did::WasmMethodRelationship; @@ -44,14 +45,14 @@ impl WasmAccount { .ok_or(MissingRequiredField("fragment")) .wasm_result()?; - let account: Rc> = Rc::clone(&self.0); + let account: Rc> = Rc::clone(&self.0); let promise: Promise = future_to_promise(async move { if relationships.is_empty() { return Ok(JsValue::undefined()); } - let mut account: RefMut = account.borrow_mut(); - let mut updater: IdentityUpdater<'_> = account.update_identity(); - let mut attach_relationship: AttachMethodRelationshipBuilder<'_> = + let mut account: RefMut = account.borrow_mut(); + let mut updater: IdentityUpdater<'_, Rc> = account.update_identity(); + let mut attach_relationship: AttachMethodRelationshipBuilder<'_, Rc> = updater.attach_method_relationship().fragment(fragment); for relationship in relationships { diff --git a/bindings/wasm/src/account/wasm_account/update/create_method.rs b/bindings/wasm/src/account/wasm_account/update/create_method.rs index 170638ffbe..09b5c52648 100644 --- a/bindings/wasm/src/account/wasm_account/update/create_method.rs +++ b/bindings/wasm/src/account/wasm_account/update/create_method.rs @@ -5,19 +5,20 @@ use std::cell::RefCell; use std::cell::RefMut; use std::rc::Rc; -use identity::account::Account; use identity::account::CreateMethodBuilder; use identity::account::IdentityUpdater; use identity::account::MethodSecret; use identity::account::UpdateError::MissingRequiredField; use identity::did::MethodScope; use identity::did::MethodType; +use identity::iota::Client; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; use crate::account::types::WasmMethodSecret; +use crate::account::wasm_account::account::AccountRc; use crate::account::wasm_account::WasmAccount; use crate::common::PromiseVoid; use crate::did::WasmMethodScope; @@ -41,11 +42,11 @@ impl WasmAccount { let method_secret: Option = options.methodSecret().map(|ms| ms.0); - let account: Rc> = Rc::clone(&self.0); + let account: Rc> = Rc::clone(&self.0); let promise: Promise = future_to_promise(async move { - let mut account: RefMut = account.borrow_mut(); - let mut updater: IdentityUpdater<'_> = account.update_identity(); - let mut create_method: CreateMethodBuilder<'_> = updater.create_method().fragment(fragment); + let mut account: RefMut = account.borrow_mut(); + let mut updater: IdentityUpdater<'_, Rc> = account.update_identity(); + let mut create_method: CreateMethodBuilder<'_, Rc> = updater.create_method().fragment(fragment); if let Some(type_) = method_type { create_method = create_method.type_(type_); diff --git a/bindings/wasm/src/account/wasm_account/update/create_service.rs b/bindings/wasm/src/account/wasm_account/update/create_service.rs index 8c9215f9ec..733e68f4c0 100644 --- a/bindings/wasm/src/account/wasm_account/update/create_service.rs +++ b/bindings/wasm/src/account/wasm_account/update/create_service.rs @@ -5,17 +5,18 @@ use std::cell::RefCell; use std::cell::RefMut; use std::rc::Rc; -use identity::account::Account; use identity::account::CreateServiceBuilder; use identity::account::IdentityUpdater; use identity::account::UpdateError::MissingRequiredField; use identity::core::Object; use identity::core::Url; +use identity::iota::Client; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; +use crate::account::wasm_account::account::AccountRc; use crate::account::wasm_account::WasmAccount; use crate::common::PromiseVoid; use crate::error::Result; @@ -39,11 +40,11 @@ impl WasmAccount { let endpoint: Url = Url::parse(endpoint.as_str()).wasm_result()?; let properties: Option = options.properties().into_serde().wasm_result()?; - let account: Rc> = Rc::clone(&self.0); + let account: Rc> = Rc::clone(&self.0); let promise: Promise = future_to_promise(async move { - let mut account: RefMut = account.borrow_mut(); - let mut updater: IdentityUpdater<'_> = account.update_identity(); - let mut create_service: CreateServiceBuilder<'_> = updater + let mut account: RefMut = account.borrow_mut(); + let mut updater: IdentityUpdater<'_, Rc> = account.update_identity(); + let mut create_service: CreateServiceBuilder<'_, Rc> = updater .create_service() .fragment(fragment) .type_(service_type) @@ -68,7 +69,7 @@ extern "C" { #[wasm_bindgen(getter, method)] pub fn fragment(this: &CreateServiceOptions) -> Option; - #[wasm_bindgen(getter, method, js_name= type)] + #[wasm_bindgen(getter, method, js_name = type)] pub fn type_(this: &CreateServiceOptions) -> Option; #[wasm_bindgen(getter, method)] diff --git a/bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs b/bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs index c23980df1e..9d60dcba3f 100644 --- a/bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs +++ b/bindings/wasm/src/account/wasm_account/update/detach_method_relationships.rs @@ -5,17 +5,18 @@ use std::cell::RefCell; use std::cell::RefMut; use std::rc::Rc; -use identity::account::Account; use identity::account::DetachMethodRelationshipBuilder; use identity::account::IdentityUpdater; use identity::account::UpdateError::MissingRequiredField; use identity::core::OneOrMany; use identity::did::MethodRelationship; +use identity::iota::Client; use js_sys::Promise; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; +use crate::account::wasm_account::account::AccountRc; use crate::account::wasm_account::WasmAccount; use crate::common::PromiseVoid; use crate::did::WasmMethodRelationship; @@ -41,15 +42,15 @@ impl WasmAccount { .ok_or(MissingRequiredField("fragment")) .wasm_result()?; - let account: Rc> = Rc::clone(&self.0); + let account: Rc> = Rc::clone(&self.0); let promise: Promise = future_to_promise(async move { if relationships.is_empty() { return Ok(JsValue::undefined()); } - let mut account: RefMut = account.borrow_mut(); - let mut updater: IdentityUpdater<'_> = account.update_identity(); - let mut detach_relationship: DetachMethodRelationshipBuilder<'_> = + let mut account: RefMut = account.borrow_mut(); + let mut updater: IdentityUpdater<'_, Rc> = account.update_identity(); + let mut detach_relationship: DetachMethodRelationshipBuilder<'_, Rc> = updater.detach_method_relationship().fragment(fragment); for relationship in relationships { diff --git a/bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs b/bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs index 6d660ee78b..431218319b 100644 --- a/bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs +++ b/bindings/wasm/src/account/wasm_account/update/set_also_known_as.rs @@ -1,22 +1,23 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::error::Result; -use crate::error::WasmResult; -use identity::account::Account; +use std::cell::RefCell; +use std::rc::Rc; + use identity::core::OneOrMany; use identity::core::OrderedSet; use identity::core::Url; - use js_sys::Promise; -use std::cell::RefCell; -use std::rc::Rc; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; +use crate::account::wasm_account::account::AccountRc; +use crate::account::wasm_account::WasmAccount; +use crate::common::PromiseVoid; +use crate::error::Result; +use crate::error::WasmResult; + #[wasm_bindgen(js_class = Account)] impl WasmAccount { /// Sets the `alsoKnownAs` property in the DID document. @@ -31,7 +32,7 @@ impl WasmAccount { } } - let account: Rc> = Rc::clone(&self.0); + let account: Rc> = Rc::clone(&self.0); let promise: Promise = future_to_promise(async move { account .borrow_mut() diff --git a/bindings/wasm/src/account/wasm_account/update/set_controller.rs b/bindings/wasm/src/account/wasm_account/update/set_controller.rs index 388316f840..f7d239e5df 100644 --- a/bindings/wasm/src/account/wasm_account/update/set_controller.rs +++ b/bindings/wasm/src/account/wasm_account/update/set_controller.rs @@ -1,22 +1,24 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use crate::account::wasm_account::WasmAccount; -use crate::common::PromiseVoid; -use crate::error::Result; -use crate::error::WasmResult; -use identity::account::Account; +use std::cell::RefCell; +use std::rc::Rc; + use identity::core::OneOrMany; use identity::core::OneOrSet; use identity::core::OrderedSet; use identity::iota::IotaDID; use js_sys::Promise; -use std::cell::RefCell; -use std::rc::Rc; use wasm_bindgen::prelude::*; use wasm_bindgen::JsCast; use wasm_bindgen_futures::future_to_promise; +use crate::account::wasm_account::account::AccountRc; +use crate::account::wasm_account::WasmAccount; +use crate::common::PromiseVoid; +use crate::error::Result; +use crate::error::WasmResult; + #[wasm_bindgen(js_class = Account)] impl WasmAccount { /// Sets the controllers of the DID document. @@ -43,7 +45,7 @@ impl WasmAccount { None }; - let account: Rc> = Rc::clone(&self.0); + let account: Rc> = Rc::clone(&self.0); let promise: Promise = future_to_promise(async move { account diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index cc1de930ed..f917d09efa 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -1,6 +1,8 @@ // Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::fmt::Debug; +use std::ops::Deref; use std::sync::atomic::AtomicUsize; use std::sync::atomic::Ordering; use std::sync::Arc; @@ -19,6 +21,7 @@ use identity_iota::tangle::Client; use identity_iota::tangle::MessageId; use identity_iota::tangle::MessageIdExt; use identity_iota::tangle::PublishType; +use identity_iota::tangle::SharedPtr; use crate::account::AccountBuilder; use crate::account::PublishOptions; @@ -42,27 +45,33 @@ use super::AccountConfig; /// It handles private keys, writing to storage and /// publishing to the Tangle. #[derive(Debug)] -pub struct Account { +pub struct Account> +where + C: SharedPtr, +{ config: AccountConfig, storage: Arc, - client: Arc, + client: C, actions: AtomicUsize, chain_state: ChainState, state: IdentityState, } -impl Account { +impl Account +where + C: SharedPtr, +{ // =========================================================================== // Constructors // =========================================================================== /// Creates a new [AccountBuilder]. - pub fn builder() -> AccountBuilder { + pub fn builder() -> AccountBuilder { AccountBuilder::new() } /// Creates a new `Account` instance with the given `config`. - async fn with_setup(setup: AccountSetup, chain_state: ChainState, state: IdentityState) -> Result { + async fn with_setup(setup: AccountSetup, chain_state: ChainState, state: IdentityState) -> Result { Ok(Self { config: setup.config, storage: setup.storage, @@ -79,11 +88,11 @@ impl Account { /// is automatically determined by the [`Client`] used to publish it. /// /// See [`IdentitySetup`] to customize the identity creation. - pub(crate) async fn create_identity(account_setup: AccountSetup, identity_setup: IdentitySetup) -> Result { + pub(crate) async fn create_identity(account_setup: AccountSetup, identity_setup: IdentitySetup) -> Result { let state: IdentityState = create_identity( identity_setup, - account_setup.client.network().name(), - account_setup.storage.as_ref(), + account_setup.client.deref().network().name(), + account_setup.storage.deref(), ) .await?; @@ -102,7 +111,7 @@ impl Account { /// /// Callers are expected **not** to load the same [`IotaDID`] into more than one account, /// as that would cause race conditions when updating the identity. - pub(crate) async fn load_identity(setup: AccountSetup, did: IotaDID) -> Result { + pub(crate) async fn load_identity(setup: AccountSetup, did: IotaDID) -> Result { // Ensure the DID matches the client network. if did.network_str() != setup.client.network().name_str() { return Err(Error::IotaError(identity_iota::Error::IncompatibleNetwork(format!( @@ -193,7 +202,7 @@ impl Account { /// /// On this type, various operations can be executed /// that modify an identity, such as creating services or methods. - pub fn update_identity(&mut self) -> IdentityUpdater<'_> { + pub fn update_identity(&mut self) -> IdentityUpdater<'_, C> { IdentityUpdater::new(self) } @@ -219,7 +228,7 @@ impl Account { /// Note: This will remove all associated document updates and key material - recovery is NOT POSSIBLE! pub async fn delete_identity(self) -> Result<()> { // Remove all associated keys and events - self.storage().as_ref().purge(self.did()).await?; + self.storage().deref().purge(self.did()).await?; // Write the changes to disk self.save(false).await?; @@ -242,7 +251,7 @@ impl Account { let location: KeyLocation = state.method_location(method.key_type(), fragment.to_owned())?; state - .sign_data(self.did(), self.storage().as_ref(), &location, data, options) + .sign_data(self.did(), self.storage().deref(), &location, data, options) .await?; Ok(()) @@ -303,7 +312,7 @@ impl Account { // This should be mapped to a fatal error in the future. self .storage() - .as_ref() + .deref() .state(self.did()) .await? .ok_or(Error::IdentityNotFound) @@ -311,9 +320,7 @@ impl Account { pub(crate) async fn process_update(&mut self, update: Update) -> Result<()> { let did = self.did().to_owned(); - let storage = Arc::clone(&self.storage); - - update.process(&did, &mut self.state, storage.as_ref()).await?; + update.process(&did, &mut self.state, self.storage.deref()).await?; self.increment_actions(); @@ -353,7 +360,7 @@ impl Account { signing_state .sign_data( self.did(), - self.storage().as_ref(), + self.storage().deref(), &signing_key_location, document, SignatureOptions::default(), @@ -497,7 +504,7 @@ impl Account { old_state .sign_data( self.did(), - self.storage().as_ref(), + self.storage().deref(), &signing_key_location, &mut diff, SignatureOptions::default(), @@ -530,10 +537,10 @@ impl Account { async fn save(&self, force: bool) -> Result<()> { match self.config.autosave { AutoSave::Every => { - self.storage().as_ref().flush_changes().await?; + self.storage().deref().flush_changes().await?; } AutoSave::Batch(step) if force || (step != 0 && self.actions() % step == 0) => { - self.storage().as_ref().flush_changes().await?; + self.storage().deref().flush_changes().await?; } AutoSave::Batch(_) | AutoSave::Never => {} } diff --git a/identity-account/src/account/builder.rs b/identity-account/src/account/builder.rs index ae8e8b05af..622162bb00 100644 --- a/identity-account/src/account/builder.rs +++ b/identity-account/src/account/builder.rs @@ -11,6 +11,7 @@ use zeroize::Zeroize; use identity_iota::did::IotaDID; use identity_iota::tangle::Client; use identity_iota::tangle::ClientBuilder; +use identity_iota::tangle::SharedPtr; use crate::account::Account; use crate::error::Result; @@ -45,15 +46,21 @@ pub enum AccountStorage { /// This means a builder can be reconfigured in-between account creations, without affecting /// the configuration of previously built accounts. #[derive(Debug)] -pub struct AccountBuilder { +pub struct AccountBuilder> +where + C: SharedPtr, +{ config: AccountConfig, storage_template: Option, storage: Option>, client_builder: Option, - client: Option>, + client: Option, } -impl AccountBuilder { +impl AccountBuilder +where + C: SharedPtr, +{ /// Creates a new `AccountBuilder`. pub fn new() -> Self { Self { @@ -135,7 +142,7 @@ impl AccountBuilder { /// NOTE: this overwrites any [`ClientBuilder`] previously set by /// [`AccountBuilder::client_builder`]. #[must_use] - pub fn client(mut self, client: Arc) -> Self { + pub fn client(mut self, client: C) -> Self { self.client = Some(client); self.client_builder = None; self @@ -156,24 +163,24 @@ impl AccountBuilder { /// to [`AccountBuilder::client_builder`]. /// /// If neither is set, instantiates and stores a default [`Client`]. - async fn get_or_build_client(&mut self) -> Result> { + async fn get_or_build_client(&mut self) -> Result { if let Some(client) = &self.client { - Ok(Arc::clone(client)) + Ok(C::clone(client)) } else if let Some(client_builder) = self.client_builder.take() { - let client: Arc = Arc::new(client_builder.build().await?); - self.client = Some(Arc::clone(&client)); + let client: C = C::from(client_builder.build().await?); + self.client = Some(C::clone(&client)); Ok(client) } else { - let client: Arc = Arc::new(Client::new().await?); - self.client = Some(Arc::clone(&client)); + let client: C = C::from(Client::new().await?); + self.client = Some(C::clone(&client)); Ok(client) } } - async fn build_setup(&mut self) -> Result { - let client: Arc = self.get_or_build_client().await?; + async fn build_setup(&mut self) -> Result> { + let client: C = self.get_or_build_client().await?; - Ok(AccountSetup::new( + Ok(AccountSetup::::new( self.get_storage().await?, client, self.config.clone(), @@ -187,8 +194,8 @@ impl AccountBuilder { /// by the [`Client`] used to publish it. /// /// See [`IdentitySetup`] to customize the identity creation. - pub async fn create_identity(&mut self, input: IdentitySetup) -> Result { - let setup: AccountSetup = self.build_setup().await?; + pub async fn create_identity(&mut self, input: IdentitySetup) -> Result> { + let setup: AccountSetup = self.build_setup().await?; Account::create_identity(setup, input).await } @@ -199,13 +206,16 @@ impl AccountBuilder { /// /// Callers are expected **not** to load the same [`IotaDID`] into more than one account, /// as that would cause race conditions when updating the identity. - pub async fn load_identity(&mut self, did: IotaDID) -> Result { - let setup: AccountSetup = self.build_setup().await?; + pub async fn load_identity(&mut self, did: IotaDID) -> Result> { + let setup: AccountSetup = self.build_setup().await?; Account::load_identity(setup, did).await } } -impl Default for AccountBuilder { +impl Default for AccountBuilder +where + C: SharedPtr, +{ fn default() -> Self { Self::new() } diff --git a/identity-account/src/account/config.rs b/identity-account/src/account/config.rs index 48ed6cd271..1b12fcb567 100644 --- a/identity-account/src/account/config.rs +++ b/identity-account/src/account/config.rs @@ -4,6 +4,7 @@ use std::sync::Arc; use identity_iota::tangle::Client; +use identity_iota::tangle::SharedPtr; use crate::storage::Storage; @@ -15,16 +16,22 @@ use crate::storage::Storage; /// /// [`Account`]([crate::account::Account]) #[derive(Clone, Debug)] -pub(crate) struct AccountSetup { +pub(crate) struct AccountSetup> +where + C: SharedPtr, +{ pub(crate) storage: Arc, - pub(crate) client: Arc, + pub(crate) client: C, pub(crate) config: AccountConfig, } -impl AccountSetup { +impl AccountSetup +where + C: SharedPtr, +{ /// Create a new setup from the given [`Storage`] implementation /// and with defaults for [`Config`] and [`Client`]. - pub(crate) fn new(storage: Arc, client: Arc, config: AccountConfig) -> Self { + pub(crate) fn new(storage: Arc, client: C, config: AccountConfig) -> Self { Self { storage, client, diff --git a/identity-account/src/identity/identity_updater.rs b/identity-account/src/identity/identity_updater.rs index 5c70ac18ac..f0e6030216 100644 --- a/identity-account/src/identity/identity_updater.rs +++ b/identity-account/src/identity/identity_updater.rs @@ -1,17 +1,26 @@ -// Copyright 2020-2021 IOTA Stiftung +// Copyright 2020-2022 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use identity_iota::tangle::Client; +use identity_iota::tangle::SharedPtr; + use crate::account::Account; /// A struct created by the [`Account::update_identity`] method, that /// allows executing various updates on the identity it was created on. #[derive(Debug)] -pub struct IdentityUpdater<'account> { - pub(crate) account: &'account mut Account, +pub struct IdentityUpdater<'account, C> +where + C: SharedPtr, +{ + pub(crate) account: &'account mut Account, } -impl<'account> IdentityUpdater<'account> { - pub(crate) fn new(account: &'account mut Account) -> Self { +impl<'account, C> IdentityUpdater<'account, C> +where + C: SharedPtr, +{ + pub(crate) fn new(account: &'account mut Account) -> Self { Self { account } } } diff --git a/identity-account/src/updates/macros.rs b/identity-account/src/updates/macros.rs index 8eaeb2c848..85d43b6dc2 100644 --- a/identity-account/src/updates/macros.rs +++ b/identity-account/src/updates/macros.rs @@ -34,14 +34,20 @@ macro_rules! impl_update_builder { paste::paste! { $(#[$doc])* #[derive(Debug)] - pub struct [<$ident Builder>]<'account> { - account: &'account mut Account, + pub struct [<$ident Builder>]<'account, C> + where + C: identity_iota::tangle::SharedPtr, + { + account: &'account mut Account, $( $field: Option<$ty>, )* } - impl<'account> [<$ident Builder>]<'account> { + impl<'account, C> [<$ident Builder>]<'account, C> + where + C: identity_iota::tangle::SharedPtr, + { $( #[must_use] pub fn $field>(mut self, value: VALUE) -> Self { @@ -50,7 +56,10 @@ macro_rules! impl_update_builder { } )* - pub fn new(account: &'account mut Account) -> [<$ident Builder>]<'account> { + pub fn new(account: &'account mut Account) -> [<$ident Builder>]<'account, C> + where + C: identity_iota::tangle::SharedPtr, + { [<$ident Builder>] { account, $( @@ -70,9 +79,12 @@ macro_rules! impl_update_builder { } } - impl<'account> $crate::identity::IdentityUpdater<'account> { + impl<'account, C> $crate::identity::IdentityUpdater<'account, C> + where + C: identity_iota::tangle::SharedPtr, + { /// Creates a new builder to modify the identity. See the documentation of the return type for details. - pub fn [<$ident:snake>](&'account mut self) -> [<$ident Builder>]<'account> { + pub fn [<$ident:snake>](&'account mut self) -> [<$ident Builder>]<'account, C> { [<$ident Builder>]::new(self.account) } } diff --git a/identity-account/src/updates/update.rs b/identity-account/src/updates/update.rs index eb6add28a0..3574047ec5 100644 --- a/identity-account/src/updates/update.rs +++ b/identity-account/src/updates/update.rs @@ -26,7 +26,9 @@ use identity_iota::did::IotaDIDUrl; use identity_iota::document::IotaDocument; use identity_iota::document::IotaService; use identity_iota::document::IotaVerificationMethod; +use identity_iota::tangle::Client; use identity_iota::tangle::NetworkName; +use identity_iota::tangle::SharedPtr; use crate::account::Account; use crate::error::Result; @@ -370,7 +372,10 @@ AttachMethodRelationship { @default relationships Vec, }); -impl<'account> AttachMethodRelationshipBuilder<'account> { +impl<'account, C> AttachMethodRelationshipBuilder<'account, C> +where + C: SharedPtr, +{ #[must_use] pub fn relationship(mut self, value: MethodRelationship) -> Self { self.relationships.get_or_insert_with(Default::default).push(value); @@ -389,7 +394,10 @@ DetachMethodRelationship { @default relationships Vec, }); -impl<'account> DetachMethodRelationshipBuilder<'account> { +impl<'account, C> DetachMethodRelationshipBuilder<'account, C> +where + C: SharedPtr, +{ #[must_use] pub fn relationship(mut self, value: MethodRelationship) -> Self { self.relationships.get_or_insert_with(Default::default).push(value); diff --git a/identity-iota/src/tangle/client.rs b/identity-iota/src/tangle/client.rs index bfbd5bc388..92782e6078 100644 --- a/identity-iota/src/tangle/client.rs +++ b/identity-iota/src/tangle/client.rs @@ -30,8 +30,7 @@ use crate::tangle::TangleRef; use crate::tangle::TangleResolve; /// Client for performing IOTA Identity operations on the Tangle. -#[cfg_attr(all(target_arch = "wasm32", not(target_os = "wasi")), derive(Debug, Clone))] -#[cfg_attr(not(all(target_arch = "wasm32", not(target_os = "wasi"))), derive(Debug))] +#[derive(Debug)] pub struct Client { pub(crate) client: IotaClient, pub(crate) network: Network, diff --git a/identity-iota/src/tangle/mod.rs b/identity-iota/src/tangle/mod.rs index 9080febb1b..683c39ea95 100644 --- a/identity-iota/src/tangle/mod.rs +++ b/identity-iota/src/tangle/mod.rs @@ -25,6 +25,7 @@ pub use self::publish::PublishType; pub use self::receipt::Receipt; pub use self::resolver::Resolver; pub use self::resolver::ResolverBuilder; +pub use self::traits::SharedPtr; pub use self::traits::TangleRef; pub use self::traits::TangleResolve; diff --git a/identity-iota/src/tangle/resolver.rs b/identity-iota/src/tangle/resolver.rs index adf195a2f5..d8e5891415 100644 --- a/identity-iota/src/tangle/resolver.rs +++ b/identity-iota/src/tangle/resolver.rs @@ -19,6 +19,7 @@ use crate::error::Result; use crate::tangle::Client; use crate::tangle::ClientBuilder; use crate::tangle::NetworkName; +use crate::tangle::SharedPtr; use crate::tangle::TangleResolve; /// A `Resolver` supports resolving DID Documents across different Tangle networks using @@ -27,16 +28,16 @@ use crate::tangle::TangleResolve; /// Also provides convenience functions for resolving DID Documents associated with /// verifiable [`Credentials`][Credential] and [`Presentations`][Presentation]. #[derive(Debug)] -pub struct Resolver> +pub struct Resolver> where - T: Clone + AsRef + From, + C: SharedPtr, { - client_map: HashMap, + client_map: HashMap, } -impl Resolver +impl Resolver where - T: Clone + AsRef + From, + C: SharedPtr, { /// Constructs a new [`Resolver`] with a default [`Client`] for /// the [`Mainnet`](crate::tangle::Network::Mainnet). @@ -45,8 +46,8 @@ where pub async fn new() -> Result { let client: Client = Client::new().await?; - let mut client_map: HashMap = HashMap::new(); - client_map.insert(client.network.name(), T::from(client)); + let mut client_map: HashMap = HashMap::new(); + client_map.insert(client.network.name(), C::from(client)); Ok(Self { client_map }) } @@ -56,12 +57,12 @@ where } /// Returns the [`Client`] corresponding to the given [`NetworkName`] if one exists. - pub fn get_client(&self, network_name: &NetworkName) -> Option<&T> { + pub fn get_client(&self, network_name: &NetworkName) -> Option<&C> { self.client_map.get(network_name) } /// Returns the [`Client`] corresponding to the [`NetworkName`] on the given ['IotaDID']. - fn get_client_for_did(&self, did: &IotaDID) -> Result<&T> { + fn get_client_for_did(&self, did: &IotaDID) -> Result<&C> { self.get_client(&did.network()?.name()).ok_or_else(|| { Error::DIDNotFound(format!( "DID network '{}' does not match any resolver client network", @@ -72,13 +73,13 @@ where /// Fetches the [`IotaDocument`] of the given [`IotaDID`]. pub async fn resolve(&self, did: &IotaDID) -> Result { - let client: &Client = self.get_client_for_did(did)?.as_ref(); + let client: &Client = self.get_client_for_did(did)?.deref(); client.read_document(did).await } /// Fetches the [`DocumentHistory`] of the given [`IotaDID`]. pub async fn resolve_history(&self, did: &IotaDID) -> Result { - let client: &Client = self.get_client_for_did(did)?.as_ref(); + let client: &Client = self.get_client_for_did(did)?.deref(); client.resolve_history(did).await } @@ -87,7 +88,7 @@ where /// /// NOTE: the document must have been published to the Tangle and have a valid message id. pub async fn resolve_diff_history(&self, document: &ResolvedIotaDocument) -> Result> { - let client: &Client = self.get_client_for_did(document.document.id())?.as_ref(); + let client: &Client = self.get_client_for_did(document.document.id())?.deref(); client.resolve_diff_history(document).await } @@ -133,22 +134,22 @@ where /// Builder for configuring [`Clients`][Client] when constructing a [`Resolver`]. #[derive(Default)] -pub struct ResolverBuilder> +pub struct ResolverBuilder> where - T: Clone + AsRef + From, + C: SharedPtr, { - clients: HashMap>, + clients: HashMap>, } #[allow(clippy::large_enum_variant)] -enum ClientOrBuilder { - Client(T), +enum ClientOrBuilder { + Client(C), Builder(ClientBuilder), } -impl ResolverBuilder +impl ResolverBuilder where - T: Clone + AsRef + From, + C: SharedPtr, { /// Constructs a new [`ResolverBuilder`] with no [`Clients`][Client] configured. pub fn new() -> Self { @@ -161,10 +162,10 @@ where /// /// NOTE: replaces any previous [`Client`] or [`ClientBuilder`] with the same [`NetworkName`]. #[must_use] - pub fn client(mut self, client: T) -> Self { + pub fn client(mut self, client: C) -> Self { self .clients - .insert(client.as_ref().network.name(), ClientOrBuilder::Client(client)); + .insert(client.deref().network.name(), ClientOrBuilder::Client(client)); self } @@ -179,12 +180,12 @@ where } /// Constructs a new [`Resolver`] based on the builder configuration. - pub async fn build(self) -> Result> { - let mut client_map: HashMap = HashMap::new(); + pub async fn build(self) -> Result> { + let mut client_map: HashMap = HashMap::new(); for (network_name, client_or_builder) in self.clients { - let client: T = match client_or_builder { + let client: C = match client_or_builder { ClientOrBuilder::Client(client) => client, - ClientOrBuilder::Builder(builder) => T::from(builder.build().await?), + ClientOrBuilder::Builder(builder) => C::from(builder.build().await?), }; client_map.insert(network_name, client); } diff --git a/identity-iota/src/tangle/traits.rs b/identity-iota/src/tangle/traits.rs index f32305b650..6c753872a1 100644 --- a/identity-iota/src/tangle/traits.rs +++ b/identity-iota/src/tangle/traits.rs @@ -1,9 +1,14 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 +use std::ops::Deref; +use std::rc::Rc; +use std::sync::Arc; + use crate::did::IotaDID; use crate::document::ResolvedIotaDocument; use crate::error::Result; +use crate::tangle::traits::private::Sealed; use crate::tangle::MessageId; pub trait TangleRef { @@ -23,3 +28,24 @@ pub trait TangleRef { pub trait TangleResolve { async fn resolve(&self, did: &IotaDID) -> Result; } + +// Replace by higher-kinded type when `CoerceUnsized` is stabilized, otherwise we cannot +// support unsized types like dynamic traits. +// See: https://github.com/iotaledger/identity.rs/pull/707 +/// Sealed trait to generalize over `Arc` and `Rc` for sized types. +pub trait SharedPtr: Clone + From + Deref + Sealed {} + +impl SharedPtr for Rc {} + +impl SharedPtr for Arc {} + +mod private { + use std::rc::Rc; + use std::sync::Arc; + + pub trait Sealed {} + + impl Sealed for Rc {} + + impl Sealed for Arc {} +}