From 3bd1819e03004c0b4f9e60072c9ac76b98c754ce Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 26 Aug 2021 16:52:53 +0200 Subject: [PATCH 01/25] Impl basic `account.new_command` idea --- examples/Cargo.toml | 102 +++++++++++----------- examples/account/methods.rs | 108 +++++++++++++++--------- identity-account/src/account/account.rs | 25 ++++++ identity-account/src/events/command.rs | 6 +- identity-account/src/events/macros.rs | 37 +++++--- identity-iota/Cargo.toml | 2 +- 6 files changed, 171 insertions(+), 109 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 323c810625..66592b9d67 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -10,74 +10,74 @@ pretty_env_logger = { version = "0.4" } rand = { version = "0.8" } tokio = { version = "1.5", features = ["full"] } -[[example]] -name = "getting_started" -path = "getting_started.rs" +# [[example]] +# name = "getting_started" +# path = "getting_started.rs" -[[example]] -name = "account_basic" -path = "account/basic.rs" +# [[example]] +# name = "account_basic" +# path = "account/basic.rs" -[[example]] -name = "account_config" -path = "account/config.rs" +# [[example]] +# name = "account_config" +# path = "account/config.rs" [[example]] name = "account_methods" path = "account/methods.rs" -[[example]] -name = "account_services" -path = "account/services.rs" +# [[example]] +# name = "account_services" +# path = "account/services.rs" -[[example]] -name = "account_signing" -path = "account/signing.rs" +# [[example]] +# name = "account_signing" +# path = "account/signing.rs" -[[example]] -name = "account_stronghold" -path = "account/stronghold.rs" +# [[example]] +# name = "account_stronghold" +# path = "account/stronghold.rs" -[[example]] -name = "account_private_tangle" -path = "account/private_tangle.rs" +# [[example]] +# name = "account_private_tangle" +# path = "account/private_tangle.rs" -[[example]] -name = "create_did" -path = "low-level-api/create_did.rs" +# [[example]] +# name = "create_did" +# path = "low-level-api/create_did.rs" -[[example]] -name = "create_vc" -path = "low-level-api/create_vc.rs" +# [[example]] +# name = "create_vc" +# path = "low-level-api/create_vc.rs" -[[example]] -name = "create_vp" -path = "low-level-api/create_vp.rs" +# [[example]] +# name = "create_vp" +# path = "low-level-api/create_vp.rs" -[[example]] -name = "diff_chain" -path = "low-level-api/diff_chain.rs" +# [[example]] +# name = "diff_chain" +# path = "low-level-api/diff_chain.rs" -[[example]] -name = "resolve_history" -path = "low-level-api/resolve_history.rs" +# [[example]] +# name = "resolve_history" +# path = "low-level-api/resolve_history.rs" -[[example]] -name = "manipulate_did" -path = "low-level-api/manipulate_did.rs" +# [[example]] +# name = "manipulate_did" +# path = "low-level-api/manipulate_did.rs" -[[example]] -name = "merkle_key" -path = "low-level-api/merkle_key.rs" +# [[example]] +# name = "merkle_key" +# path = "low-level-api/merkle_key.rs" -[[example]] -name = "resolution" -path = "low-level-api/resolution.rs" +# [[example]] +# name = "resolution" +# path = "low-level-api/resolution.rs" -[[example]] -name = "private_tangle" -path = "low-level-api/private_tangle.rs" +# [[example]] +# name = "private_tangle" +# path = "low-level-api/private_tangle.rs" -[[example]] -name = "revoke_vc" -path = "low-level-api/revoke_vc.rs" +# [[example]] +# name = "revoke_vc" +# path = "low-level-api/revoke_vc.rs" diff --git a/examples/account/methods.rs b/examples/account/methods.rs index cb8234f629..b291a6487d 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -22,63 +22,87 @@ async fn main() -> Result<()> { let snapshot: IdentitySnapshot = account.create_identity(IdentityCreate::default()).await?; // Retrieve the DID from the newly created Identity state. - let document: &IotaDID = snapshot.identity().try_did()?; + let did: &IotaDID = snapshot.identity().try_did()?; // Add a new Ed25519 (defualt) verification method to the identity - the // verification method is included as an embedded authentication method. - let command: Command = Command::create_method() + // let command: Command = Command::create_method() + // .scope(MethodScope::Authentication) + // .fragment("my-auth-key") + // .finish()?; + + account + .new_update() + .create_method() .scope(MethodScope::Authentication) .fragment("my-auth-key") - .finish()?; - - // Process the command and update the identity state. - account.update_identity(document, command).await?; - - // Fetch and log the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - println!( - "[Example] Tangle Document (1) = {:#?}", - account.resolve_identity(document).await? - ); - - // Add another Ed25519 verification method to the identity - let command: Command = Command::create_method().fragment("my-next-key").finish()?; - - // Process the command and update the identity state. - account.update_identity(document, command).await?; + .apply(did) + .await?; - // Associate the newly created method with additional verification relationships - let command: Command = Command::attach_method() - .fragment("my-next-key") - .scope(MethodScope::CapabilityDelegation) - .scope(MethodScope::CapabilityInvocation) + let command1 = account + .new_update() + .create_method() + .scope(MethodScope::Authentication) + .fragment("my-auth-key") .finish()?; - // Process the command and update the identity state. - account.update_identity(document, command).await?; - - // Fetch and log the DID Document from the Tangle - // - // This is an optional step to ensure DID Document consistency. - println!( - "[Example] Tangle Document (2) = {:#?}", - account.resolve_identity(document).await? - ); + let command2 = account + .new_update() + .create_method() + .scope(MethodScope::Authentication) + .fragment("my-auth-key") + .finish()?; - // Remove the original Ed25519 verification method - let command: Command = Command::delete_method().fragment("my-auth-key").finish()?; + account.batch_update(did, &[command1, command2]).await?; // Process the command and update the identity state. - account.update_identity(document, command).await?; + // account.update_identity(document, command).await?; // Fetch and log the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. - println!( - "[Example] Tangle Document (3) = {:#?}", - account.resolve_identity(document).await? - ); + // println!( + // "[Example] Tangle Document (1) = {:#?}", + // account.resolve_identity(document).await? + // ); + + // // Add another Ed25519 verification method to the identity + // let command: Command = Command::create_method().fragment("my-next-key").finish()?; + + // // Process the command and update the identity state. + // account.update_identity(document, command).await?; + + // // Associate the newly created method with additional verification relationships + // let command: Command = Command::attach_method() + // .fragment("my-next-key") + // .scope(MethodScope::CapabilityDelegation) + // .scope(MethodScope::CapabilityInvocation) + // .finish()?; + + // // Process the command and update the identity state. + // account.update_identity(document, command).await?; + + // // Fetch and log the DID Document from the Tangle + // // + // // This is an optional step to ensure DID Document consistency. + // println!( + // "[Example] Tangle Document (2) = {:#?}", + // account.resolve_identity(document).await? + // ); + + // // Remove the original Ed25519 verification method + // let command: Command = Command::delete_method().fragment("my-auth-key").finish()?; + + // // Process the command and update the identity state. + // account.update_identity(document, command).await?; + + // // Fetch and log the DID Document from the Tangle + // // + // // This is an optional step to ensure DID Document consistency. + // println!( + // "[Example] Tangle Document (3) = {:#?}", + // account.resolve_identity(document).await? + // ); Ok(()) } diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index e9fc62cbc3..ca1d2421c7 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -27,6 +27,7 @@ use crate::error::Result; use crate::events::Command; use crate::events::Commit; use crate::events::Context; +use crate::events::CreateMethodBuilder; use crate::events::Event; use crate::events::EventData; use crate::identity::IdentityCreate; @@ -43,6 +44,17 @@ use crate::types::KeyLocation; const OSC: Ordering = Ordering::SeqCst; +pub struct Updater<'account, K: IdentityKey> { + account: &'account Account, + key: K, +} + +impl<'account, K: IdentityKey> Updater<'account, K> { + pub fn create_method(self) -> CreateMethodBuilder<'account, K> { + CreateMethodBuilder::new(self.account, self.key) + } +} + #[derive(Debug)] pub struct Account { config: Arc, @@ -52,6 +64,19 @@ pub struct Account { } impl Account { + pub fn new_update<'account, K: IdentityKey>(&'account self, key: K) -> Updater<'account, K> { + Updater { account: &self, key } + } + + pub async fn batch_update(&self, updates: &[Command]) -> Result<()> { + + // for update in updates { + // self.update_identity(key, update)?; + // } + + Ok(()) + } + /// Creates a new [AccountBuilder]. pub fn builder() -> AccountBuilder { AccountBuilder::new() diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index 3f7fdda29e..4999ecd3c6 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -21,6 +21,8 @@ use crate::identity::TinyService; use crate::storage::Storage; use crate::types::Generation; use crate::types::KeyLocation; +use crate::account::Account; +use crate::identity::IdentityKey; // Supported authentication method types. const AUTH_TYPES: &[MethodType] = &[MethodType::Ed25519VerificationKey2018]; @@ -249,7 +251,7 @@ impl_command_builder!(AttachMethod { @default scopes Vec, }); -impl AttachMethodBuilder { +impl<'account, K: IdentityKey> AttachMethodBuilder<'account, K> { pub fn scope(mut self, value: MethodScope) -> Self { self.scopes.get_or_insert_with(Default::default).push(value); self @@ -261,7 +263,7 @@ impl_command_builder!(DetachMethod { @default scopes Vec, }); -impl DetachMethodBuilder { +impl<'account, K: IdentityKey> DetachMethodBuilder<'account, K> { pub fn scope(mut self, value: MethodScope) -> Self { self.scopes.get_or_insert_with(Default::default).push(value); self diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index 2bef112131..5a2ebaa597 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -32,14 +32,16 @@ macro_rules! impl_command_builder { }; ($ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { paste::paste! { - #[derive(Clone, Debug, PartialEq)] - pub struct [<$ident Builder>] { + #[derive(Clone, Debug)] + pub struct [<$ident Builder>]<'account, K: IdentityKey> { + account: &'account Account, + key: K, $( $field: Option<$ty>, )* } - impl [<$ident Builder>] { + impl<'account, K: IdentityKey> [<$ident Builder>]<'account, K> { $( pub fn $field>(mut self, value: VALUE) -> Self { self.$field = Some(value.into()); @@ -47,8 +49,10 @@ macro_rules! impl_command_builder { } )* - pub fn new() -> [<$ident Builder>] { + pub fn new(account: &'account Account, key: K) -> [<$ident Builder>]<'account, K> { [<$ident Builder>] { + account, + key, $( $field: None, )* @@ -62,19 +66,26 @@ macro_rules! impl_command_builder { )* }) } - } - impl Default for [<$ident Builder>] { - fn default() -> Self { - Self::new() + pub async fn apply(self) -> $crate::Result<()> { + let account = self.account; + let update = self.finish()?; + account.update_identity(self.key, update).await?; + Ok(()) } } - impl $crate::events::Command { - pub fn [<$ident:snake>]() -> [<$ident Builder>] { - [<$ident Builder>]::new() - } - } + // impl Default for [<$ident Builder>] { + // fn default() -> Self { + // Self::new() + // } + // } + + // impl $crate::events::Command { + // pub fn [<$ident:snake>]() -> [<$ident Builder>] { + // [<$ident Builder>]::new() + // } + // } } }; } diff --git a/identity-iota/Cargo.toml b/identity-iota/Cargo.toml index 45e6d113c0..62a53742d3 100644 --- a/identity-iota/Cargo.toml +++ b/identity-iota/Cargo.toml @@ -26,7 +26,7 @@ thiserror = { version = "1.0", default-features = false } [dependencies.iota-client] git = "https://github.com/iotaledger/iota.rs" -rev = "0a011ec37f70874358cf530d3b8e4817c69c9a4c" +rev = "cb44f65979102ce32eb0904c5dc2cebeff64d43e" default-features = false [dependencies.iota-crypto] From 4928954e5dc31c0c194121aafc4ad4a9301c5edb Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Tue, 7 Sep 2021 13:57:11 +0200 Subject: [PATCH 02/25] Fix apply impl --- examples/account/methods.rs | 20 ++------------------ identity-account/src/account/account.rs | 21 ++++++--------------- identity-account/src/events/macros.rs | 8 ++++++-- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/examples/account/methods.rs b/examples/account/methods.rs index b291a6487d..e1d195f096 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -32,29 +32,13 @@ async fn main() -> Result<()> { // .finish()?; account - .new_update() + .update_identity(did) .create_method() .scope(MethodScope::Authentication) .fragment("my-auth-key") - .apply(did) + .apply() .await?; - let command1 = account - .new_update() - .create_method() - .scope(MethodScope::Authentication) - .fragment("my-auth-key") - .finish()?; - - let command2 = account - .new_update() - .create_method() - .scope(MethodScope::Authentication) - .fragment("my-auth-key") - .finish()?; - - account.batch_update(did, &[command1, command2]).await?; - // Process the command and update the identity state. // account.update_identity(document, command).await?; diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index ca1d2421c7..a4eae0e9d5 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -44,14 +44,14 @@ use crate::types::KeyLocation; const OSC: Ordering = Ordering::SeqCst; -pub struct Updater<'account, K: IdentityKey> { +pub struct Updater<'account, K: IdentityKey + Clone> { account: &'account Account, key: K, } -impl<'account, K: IdentityKey> Updater<'account, K> { - pub fn create_method(self) -> CreateMethodBuilder<'account, K> { - CreateMethodBuilder::new(self.account, self.key) +impl<'account, K: IdentityKey + Clone> Updater<'account, K> { + pub fn create_method(&self) -> CreateMethodBuilder<'account, K> { + CreateMethodBuilder::new(self.account, self.key.clone()) } } @@ -64,19 +64,10 @@ pub struct Account { } impl Account { - pub fn new_update<'account, K: IdentityKey>(&'account self, key: K) -> Updater<'account, K> { + pub fn update_identity<'account, K: IdentityKey + Clone>(&'account self, key: K) -> Updater<'account, K> { Updater { account: &self, key } } - pub async fn batch_update(&self, updates: &[Command]) -> Result<()> { - - // for update in updates { - // self.update_identity(key, update)?; - // } - - Ok(()) - } - /// Creates a new [AccountBuilder]. pub fn builder() -> AccountBuilder { AccountBuilder::new() @@ -178,7 +169,7 @@ impl Account { } /// Updates the identity specified by the given `key` with the given `command`. - pub async fn update_identity(&self, key: K, command: Command) -> Result<()> { + pub async fn update_identity_old(&self, key: K, command: Command) -> Result<()> { let identity: IdentityId = self.try_resolve_id(key).await?; self.process(identity, command, true).await?; diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index 5a2ebaa597..fc8490ae07 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -69,8 +69,12 @@ macro_rules! impl_command_builder { pub async fn apply(self) -> $crate::Result<()> { let account = self.account; - let update = self.finish()?; - account.update_identity(self.key, update).await?; + let update = $crate::events::Command::$ident { + $( + $field: impl_command_builder!(@finish self $requirement $field $ty $(= $value)?), + )* + }; + account.update_identity_old(self.key, update).await?; Ok(()) } } From 58ad21997ac24f94ddd072a15a973faf5b2aeb91 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Tue, 7 Sep 2021 16:04:01 +0200 Subject: [PATCH 03/25] Enable multiple updates on an updater --- identity-account/src/account/account.rs | 8 ++++---- identity-account/src/events/command.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index a4eae0e9d5..b563db21d7 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -44,12 +44,12 @@ use crate::types::KeyLocation; const OSC: Ordering = Ordering::SeqCst; -pub struct Updater<'account, K: IdentityKey + Clone> { +pub struct IdentityUpdater<'account, K: IdentityKey + Clone> { account: &'account Account, key: K, } -impl<'account, K: IdentityKey + Clone> Updater<'account, K> { +impl<'account, K: IdentityKey + Clone> IdentityUpdater<'account, K> { pub fn create_method(&self) -> CreateMethodBuilder<'account, K> { CreateMethodBuilder::new(self.account, self.key.clone()) } @@ -64,8 +64,8 @@ pub struct Account { } impl Account { - pub fn update_identity<'account, K: IdentityKey + Clone>(&'account self, key: K) -> Updater<'account, K> { - Updater { account: &self, key } + pub fn update_identity<'account, K: IdentityKey + Clone>(&'account self, key: K) -> IdentityUpdater<'account, K> { + IdentityUpdater { account: &self, key } } /// Creates a new [AccountBuilder]. diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index 4999ecd3c6..e476688726 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -10,19 +10,19 @@ use identity_did::verification::MethodScope; use identity_did::verification::MethodType; use identity_iota::did::IotaDID; +use crate::account::Account; use crate::error::Result; use crate::events::CommandError; use crate::events::Context; use crate::events::Event; use crate::events::EventData; +use crate::identity::IdentityKey; use crate::identity::IdentityState; use crate::identity::TinyMethod; use crate::identity::TinyService; use crate::storage::Storage; use crate::types::Generation; use crate::types::KeyLocation; -use crate::account::Account; -use crate::identity::IdentityKey; // Supported authentication method types. const AUTH_TYPES: &[MethodType] = &[MethodType::Ed25519VerificationKey2018]; From 53373805cb4aa729533f0cec0e5f9edce4cd19b1 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 10:56:07 +0200 Subject: [PATCH 04/25] Flesh out `IdentityUpdater` impl --- examples/Cargo.toml | 66 +++++++------- examples/account/methods.rs | 85 ++++++++----------- identity-account/src/account/account.rs | 17 +--- identity-account/src/events/macros.rs | 10 +-- .../src/identity/identity_updater.rs | 25 ++++++ identity-account/src/identity/mod.rs | 2 + 6 files changed, 104 insertions(+), 101 deletions(-) create mode 100644 identity-account/src/identity/identity_updater.rs diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 66592b9d67..6f36163832 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -14,9 +14,9 @@ tokio = { version = "1.5", features = ["full"] } # name = "getting_started" # path = "getting_started.rs" -# [[example]] -# name = "account_basic" -# path = "account/basic.rs" +[[example]] +name = "account_basic" +path = "account/basic.rs" # [[example]] # name = "account_config" @@ -42,42 +42,42 @@ path = "account/methods.rs" # name = "account_private_tangle" # path = "account/private_tangle.rs" -# [[example]] -# name = "create_did" -# path = "low-level-api/create_did.rs" +[[example]] +name = "create_did" +path = "low-level-api/create_did.rs" -# [[example]] -# name = "create_vc" -# path = "low-level-api/create_vc.rs" +[[example]] +name = "create_vc" +path = "low-level-api/create_vc.rs" -# [[example]] -# name = "create_vp" -# path = "low-level-api/create_vp.rs" +[[example]] +name = "create_vp" +path = "low-level-api/create_vp.rs" -# [[example]] -# name = "diff_chain" -# path = "low-level-api/diff_chain.rs" +[[example]] +name = "diff_chain" +path = "low-level-api/diff_chain.rs" -# [[example]] -# name = "resolve_history" -# path = "low-level-api/resolve_history.rs" +[[example]] +name = "resolve_history" +path = "low-level-api/resolve_history.rs" -# [[example]] -# name = "manipulate_did" -# path = "low-level-api/manipulate_did.rs" +[[example]] +name = "manipulate_did" +path = "low-level-api/manipulate_did.rs" -# [[example]] -# name = "merkle_key" -# path = "low-level-api/merkle_key.rs" +[[example]] +name = "merkle_key" +path = "low-level-api/merkle_key.rs" -# [[example]] -# name = "resolution" -# path = "low-level-api/resolution.rs" +[[example]] +name = "resolution" +path = "low-level-api/resolution.rs" -# [[example]] -# name = "private_tangle" -# path = "low-level-api/private_tangle.rs" +[[example]] +name = "private_tangle" +path = "low-level-api/private_tangle.rs" -# [[example]] -# name = "revoke_vc" -# path = "low-level-api/revoke_vc.rs" +[[example]] +name = "revoke_vc" +path = "low-level-api/revoke_vc.rs" diff --git a/examples/account/methods.rs b/examples/account/methods.rs index e1d195f096..9c25389ba8 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -7,6 +7,7 @@ use identity::account::Account; use identity::account::Command; use identity::account::IdentityCreate; use identity::account::IdentitySnapshot; +use identity::account::IdentityUpdater; use identity::account::Result; use identity::did::MethodScope; use identity::iota::IotaDID; @@ -24,69 +25,55 @@ async fn main() -> Result<()> { // Retrieve the DID from the newly created Identity state. let did: &IotaDID = snapshot.identity().try_did()?; + // Get the updater for the given `did`, so we can run multiple updates on it. + let did_updater: IdentityUpdater<'_, _> = account.update_identity(did); + // Add a new Ed25519 (defualt) verification method to the identity - the // verification method is included as an embedded authentication method. - // let command: Command = Command::create_method() - // .scope(MethodScope::Authentication) - // .fragment("my-auth-key") - // .finish()?; - - account - .update_identity(did) + did_updater .create_method() .scope(MethodScope::Authentication) .fragment("my-auth-key") .apply() .await?; - // Process the command and update the identity state. - // account.update_identity(document, command).await?; - // Fetch and log the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. - // println!( - // "[Example] Tangle Document (1) = {:#?}", - // account.resolve_identity(document).await? - // ); - - // // Add another Ed25519 verification method to the identity - // let command: Command = Command::create_method().fragment("my-next-key").finish()?; - - // // Process the command and update the identity state. - // account.update_identity(document, command).await?; - - // // Associate the newly created method with additional verification relationships - // let command: Command = Command::attach_method() - // .fragment("my-next-key") - // .scope(MethodScope::CapabilityDelegation) - // .scope(MethodScope::CapabilityInvocation) - // .finish()?; - - // // Process the command and update the identity state. - // account.update_identity(document, command).await?; + println!( + "[Example] Tangle Document (1) = {:#?}", + account.resolve_identity(did).await? + ); + + // Add another Ed25519 verification method to the identity + did_updater.create_method().fragment("my-next-key").apply().await?; + + // Associate the newly created method with additional verification relationships + did_updater + .attach_method() + .fragment("my-next-key") + .scope(MethodScope::CapabilityDelegation) + .scope(MethodScope::CapabilityInvocation) + .finish()?; - // // Fetch and log the DID Document from the Tangle - // // - // // This is an optional step to ensure DID Document consistency. - // println!( - // "[Example] Tangle Document (2) = {:#?}", - // account.resolve_identity(document).await? - // ); - - // // Remove the original Ed25519 verification method - // let command: Command = Command::delete_method().fragment("my-auth-key").finish()?; + // Fetch and log the DID Document from the Tangle + // + // This is an optional step to ensure DID Document consistency. + println!( + "[Example] Tangle Document (2) = {:#?}", + account.resolve_identity(did).await? + ); - // // Process the command and update the identity state. - // account.update_identity(document, command).await?; + // Remove the original Ed25519 verification method + did_updater.delete_method().fragment("my-auth-key").finish()?; - // // Fetch and log the DID Document from the Tangle - // // - // // This is an optional step to ensure DID Document consistency. - // println!( - // "[Example] Tangle Document (3) = {:#?}", - // account.resolve_identity(document).await? - // ); + // Fetch and log the DID Document from the Tangle + // + // This is an optional step to ensure DID Document consistency. + println!( + "[Example] Tangle Document (3) = {:#?}", + account.resolve_identity(did).await? + ); Ok(()) } diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index b563db21d7..c7fe4c7be4 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -27,7 +27,6 @@ use crate::error::Result; use crate::events::Command; use crate::events::Commit; use crate::events::Context; -use crate::events::CreateMethodBuilder; use crate::events::Event; use crate::events::EventData; use crate::identity::IdentityCreate; @@ -37,6 +36,7 @@ use crate::identity::IdentityKey; use crate::identity::IdentitySnapshot; use crate::identity::IdentityState; use crate::identity::IdentityTag; +use crate::identity::IdentityUpdater; use crate::identity::TinyMethod; use crate::storage::Storage; use crate::types::Generation; @@ -44,17 +44,6 @@ use crate::types::KeyLocation; const OSC: Ordering = Ordering::SeqCst; -pub struct IdentityUpdater<'account, K: IdentityKey + Clone> { - account: &'account Account, - key: K, -} - -impl<'account, K: IdentityKey + Clone> IdentityUpdater<'account, K> { - pub fn create_method(&self) -> CreateMethodBuilder<'account, K> { - CreateMethodBuilder::new(self.account, self.key.clone()) - } -} - #[derive(Debug)] pub struct Account { config: Arc, @@ -64,8 +53,8 @@ pub struct Account { } impl Account { - pub fn update_identity<'account, K: IdentityKey + Clone>(&'account self, key: K) -> IdentityUpdater<'account, K> { - IdentityUpdater { account: &self, key } + pub fn update_identity(&self, key: K) -> IdentityUpdater<'_, K> { + IdentityUpdater::new(&self, key) } /// Creates a new [AccountBuilder]. diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index fc8490ae07..985a68f52b 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -85,11 +85,11 @@ macro_rules! impl_command_builder { // } // } - // impl $crate::events::Command { - // pub fn [<$ident:snake>]() -> [<$ident Builder>] { - // [<$ident Builder>]::new() - // } - // } + impl<'account, K: IdentityKey + Clone> $crate::identity::IdentityUpdater<'account, K> { + pub fn [<$ident:snake>](&self) -> [<$ident Builder>]<'account, K> { + [<$ident Builder>]::new(self.account(), self.key().clone()) + } + } } }; } diff --git a/identity-account/src/identity/identity_updater.rs b/identity-account/src/identity/identity_updater.rs new file mode 100644 index 0000000000..2b613c6b43 --- /dev/null +++ b/identity-account/src/identity/identity_updater.rs @@ -0,0 +1,25 @@ +// Copyright 2020-2021 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +use crate::account::Account; + +use super::IdentityKey; + +pub struct IdentityUpdater<'account, K: IdentityKey + Clone> { + account: &'account Account, + key: K, +} + +impl<'account, K: IdentityKey + Clone> IdentityUpdater<'account, K> { + pub fn new(account: &'account Account, key: K) -> Self { + Self { account, key } + } + + pub fn account(&self) -> &'account Account { + self.account + } + + pub fn key(&self) -> &K { + &self.key + } +} diff --git a/identity-account/src/identity/mod.rs b/identity-account/src/identity/mod.rs index d6251e8ab2..246933ce9e 100644 --- a/identity-account/src/identity/mod.rs +++ b/identity-account/src/identity/mod.rs @@ -9,6 +9,7 @@ mod identity_name; mod identity_snapshot; mod identity_state; mod identity_tag; +mod identity_updater; pub use self::identity_create::*; pub use self::identity_id::*; @@ -18,3 +19,4 @@ pub use self::identity_name::*; pub use self::identity_snapshot::*; pub use self::identity_state::*; pub use self::identity_tag::*; +pub use self::identity_updater::*; From 6a66ac3006c224b4623b95a1b947f17ac88f82cf Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 12:14:08 +0200 Subject: [PATCH 05/25] Fix command tests --- examples/account/methods.rs | 6 +- identity-account/src/account/account.rs | 23 +++--- identity-account/src/events/macros.rs | 10 +-- identity-account/tests/commands.rs | 100 +++++++++++++----------- 4 files changed, 71 insertions(+), 68 deletions(-) diff --git a/examples/account/methods.rs b/examples/account/methods.rs index 9c25389ba8..66254c28f0 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -4,7 +4,6 @@ //! cargo run --example account_methods use identity::account::Account; -use identity::account::Command; use identity::account::IdentityCreate; use identity::account::IdentitySnapshot; use identity::account::IdentityUpdater; @@ -54,7 +53,8 @@ async fn main() -> Result<()> { .fragment("my-next-key") .scope(MethodScope::CapabilityDelegation) .scope(MethodScope::CapabilityInvocation) - .finish()?; + .apply() + .await?; // Fetch and log the DID Document from the Tangle // @@ -65,7 +65,7 @@ async fn main() -> Result<()> { ); // Remove the original Ed25519 verification method - did_updater.delete_method().fragment("my-auth-key").finish()?; + did_updater.delete_method().fragment("my-auth-key").apply().await?; // Fetch and log the DID Document from the Tangle // diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index c7fe4c7be4..3b7afe2f27 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -53,10 +53,6 @@ pub struct Account { } impl Account { - pub fn update_identity(&self, key: K) -> IdentityUpdater<'_, K> { - IdentityUpdater::new(&self, key) - } - /// Creates a new [AccountBuilder]. pub fn builder() -> AccountBuilder { AccountBuilder::new() @@ -157,13 +153,8 @@ impl Account { Ok(snapshot) } - /// Updates the identity specified by the given `key` with the given `command`. - pub async fn update_identity_old(&self, key: K, command: Command) -> Result<()> { - let identity: IdentityId = self.try_resolve_id(key).await?; - - self.process(identity, command, true).await?; - - Ok(()) + pub fn update_identity(&self, key: K) -> IdentityUpdater<'_, K> { + IdentityUpdater::new(self, key) } /// Removes the identity specified by the given `key`. @@ -229,6 +220,16 @@ impl Account { // Misc. Private // =========================================================================== + #[doc(hidden)] + // Updates the identity specified by the given `key` with the given `command`. + pub async fn apply_command(&self, key: K, command: Command) -> Result<()> { + let identity: IdentityId = self.try_resolve_id(key).await?; + + self.process(identity, command, true).await?; + + Ok(()) + } + #[doc(hidden)] pub async fn process(&self, id: IdentityId, command: Command, persist: bool) -> Result<()> { // Load the latest state snapshot from storage diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index 985a68f52b..fd778e1b07 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -59,14 +59,6 @@ macro_rules! impl_command_builder { } } - pub fn finish(self) -> $crate::Result<$crate::events::Command> { - Ok($crate::events::Command::$ident { - $( - $field: impl_command_builder!(@finish self $requirement $field $ty $(= $value)?), - )* - }) - } - pub async fn apply(self) -> $crate::Result<()> { let account = self.account; let update = $crate::events::Command::$ident { @@ -74,7 +66,7 @@ macro_rules! impl_command_builder { $field: impl_command_builder!(@finish self $requirement $field $ty $(= $value)?), )* }; - account.update_identity_old(self.key, update).await?; + account.apply_command(self.key, update).await?; Ok(()) } } diff --git a/identity-account/tests/commands.rs b/identity-account/tests/commands.rs index b789ce5a38..8284e1d38c 100644 --- a/identity-account/tests/commands.rs +++ b/identity-account/tests/commands.rs @@ -13,6 +13,7 @@ use identity_account::identity::TinyMethod; use identity_account::storage::MemStore; use identity_account::types::Generation; use identity_core::common::UnixTimestamp; +use identity_did::verification::MethodScope; use identity_did::verification::MethodType; async fn new_account() -> Result { @@ -34,9 +35,12 @@ async fn test_create_identity() -> Result<()> { assert_eq!(snapshot.identity().created(), UnixTimestamp::EPOCH); assert_eq!(snapshot.identity().updated(), UnixTimestamp::EPOCH); - account - .process(identity, Command::create_identity().finish().unwrap(), false) - .await?; + let command: Command = Command::CreateIdentity { + network: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; + + account.process(identity, command, false).await?; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -61,7 +65,11 @@ async fn test_create_identity_invalid_method() -> Result<()> { assert_eq!(snapshot.sequence(), Generation::new()); for type_ in TYPES.iter().copied() { - let command: Command = Command::create_identity().authentication(type_).finish().unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + authentication: type_, + }; + let output: Result<()> = account.process(identity, command, false).await; assert!(matches!( @@ -87,10 +95,10 @@ async fn test_create_identity_already_exists() -> Result<()> { // initial snapshot version = 0 assert_eq!(snapshot.sequence(), Generation::new()); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command.clone(), false).await?; @@ -119,18 +127,18 @@ async fn test_create_method() -> Result<()> { let account: Account = new_account().await?; let identity: IdentityId = IdentityId::from_u32(1); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command, false).await?; - let command: Command = Command::create_method() - .type_(MethodType::Ed25519VerificationKey2018) - .fragment("key-1") - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + type_: MethodType::Ed25519VerificationKey2018, + fragment: "key-1".to_owned(), + }; account.process(identity, command, false).await?; @@ -156,18 +164,18 @@ async fn test_create_method_reserved_fragment() -> Result<()> { let account: Account = new_account().await?; let identity: IdentityId = IdentityId::from_u32(1); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command, false).await?; - let command: Command = Command::create_method() - .type_(MethodType::Ed25519VerificationKey2018) - .fragment("_sign-123") - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + type_: MethodType::Ed25519VerificationKey2018, + fragment: "_sign-123".to_owned(), + }; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -194,18 +202,18 @@ async fn test_create_method_duplicate_fragment() -> Result<()> { let account: Account = new_account().await?; let identity: IdentityId = IdentityId::from_u32(1); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command, false).await?; - let command: Command = Command::create_method() - .type_(MethodType::Ed25519VerificationKey2018) - .fragment("key-1") - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + type_: MethodType::Ed25519VerificationKey2018, + fragment: "key-1".to_owned(), + }; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; assert_eq!(snapshot.sequence(), Generation::from_u32(3)); @@ -233,18 +241,18 @@ async fn test_delete_method() -> Result<()> { let account: Account = new_account().await?; let identity: IdentityId = IdentityId::from_u32(1); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command, false).await?; - let command: Command = Command::create_method() - .type_(MethodType::Ed25519VerificationKey2018) - .fragment("key-1") - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + type_: MethodType::Ed25519VerificationKey2018, + fragment: "key-1".to_owned(), + }; let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; assert_eq!(snapshot.sequence(), Generation::from_u32(3)); @@ -259,7 +267,9 @@ async fn test_delete_method() -> Result<()> { assert!(snapshot.identity().methods().get("key-1").is_some()); assert!(snapshot.identity().methods().fetch("key-1").is_ok()); - let command: Command = Command::delete_method().fragment("key-1").finish().unwrap(); + let command: Command = Command::DeleteMethod { + fragment: "key-1".to_owned(), + }; account.process(identity, command, false).await?; From f2f3faf05d9a58d14da20004b822809a07720bef Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Tue, 7 Sep 2021 10:42:57 +0200 Subject: [PATCH 06/25] Rename document to did in all examples --- examples/account/basic.rs | 6 +++--- examples/account/private_tangle.rs | 6 +++--- examples/account/services.rs | 6 +++--- examples/account/signing.rs | 10 +++++----- examples/account/stronghold.rs | 8 ++++---- 5 files changed, 18 insertions(+), 18 deletions(-) diff --git a/examples/account/basic.rs b/examples/account/basic.rs index d557b83966..820acfca0e 100644 --- a/examples/account/basic.rs +++ b/examples/account/basic.rs @@ -21,7 +21,7 @@ async fn main() -> Result<()> { let snapshot: IdentitySnapshot = account.create_identity(IdentityCreate::default()).await?; // Retrieve the DID from the newly created Identity state. - let document: &IotaDID = snapshot.identity().try_did()?; + let did: &IotaDID = snapshot.identity().try_did()?; println!("[Example] Local Snapshot = {:#?}", snapshot); println!("[Example] Local Document = {:#?}", snapshot.identity().to_document()?); @@ -30,12 +30,12 @@ async fn main() -> Result<()> { // Fetch the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. - let resolved: IotaDocument = account.resolve_identity(document).await?; + let resolved: IotaDocument = account.resolve_identity(did).await?; println!("[Example] Tangle Document = {:#?}", resolved); // Delete the identity and all associated keys - account.delete_identity(document).await?; + account.delete_identity(did).await?; Ok(()) } diff --git a/examples/account/private_tangle.rs b/examples/account/private_tangle.rs index 84d5a883bc..b9a7cdc3b4 100644 --- a/examples/account/private_tangle.rs +++ b/examples/account/private_tangle.rs @@ -43,7 +43,7 @@ async fn main() -> Result<()> { let snapshot: IdentitySnapshot = account.create_identity(id_create).await?; // Retrieve the DID from the newly created Identity state. - let document: &IotaDID = snapshot.identity().try_did()?; + let did: &IotaDID = snapshot.identity().try_did()?; println!("[Example] Local Snapshot = {:#?}", snapshot); println!("[Example] Local Document = {:#?}", snapshot.identity().to_document()?); @@ -52,12 +52,12 @@ async fn main() -> Result<()> { // Fetch the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. - let resolved: IotaDocument = account.resolve_identity(document).await?; + let resolved: IotaDocument = account.resolve_identity(did).await?; println!("[Example] Tangle Document = {:#?}", resolved); // Delete the identity and all associated keys - account.delete_identity(document).await?; + account.delete_identity(did).await?; Ok(()) } diff --git a/examples/account/services.rs b/examples/account/services.rs index 0177dcdd99..b457a98dea 100644 --- a/examples/account/services.rs +++ b/examples/account/services.rs @@ -22,7 +22,7 @@ async fn main() -> Result<()> { let snapshot: IdentitySnapshot = account.create_identity(IdentityCreate::default()).await?; // Retrieve the DID from the newly created Identity state. - let document: &IotaDID = snapshot.identity().try_did()?; + let did: &IotaDID = snapshot.identity().try_did()?; let command: Command = Command::create_service() .fragment("my-service-1") @@ -31,14 +31,14 @@ async fn main() -> Result<()> { .finish()?; // Process the command and update the identity state. - account.update_identity(document, command).await?; + account.update_identity(did, command).await?; // Fetch and log the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. println!( "[Example] Tangle Document (1) = {:#?}", - account.resolve_identity(document).await? + account.resolve_identity(did).await? ); Ok(()) diff --git a/examples/account/signing.rs b/examples/account/signing.rs index 1de9e6873e..247547bf1e 100644 --- a/examples/account/signing.rs +++ b/examples/account/signing.rs @@ -28,7 +28,7 @@ async fn main() -> Result<()> { let snapshot: IdentitySnapshot = account.create_identity(IdentityCreate::default()).await?; // Retrieve the DID from the newly created Identity state. - let document: &IotaDID = snapshot.identity().try_did()?; + let did: &IotaDID = snapshot.identity().try_did()?; println!("[Example] Local Snapshot = {:#?}", snapshot); println!("[Example] Local Document = {:#?}", snapshot.identity().to_document()?); @@ -37,7 +37,7 @@ async fn main() -> Result<()> { let command: Command = Command::create_method().fragment("key-1").finish()?; // Process the command and update the identity state. - account.update_identity(document, command).await?; + account.update_identity(did, command).await?; // Create a subject DID for the recipient of a `UniversityDegree` credential. let subject_key: KeyPair = KeyPair::new_ed25519()?; @@ -54,20 +54,20 @@ async fn main() -> Result<()> { // Issue an unsigned Credential... let mut credential: Credential = Credential::builder(Default::default()) - .issuer(Url::parse(document.as_str())?) + .issuer(Url::parse(did.as_str())?) .type_("UniversityDegreeCredential") .subject(subject) .build()?; // ...and sign the Credential with the previously created Verification Method - account.sign(document, "key-1", &mut credential).await?; + account.sign(did, "key-1", &mut credential).await?; println!("[Example] Local Credential = {:#}", credential); // Fetch the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. - let resolved: IotaDocument = account.resolve_identity(document).await?; + let resolved: IotaDocument = account.resolve_identity(did).await?; println!("[Example] Tangle Document = {:#?}", resolved); diff --git a/examples/account/stronghold.rs b/examples/account/stronghold.rs index 182cc073fc..3e1d361a46 100644 --- a/examples/account/stronghold.rs +++ b/examples/account/stronghold.rs @@ -30,7 +30,7 @@ async fn main() -> Result<()> { let snapshot1: IdentitySnapshot = account.create_identity(IdentityCreate::default()).await?; // Retrieve the DID from the newly created Identity state. - let document1: &IotaDID = snapshot1.identity().try_did()?; + let did1: &IotaDID = snapshot1.identity().try_did()?; println!("[Example] Local Snapshot = {:#?}", snapshot1); println!("[Example] Local Document = {:#?}", snapshot1.identity().to_document()?); @@ -39,16 +39,16 @@ async fn main() -> Result<()> { // Fetch the DID Document from the Tangle // // This is an optional step to ensure DID Document consistency. - let resolved: IotaDocument = account.resolve_identity(document1).await?; + let resolved: IotaDocument = account.resolve_identity(did1).await?; println!("[Example] Tangle Document = {:#?}", resolved); // Create another new Identity let snapshot2: IdentitySnapshot = account.create_identity(IdentityCreate::default()).await?; - let document2: &IotaDID = snapshot2.identity().try_did()?; + let did2: &IotaDID = snapshot2.identity().try_did()?; // Anndddd delete it - account.delete_identity(document2).await?; + account.delete_identity(did2).await?; Ok(()) } From 59e0cca1318a7d36f7bdb30808cc99e17b60aaf3 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 15:16:59 +0200 Subject: [PATCH 07/25] Update examples to use new update syntax --- examples/Cargo.toml | 36 ++++++++++++++++++------------------ examples/account/services.rs | 11 +++++------ examples/account/signing.rs | 11 ++++++----- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/examples/Cargo.toml b/examples/Cargo.toml index 6f36163832..323c810625 100644 --- a/examples/Cargo.toml +++ b/examples/Cargo.toml @@ -10,37 +10,37 @@ pretty_env_logger = { version = "0.4" } rand = { version = "0.8" } tokio = { version = "1.5", features = ["full"] } -# [[example]] -# name = "getting_started" -# path = "getting_started.rs" +[[example]] +name = "getting_started" +path = "getting_started.rs" [[example]] name = "account_basic" path = "account/basic.rs" -# [[example]] -# name = "account_config" -# path = "account/config.rs" +[[example]] +name = "account_config" +path = "account/config.rs" [[example]] name = "account_methods" path = "account/methods.rs" -# [[example]] -# name = "account_services" -# path = "account/services.rs" +[[example]] +name = "account_services" +path = "account/services.rs" -# [[example]] -# name = "account_signing" -# path = "account/signing.rs" +[[example]] +name = "account_signing" +path = "account/signing.rs" -# [[example]] -# name = "account_stronghold" -# path = "account/stronghold.rs" +[[example]] +name = "account_stronghold" +path = "account/stronghold.rs" -# [[example]] -# name = "account_private_tangle" -# path = "account/private_tangle.rs" +[[example]] +name = "account_private_tangle" +path = "account/private_tangle.rs" [[example]] name = "create_did" diff --git a/examples/account/services.rs b/examples/account/services.rs index b457a98dea..fd298abafc 100644 --- a/examples/account/services.rs +++ b/examples/account/services.rs @@ -4,7 +4,6 @@ //! cargo run --example account_services use identity::account::Account; -use identity::account::Command; use identity::account::IdentityCreate; use identity::account::IdentitySnapshot; use identity::account::Result; @@ -24,14 +23,14 @@ async fn main() -> Result<()> { // Retrieve the DID from the newly created Identity state. let did: &IotaDID = snapshot.identity().try_did()?; - let command: Command = Command::create_service() + account + .update_identity(did) + .create_service() .fragment("my-service-1") .type_("MyCustomService") .endpoint(Url::parse("https://example.com")?) - .finish()?; - - // Process the command and update the identity state. - account.update_identity(did, command).await?; + .apply() + .await?; // Fetch and log the DID Document from the Tangle // diff --git a/examples/account/signing.rs b/examples/account/signing.rs index 247547bf1e..bdf0736181 100644 --- a/examples/account/signing.rs +++ b/examples/account/signing.rs @@ -4,7 +4,6 @@ //! cargo run --example account_signing use identity::account::Account; -use identity::account::Command; use identity::account::IdentityCreate; use identity::account::IdentitySnapshot; use identity::account::Result; @@ -34,10 +33,12 @@ async fn main() -> Result<()> { println!("[Example] Local Document = {:#?}", snapshot.identity().to_document()?); // Add a new Ed25519 Verification Method to the identity - let command: Command = Command::create_method().fragment("key-1").finish()?; - - // Process the command and update the identity state. - account.update_identity(did, command).await?; + account + .update_identity(did) + .create_method() + .fragment("key-1") + .apply() + .await?; // Create a subject DID for the recipient of a `UniversityDegree` credential. let subject_key: KeyPair = KeyPair::new_ed25519()?; From 4fdee7fd9a8f6c8094152887f2599485a7bd5983 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 16:13:51 +0200 Subject: [PATCH 08/25] Add documentation --- examples/account/methods.rs | 2 +- examples/account/services.rs | 1 + identity-account/src/account/account.rs | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/examples/account/methods.rs b/examples/account/methods.rs index 66254c28f0..78fdb89028 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -27,7 +27,7 @@ async fn main() -> Result<()> { // Get the updater for the given `did`, so we can run multiple updates on it. let did_updater: IdentityUpdater<'_, _> = account.update_identity(did); - // Add a new Ed25519 (defualt) verification method to the identity - the + // Add a new Ed25519 (default) verification method to the identity - the // verification method is included as an embedded authentication method. did_updater .create_method() diff --git a/examples/account/services.rs b/examples/account/services.rs index fd298abafc..cca96d07d1 100644 --- a/examples/account/services.rs +++ b/examples/account/services.rs @@ -23,6 +23,7 @@ async fn main() -> Result<()> { // Retrieve the DID from the newly created Identity state. let did: &IotaDID = snapshot.identity().try_did()?; + // Add a new service to the identity. account .update_identity(did) .create_service() diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index 3b7afe2f27..fd94588148 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -153,6 +153,10 @@ impl Account { Ok(snapshot) } + /// Return the `IdentityUpdater` for the given `key`. + /// + /// On this type, various operations can be executed + /// that modify an identity, such as creating services or methods. pub fn update_identity(&self, key: K) -> IdentityUpdater<'_, K> { IdentityUpdater::new(self, key) } From 85a3cb75b7b27deeb740f7284e29c4f9cb41bd15 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 16:17:03 +0200 Subject: [PATCH 09/25] Remove Default impl, use proper macro path for key --- identity-account/src/events/macros.rs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index fd778e1b07..fa546cb704 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -33,7 +33,7 @@ macro_rules! impl_command_builder { ($ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { paste::paste! { #[derive(Clone, Debug)] - pub struct [<$ident Builder>]<'account, K: IdentityKey> { + pub struct [<$ident Builder>]<'account, K: $crate::identity::IdentityKey> { account: &'account Account, key: K, $( @@ -41,7 +41,7 @@ macro_rules! impl_command_builder { )* } - impl<'account, K: IdentityKey> [<$ident Builder>]<'account, K> { + impl<'account, K: $crate::identity::IdentityKey> [<$ident Builder>]<'account, K> { $( pub fn $field>(mut self, value: VALUE) -> Self { self.$field = Some(value.into()); @@ -71,13 +71,7 @@ macro_rules! impl_command_builder { } } - // impl Default for [<$ident Builder>] { - // fn default() -> Self { - // Self::new() - // } - // } - - impl<'account, K: IdentityKey + Clone> $crate::identity::IdentityUpdater<'account, K> { + impl<'account, K: $crate::identity::IdentityKey + Clone> $crate::identity::IdentityUpdater<'account, K> { pub fn [<$ident:snake>](&self) -> [<$ident Builder>]<'account, K> { [<$ident Builder>]::new(self.account(), self.key().clone()) } From 128eb30e340298ee5ecb8a79e256947c38279518 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 16:24:13 +0200 Subject: [PATCH 10/25] Document `IdentityUpdater`, derive basic traits --- identity-account/src/account/account.rs | 5 ++--- identity-account/src/identity/identity_updater.rs | 9 ++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index fd94588148..40207b3f7b 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -224,9 +224,8 @@ impl Account { // Misc. Private // =========================================================================== - #[doc(hidden)] - // Updates the identity specified by the given `key` with the given `command`. - pub async fn apply_command(&self, key: K, command: Command) -> Result<()> { + /// Updates the identity specified by the given `key` with the given `command`. + pub(crate) async fn apply_command(&self, key: K, command: Command) -> Result<()> { let identity: IdentityId = self.try_resolve_id(key).await?; self.process(identity, command, true).await?; diff --git a/identity-account/src/identity/identity_updater.rs b/identity-account/src/identity/identity_updater.rs index 2b613c6b43..ccadee6d35 100644 --- a/identity-account/src/identity/identity_updater.rs +++ b/identity-account/src/identity/identity_updater.rs @@ -5,21 +5,24 @@ use crate::account::Account; use super::IdentityKey; +/// A struct created by the [`Account::update_identity`] method, that +/// allows executing various updates on the identity it was created on. +#[derive(Debug, Clone)] pub struct IdentityUpdater<'account, K: IdentityKey + Clone> { account: &'account Account, key: K, } impl<'account, K: IdentityKey + Clone> IdentityUpdater<'account, K> { - pub fn new(account: &'account Account, key: K) -> Self { + pub(crate) fn new(account: &'account Account, key: K) -> Self { Self { account, key } } - pub fn account(&self) -> &'account Account { + pub(crate) fn account(&self) -> &'account Account { self.account } - pub fn key(&self) -> &K { + pub(crate) fn key(&self) -> &K { &self.key } } From eb5b4eb0482e22941e322d9667bc78f308e4b4a8 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 16:45:30 +0200 Subject: [PATCH 11/25] Update recently added tests --- identity-account/tests/commands.rs | 75 ++++++++++++++++-------------- 1 file changed, 39 insertions(+), 36 deletions(-) diff --git a/identity-account/tests/commands.rs b/identity-account/tests/commands.rs index 3d96e33611..5324039ffe 100644 --- a/identity-account/tests/commands.rs +++ b/identity-account/tests/commands.rs @@ -334,21 +334,22 @@ async fn test_create_method_from_secret_key() -> Result<()> { let account: Account = new_account().await?; let identity: IdentityId = IdentityId::from_u32(1); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + method_secret: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command, false).await?; let keypair = KeyPair::new_ed25519()?; - let command: Command = Command::create_method() - .type_(MethodType::Ed25519VerificationKey2018) - .fragment("key-1") - .method_secret(MethodSecret::Ed25519(keypair.secret().clone())) - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + method_secret: Some(MethodSecret::Ed25519(keypair.secret().clone())), + type_: MethodType::Ed25519VerificationKey2018, + fragment: "key-1".to_owned(), + }; account.process(identity, command, false).await?; @@ -368,22 +369,23 @@ async fn test_create_method_from_invalid_secret_key() -> Result<()> { let account: Account = new_account().await?; let identity: IdentityId = IdentityId::from_u32(1); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + method_secret: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command, false).await?; let secret_bytes: Box<[u8]> = Box::new([0; 33]); let secret_key = SecretKey::from(secret_bytes); - let command: Command = Command::create_method() - .type_(MethodType::Ed25519VerificationKey2018) - .fragment("key-1") - .method_secret(MethodSecret::Ed25519(secret_key)) - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + method_secret: Some(MethodSecret::Ed25519(secret_key)), + type_: MethodType::Ed25519VerificationKey2018, + fragment: "key-1".to_owned(), + }; let err = account.process(identity, command, false).await.unwrap_err(); @@ -397,22 +399,23 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { let account: Account = new_account().await?; let identity: IdentityId = IdentityId::from_u32(1); - let command: Command = Command::create_identity() - .authentication(MethodType::Ed25519VerificationKey2018) - .finish() - .unwrap(); + let command: Command = Command::CreateIdentity { + network: None, + method_secret: None, + authentication: MethodType::Ed25519VerificationKey2018, + }; account.process(identity, command, false).await?; let secret_bytes: Box<[u8]> = Box::new([0; 32]); let secret_key = SecretKey::from(secret_bytes); - let command: Command = Command::create_method() - .type_(MethodType::MerkleKeyCollection2021) - .fragment("key-1") - .method_secret(MethodSecret::Ed25519(secret_key)) - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + method_secret: Some(MethodSecret::Ed25519(secret_key)), + type_: MethodType::MerkleKeyCollection2021, + fragment: "key-1".to_owned(), + }; let err = account.process(identity, command, false).await.unwrap_err(); @@ -420,12 +423,12 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { let key_collection = KeyCollection::new_ed25519(4).unwrap(); - let command: Command = Command::create_method() - .type_(MethodType::Ed25519VerificationKey2018) - .fragment("key-2") - .method_secret(MethodSecret::MerkleKeyCollection(key_collection)) - .finish() - .unwrap(); + let command: Command = Command::CreateMethod { + scope: MethodScope::default(), + method_secret: Some(MethodSecret::MerkleKeyCollection(key_collection)), + type_: MethodType::Ed25519VerificationKey2018, + fragment: "key-1".to_owned(), + }; let err = account.process(identity, command, false).await.unwrap_err(); From 49e6e9c460aaeaa95104856d8af8875f389ebff1 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Wed, 8 Sep 2021 16:48:06 +0200 Subject: [PATCH 12/25] Roll back name change --- identity-account/tests/commands.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity-account/tests/commands.rs b/identity-account/tests/commands.rs index 5324039ffe..3fbc25e1dc 100644 --- a/identity-account/tests/commands.rs +++ b/identity-account/tests/commands.rs @@ -427,7 +427,7 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { scope: MethodScope::default(), method_secret: Some(MethodSecret::MerkleKeyCollection(key_collection)), type_: MethodType::Ed25519VerificationKey2018, - fragment: "key-1".to_owned(), + fragment: "key-2".to_owned(), }; let err = account.process(identity, command, false).await.unwrap_err(); From b1fa8ebadedfe546cd7dec933cad36df4d4f4a4f Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 09:36:35 +0200 Subject: [PATCH 13/25] Reference `key` to get rid of `Clone` requirement --- examples/account/methods.rs | 2 +- identity-account/src/account/account.rs | 19 +++++++++++-------- identity-account/src/events/command.rs | 4 ++-- identity-account/src/events/macros.rs | 18 +++++++++--------- .../src/identity/identity_index.rs | 2 +- .../src/identity/identity_updater.rs | 18 +++++------------- 6 files changed, 29 insertions(+), 34 deletions(-) diff --git a/examples/account/methods.rs b/examples/account/methods.rs index 78fdb89028..adfdd48030 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -25,7 +25,7 @@ async fn main() -> Result<()> { let did: &IotaDID = snapshot.identity().try_did()?; // Get the updater for the given `did`, so we can run multiple updates on it. - let did_updater: IdentityUpdater<'_, _> = account.update_identity(did); + let did_updater: IdentityUpdater<'_, '_, _> = account.update_identity(did); // Add a new Ed25519 (default) verification method to the identity - the // verification method is included as an embedded authentication method. diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index 5bf4ec4e66..dabd75dc68 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -111,7 +111,7 @@ impl Account { /// Finds and returns the state snapshot for the identity specified by given `key`. pub async fn find_identity(&self, key: K) -> Result> { - match self.resolve_id(key).await { + match self.resolve_id(&key).await { Some(identity) => self.load_snapshot(identity).await.map(Some), None => Ok(None), } @@ -154,11 +154,14 @@ impl Account { Ok(snapshot) } - /// Return the `IdentityUpdater` for the given `key`. + /// Returns the `IdentityUpdater` for the given `key`. /// /// On this type, various operations can be executed /// that modify an identity, such as creating services or methods. - pub fn update_identity(&self, key: K) -> IdentityUpdater<'_, K> { + pub fn update_identity<'account, 'key, K: IdentityKey>( + &'account self, + key: &'key K, + ) -> IdentityUpdater<'account, 'key, K> { IdentityUpdater::new(self, key) } @@ -186,7 +189,7 @@ impl Account { /// Resolves the DID Document associated with the specified `key`. pub async fn resolve_identity(&self, key: K) -> Result { - let identity: IdentityId = self.try_resolve_id(key).await?; + let identity: IdentityId = self.try_resolve_id(&key).await?; let snapshot: IdentitySnapshot = self.load_snapshot(identity).await?; let document: &IotaDID = snapshot.identity().try_did()?; @@ -200,7 +203,7 @@ impl Account { K: IdentityKey, U: Serialize + SetSignature, { - let identity: IdentityId = self.try_resolve_id(key).await?; + let identity: IdentityId = self.try_resolve_id(&key).await?; let snapshot: IdentitySnapshot = self.load_snapshot(identity).await?; let state: &IdentityState = snapshot.identity(); @@ -213,11 +216,11 @@ impl Account { Ok(()) } - async fn resolve_id(&self, key: K) -> Option { + async fn resolve_id(&self, key: &K) -> Option { self.index.read().await.get(key) } - async fn try_resolve_id(&self, key: K) -> Result { + async fn try_resolve_id(&self, key: &K) -> Result { self.resolve_id(key).await.ok_or(Error::IdentityNotFound) } @@ -226,7 +229,7 @@ impl Account { // =========================================================================== /// Updates the identity specified by the given `key` with the given `command`. - pub(crate) async fn apply_command(&self, key: K, command: Command) -> Result<()> { + pub(crate) async fn apply_command(&self, key: &K, command: Command) -> Result<()> { let identity: IdentityId = self.try_resolve_id(key).await?; self.process(identity, command, true).await?; diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index 1632f88d5d..6f527334e7 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -313,7 +313,7 @@ impl_command_builder!(AttachMethod { @default scopes Vec, }); -impl<'account, K: IdentityKey> AttachMethodBuilder<'account, K> { +impl<'account, 'key, K: IdentityKey> AttachMethodBuilder<'account, 'key, K> { pub fn scope(mut self, value: MethodScope) -> Self { self.scopes.get_or_insert_with(Default::default).push(value); self @@ -325,7 +325,7 @@ impl_command_builder!(DetachMethod { @default scopes Vec, }); -impl<'account, K: IdentityKey> DetachMethodBuilder<'account, K> { +impl<'account, 'key, K: IdentityKey> DetachMethodBuilder<'account, 'key, K> { pub fn scope(mut self, value: MethodScope) -> Self { self.scopes.get_or_insert_with(Default::default).push(value); self diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index fa546cb704..6947177c3d 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -33,15 +33,15 @@ macro_rules! impl_command_builder { ($ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { paste::paste! { #[derive(Clone, Debug)] - pub struct [<$ident Builder>]<'account, K: $crate::identity::IdentityKey> { + pub struct [<$ident Builder>]<'account, 'key, K: $crate::identity::IdentityKey> { account: &'account Account, - key: K, + key: &'key K, $( $field: Option<$ty>, )* } - impl<'account, K: $crate::identity::IdentityKey> [<$ident Builder>]<'account, K> { + impl<'account, 'key, K: $crate::identity::IdentityKey> [<$ident Builder>]<'account, 'key, K> { $( pub fn $field>(mut self, value: VALUE) -> Self { self.$field = Some(value.into()); @@ -49,7 +49,7 @@ macro_rules! impl_command_builder { } )* - pub fn new(account: &'account Account, key: K) -> [<$ident Builder>]<'account, K> { + pub fn new(account: &'account Account, key: &'key K) -> [<$ident Builder>]<'account, 'key, K> { [<$ident Builder>] { account, key, @@ -60,20 +60,20 @@ macro_rules! impl_command_builder { } pub async fn apply(self) -> $crate::Result<()> { - let account = self.account; let update = $crate::events::Command::$ident { $( $field: impl_command_builder!(@finish self $requirement $field $ty $(= $value)?), )* }; - account.apply_command(self.key, update).await?; + + self.account.apply_command(self.key, update).await?; Ok(()) } } - impl<'account, K: $crate::identity::IdentityKey + Clone> $crate::identity::IdentityUpdater<'account, K> { - pub fn [<$ident:snake>](&self) -> [<$ident Builder>]<'account, K> { - [<$ident Builder>]::new(self.account(), self.key().clone()) + impl<'account, 'key, K: $crate::identity::IdentityKey + Clone> $crate::identity::IdentityUpdater<'account, 'key, K> { + pub fn [<$ident:snake>](&self) -> [<$ident Builder>]<'account, 'key, K> { + [<$ident Builder>]::new(self.account, self.key) } } } diff --git a/identity-account/src/identity/identity_index.rs b/identity-account/src/identity/identity_index.rs index 1358ffe431..f4fa53289d 100644 --- a/identity-account/src/identity/identity_index.rs +++ b/identity-account/src/identity/identity_index.rs @@ -39,7 +39,7 @@ impl IdentityIndex { } /// Returns the id of the identity matching the given `key`. - pub fn get(&self, key: K) -> Option { + pub fn get(&self, key: &K) -> Option { key.scan(self.data.iter()) } diff --git a/identity-account/src/identity/identity_updater.rs b/identity-account/src/identity/identity_updater.rs index ccadee6d35..e5e9f9f6fb 100644 --- a/identity-account/src/identity/identity_updater.rs +++ b/identity-account/src/identity/identity_updater.rs @@ -8,21 +8,13 @@ use super::IdentityKey; /// A struct created by the [`Account::update_identity`] method, that /// allows executing various updates on the identity it was created on. #[derive(Debug, Clone)] -pub struct IdentityUpdater<'account, K: IdentityKey + Clone> { - account: &'account Account, - key: K, +pub struct IdentityUpdater<'account, 'key, K: IdentityKey> { + pub(crate) account: &'account Account, + pub(crate) key: &'key K, } -impl<'account, K: IdentityKey + Clone> IdentityUpdater<'account, K> { - pub(crate) fn new(account: &'account Account, key: K) -> Self { +impl<'account, 'key, K: IdentityKey> IdentityUpdater<'account, 'key, K> { + pub(crate) fn new(account: &'account Account, key: &'key K) -> Self { Self { account, key } } - - pub(crate) fn account(&self) -> &'account Account { - self.account - } - - pub(crate) fn key(&self) -> &K { - &self.key - } } From 5b624ec6928ea1caae96a25222a6a65da0195e92 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 09:46:41 +0200 Subject: [PATCH 14/25] Remove `CreateIdentityBuilder` --- identity-account/src/events/command.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index 6f527334e7..f00d05b26d 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -291,12 +291,6 @@ async fn insert_method_secret( // Command Builders // ============================================================================= -impl_command_builder!(CreateIdentity { - @optional network String, - @optional method_secret MethodSecret, - @defaulte authentication MethodType = Ed25519VerificationKey2018, -}); - impl_command_builder!(CreateMethod { @defaulte type_ MethodType = Ed25519VerificationKey2018, @default scope MethodScope, From 63e9fd72e5a95fab0de4f7076c234e65ecc23685 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 09:48:53 +0200 Subject: [PATCH 15/25] Call `update_identity` on each update --- examples/account/methods.rs | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/examples/account/methods.rs b/examples/account/methods.rs index adfdd48030..417f2fd093 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -24,12 +24,10 @@ async fn main() -> Result<()> { // Retrieve the DID from the newly created Identity state. let did: &IotaDID = snapshot.identity().try_did()?; - // Get the updater for the given `did`, so we can run multiple updates on it. - let did_updater: IdentityUpdater<'_, '_, _> = account.update_identity(did); - // Add a new Ed25519 (default) verification method to the identity - the // verification method is included as an embedded authentication method. - did_updater + account + .update_identity(did) .create_method() .scope(MethodScope::Authentication) .fragment("my-auth-key") @@ -45,10 +43,16 @@ async fn main() -> Result<()> { ); // Add another Ed25519 verification method to the identity - did_updater.create_method().fragment("my-next-key").apply().await?; + account + .update_identity(did) + .create_method() + .fragment("my-next-key") + .apply() + .await?; // Associate the newly created method with additional verification relationships - did_updater + account + .update_identity(did) .attach_method() .fragment("my-next-key") .scope(MethodScope::CapabilityDelegation) @@ -65,7 +69,12 @@ async fn main() -> Result<()> { ); // Remove the original Ed25519 verification method - did_updater.delete_method().fragment("my-auth-key").apply().await?; + account + .update_identity(did) + .delete_method() + .fragment("my-auth-key") + .apply() + .await?; // Fetch and log the DID Document from the Tangle // From 59ab68e92afee32276d7f7d02a13f659c25aa409 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 10:04:46 +0200 Subject: [PATCH 16/25] Move tests inside the account crate --- examples/account/methods.rs | 1 - identity-account/src/account/account.rs | 3 +-- identity-account/src/events/command.rs | 4 +-- identity-account/src/lib.rs | 2 ++ identity-account/{ => src}/tests/commands.rs | 26 ++++++++++---------- identity-account/src/tests/mod.rs | 4 +++ 6 files changed, 22 insertions(+), 18 deletions(-) rename identity-account/{ => src}/tests/commands.rs (96%) create mode 100644 identity-account/src/tests/mod.rs diff --git a/examples/account/methods.rs b/examples/account/methods.rs index 417f2fd093..4b22ea29f5 100644 --- a/examples/account/methods.rs +++ b/examples/account/methods.rs @@ -6,7 +6,6 @@ use identity::account::Account; use identity::account::IdentityCreate; use identity::account::IdentitySnapshot; -use identity::account::IdentityUpdater; use identity::account::Result; use identity::did::MethodScope; use identity::iota::IotaDID; diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index dabd75dc68..35a618db73 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -237,8 +237,7 @@ impl Account { Ok(()) } - #[doc(hidden)] - pub async fn process(&self, id: IdentityId, command: Command, persist: bool) -> Result<()> { + pub(crate) async fn process(&self, id: IdentityId, command: Command, persist: bool) -> Result<()> { // Load the latest state snapshot from storage let root: IdentitySnapshot = self.load_snapshot(id).await?; diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index f00d05b26d..ac35619b45 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -31,7 +31,7 @@ use crate::types::MethodSecret; const AUTH_TYPES: &[MethodType] = &[MethodType::Ed25519VerificationKey2018]; #[derive(Clone, Debug)] -pub enum Command { +pub(crate) enum Command { CreateIdentity { network: Option, method_secret: Option, @@ -66,7 +66,7 @@ pub enum Command { } impl Command { - pub async fn process(self, context: Context<'_>) -> Result>> { + pub(crate) async fn process(self, context: Context<'_>) -> Result>> { let state: &IdentityState = context.state(); let store: &dyn Storage = context.store(); diff --git a/identity-account/src/lib.rs b/identity-account/src/lib.rs index f3416f5d78..200829ca78 100644 --- a/identity-account/src/lib.rs +++ b/identity-account/src/lib.rs @@ -28,6 +28,8 @@ pub mod identity; pub mod storage; #[cfg(feature = "stronghold")] pub mod stronghold; +#[cfg(test)] +mod tests; pub mod types; pub mod utils; diff --git a/identity-account/tests/commands.rs b/identity-account/src/tests/commands.rs similarity index 96% rename from identity-account/tests/commands.rs rename to identity-account/src/tests/commands.rs index 3fbc25e1dc..7b09e95ba0 100644 --- a/identity-account/tests/commands.rs +++ b/identity-account/src/tests/commands.rs @@ -1,19 +1,19 @@ // Copyright 2020-2021 IOTA Stiftung // SPDX-License-Identifier: Apache-2.0 -use identity_account::account::Account; -use identity_account::account::Config; -use identity_account::error::Error; -use identity_account::error::Result; -use identity_account::events::Command; -use identity_account::events::CommandError; -use identity_account::identity::IdentityCreate; -use identity_account::identity::IdentityId; -use identity_account::identity::IdentitySnapshot; -use identity_account::identity::TinyMethod; -use identity_account::storage::MemStore; -use identity_account::types::Generation; -use identity_account::types::MethodSecret; +use crate::account::Account; +use crate::account::Config; +use crate::error::Error; +use crate::error::Result; +use crate::events::Command; +use crate::events::CommandError; +use crate::identity::IdentityCreate; +use crate::identity::IdentityId; +use crate::identity::IdentitySnapshot; +use crate::identity::TinyMethod; +use crate::storage::MemStore; +use crate::types::Generation; +use crate::types::MethodSecret; use identity_core::common::UnixTimestamp; use identity_core::crypto::KeyCollection; use identity_core::crypto::KeyPair; diff --git a/identity-account/src/tests/mod.rs b/identity-account/src/tests/mod.rs new file mode 100644 index 0000000000..c5662c68d4 --- /dev/null +++ b/identity-account/src/tests/mod.rs @@ -0,0 +1,4 @@ +// Copyright 2020-2021 IOTA Stiftung +// SPDX-License-Identifier: Apache-2.0 + +mod commands; From eb5f2c57905c717f832c3a022d3245d10a5381fc Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 11:00:39 +0200 Subject: [PATCH 17/25] Document `IdentityUpdater` methods --- identity-account/src/events/command.rs | 50 ++++++++++++++++++++++---- identity-account/src/events/macros.rs | 7 ++-- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index ac35619b45..c1a52d33d1 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -291,18 +291,37 @@ async fn insert_method_secret( // Command Builders // ============================================================================= -impl_command_builder!(CreateMethod { +impl_command_builder!( +/// Create a new method on an identity. +/// +/// # Parameters +/// - `type_`: the type of the method, defaults to [`MethodType::Ed25519VerificationKey2018`]. +/// - `scope`: the scope of the method, defaults to [`MethodScope::default`]. +/// - `fragment`: the identifier of the method in the document, required. +/// - `method_secret`: the secret key to use for the method, optional. Will be generated when omitted. +CreateMethod { @defaulte type_ MethodType = Ed25519VerificationKey2018, @default scope MethodScope, @required fragment String, @optional method_secret MethodSecret }); -impl_command_builder!(DeleteMethod { +impl_command_builder!( +/// Delete a method on an identity. +/// +/// # Parameters +/// - `fragment`: the identifier of the method in the document, required. +DeleteMethod { @required fragment String, }); -impl_command_builder!(AttachMethod { +impl_command_builder!( +/// Attach one or more verification relationships to a method on an identity. +/// +/// # Parameters +/// - `scopes`: the scopes to add, defaults to an empty [`Vec`]. +/// - `fragment`: the identifier of the method in the document, required. +AttachMethod { @required fragment String, @default scopes Vec, }); @@ -314,7 +333,13 @@ impl<'account, 'key, K: IdentityKey> AttachMethodBuilder<'account, 'key, K> { } } -impl_command_builder!(DetachMethod { +impl_command_builder!( +/// Detaches one or more verification relationships from a method on an identity. +/// +/// # Parameters +/// - `scopes`: the scopes to remove, defaults to an empty [`Vec`]. +/// - `fragment`: the identifier of the method in the document, required. +DetachMethod { @required fragment String, @default scopes Vec, }); @@ -326,13 +351,26 @@ impl<'account, 'key, K: IdentityKey> DetachMethodBuilder<'account, 'key, K> { } } -impl_command_builder!(CreateService { +impl_command_builder!( +/// Create a new service on an identity. +/// +/// # Parameters +/// - `type_`: the type of the service, e.g. `"LinkedDomains"`, required. +/// - `fragment`: the identifier of the service in the document, required. +/// - `endpoint`: the url of the service, required. +/// - `properties`: additional properties of the service, optional. +CreateService { @required fragment String, @required type_ String, @required endpoint Url, @optional properties Object, }); -impl_command_builder!(DeleteService { +impl_command_builder!( +/// Delete a service on an identity. +/// +/// # Parameters +/// - `fragment`: the identifier of the service in the document, required. +DeleteService { @required fragment String, }); diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index 6947177c3d..844c1c5861 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -30,8 +30,9 @@ macro_rules! impl_command_builder { )), } }; - ($ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { + ($(#[$meta:meta])* $ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { paste::paste! { + $(#[$meta])* #[derive(Clone, Debug)] pub struct [<$ident Builder>]<'account, 'key, K: $crate::identity::IdentityKey> { account: &'account Account, @@ -66,11 +67,11 @@ macro_rules! impl_command_builder { )* }; - self.account.apply_command(self.key, update).await?; - Ok(()) + self.account.apply_command(self.key, update).await } } + /// Creates a new builder to modify the identity. See the documentation of the return type for details. impl<'account, 'key, K: $crate::identity::IdentityKey + Clone> $crate::identity::IdentityUpdater<'account, 'key, K> { pub fn [<$ident:snake>](&self) -> [<$ident Builder>]<'account, 'key, K> { [<$ident Builder>]::new(self.account, self.key) From f6d692021058e0537375065157696363f5c8695c Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 11:11:07 +0200 Subject: [PATCH 18/25] Rename `CommandError` -> `UpdateError` Avoids having `Command` in the public interface. --- identity-account/src/error.rs | 2 +- identity-account/src/events/command.rs | 51 ++++++++++++-------------- identity-account/src/events/error.rs | 4 +- identity-account/src/tests/commands.rs | 18 ++++----- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/identity-account/src/error.rs b/identity-account/src/error.rs index ce131b4f48..5111f0ac95 100644 --- a/identity-account/src/error.rs +++ b/identity-account/src/error.rs @@ -90,7 +90,7 @@ pub enum Error { ServiceNotFound, /// Caused by attempting to perform a command in an invalid context. #[error("Command Error: {0}")] - CommandError(#[from] crate::events::CommandError), + CommandError(#[from] crate::events::UpdateError), #[error("Invalid Secret Key: {0}")] InvalidSecretKey(String), } diff --git a/identity-account/src/events/command.rs b/identity-account/src/events/command.rs index c1a52d33d1..439b8cca6f 100644 --- a/identity-account/src/events/command.rs +++ b/identity-account/src/events/command.rs @@ -13,10 +13,10 @@ use identity_iota::did::IotaDID; use crate::account::Account; use crate::error::Result; -use crate::events::CommandError; use crate::events::Context; use crate::events::Event; use crate::events::EventData; +use crate::events::UpdateError; use crate::identity::IdentityId; use crate::identity::IdentityKey; use crate::identity::IdentityState; @@ -81,12 +81,12 @@ impl Command { authentication, } => { // The state must not be initialized - ensure!(state.did().is_none(), CommandError::DocumentAlreadyExists); + ensure!(state.did().is_none(), UpdateError::DocumentAlreadyExists); // The authentication method type must be valid ensure!( AUTH_TYPES.contains(&authentication), - CommandError::InvalidMethodType(authentication) + UpdateError::InvalidMethodType(authentication) ); let generation: Generation = state.auth_generation(); @@ -96,7 +96,7 @@ impl Command { // TODO: config: strict ensure!( !store.key_exists(state.id(), &location).await?, - CommandError::DuplicateKeyLocation(location) + UpdateError::DuplicateKeyLocation(location) ); let public: PublicKey = if let Some(method_secret_key) = method_secret { @@ -125,27 +125,27 @@ impl Command { method_secret, } => { // The state must be initialized - ensure!(state.did().is_some(), CommandError::DocumentNotFound); + ensure!(state.did().is_some(), UpdateError::DocumentNotFound); let location: KeyLocation = state.key_location(type_, fragment)?; // The key location must not be an authentication location ensure!( !location.is_authentication(), - CommandError::InvalidMethodFragment("reserved") + UpdateError::InvalidMethodFragment("reserved") ); // The key location must be available // TODO: config: strict ensure!( !store.key_exists(state.id(), &location).await?, - CommandError::DuplicateKeyLocation(location) + UpdateError::DuplicateKeyLocation(location) ); // The verification method must not exist ensure!( !state.methods().contains(location.fragment()), - CommandError::DuplicateKeyFragment(location.fragment.clone()), + UpdateError::DuplicateKeyFragment(location.fragment.clone()), ); let public: PublicKey = if let Some(method_secret_key) = method_secret { @@ -161,52 +161,52 @@ impl Command { } Self::DeleteMethod { fragment } => { // The state must be initialized - ensure!(state.did().is_some(), CommandError::DocumentNotFound); + ensure!(state.did().is_some(), UpdateError::DocumentNotFound); let fragment: Fragment = Fragment::new(fragment); // The fragment must not be an authentication location ensure!( !KeyLocation::is_authentication_fragment(&fragment), - CommandError::InvalidMethodFragment("reserved") + UpdateError::InvalidMethodFragment("reserved") ); // The verification method must exist - ensure!(state.methods().contains(fragment.name()), CommandError::MethodNotFound); + ensure!(state.methods().contains(fragment.name()), UpdateError::MethodNotFound); Ok(Some(vec![Event::new(EventData::MethodDeleted(fragment))])) } Self::AttachMethod { fragment, scopes } => { // The state must be initialized - ensure!(state.did().is_some(), CommandError::DocumentNotFound); + ensure!(state.did().is_some(), UpdateError::DocumentNotFound); let fragment: Fragment = Fragment::new(fragment); // The fragment must not be an authentication location ensure!( !KeyLocation::is_authentication_fragment(&fragment), - CommandError::InvalidMethodFragment("reserved") + UpdateError::InvalidMethodFragment("reserved") ); // The verification method must exist - ensure!(state.methods().contains(fragment.name()), CommandError::MethodNotFound); + ensure!(state.methods().contains(fragment.name()), UpdateError::MethodNotFound); Ok(Some(vec![Event::new(EventData::MethodAttached(fragment, scopes))])) } Self::DetachMethod { fragment, scopes } => { // The state must be initialized - ensure!(state.did().is_some(), CommandError::DocumentNotFound); + ensure!(state.did().is_some(), UpdateError::DocumentNotFound); let fragment: Fragment = Fragment::new(fragment); // The fragment must not be an authentication location ensure!( !KeyLocation::is_authentication_fragment(&fragment), - CommandError::InvalidMethodFragment("reserved") + UpdateError::InvalidMethodFragment("reserved") ); // The verification method must exist - ensure!(state.methods().contains(fragment.name()), CommandError::MethodNotFound); + ensure!(state.methods().contains(fragment.name()), UpdateError::MethodNotFound); Ok(Some(vec![Event::new(EventData::MethodDetached(fragment, scopes))])) } @@ -217,12 +217,12 @@ impl Command { properties, } => { // The state must be initialized - ensure!(state.did().is_some(), CommandError::DocumentNotFound); + ensure!(state.did().is_some(), UpdateError::DocumentNotFound); // The service must not exist ensure!( !state.services().contains(&fragment), - CommandError::DuplicateServiceFragment(fragment), + UpdateError::DuplicateServiceFragment(fragment), ); let service: TinyService = TinyService::new(fragment, type_, endpoint, properties); @@ -231,15 +231,12 @@ impl Command { } Self::DeleteService { fragment } => { // The state must be initialized - ensure!(state.did().is_some(), CommandError::DocumentNotFound); + ensure!(state.did().is_some(), UpdateError::DocumentNotFound); let fragment: Fragment = Fragment::new(fragment); // The service must exist - ensure!( - state.services().contains(fragment.name()), - CommandError::ServiceNotFound - ); + ensure!(state.services().contains(fragment.name()), UpdateError::ServiceNotFound); Ok(Some(vec![Event::new(EventData::ServiceDeleted(fragment))])) } @@ -258,7 +255,7 @@ async fn insert_method_secret( MethodSecret::Ed25519(secret_key) => { ensure!( secret_key.as_ref().len() == ed25519::SECRET_KEY_LENGTH, - CommandError::InvalidMethodSecret(format!( + UpdateError::InvalidMethodSecret(format!( "an ed25519 secret key requires {} bytes, found {}", ed25519::SECRET_KEY_LENGTH, secret_key.as_ref().len() @@ -267,7 +264,7 @@ async fn insert_method_secret( ensure!( matches!(method_type, MethodType::Ed25519VerificationKey2018), - CommandError::InvalidMethodSecret( + UpdateError::InvalidMethodSecret( "MethodType::Ed25519VerificationKey2018 can only be used with an ed25519 method secret".to_owned(), ) ); @@ -277,7 +274,7 @@ async fn insert_method_secret( MethodSecret::MerkleKeyCollection(_) => { ensure!( matches!(method_type, MethodType::MerkleKeyCollection2021), - CommandError::InvalidMethodSecret( + UpdateError::InvalidMethodSecret( "MethodType::MerkleKeyCollection2021 can only be used with a MerkleKeyCollection method secret".to_owned(), ) ); diff --git a/identity-account/src/events/error.rs b/identity-account/src/events/error.rs index 39c3f7fbb3..5333edab80 100644 --- a/identity-account/src/events/error.rs +++ b/identity-account/src/events/error.rs @@ -6,9 +6,9 @@ use identity_did::verification::MethodType; use crate::types::KeyLocation; -/// Errors than may occur while processing a [Command][crate::events::Command]. +/// Errors than may occur while processing an update in the [`Account`][crate::account::Account]. #[derive(Debug, thiserror::Error)] -pub enum CommandError { +pub enum UpdateError { #[error("document already exists")] DocumentAlreadyExists, #[error("document not found")] diff --git a/identity-account/src/tests/commands.rs b/identity-account/src/tests/commands.rs index 7b09e95ba0..b2eb2d28df 100644 --- a/identity-account/src/tests/commands.rs +++ b/identity-account/src/tests/commands.rs @@ -6,7 +6,7 @@ use crate::account::Config; use crate::error::Error; use crate::error::Result; use crate::events::Command; -use crate::events::CommandError; +use crate::events::UpdateError; use crate::identity::IdentityCreate; use crate::identity::IdentityId; use crate::identity::IdentitySnapshot; @@ -82,7 +82,7 @@ async fn test_create_identity_invalid_method() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(CommandError::InvalidMethodType(_)) + Error::CommandError(UpdateError::InvalidMethodType(_)) )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -154,7 +154,7 @@ async fn test_create_identity_already_exists() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(CommandError::DocumentAlreadyExists), + Error::CommandError(UpdateError::DocumentAlreadyExists), )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -204,7 +204,7 @@ async fn test_create_identity_from_invalid_secret_key() -> Result<()> { let err = account.create_identity(id_create).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(CommandError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); Ok(()) } @@ -277,7 +277,7 @@ async fn test_create_method_reserved_fragment() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(CommandError::InvalidMethodFragment(_)), + Error::CommandError(UpdateError::InvalidMethodFragment(_)), )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -320,7 +320,7 @@ async fn test_create_method_duplicate_fragment() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(CommandError::DuplicateKeyFragment(_)), + Error::CommandError(UpdateError::DuplicateKeyFragment(_)), )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -389,7 +389,7 @@ async fn test_create_method_from_invalid_secret_key() -> Result<()> { let err = account.process(identity, command, false).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(CommandError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); Ok(()) } @@ -419,7 +419,7 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { let err = account.process(identity, command, false).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(CommandError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); let key_collection = KeyCollection::new_ed25519(4).unwrap(); @@ -432,7 +432,7 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { let err = account.process(identity, command, false).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(CommandError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); Ok(()) } From 236ee1f9dc168d987e981d00985f3d81ba176d8a Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 11:12:23 +0200 Subject: [PATCH 19/25] Update stray occurrence of `CommandError` --- identity-account/src/events/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index 844c1c5861..edb38a7bd2 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -26,7 +26,7 @@ macro_rules! impl_command_builder { match $this.$field { Some(value) => value, None => return Err($crate::Error::CommandError( - $crate::events::CommandError::MissingRequiredField(stringify!($field)), + $crate::events::UpdateError::MissingRequiredField(stringify!($field)), )), } }; From 80368160e34a1acba94b7911a8492b0f799821d0 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 11:19:49 +0200 Subject: [PATCH 20/25] Use better name for captured attribute in macro --- identity-account/src/events/macros.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index edb38a7bd2..31b18590c2 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -30,9 +30,9 @@ macro_rules! impl_command_builder { )), } }; - ($(#[$meta:meta])* $ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { + ($(#[$doc:meta])* $ident:ident { $(@ $requirement:ident $field:ident $ty:ty $(= $value:expr)?),* $(,)* }) => { paste::paste! { - $(#[$meta])* + $(#[$doc])* #[derive(Clone, Debug)] pub struct [<$ident Builder>]<'account, 'key, K: $crate::identity::IdentityKey> { account: &'account Account, From 77e1ab99b118915b1ad7603599c4f3dc4345e84e Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 11:30:36 +0200 Subject: [PATCH 21/25] Move lazy test inside the crate & update syntax --- examples/account/lazy.rs | 25 +++++++++------ identity-account/{ => src}/tests/lazy.rs | 40 +++++++++++++----------- identity-account/src/tests/mod.rs | 1 + 3 files changed, 39 insertions(+), 27 deletions(-) rename identity-account/{ => src}/tests/lazy.rs (81%) diff --git a/examples/account/lazy.rs b/examples/account/lazy.rs index 995275364f..5356f7f058 100644 --- a/examples/account/lazy.rs +++ b/examples/account/lazy.rs @@ -4,7 +4,6 @@ //! cargo run --example account_lazy use identity::account::Account; -use identity::account::Command; use identity::account::IdentityCreate; use identity::account::IdentitySnapshot; use identity::account::Result; @@ -27,28 +26,36 @@ async fn main() -> Result<()> { // Retrieve the DID from the newly created Identity state. let did: &IotaDID = snapshot.identity().try_did()?; - let command: Command = Command::create_service() + account + .update_identity(did) + .create_service() .fragment("example-service") .type_("LinkedDomains") .endpoint(Url::parse("https://example.org")?) - .finish()?; - account.update_identity(did, command).await?; + .apply() + .await?; // Publish the newly created DID document, // including the new service, to the tangle. account.publish_updates(did).await?; // Add another service. - let command: Command = Command::create_service() + account + .update_identity(did) + .create_service() .fragment("another-service") .type_("LinkedDomains") .endpoint(Url::parse("https://example.org")?) - .finish()?; - account.update_identity(did, command).await?; + .apply() + .await?; // Delete the previously added service. - let command: Command = Command::delete_service().fragment("example-service").finish()?; - account.update_identity(did, command).await?; + account + .update_identity(did) + .delete_service() + .fragment("example-service") + .apply() + .await?; // Publish the updates as one message to the tangle. account.publish_updates(did).await?; diff --git a/identity-account/tests/lazy.rs b/identity-account/src/tests/lazy.rs similarity index 81% rename from identity-account/tests/lazy.rs rename to identity-account/src/tests/lazy.rs index e935280fea..c64bd78cdf 100644 --- a/identity-account/tests/lazy.rs +++ b/identity-account/src/tests/lazy.rs @@ -3,11 +3,10 @@ use std::pin::Pin; +use crate::account::Account; +use crate::identity::{IdentityCreate, IdentitySnapshot, IdentityUpdater}; +use crate::{Error as AccountError, Result}; use futures::Future; -use identity_account::account::Account; -use identity_account::events::Command; -use identity_account::identity::{IdentityCreate, IdentitySnapshot}; -use identity_account::{Error as AccountError, Result}; use identity_core::common::Url; use identity_iota::chain::DocumentHistory; use identity_iota::did::{IotaDID, IotaVerificationMethod}; @@ -35,19 +34,23 @@ async fn test_lazy_updates() -> Result<()> { let did: &IotaDID = snapshot.identity().try_did()?; - let command: Command = Command::create_service() + let did_updater: IdentityUpdater<'_, '_, _> = account.update_identity(did); + + did_updater + .create_service() .fragment("my-service") - .type_("url") + .type_("LinkedDomains") .endpoint(Url::parse("https://example.org").unwrap()) - .finish()?; - account.update_identity(did, command).await?; + .apply() + .await?; - let command: Command = Command::create_service() + did_updater + .create_service() .fragment("my-other-service") - .type_("url") + .type_("LinkedDomains") .endpoint(Url::parse("https://example.org").unwrap()) - .finish()?; - account.update_identity(did, command).await?; + .apply() + .await?; account.publish_updates(did).await?; @@ -73,14 +76,15 @@ async fn test_lazy_updates() -> Result<()> { // More updates to the identity // =========================================================================== - let command: Command = Command::delete_service().fragment("my-service").finish()?; - account.update_identity(did, command).await?; + did_updater.delete_service().fragment("my-service").apply().await?; - let command: Command = Command::delete_service().fragment("my-other-service").finish()?; - account.update_identity(did, command).await?; + did_updater + .delete_service() + .fragment("my-other-service") + .apply() + .await?; - let command: Command = Command::create_method().fragment("new-method").finish()?; - account.update_identity(did, command).await?; + did_updater.create_method().fragment("new-method").apply().await?; account.publish_updates(did).await?; diff --git a/identity-account/src/tests/mod.rs b/identity-account/src/tests/mod.rs index c5662c68d4..4cc77c616c 100644 --- a/identity-account/src/tests/mod.rs +++ b/identity-account/src/tests/mod.rs @@ -2,3 +2,4 @@ // SPDX-License-Identifier: Apache-2.0 mod commands; +mod lazy; From 4c36927ce52cc7652a89cb0fafa87b0588a7e54f Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 11:50:49 +0200 Subject: [PATCH 22/25] Remove `Clone` bound --- identity-account/src/events/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index 31b18590c2..c6244b9445 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -72,7 +72,7 @@ macro_rules! impl_command_builder { } /// Creates a new builder to modify the identity. See the documentation of the return type for details. - impl<'account, 'key, K: $crate::identity::IdentityKey + Clone> $crate::identity::IdentityUpdater<'account, 'key, K> { + impl<'account, 'key, K: $crate::identity::IdentityKey> $crate::identity::IdentityUpdater<'account, 'key, K> { pub fn [<$ident:snake>](&self) -> [<$ident Builder>]<'account, 'key, K> { [<$ident Builder>]::new(self.account, self.key) } From c7bbde697ed999a0bb661c4813e8a0a13537e5f1 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 13:24:57 +0200 Subject: [PATCH 23/25] Rename `CommandError` -> `UpdateError` in account --- identity-account/src/error.rs | 6 +++--- identity-account/src/events/macros.rs | 4 ++-- identity-account/src/tests/commands.rs | 16 ++++++++-------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/identity-account/src/error.rs b/identity-account/src/error.rs index 5111f0ac95..cabd863626 100644 --- a/identity-account/src/error.rs +++ b/identity-account/src/error.rs @@ -88,9 +88,9 @@ pub enum Error { /// Caused by attempting to find a service that does not exist. #[error("Service not found")] ServiceNotFound, - /// Caused by attempting to perform a command in an invalid context. - #[error("Command Error: {0}")] - CommandError(#[from] crate::events::UpdateError), + /// Caused by attempting to perform an upate in an invalid context. + #[error("Update Error: {0}")] + UpdateError(#[from] crate::events::UpdateError), #[error("Invalid Secret Key: {0}")] InvalidSecretKey(String), } diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index c6244b9445..f90c62fc1a 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -4,7 +4,7 @@ macro_rules! ensure { ($cond:expr, $error:expr $(,)?) => { if !$cond { - return Err($crate::Error::CommandError($error)); + return Err($crate::Error::UpdateError($error)); } }; } @@ -25,7 +25,7 @@ macro_rules! impl_command_builder { (@finish $this:ident required $field:ident $ty:ty) => { match $this.$field { Some(value) => value, - None => return Err($crate::Error::CommandError( + None => return Err($crate::Error::UpdateError( $crate::events::UpdateError::MissingRequiredField(stringify!($field)), )), } diff --git a/identity-account/src/tests/commands.rs b/identity-account/src/tests/commands.rs index b2eb2d28df..8fc4891153 100644 --- a/identity-account/src/tests/commands.rs +++ b/identity-account/src/tests/commands.rs @@ -82,7 +82,7 @@ async fn test_create_identity_invalid_method() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(UpdateError::InvalidMethodType(_)) + Error::UpdateError(UpdateError::InvalidMethodType(_)) )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -154,7 +154,7 @@ async fn test_create_identity_already_exists() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(UpdateError::DocumentAlreadyExists), + Error::UpdateError(UpdateError::DocumentAlreadyExists), )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -204,7 +204,7 @@ async fn test_create_identity_from_invalid_secret_key() -> Result<()> { let err = account.create_identity(id_create).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::UpdateError(UpdateError::InvalidMethodSecret(_)))); Ok(()) } @@ -277,7 +277,7 @@ async fn test_create_method_reserved_fragment() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(UpdateError::InvalidMethodFragment(_)), + Error::UpdateError(UpdateError::InvalidMethodFragment(_)), )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -320,7 +320,7 @@ async fn test_create_method_duplicate_fragment() -> Result<()> { assert!(matches!( output.unwrap_err(), - Error::CommandError(UpdateError::DuplicateKeyFragment(_)), + Error::UpdateError(UpdateError::DuplicateKeyFragment(_)), )); let snapshot: IdentitySnapshot = account.load_snapshot(identity).await?; @@ -389,7 +389,7 @@ async fn test_create_method_from_invalid_secret_key() -> Result<()> { let err = account.process(identity, command, false).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::UpdateError(UpdateError::InvalidMethodSecret(_)))); Ok(()) } @@ -419,7 +419,7 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { let err = account.process(identity, command, false).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::UpdateError(UpdateError::InvalidMethodSecret(_)))); let key_collection = KeyCollection::new_ed25519(4).unwrap(); @@ -432,7 +432,7 @@ async fn test_create_method_with_type_secret_mismatch() -> Result<()> { let err = account.process(identity, command, false).await.unwrap_err(); - assert!(matches!(err, Error::CommandError(UpdateError::InvalidMethodSecret(_)))); + assert!(matches!(err, Error::UpdateError(UpdateError::InvalidMethodSecret(_)))); Ok(()) } From ebc5434dee03e4dd179f8d1c292d635209952530 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 13:33:15 +0200 Subject: [PATCH 24/25] Make `resolve_id` method signatures consistent --- identity-account/src/account/account.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/identity-account/src/account/account.rs b/identity-account/src/account/account.rs index 43fb8c4919..3f0e335e07 100644 --- a/identity-account/src/account/account.rs +++ b/identity-account/src/account/account.rs @@ -226,7 +226,7 @@ impl Account { self.resolve_id(key).await.ok_or(Error::IdentityNotFound) } - async fn try_resolve_id_lock(&self, key: K) -> Result { + async fn try_resolve_id_lock(&self, key: &K) -> Result { self.index.write().await.get_lock(key).ok_or(Error::IdentityNotFound) } @@ -448,7 +448,7 @@ impl Account { /// Push all unpublished changes for the given identity to the tangle in a single message. pub async fn publish_updates(&self, key: K) -> Result<()> { - let identity_lock: IdentityLock = self.try_resolve_id_lock(key).await?; + let identity_lock: IdentityLock = self.try_resolve_id_lock(&key).await?; let identity: RwLockWriteGuard<'_, IdentityId> = identity_lock.write().await; // Get the last commit generation that was published to the tangle. From 24ed8897203c7ba945580f3f1094aace2c477c03 Mon Sep 17 00:00:00 2001 From: PhilippGackstatter Date: Thu, 9 Sep 2021 13:44:18 +0200 Subject: [PATCH 25/25] Move builder doc to the fn instead of the impl --- identity-account/src/events/macros.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/identity-account/src/events/macros.rs b/identity-account/src/events/macros.rs index f90c62fc1a..b47621aeef 100644 --- a/identity-account/src/events/macros.rs +++ b/identity-account/src/events/macros.rs @@ -71,8 +71,8 @@ macro_rules! impl_command_builder { } } - /// Creates a new builder to modify the identity. See the documentation of the return type for details. impl<'account, 'key, K: $crate::identity::IdentityKey> $crate::identity::IdentityUpdater<'account, 'key, K> { + /// Creates a new builder to modify the identity. See the documentation of the return type for details. pub fn [<$ident:snake>](&self) -> [<$ident Builder>]<'account, 'key, K> { [<$ident Builder>]::new(self.account, self.key) }