#![cfg(feature = "didcomm")]
use std::sync::Arc;
use std::time::Duration;
use affinidi_messaging_didcomm_service::{
DIDCommService, ListenerConfig, RestartPolicy, RetryConfig,
};
use affinidi_tdk::common::config::TDKConfig;
use affinidi_tdk::secrets_resolver::secrets::Secret;
use affinidi_tdk_common::profiles::TDKProfile;
use async_trait::async_trait;
use serde_json::json;
use crate::didcomm_bridge::DIDCommBridge;
use crate::messaging::handshake::{
HandshakeStage, ListenerProver, ProverFailure, ResolvedMediator,
};
const TRUST_PING_TYPE: &str = "https://didcomm.org/trust-ping/2.0/ping";
const TRUST_PING_RESPONSE_TYPE: &str = "https://didcomm.org/trust-ping/2.0/ping-response";
const PROBLEM_REPORT_TYPE: &str = "https://didcomm.org/report-problem/2.0/problem-report";
fn default_backoff() -> RetryConfig {
RetryConfig {
initial_delay_secs: 1,
max_delay_secs: 60,
}
}
pub trait ListenerConfigBuilder: Send + Sync {
fn build(&self, resolved: &ResolvedMediator) -> ListenerConfig;
}
pub struct DIDCommServiceProver {
service: DIDCommService,
bridge: Arc<DIDCommBridge>,
config_builder: Arc<dyn ListenerConfigBuilder>,
}
impl DIDCommServiceProver {
pub fn new(
service: DIDCommService,
bridge: Arc<DIDCommBridge>,
config_builder: Arc<dyn ListenerConfigBuilder>,
) -> Self {
Self {
service,
bridge,
config_builder,
}
}
}
#[async_trait]
impl ListenerProver for DIDCommServiceProver {
async fn prove(
&self,
resolved: &ResolvedMediator,
vta_did: &str,
timeout: Duration,
) -> Result<(), ProverFailure> {
let listener_id = resolved.mediator_did.clone();
let config = self.config_builder.build(resolved);
if let Err(e) = self.service.add_listener(config).await {
return Err(ProverFailure {
stage: HandshakeStage::Connect,
cause: format!("add_listener failed: {e}"),
});
}
if let Err(e) = self.service.wait_connected(&listener_id, timeout).await {
let _ = self.service.remove_listener(&listener_id).await;
return Err(ProverFailure {
stage: HandshakeStage::Authenticate,
cause: format!("wait_connected failed: {e}"),
});
}
let result = self
.bridge
.send_and_wait_via(
&listener_id,
vta_did, TRUST_PING_TYPE,
json!({
"response_requested": true,
}),
TRUST_PING_RESPONSE_TYPE,
PROBLEM_REPORT_TYPE,
timeout.as_secs(),
)
.await;
if let Err(e) = result {
let _ = self.service.remove_listener(&listener_id).await;
return Err(ProverFailure {
stage: HandshakeStage::TrustPing,
cause: format!("trust-ping round-trip failed: {e}"),
});
}
Ok(())
}
}
pub fn default_restart_policy() -> RestartPolicy {
RestartPolicy::Always {
backoff: default_backoff(),
}
}
pub async fn try_build_from_parts(
bridge: &Arc<DIDCommBridge>,
vta_did: &str,
secrets_resolver: &Arc<affinidi_tdk::secrets_resolver::ThreadedSecretsResolver>,
signing_vm_id: &str,
ka_vm_id: &str,
) -> Option<DIDCommServiceProver> {
use affinidi_tdk::secrets_resolver::SecretsResolver;
let service = bridge.try_get_service()?;
let mut secrets = Vec::with_capacity(2);
if let Some(s) = secrets_resolver.get_secret(signing_vm_id).await {
secrets.push(s);
}
if let Some(s) = secrets_resolver.get_secret(ka_vm_id).await {
secrets.push(s);
}
if secrets.is_empty() {
return None;
}
let builder = Arc::new(StaticListenerConfigBuilder::new(vta_did, secrets, None));
Some(DIDCommServiceProver::new(
service,
Arc::clone(bridge),
builder,
))
}
pub struct StaticListenerConfigBuilder {
vta_did: String,
secrets: Vec<Secret>,
tdk_config: Option<TDKConfig>,
}
impl StaticListenerConfigBuilder {
pub fn new(
vta_did: impl Into<String>,
secrets: Vec<Secret>,
tdk_config: Option<TDKConfig>,
) -> Self {
Self {
vta_did: vta_did.into(),
secrets,
tdk_config,
}
}
}
impl ListenerConfigBuilder for StaticListenerConfigBuilder {
fn build(&self, resolved: &ResolvedMediator) -> ListenerConfig {
let profile = TDKProfile::new(
"VTA",
&self.vta_did,
Some(&resolved.mediator_did),
self.secrets.clone(),
);
ListenerConfig {
id: resolved.mediator_did.clone(),
profile,
restart_policy: default_restart_policy(),
tdk_config: self.tdk_config.clone(),
..Default::default()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::messaging::handshake::HandshakeStage;
#[test]
fn handshake_stages_used_by_prover() {
let stages = [
HandshakeStage::Connect,
HandshakeStage::Authenticate,
HandshakeStage::TrustPing,
];
assert_eq!(stages.len(), 3);
}
#[test]
fn default_backoff_matches_spec() {
let b = default_backoff();
assert_eq!(b.initial_delay_secs, 1);
assert_eq!(b.max_delay_secs, 60);
}
}