use std::ops::ControlFlow;
use auths_id::keri::Event;
use auths_id::keri::types::Prefix;
use auths_id::ports::registry::RegistryBackend;
use crate::context::AuthsContext;
use crate::domains::identity::error::SetupError;
pub struct LocalSigner {
pub signer_did: String,
pub root_did: String,
pub anchor_seq: Option<u128>,
}
impl LocalSigner {
pub fn is_delegated(&self) -> bool {
self.signer_did != self.root_did
}
}
pub fn resolve_local_signer(ctx: &AuthsContext) -> Result<LocalSigner, SetupError> {
if let Ok(managed) = ctx.identity_storage.load_identity() {
let root_did = managed.controller_did.to_string();
let anchor_seq = root_tip_seq(ctx, &root_did);
if let Ok(root_prefix) = auths_id::keri::parse_did_keri(&root_did)
&& let Ok(devices) = auths_id::keri::delegation::list_delegated_devices(
ctx.registry.as_ref(),
&root_prefix,
)
&& let Some(dev) = devices.iter().find(|d| is_local_signing_device(ctx, d))
{
return Ok(LocalSigner {
signer_did: format!("did:keri:{}", dev.device_prefix),
root_did,
anchor_seq,
});
}
return Ok(LocalSigner {
signer_did: root_did.clone(),
root_did,
anchor_seq,
});
}
let mut prefixes: Vec<String> = Vec::new();
ctx.registry
.visit_identities(&mut |prefix| {
prefixes.push(prefix.to_string());
ControlFlow::Continue(())
})
.map_err(|e| {
SetupError::StorageError(
auths_id::error::StorageError::InvalidData(e.to_string()).into(),
)
})?;
for prefix_str in prefixes {
let prefix = Prefix::new_unchecked(prefix_str);
if let Ok(Event::Dip(dip)) = ctx.registry.get_event(&prefix, 0) {
let root_did = format!("did:keri:{}", dip.di);
let anchor_seq = root_tip_seq(ctx, &root_did);
return Ok(LocalSigner {
signer_did: format!("did:keri:{}", dip.i),
root_did,
anchor_seq,
});
}
}
Err(SetupError::StorageError(
auths_id::error::StorageError::InvalidData(
"no local signing identity found (neither a root identity nor a delegated \
device). Run `auths init`, or pair this device with `auths pair --join`."
.to_string(),
)
.into(),
))
}
fn is_local_signing_device(
ctx: &AuthsContext,
dev: &auths_id::keri::delegation::DelegatedDeviceInfo,
) -> bool {
use auths_id::keri::delegation::DelegatedRole;
if dev.revoked || matches!(dev.role, DelegatedRole::Agent) {
return false;
}
let device_did = format!("did:keri:{}", dev.device_prefix);
match auths_core::storage::keychain::IdentityDID::parse(&device_did) {
Ok(did) => ctx
.key_storage
.list_aliases_for_identity(&did)
.map(|aliases| !aliases.is_empty())
.unwrap_or(false),
Err(_) => false,
}
}
fn root_tip_seq(ctx: &AuthsContext, root_did: &str) -> Option<u128> {
let prefix = root_did.strip_prefix("did:keri:")?;
ctx.registry
.get_tip(&Prefix::new_unchecked(prefix.to_string()))
.ok()
.map(|tip| tip.sequence)
}