#[cfg(feature = "test")]
use iota_sdk::client::Client;
use crate::block::address::Address;
use crate::block::output::feature::SenderFeature;
use crate::block::output::unlock_condition::GovernorAddressUnlockCondition;
use crate::block::output::unlock_condition::StateControllerAddressUnlockCondition;
use crate::block::output::AliasId;
use crate::block::output::AliasOutput;
use crate::block::output::AliasOutputBuilder;
use crate::block::output::Feature;
use crate::block::output::OutputId;
use crate::block::output::RentStructure;
use crate::block::output::UnlockCondition;
use crate::block::protocol::ProtocolParameters;
use crate::Error;
use crate::IotaDID;
use crate::IotaDocument;
use crate::NetworkName;
use crate::Result;
#[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)]
#[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))]
pub trait IotaIdentityClient {
async fn get_alias_output(&self, alias_id: AliasId) -> Result<(OutputId, AliasOutput)>;
async fn get_protocol_parameters(&self) -> Result<ProtocolParameters>;
}
#[cfg_attr(feature = "send-sync-client-ext", async_trait::async_trait)]
#[cfg_attr(not(feature = "send-sync-client-ext"), async_trait::async_trait(?Send))]
pub trait IotaIdentityClientExt: IotaIdentityClient {
async fn new_did_output(
&self,
address: Address,
document: IotaDocument,
rent_structure: Option<RentStructure>,
) -> Result<AliasOutput> {
let rent_structure: RentStructure = if let Some(rent) = rent_structure {
rent
} else {
self.get_rent_structure().await?
};
AliasOutputBuilder::new_with_minimum_storage_deposit(rent_structure, AliasId::null())
.with_state_index(0)
.with_foundry_counter(0)
.with_state_metadata(document.pack()?)
.add_feature(Feature::Sender(SenderFeature::new(address)))
.add_unlock_condition(UnlockCondition::StateControllerAddress(
StateControllerAddressUnlockCondition::new(address),
))
.add_unlock_condition(UnlockCondition::GovernorAddress(GovernorAddressUnlockCondition::new(
address,
)))
.finish()
.map_err(Error::AliasOutputBuildError)
}
async fn update_did_output(&self, document: IotaDocument) -> Result<AliasOutput> {
let id: AliasId = AliasId::from(document.id());
let (_, alias_output) = self.get_alias_output(id).await?;
let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output)
.with_state_index(alias_output.state_index() + 1)
.with_state_metadata(document.pack()?);
if alias_output.alias_id().is_null() {
alias_output_builder = alias_output_builder.with_alias_id(id);
}
alias_output_builder.finish().map_err(Error::AliasOutputBuildError)
}
async fn deactivate_did_output(&self, did: &IotaDID) -> Result<AliasOutput> {
let alias_id: AliasId = AliasId::from(did);
let (_, alias_output) = self.get_alias_output(alias_id).await?;
let mut alias_output_builder: AliasOutputBuilder = AliasOutputBuilder::from(&alias_output)
.with_state_index(alias_output.state_index() + 1)
.with_state_metadata(Vec::new());
if alias_output.alias_id().is_null() {
alias_output_builder = alias_output_builder.with_alias_id(alias_id);
}
alias_output_builder.finish().map_err(Error::AliasOutputBuildError)
}
async fn resolve_did(&self, did: &IotaDID) -> Result<IotaDocument> {
validate_network(self, did).await?;
let id: AliasId = AliasId::from(did);
let (_, alias_output) = self.get_alias_output(id).await?;
IotaDocument::unpack_from_output(did, &alias_output, true)
}
async fn resolve_did_output(&self, did: &IotaDID) -> Result<AliasOutput> {
validate_network(self, did).await?;
let id: AliasId = AliasId::from(did);
self.get_alias_output(id).await.map(|(_, alias_output)| alias_output)
}
async fn network_name(&self) -> Result<NetworkName> {
self.get_network_hrp().await.and_then(NetworkName::try_from)
}
async fn get_rent_structure(&self) -> Result<RentStructure> {
self
.get_protocol_parameters()
.await
.map(|parameters| *parameters.rent_structure())
}
async fn get_token_supply(&self) -> Result<u64> {
self
.get_protocol_parameters()
.await
.map(|parameters| parameters.token_supply())
}
async fn get_network_hrp(&self) -> Result<String> {
self
.get_protocol_parameters()
.await
.map(|parameters| parameters.bech32_hrp().to_string())
}
}
#[cfg(not(feature = "test"))]
impl<T> IotaIdentityClientExt for T where T: IotaIdentityClient {}
#[cfg(feature = "test")]
impl IotaIdentityClientExt for Client {}
pub(super) async fn validate_network<T>(client: &T, did: &IotaDID) -> Result<()>
where
T: IotaIdentityClient + ?Sized,
{
let network_hrp: String = client
.get_protocol_parameters()
.await
.map(|parameters| parameters.bech32_hrp().to_string())?;
if did.network_str() != network_hrp.as_str() {
return Err(Error::NetworkMismatch {
expected: did.network_str().to_owned(),
actual: network_hrp,
});
};
Ok(())
}