Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Internalize Account commands #391

Merged
merged 27 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3bd1819
Impl basic `account.new_command` idea
PhilippGackstatter Aug 26, 2021
4928954
Fix apply impl
PhilippGackstatter Sep 7, 2021
58ad219
Enable multiple updates on an updater
PhilippGackstatter Sep 7, 2021
5337380
Flesh out `IdentityUpdater` impl
PhilippGackstatter Sep 8, 2021
6a66ac3
Fix command tests
PhilippGackstatter Sep 8, 2021
f2f3faf
Rename document to did in all examples
PhilippGackstatter Sep 7, 2021
59e0cca
Update examples to use new update syntax
PhilippGackstatter Sep 8, 2021
4fdee7f
Add documentation
PhilippGackstatter Sep 8, 2021
85a3cb7
Remove Default impl, use proper macro path for key
PhilippGackstatter Sep 8, 2021
128eb30
Document `IdentityUpdater`, derive basic traits
PhilippGackstatter Sep 8, 2021
7dba1f6
Merge branch 'dev' into feat/account-internalize-commands
PhilippGackstatter Sep 8, 2021
eb5b4eb
Update recently added tests
PhilippGackstatter Sep 8, 2021
49e6e9c
Roll back name change
PhilippGackstatter Sep 8, 2021
b1fa8eb
Reference `key` to get rid of `Clone` requirement
PhilippGackstatter Sep 9, 2021
5b624ec
Remove `CreateIdentityBuilder`
PhilippGackstatter Sep 9, 2021
63e9fd7
Call `update_identity` on each update
PhilippGackstatter Sep 9, 2021
59ab68e
Move tests inside the account crate
PhilippGackstatter Sep 9, 2021
eb5f2c5
Document `IdentityUpdater` methods
PhilippGackstatter Sep 9, 2021
f6d6920
Rename `CommandError` -> `UpdateError`
PhilippGackstatter Sep 9, 2021
236ee1f
Update stray occurrence of `CommandError`
PhilippGackstatter Sep 9, 2021
8036816
Use better name for captured attribute in macro
PhilippGackstatter Sep 9, 2021
e2eb5e7
Merge branch 'dev' into feat/account-internalize-commands
PhilippGackstatter Sep 9, 2021
77e1ab9
Move lazy test inside the crate & update syntax
PhilippGackstatter Sep 9, 2021
4c36927
Remove `Clone` bound
PhilippGackstatter Sep 9, 2021
c7bbde6
Rename `CommandError` -> `UpdateError` in account
PhilippGackstatter Sep 9, 2021
ebc5434
Make `resolve_id` method signatures consistent
PhilippGackstatter Sep 9, 2021
24ed889
Move builder doc to the fn instead of the impl
PhilippGackstatter Sep 9, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
25 changes: 16 additions & 9 deletions examples/account/lazy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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?;
Expand Down
43 changes: 23 additions & 20 deletions examples/account/methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::Result;
Expand All @@ -24,15 +23,15 @@ async fn main() -> Result<()> {
// Retrieve the DID from the newly created Identity state.
let did: &IotaDID = snapshot.identity().try_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.
let command: Command = Command::create_method()
account
.update_identity(did)
.create_method()
.scope(MethodScope::Authentication)
.fragment("my-auth-key")
.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
//
Expand All @@ -43,20 +42,22 @@ async fn main() -> Result<()> {
);

// 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(did, command).await?;
account
.update_identity(did)
.create_method()
.fragment("my-next-key")
.apply()
.await?;

// Associate the newly created method with additional verification relationships
let command: Command = Command::attach_method()
account
.update_identity(did)
.attach_method()
.fragment("my-next-key")
.scope(MethodScope::CapabilityDelegation)
.scope(MethodScope::CapabilityInvocation)
.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
//
Expand All @@ -67,10 +68,12 @@ async fn main() -> Result<()> {
);

// 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(did, command).await?;
account
.update_identity(did)
.delete_method()
.fragment("my-auth-key")
.apply()
.await?;

// Fetch and log the DID Document from the Tangle
//
Expand Down
12 changes: 6 additions & 6 deletions examples/account/services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,14 +23,15 @@ 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()
// Add a new service to the identity.
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
//
Expand Down
11 changes: 6 additions & 5 deletions examples/account/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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()?;
Expand Down
43 changes: 27 additions & 16 deletions identity-account/src/account/account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::identity::IdentityLock;
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;
Expand Down Expand Up @@ -112,7 +113,7 @@ impl Account {

/// Finds and returns the state snapshot for the identity specified by given `key`.
pub async fn find_identity<K: IdentityKey>(&self, key: K) -> Result<Option<IdentitySnapshot>> {
match self.resolve_id(key).await {
match self.resolve_id(&key).await {
Some(identity) => self.load_snapshot(identity).await.map(Some),
None => Ok(None),
}
Expand Down Expand Up @@ -155,15 +156,15 @@ impl Account {
Ok(snapshot)
}

/// Updates the identity specified by the given `key` with the given `command`.
pub async fn update_identity<K: IdentityKey>(&self, key: K, command: Command) -> Result<()> {
// Hold on to an `IdentityId`s individual lock until we've finished processing the update.
let identity_lock = self.try_resolve_id_lock(key).await?;
let identity: RwLockWriteGuard<'_, IdentityId> = identity_lock.write().await;

self.process(*identity, command, true).await?;

Ok(())
/// 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<'account, 'key, K: IdentityKey>(
&'account self,
key: &'key K,
) -> IdentityUpdater<'account, 'key, K> {
IdentityUpdater::new(self, key)
}

/// Removes the identity specified by the given `key`.
Expand All @@ -190,7 +191,7 @@ impl Account {

/// Resolves the DID Document associated with the specified `key`.
pub async fn resolve_identity<K: IdentityKey>(&self, key: K) -> Result<IotaDocument> {
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()?;

Expand All @@ -204,7 +205,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();

Expand All @@ -217,11 +218,11 @@ impl Account {
Ok(())
}

async fn resolve_id<K: IdentityKey>(&self, key: K) -> Option<IdentityId> {
async fn resolve_id<K: IdentityKey>(&self, key: &K) -> Option<IdentityId> {
self.index.read().await.get(key)
}

async fn try_resolve_id<K: IdentityKey>(&self, key: K) -> Result<IdentityId> {
async fn try_resolve_id<K: IdentityKey>(&self, key: &K) -> Result<IdentityId> {
self.resolve_id(key).await.ok_or(Error::IdentityNotFound)
}

Expand All @@ -233,8 +234,18 @@ impl Account {
// Misc. Private
// ===========================================================================

#[doc(hidden)]
pub async fn process(&self, id: IdentityId, command: Command, persist: bool) -> Result<()> {
/// Updates the identity specified by the given `key` with the given `command`.
pub(crate) async fn apply_command<K: IdentityKey>(&self, key: &K, command: Command) -> Result<()> {
// Hold on to an `IdentityId`s individual lock until we've finished processing the update.
let identity_lock = self.try_resolve_id_lock(key).await?;
let identity: RwLockWriteGuard<'_, IdentityId> = identity_lock.write().await;

self.process(*identity, command, true).await?;

Ok(())
}

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?;

Expand Down
2 changes: 1 addition & 1 deletion identity-account/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
cycraig marked this conversation as resolved.
Show resolved Hide resolved
#[error("Invalid Secret Key: {0}")]
InvalidSecretKey(String),
}
Expand Down