Skip to content

Commit

Permalink
Replace ClientMap with Client in Account (#582)
Browse files Browse the repository at this point in the history
* Replace ClientMap with Client in Account

* Change NetworkName debug format to string

* Add Account error for mismatched identity networks

* Fix formatting

* Enhance doc comments from code review

Fix doc comment backticks.

* Apply suggestions from code review

* Update licence headers in modified files

* Add error on publishing a DID to a different network

* Remove network from IdentitySetup

Automatically determine the network for AccountBuilder::create_identity using
the configured Client.

* Fix account network test

* Update licence headers
  • Loading branch information
cycraig authored Jan 13, 2022
1 parent 0eaab9d commit f695052
Show file tree
Hide file tree
Showing 17 changed files with 259 additions and 245 deletions.
4 changes: 2 additions & 2 deletions bindings/wasm/examples/src/private_tangle.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2021 IOTA Stiftung
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

import {
Expand Down Expand Up @@ -33,7 +33,7 @@ async function privateTangle(restURL, networkName) {
config.setNetwork(network);

// This URL points to the REST API of the locally running hornet node.
config.setNode(restURL || "http://127.0.0.1:14265/");
config.setPrimaryNode(restURL || "http://127.0.0.1:14265/");

// Use DIDMessageEncoding.Json instead to publish plaintext messages to the Tangle for debugging.
config.setEncoding(DIDMessageEncoding.JsonBrotli);
Expand Down
36 changes: 13 additions & 23 deletions examples/account/config.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2021 IOTA Stiftung
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! cargo run --example account_config
Expand All @@ -9,6 +9,7 @@ use identity::account::AccountStorage;
use identity::account::AutoSave;
use identity::account::IdentitySetup;
use identity::account::Result;
use identity::iota::ClientBuilder;
use identity::iota::ExplorerUrl;
use identity::iota::IotaDID;
use identity::iota::Network;
Expand All @@ -30,7 +31,7 @@ async fn main() -> Result<()> {
// If you deployed an explorer locally this would usually be `http://127.0.0.1:8082`
let explorer = ExplorerUrl::parse("https://explorer.iota.org/devnet")?;

// In a locally running one-click tangle, this would often be `http://127.0.0.1:14265`
// In a locally running one-click tangle, this would usually be `http://127.0.0.1:14265`
let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe";

// Create a new Account with explicit configuration
Expand All @@ -41,28 +42,17 @@ async fn main() -> Result<()> {
.autopublish(true) // publish to the tangle automatically on every update
.milestone(4) // save a snapshot every 4 actions
.storage(AccountStorage::Memory) // use the default in-memory storage
// configure a mainnet Tangle client with node and permanode
.client(Network::Mainnet, |builder| {
builder
// Manipulate this in order to manually appoint nodes
.node("https://chrysalis-nodes.iota.org")
.unwrap() // unwrap is safe, we provided a valid node URL
// Set a permanode from the same network (Important)
.permanode("https://chrysalis-chronicle.iota.org/api/mainnet/", None, None)
.unwrap() // unwrap is safe, we provided a valid permanode URL
})
// Configure a client for the private network, here `dev`
// Also set the URL that points to the REST API of the node
.client(network.clone(), |builder| {
// unwrap is safe, we provided a valid node URL
builder.node(private_node_url).unwrap()
});
.client_builder(
// Configure a client for the private network
ClientBuilder::new()
.network(network.clone())
.primary_node(private_node_url, None, None)?
// .permanode(<permanode_url>, None, None)? // set a permanode for the same network
);

// Create an identity specifically on the devnet by passing `network_name`
// The same applies if we wanted to create an identity on a private tangle
let identity_setup: IdentitySetup = IdentitySetup::new().network(network_name)?;

let identity: Account = match builder.create_identity(identity_setup).await {
// Create an identity and publish it.
// The created DID will use the network name configured for the client.
let identity: Account = match builder.create_identity(IdentitySetup::default()).await {
Ok(identity) => identity,
Err(err) => {
eprintln!("[Example] Error: {:?}", err);
Expand Down
6 changes: 3 additions & 3 deletions examples/low-level-api/private_tangle.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2021 IOTA Stiftung
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

//! A basic example that generates and publishes a DID Document
Expand Down Expand Up @@ -31,7 +31,7 @@ pub async fn main() -> Result<()> {
// If you deployed an explorer locally this would usually be `http://127.0.0.1:8082`
let explorer = ExplorerUrl::parse("https://explorer.iota.org/devnet")?;

// In a locally running one-click tangle, this would often be `http://127.0.0.1:14265`
// In a locally running one-click tangle, this would usually be `http://127.0.0.1:14265`
let private_node_url = "https://api.lb-0.h.chrysalis-devnet.iota.cafe";

// Use DIDMessageEncoding::Json instead to publish plaintext messages to the Tangle for debugging.
Expand All @@ -40,7 +40,7 @@ pub async fn main() -> Result<()> {
let client = ClientBuilder::new()
.network(network.clone())
.encoding(encoding)
.node(private_node_url)?
.primary_node(private_node_url, None, None)?
.build()
.await?;

Expand Down
51 changes: 29 additions & 22 deletions identity-account/src/account/account.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2020-2021 IOTA Stiftung
// Copyright 2020-2022 IOTA Stiftung
// SPDX-License-Identifier: Apache-2.0

use std::sync::atomic::AtomicUsize;
Expand All @@ -16,15 +16,12 @@ use identity_iota::document::IotaDocument;
use identity_iota::document::IotaVerificationMethod;
use identity_iota::document::ResolvedIotaDocument;
use identity_iota::tangle::Client;
use identity_iota::tangle::ClientMap;
use identity_iota::tangle::MessageId;
use identity_iota::tangle::MessageIdExt;
use identity_iota::tangle::PublishType;
use identity_iota::tangle::TangleResolve;

use crate::account::AccountBuilder;
use crate::account::PublishOptions;
use crate::error::Result;
use crate::identity::ChainState;
use crate::identity::DIDLease;
use crate::identity::IdentitySetup;
Expand All @@ -35,6 +32,7 @@ use crate::types::KeyLocation;
use crate::updates::create_identity;
use crate::updates::Update;
use crate::Error;
use crate::Result;

use super::config::AccountSetup;
use super::config::AutoSave;
Expand All @@ -48,13 +46,12 @@ use super::AccountConfig;
pub struct Account {
config: AccountConfig,
storage: Arc<dyn Storage>,
client_map: Arc<ClientMap>,
client: Arc<Client>,
actions: AtomicUsize,
chain_state: ChainState,
state: IdentityState,
// The lease is not read, but releases the DID storage lease when the Account is dropped.
_did_lease: DIDLease,
/* This field is not read, but has special behaviour on drop which is why it is needed in
* the Account. */
}

impl Account {
Expand All @@ -77,7 +74,7 @@ impl Account {
Ok(Self {
config: setup.config,
storage: setup.storage,
client_map: setup.client_map,
client: setup.client,
actions: AtomicUsize::new(0),
chain_state,
state,
Expand All @@ -86,14 +83,20 @@ impl Account {
}

/// Creates a new identity and returns an [`Account`] instance to manage it.
/// The identity is stored locally in the [`Storage`] given in [`AccountSetup`], and published
/// using the [`ClientMap`].
///
/// The identity is stored locally in the [`Storage`] given in [`AccountSetup`]. The DID network
/// is automatically determined by the [`Client`] used to publish it.
///
/// See [`IdentitySetup`] to customize the identity creation.
pub(crate) async fn create_identity(setup: AccountSetup, input: IdentitySetup) -> Result<Self> {
let (did_lease, state): (DIDLease, IdentityState) = create_identity(input, setup.storage.as_ref()).await?;
pub(crate) async fn create_identity(account_setup: AccountSetup, identity_setup: IdentitySetup) -> Result<Self> {
let (did_lease, state): (DIDLease, IdentityState) = create_identity(
identity_setup,
account_setup.client.network().name(),
account_setup.storage.as_ref(),
)
.await?;

let mut account = Self::with_setup(setup, ChainState::new(), state, did_lease).await?;
let mut account = Self::with_setup(account_setup, ChainState::new(), state, did_lease).await?;

account.store_state().await?;

Expand All @@ -104,6 +107,15 @@ impl Account {

/// Creates an [`Account`] for an existing identity, if it exists in the [`Storage`].
pub(crate) async fn load_identity(setup: AccountSetup, did: IotaDID) -> Result<Self> {
// Ensure the DID matches the client network.
if did.network_str() != setup.client.network().name_str() {
return Err(Error::IotaError(identity_iota::Error::IncompatibleNetwork(format!(
"DID network {} does not match account network {}",
did.network_str(),
setup.client.network().name_str()
))));
}

// Ensure the did exists in storage
let state = setup.storage.state(&did).await?.ok_or(Error::IdentityNotFound)?;
let chain_state = setup.storage.chain_state(&did).await?.ok_or(Error::IdentityNotFound)?;
Expand Down Expand Up @@ -142,11 +154,6 @@ impl Account {
self.actions.fetch_add(1, Ordering::SeqCst);
}

/// Adds a pre-configured `Client` for Tangle interactions.
pub fn set_client(&self, client: Client) {
self.client_map.insert(client);
}

/// Returns the did of the managed identity.
pub fn did(&self) -> &IotaDID {
self.document().id()
Expand Down Expand Up @@ -185,7 +192,7 @@ impl Account {

/// Resolves the DID Document associated with this `Account` from the Tangle.
pub async fn resolve_identity(&self) -> Result<ResolvedIotaDocument> {
self.client_map.resolve(self.did()).await.map_err(Into::into)
self.client.read_document(self.did()).await.map_err(Into::into)
}

/// Returns the [`IdentityUpdater`] for this identity.
Expand Down Expand Up @@ -270,7 +277,7 @@ impl Account {
/// to the identity, to avoid publishing updates that would be ignored.
pub async fn fetch_state(&mut self) -> Result<()> {
let iota_did: &IotaDID = self.did();
let mut document_chain: DocumentChain = self.client_map.read_document_chain(iota_did).await?;
let mut document_chain: DocumentChain = self.client.read_document_chain(iota_did).await?;
// Checks if the local document is up to date
if document_chain.integration_message_id() == self.chain_state.last_integration_message_id()
&& (document_chain.diff().is_empty()
Expand Down Expand Up @@ -440,7 +447,7 @@ impl Account {
// Fake publishing by returning a random message id.
MessageId::new(unsafe { crypto::utils::rand::gen::<[u8; 32]>().unwrap() })
} else {
self.client_map.publish_document(&new_doc).await?.into()
self.client.publish_document(&new_doc).await?.into()
};

self.chain_state.set_last_integration_message_id(message_id);
Expand Down Expand Up @@ -506,7 +513,7 @@ impl Account {
MessageId::new(unsafe { crypto::utils::rand::gen::<[u8; 32]>().unwrap() })
} else {
self
.client_map
.client
.publish_diff(self.chain_state().last_integration_message_id(), &diff)
.await?
.into()
Expand Down
Loading

0 comments on commit f695052

Please sign in to comment.