tap_agent/
agent.rs

1use crate::agent_key_manager::{AgentKeyManager, AgentKeyManagerBuilder};
2use crate::config::AgentConfig;
3#[cfg(all(not(target_arch = "wasm32"), test))]
4use crate::did::SyncDIDResolver; // Import SyncDIDResolver trait
5use crate::error::{Error, Result};
6use crate::key_manager::KeyManager; // Add KeyManager trait
7#[cfg(not(target_arch = "wasm32"))]
8use crate::message::SecurityMode;
9#[cfg(not(target_arch = "wasm32"))]
10use crate::message_packing::{PackOptions, Packable, UnpackOptions, Unpackable};
11use async_trait::async_trait;
12#[cfg(feature = "native")]
13use reqwest::Client;
14use serde::de::DeserializeOwned;
15use serde_json::Value;
16use std::sync::Arc;
17#[cfg(feature = "native")]
18use std::time::Duration;
19use tap_msg::didcomm::PlainMessage;
20use tap_msg::TapMessageBody;
21
22/// Result of a message delivery attempt
23#[derive(Debug, Clone)]
24pub struct DeliveryResult {
25    /// The DID of the recipient
26    pub did: String,
27    /// The service endpoint URL that was used for delivery
28    pub endpoint: String,
29    /// HTTP status code if the delivery was successful
30    pub status: Option<u16>,
31    /// Error message if the delivery failed
32    pub error: Option<String>,
33}
34
35/// The Agent trait defines the interface for all TAP agents
36#[cfg(not(target_arch = "wasm32"))]
37#[async_trait]
38#[cfg(not(target_arch = "wasm32"))]
39pub trait Agent {
40    /// Gets the agent's DID
41    fn get_agent_did(&self) -> &str;
42
43    /// Gets the service endpoint URL for a recipient
44    async fn get_service_endpoint(&self, to: &str) -> Result<Option<String>>;
45
46    /// Sends a message to one or more recipients
47    async fn send_message<
48        T: TapMessageBody + serde::Serialize + Send + Sync + std::fmt::Debug + 'static,
49    >(
50        &self,
51        message: &T,
52        to: Vec<&str>,
53        deliver: bool,
54    ) -> Result<(String, Vec<DeliveryResult>)>;
55
56    /// Receives a message
57    async fn receive_message<T: TapMessageBody + DeserializeOwned + Send>(
58        &self,
59        packed_message: &str,
60    ) -> Result<T>;
61}
62
63/// A simplified Agent trait for WASM with relaxed bounds
64#[cfg(target_arch = "wasm32")]
65pub trait WasmAgent {
66    /// Gets the agent's DID
67    fn get_agent_did(&self) -> &str;
68
69    /// Pack a message for delivery
70    fn pack_message<T: TapMessageBody + serde::Serialize>(&self, message: &T) -> Result<String>;
71
72    /// Unpack a received message
73    fn unpack_message<T: TapMessageBody + DeserializeOwned>(
74        &self,
75        packed_message: &str,
76    ) -> Result<T>;
77}
78
79/// TapAgent implementation using the AgentKeyManager for cryptographic operations.
80#[derive(Debug, Clone)]
81pub struct TapAgent {
82    /// Configuration for the agent
83    pub config: AgentConfig,
84    /// Key Manager for cryptographic operations
85    key_manager: Arc<AgentKeyManager>,
86    /// DID Resolver for resolving DIDs to service endpoints
87    #[cfg(all(not(target_arch = "wasm32"), test))]
88    resolver: Option<Arc<dyn SyncDIDResolver>>,
89    /// HTTP client for sending requests
90    #[cfg(all(feature = "native", not(target_arch = "wasm32")))]
91    http_client: Option<Client>,
92}
93
94impl TapAgent {
95    /// Creates a new TapAgent with the given configuration and AgentKeyManager
96    pub fn new(config: AgentConfig, key_manager: Arc<AgentKeyManager>) -> Self {
97        #[cfg(all(feature = "native", not(target_arch = "wasm32")))]
98        {
99            let timeout = Duration::from_secs(config.timeout_seconds.unwrap_or(30));
100            let client = Client::builder().timeout(timeout).build().ok();
101
102            #[cfg(test)]
103            let agent = TapAgent {
104                config,
105                key_manager,
106                resolver: None,
107                http_client: client,
108            };
109
110            #[cfg(not(test))]
111            let agent = TapAgent {
112                config,
113                key_manager,
114                http_client: client,
115            };
116
117            agent
118        }
119
120        #[cfg(not(all(feature = "native", not(target_arch = "wasm32"))))]
121        {
122            #[cfg(all(not(target_arch = "wasm32"), test))]
123            let agent = TapAgent {
124                config,
125                key_manager,
126                resolver: None,
127            };
128
129            #[cfg(all(not(target_arch = "wasm32"), not(test)))]
130            let agent = TapAgent {
131                config,
132                key_manager,
133            };
134
135            #[cfg(target_arch = "wasm32")]
136            let agent = TapAgent {
137                config,
138                key_manager,
139            };
140
141            agent
142        }
143    }
144
145    /// Creates a new TapAgent with the given configuration, key manager, and DID resolver
146    #[cfg(all(not(target_arch = "wasm32"), test))]
147    pub fn new_with_resolver(
148        config: AgentConfig,
149        key_manager: Arc<AgentKeyManager>,
150        resolver: Arc<dyn SyncDIDResolver>,
151    ) -> Self {
152        #[cfg(feature = "native")]
153        {
154            let timeout = Duration::from_secs(config.timeout_seconds.unwrap_or(30));
155            let client = Client::builder().timeout(timeout).build().ok();
156
157            TapAgent {
158                config,
159                key_manager,
160                resolver: Some(resolver),
161                http_client: client,
162            }
163        }
164
165        #[cfg(not(feature = "native"))]
166        {
167            TapAgent {
168                config,
169                key_manager,
170                resolver: Some(resolver),
171            }
172        }
173    }
174
175    /// Creates a new TapAgent with an ephemeral key
176    ///
177    /// This function generates a new DID key for temporary use.
178    /// The key is not persisted to storage and will be lost when the agent is dropped.
179    ///
180    /// # Returns
181    ///
182    /// A tuple containing the TapAgent and the DID that was generated
183    pub async fn from_ephemeral_key() -> crate::error::Result<(Self, String)> {
184        use crate::did::{DIDGenerationOptions, KeyType};
185
186        // Create a key manager
187        let key_manager = AgentKeyManager::new();
188
189        // Generate a key
190        let key = key_manager.generate_key(DIDGenerationOptions {
191            key_type: KeyType::Ed25519,
192        })?;
193
194        // Create a config with the new DID
195        let config = AgentConfig::new(key.did.clone()).with_debug(true);
196
197        // Create the agent
198        #[cfg(all(not(target_arch = "wasm32"), test))]
199        {
200            // Create a default resolver
201            let resolver = Arc::new(crate::did::MultiResolver::default());
202            let agent = Self::new_with_resolver(config, Arc::new(key_manager), resolver);
203            Ok((agent, key.did))
204        }
205
206        #[cfg(all(not(target_arch = "wasm32"), not(test)))]
207        {
208            let agent = Self::new(config, Arc::new(key_manager));
209            Ok((agent, key.did))
210        }
211
212        #[cfg(target_arch = "wasm32")]
213        {
214            let agent = Self::new(config, Arc::new(key_manager));
215            Ok((agent, key.did))
216        }
217    }
218
219    /// Creates a new TapAgent from stored keys
220    ///
221    /// This function uses the AgentKeyManagerBuilder to load keys from storage
222    ///
223    /// # Arguments
224    ///
225    /// * `did` - Optional DID to use. If None, the default DID from storage is used.
226    /// * `debug` - Whether to enable debug mode
227    ///
228    /// # Returns
229    ///
230    /// A Result containing either the created agent or an error if no keys are available
231    pub async fn from_stored_keys(did: Option<String>, debug: bool) -> Result<Self> {
232        use crate::storage::KeyStorage;
233
234        // Load keys from storage
235        let key_manager_builder = AgentKeyManagerBuilder::new().load_from_default_storage();
236        let key_manager = key_manager_builder.build()?;
237
238        // Get the DIDs available in the key manager
239        let dids = key_manager.list_keys()?;
240        if dids.is_empty() {
241            return Err(Error::Storage(
242                "No keys found in storage. Generate keys first with 'tap-agent-cli generate --save'".to_string(),
243            ));
244        }
245
246        // Get the DID to use
247        let agent_did = if let Some(specified_did) = did {
248            if !dids.contains(&specified_did) {
249                return Err(Error::Storage(format!(
250                    "Key with DID '{}' not found in storage",
251                    specified_did
252                )));
253            }
254            specified_did
255        } else {
256            // Try to get the default DID from storage
257            let storage = KeyStorage::load_default()?;
258            storage.default_did.unwrap_or_else(|| dids[0].clone())
259        };
260
261        // Create agent config
262        let config = AgentConfig::new(agent_did).with_debug(debug);
263
264        // Create the agent
265        #[cfg(all(not(target_arch = "wasm32"), test))]
266        {
267            // Create a default resolver
268            let resolver = Arc::new(crate::did::MultiResolver::default());
269            Ok(TapAgent::new_with_resolver(
270                config,
271                Arc::new(key_manager),
272                resolver,
273            ))
274        }
275
276        #[cfg(all(not(target_arch = "wasm32"), not(test)))]
277        {
278            Ok(TapAgent::new(config, Arc::new(key_manager)))
279        }
280
281        #[cfg(target_arch = "wasm32")]
282        {
283            Ok(TapAgent::new(config, Arc::new(key_manager)))
284        }
285    }
286
287    /// Creates a new TapAgent from an existing private key
288    ///
289    /// This function creates a new TapAgent using a provided private key,
290    /// which can be useful for integrating with external key management systems
291    /// or when keys are generated outside the TAP agent.
292    ///
293    /// # Arguments
294    ///
295    /// * `private_key` - The private key bytes
296    /// * `key_type` - The type of key (Ed25519, P256, or Secp256k1)
297    /// * `debug` - Whether to enable debug mode
298    ///
299    /// # Returns
300    ///
301    /// A Result containing either the created agent or an error
302    pub async fn from_private_key(
303        private_key: &[u8],
304        key_type: crate::did::KeyType,
305        debug: bool,
306    ) -> Result<(Self, String)> {
307        use crate::did::{DIDKeyGenerator, GeneratedKey};
308        use crate::did::{VerificationMaterial, VerificationMethod, VerificationMethodType};
309        use curve25519_dalek::edwards::CompressedEdwardsY;
310        use multibase::{encode, Base};
311
312        // Create a key manager to hold our key
313        let key_manager = AgentKeyManager::new();
314
315        // Generate the appropriate key and DID based on the key type
316        let generated_key = match key_type {
317            crate::did::KeyType::Ed25519 => {
318                if private_key.len() != 32 {
319                    return Err(Error::Validation(format!(
320                        "Invalid Ed25519 private key length: {}, expected 32 bytes",
321                        private_key.len()
322                    )));
323                }
324
325                // For Ed25519, we need to derive the public key from the private key
326                let mut private_key_bytes = [0u8; 32];
327                private_key_bytes.copy_from_slice(&private_key[0..32]);
328
329                let signing_key = ed25519_dalek::SigningKey::from_bytes(&private_key_bytes);
330
331                // Get the public key
332                let verifying_key = ed25519_dalek::VerifyingKey::from(&signing_key);
333                let public_key = verifying_key.to_bytes().to_vec();
334
335                // Create did:key identifier
336                // Multicodec prefix for Ed25519: 0xed01
337                let mut prefixed_key = vec![0xed, 0x01];
338                prefixed_key.extend_from_slice(&public_key);
339
340                // Encode the key with multibase (base58btc with 'z' prefix)
341                let multibase_encoded = encode(Base::Base58Btc, &prefixed_key);
342                let did = format!("did:key:{}", multibase_encoded);
343
344                // Create the verification method ID
345                let vm_id = format!("{}#{}", did, multibase_encoded);
346
347                // Create the verification method
348                let verification_method = VerificationMethod {
349                    id: vm_id.clone(),
350                    type_: VerificationMethodType::Ed25519VerificationKey2018,
351                    controller: did.clone(),
352                    verification_material: VerificationMaterial::Multibase {
353                        public_key_multibase: multibase_encoded.clone(),
354                    },
355                };
356
357                // Create X25519 key for key agreement - Implement the ed25519_to_x25519 conversion directly
358                let x25519_method_and_agreement = {
359                    // Only Ed25519 public keys must be exactly 32 bytes
360                    if public_key.len() != 32 {
361                        None
362                    } else {
363                        // Try to create a CompressedEdwardsY from the bytes
364                        let edwards_y = match CompressedEdwardsY::from_slice(&public_key) {
365                            Ok(point) => point,
366                            Err(_) => {
367                                return Err(Error::Cryptography(
368                                    "Failed to create Edwards point".to_string(),
369                                ))
370                            }
371                        };
372
373                        // Try to decompress to get the Edwards point
374                        let edwards_point = match edwards_y.decompress() {
375                            Some(point) => point,
376                            None => {
377                                return Err(Error::Cryptography(
378                                    "Failed to decompress Edwards point".to_string(),
379                                ))
380                            }
381                        };
382
383                        // Convert to Montgomery form
384                        let montgomery_point = edwards_point.to_montgomery();
385
386                        // Get the raw bytes representation of the X25519 key
387                        let x25519_key = montgomery_point.to_bytes();
388
389                        // Prefix for X25519: 0xEC01
390                        let mut x25519_prefixed = vec![0xEC, 0x01];
391                        x25519_prefixed.extend_from_slice(&x25519_key);
392
393                        // Encode the prefixed X25519 key with multibase
394                        let x25519_multibase = encode(Base::Base58Btc, &x25519_prefixed);
395
396                        // Create the X25519 verification method ID
397                        let x25519_vm_id = format!("{}#{}", did, x25519_multibase);
398
399                        // Create the X25519 verification method
400                        let x25519_verification_method = VerificationMethod {
401                            id: x25519_vm_id.clone(),
402                            type_: VerificationMethodType::X25519KeyAgreementKey2019,
403                            controller: did.clone(),
404                            verification_material: VerificationMaterial::Multibase {
405                                public_key_multibase: x25519_multibase,
406                            },
407                        };
408
409                        Some((x25519_verification_method, x25519_vm_id))
410                    }
411                };
412
413                // Build verification methods array
414                let mut verification_methods = vec![verification_method.clone()];
415                let mut key_agreement = Vec::new();
416
417                if let Some((x25519_vm, x25519_id)) = x25519_method_and_agreement {
418                    verification_methods.push(x25519_vm);
419                    key_agreement.push(x25519_id);
420                }
421
422                // Create the DID document
423                let did_doc = crate::did::DIDDoc {
424                    id: did.clone(),
425                    verification_method: verification_methods,
426                    authentication: vec![vm_id],
427                    key_agreement,
428                    assertion_method: Vec::new(),
429                    capability_invocation: Vec::new(),
430                    capability_delegation: Vec::new(),
431                    service: Vec::new(),
432                };
433
434                // Create a GeneratedKey with all necessary fields
435                GeneratedKey {
436                    key_type: crate::did::KeyType::Ed25519,
437                    did: did.clone(),
438                    public_key,
439                    private_key: private_key.to_vec(),
440                    did_doc,
441                }
442            }
443            crate::did::KeyType::P256 => {
444                if private_key.len() != 32 {
445                    return Err(Error::Validation(format!(
446                        "Invalid P-256 private key length: {}, expected 32 bytes",
447                        private_key.len()
448                    )));
449                }
450
451                // For P-256, create a signing key from the private key
452                let signing_key = match p256::ecdsa::SigningKey::from_slice(private_key) {
453                    Ok(key) => key,
454                    Err(e) => {
455                        return Err(Error::Cryptography(format!(
456                            "Failed to create P-256 signing key: {:?}",
457                            e
458                        )))
459                    }
460                };
461
462                // Get the public key in uncompressed form
463                let public_key = signing_key
464                    .verifying_key()
465                    .to_encoded_point(false)
466                    .to_bytes()
467                    .to_vec();
468
469                // Create did:key identifier
470                // Multicodec prefix for P-256: 0x1200
471                let mut prefixed_key = vec![0x12, 0x00];
472                prefixed_key.extend_from_slice(&public_key);
473
474                // Encode the key with multibase (base58btc with 'z' prefix)
475                let multibase_encoded = encode(Base::Base58Btc, &prefixed_key);
476                let did = format!("did:key:{}", multibase_encoded);
477
478                // Create the verification method ID
479                let vm_id = format!("{}#{}", did, multibase_encoded);
480
481                // Create the verification method
482                let verification_method = VerificationMethod {
483                    id: vm_id.clone(),
484                    type_: VerificationMethodType::EcdsaSecp256k1VerificationKey2019, // Using the available type
485                    controller: did.clone(),
486                    verification_material: VerificationMaterial::Multibase {
487                        public_key_multibase: multibase_encoded.clone(),
488                    },
489                };
490
491                // Create the DID document
492                let did_doc = crate::did::DIDDoc {
493                    id: did.clone(),
494                    verification_method: vec![verification_method],
495                    authentication: vec![vm_id],
496                    key_agreement: Vec::new(),
497                    assertion_method: Vec::new(),
498                    capability_invocation: Vec::new(),
499                    capability_delegation: Vec::new(),
500                    service: Vec::new(),
501                };
502
503                // Create a GeneratedKey with all necessary fields
504                GeneratedKey {
505                    key_type: crate::did::KeyType::P256,
506                    did: did.clone(),
507                    public_key,
508                    private_key: private_key.to_vec(),
509                    did_doc,
510                }
511            }
512            crate::did::KeyType::Secp256k1 => {
513                if private_key.len() != 32 {
514                    return Err(Error::Validation(format!(
515                        "Invalid Secp256k1 private key length: {}, expected 32 bytes",
516                        private_key.len()
517                    )));
518                }
519
520                // For Secp256k1, create a signing key from the private key
521                let signing_key = match k256::ecdsa::SigningKey::from_slice(private_key) {
522                    Ok(key) => key,
523                    Err(e) => {
524                        return Err(Error::Cryptography(format!(
525                            "Failed to create Secp256k1 signing key: {:?}",
526                            e
527                        )))
528                    }
529                };
530
531                // Get the public key in uncompressed form
532                let public_key = signing_key
533                    .verifying_key()
534                    .to_encoded_point(false)
535                    .to_bytes()
536                    .to_vec();
537
538                // Create did:key identifier
539                // Multicodec prefix for Secp256k1: 0xe701
540                let mut prefixed_key = vec![0xe7, 0x01];
541                prefixed_key.extend_from_slice(&public_key);
542
543                // Encode the key with multibase (base58btc with 'z' prefix)
544                let multibase_encoded = encode(Base::Base58Btc, &prefixed_key);
545                let did = format!("did:key:{}", multibase_encoded);
546
547                // Create the verification method ID
548                let vm_id = format!("{}#{}", did, multibase_encoded);
549
550                // Create the verification method
551                let verification_method = VerificationMethod {
552                    id: vm_id.clone(),
553                    type_: VerificationMethodType::EcdsaSecp256k1VerificationKey2019,
554                    controller: did.clone(),
555                    verification_material: VerificationMaterial::Multibase {
556                        public_key_multibase: multibase_encoded.clone(),
557                    },
558                };
559
560                // Create the DID document
561                let did_doc = crate::did::DIDDoc {
562                    id: did.clone(),
563                    verification_method: vec![verification_method],
564                    authentication: vec![vm_id],
565                    key_agreement: Vec::new(),
566                    assertion_method: Vec::new(),
567                    capability_invocation: Vec::new(),
568                    capability_delegation: Vec::new(),
569                    service: Vec::new(),
570                };
571
572                // Create a GeneratedKey with all necessary fields
573                GeneratedKey {
574                    key_type: crate::did::KeyType::Secp256k1,
575                    did: did.clone(),
576                    public_key,
577                    private_key: private_key.to_vec(),
578                    did_doc,
579                }
580            }
581        };
582
583        // Create secret from the generated key and use it to add to the key manager
584        let did_generator = DIDKeyGenerator::new();
585        let _secret = did_generator.create_secret_from_key(&generated_key);
586
587        // Add the key to the key manager
588        key_manager.add_key(&generated_key)?;
589
590        // Create a config with the new DID
591        let config = AgentConfig::new(generated_key.did.clone()).with_debug(debug);
592
593        // Create the agent
594        #[cfg(all(not(target_arch = "wasm32"), test))]
595        {
596            // Create a default resolver
597            let resolver = Arc::new(crate::did::MultiResolver::default());
598            let agent = Self::new_with_resolver(config, Arc::new(key_manager), resolver);
599            Ok((agent, generated_key.did))
600        }
601
602        #[cfg(all(not(target_arch = "wasm32"), not(test)))]
603        {
604            let agent = Self::new(config, Arc::new(key_manager));
605            Ok((agent, generated_key.did))
606        }
607
608        #[cfg(target_arch = "wasm32")]
609        {
610            let agent = Self::new(config, Arc::new(key_manager));
611            Ok((agent, generated_key.did))
612        }
613    }
614
615    /// Determine the appropriate security mode for a message type
616    ///
617    /// This method implements TAP protocol rules for which security modes
618    /// should be used with different message types:
619    /// - Presentation messages use authenticated encryption (AuthCrypt)
620    /// - All other messages use digital signatures (Signed)
621    ///
622    /// If security_mode is specified in the agent config, that takes precedence.
623    ///
624    /// # Parameters
625    /// * `message_type` - The type of the message
626    ///
627    /// # Returns
628    /// The appropriate SecurityMode for the message type
629    #[cfg(not(target_arch = "wasm32"))]
630    fn determine_security_mode<T: TapMessageBody>(&self) -> SecurityMode {
631        // If security mode is explicitly configured, use that
632        if let Some(ref mode) = self.config.security_mode {
633            if mode.to_uppercase() == "AUTHCRYPT" {
634                return SecurityMode::AuthCrypt;
635            } else {
636                // Default to Signed for any other value
637                return SecurityMode::Signed;
638            }
639        }
640
641        // Otherwise use type-based rules
642        let message_type = T::message_type();
643        if message_type == crate::message::PRESENTATION_MESSAGE_TYPE {
644            SecurityMode::AuthCrypt
645        } else {
646            SecurityMode::Signed
647        }
648    }
649
650    /// Send a message to a specific endpoint
651    ///
652    /// # Parameters
653    /// * `packed_message` - The packed message to send
654    /// * `endpoint` - The endpoint URL to send the message to
655    ///
656    /// # Returns
657    /// The HTTP response status code, or error if the request failed
658    #[cfg(all(feature = "native", not(target_arch = "wasm32")))]
659    pub async fn send_to_endpoint(&self, packed_message: &str, endpoint: &str) -> Result<u16> {
660        // Get HTTP client
661        let client = self
662            .http_client
663            .as_ref()
664            .ok_or_else(|| Error::Networking("HTTP client not available".to_string()))?;
665
666        // Send the message to the endpoint via HTTP POST
667        let response = client
668            .post(endpoint)
669            .header("Content-Type", "application/didcomm-encrypted+json")
670            .body(packed_message.to_string())
671            .send()
672            .await
673            .map_err(|e| Error::Networking(format!("Failed to send message to endpoint: {}", e)))?;
674
675        // Get the status code
676        let status = response.status().as_u16();
677
678        // Log the response status
679        println!("Message sent to endpoint {}, status: {}", endpoint, status);
680
681        Ok(status)
682    }
683
684    #[cfg(any(not(feature = "native"), target_arch = "wasm32"))]
685    pub async fn send_to_endpoint(&self, _packed_message: &str, _endpoint: &str) -> Result<u16> {
686        // Feature not enabled or WASM doesn't have http_client
687        Err(crate::error::Error::NotImplemented(
688            "HTTP client not available".to_string(),
689        ))
690    }
691}
692
693#[async_trait]
694#[cfg(not(target_arch = "wasm32"))]
695impl crate::agent::Agent for TapAgent {
696    fn get_agent_did(&self) -> &str {
697        &self.config.agent_did
698    }
699
700    async fn get_service_endpoint(&self, to: &str) -> Result<Option<String>> {
701        // If it's a URL, return it directly
702        if to.starts_with("http://") || to.starts_with("https://") {
703            return Ok(Some(to.to_string()));
704        }
705
706        // If it's a DID, try to find a service endpoint using the resolver
707        if to.starts_with("did:") {
708            // Use the DID resolver from the AgentKeyManager to get the service endpoints
709            // For now, we'll use a simple approach that looks for DIDCommMessaging or Web service types
710
711            // For testing purposes, attempt to check if TapAgent has a resolver field
712            #[cfg(test)]
713            if let Some(resolver) = self.resolver.as_ref() {
714                if let Ok(Some(did_doc)) = resolver.resolve(to).await {
715                    // Look for services of type DIDCommMessaging first
716                    if let Some(service) = did_doc
717                        .service
718                        .iter()
719                        .find(|s| s.type_ == "DIDCommMessaging")
720                    {
721                        return Ok(Some(service.service_endpoint.clone()));
722                    }
723
724                    // Then try Web type
725                    if let Some(service) = did_doc.service.iter().find(|s| s.type_ == "Web") {
726                        return Ok(Some(service.service_endpoint.clone()));
727                    }
728
729                    // No matching service found in DID doc
730                    if !did_doc.service.is_empty() {
731                        // Use the first service as fallback
732                        return Ok(Some(did_doc.service[0].service_endpoint.clone()));
733                    }
734                }
735            }
736
737            // Fallback to a placeholder URL if no resolver is available or no service found
738            return Ok(Some(format!(
739                "https://example.com/did/{}",
740                to.replace(":", "_")
741            )));
742        }
743
744        // No service endpoint found
745        Ok(None)
746    }
747
748    async fn send_message<
749        T: TapMessageBody + serde::Serialize + Send + Sync + std::fmt::Debug + 'static,
750    >(
751        &self,
752        message: &T,
753        to: Vec<&str>,
754        deliver: bool,
755    ) -> Result<(String, Vec<DeliveryResult>)> {
756        if to.is_empty() {
757            return Err(Error::Validation("No recipients specified".to_string()));
758        }
759
760        // Log the plaintext message
761        println!("\n==== SENDING TAP MESSAGE ====");
762        println!("Message Type: {}", T::message_type());
763        println!("Recipients: {:?}", to);
764        println!(
765            "--- PLAINTEXT CONTENT ---\n{}",
766            serde_json::to_string_pretty(message).unwrap_or_else(|_| format!("{:?}", message))
767        );
768
769        // Convert the TapMessageBody to a PlainMessage with explicit routing
770        let plain_message =
771            message.to_didcomm_with_route(self.get_agent_did(), to.iter().copied())?;
772
773        // Determine the appropriate security mode
774        let security_mode = self.determine_security_mode::<T>();
775        println!("Security Mode: {:?}", security_mode);
776
777        // For each recipient, look up service endpoint before sending
778        for recipient in &to {
779            if let Ok(Some(endpoint)) = self.get_service_endpoint(recipient).await {
780                println!("Found service endpoint for {}: {}", recipient, endpoint);
781            }
782        }
783
784        // Create pack options for the plaintext message
785        let pack_options = PackOptions {
786            security_mode,
787            sender_kid: Some(format!("{}#keys-1", self.get_agent_did())),
788            recipient_kid: if to.len() == 1 {
789                Some(format!("{}#keys-1", to[0]))
790            } else {
791                None
792            },
793        };
794
795        // Pack the plain message using the Packable trait
796        let packed = plain_message.pack(&*self.key_manager, pack_options).await?;
797
798        // Log the packed message
799        println!("--- PACKED MESSAGE ---");
800        println!(
801            "{}",
802            serde_json::from_str::<Value>(&packed)
803                .map(|v| serde_json::to_string_pretty(&v).unwrap_or(packed.clone()))
804                .unwrap_or(packed.clone())
805        );
806        println!("=====================");
807
808        // If delivery is not requested, just return the packed message
809        if !deliver {
810            return Ok((packed, Vec::new()));
811        }
812
813        // Try to deliver the message to each recipient's service endpoint
814        let mut delivery_results = Vec::new();
815
816        for recipient in &to {
817            match self.get_service_endpoint(recipient).await {
818                Ok(Some(endpoint)) => {
819                    println!("Delivering message to {} at {}", recipient, endpoint);
820
821                    // Extract message ID for logging
822                    let message_id = match serde_json::from_str::<Value>(&packed) {
823                        Ok(json) => json
824                            .get("id")
825                            .and_then(|id| id.as_str())
826                            .map(String::from)
827                            .unwrap_or_else(|| "unknown".to_string()),
828                        Err(_) => "unknown".to_string(),
829                    };
830
831                    // Attempt to deliver the message
832                    match self.send_to_endpoint(&packed, &endpoint).await {
833                        Ok(status) => {
834                            println!(
835                                "✅ Delivered message {} to {} at {}",
836                                message_id, recipient, endpoint
837                            );
838
839                            delivery_results.push(DeliveryResult {
840                                did: recipient.to_string(),
841                                endpoint: endpoint.clone(),
842                                status: Some(status),
843                                error: None,
844                            });
845                        }
846                        Err(e) => {
847                            // Log error but don't fail
848                            let error_msg = format!(
849                                "Failed to deliver message {} to {} at {}: {}",
850                                message_id, recipient, endpoint, e
851                            );
852                            println!("❌ {}", error_msg);
853
854                            delivery_results.push(DeliveryResult {
855                                did: recipient.to_string(),
856                                endpoint: endpoint.clone(),
857                                status: None,
858                                error: Some(error_msg),
859                            });
860                        }
861                    }
862                }
863                Ok(None) => {
864                    println!(
865                        "⚠️ No service endpoint found for {}, skipping delivery",
866                        recipient
867                    );
868                }
869                Err(e) => {
870                    // Log error but don't fail
871                    let error_msg = format!(
872                        "Failed to resolve service endpoint for {}: {}",
873                        recipient, e
874                    );
875                    println!("❌ {}", error_msg);
876                }
877            }
878        }
879
880        Ok((packed, delivery_results))
881    }
882
883    async fn receive_message<T: TapMessageBody + DeserializeOwned + Send>(
884        &self,
885        packed_message: &str,
886    ) -> Result<T> {
887        // Log the received packed message
888        println!("\n==== RECEIVING TAP MESSAGE ====");
889        println!("--- PACKED MESSAGE ---");
890        println!(
891            "{}",
892            serde_json::from_str::<Value>(packed_message)
893                .map(|v| serde_json::to_string_pretty(&v).unwrap_or(packed_message.to_string()))
894                .unwrap_or(packed_message.to_string())
895        );
896        println!("---------------------");
897
898        // Create unpack options
899        let unpack_options = UnpackOptions {
900            expected_security_mode: SecurityMode::Any,
901            expected_recipient_kid: Some(format!("{}#keys-1", self.get_agent_did())),
902            require_signature: false,
903        };
904
905        // Unpack the message using the Unpackable trait
906        let plain_message: PlainMessage = String::unpack(
907            &packed_message.to_string(),
908            &*self.key_manager,
909            unpack_options,
910        )
911        .await?;
912
913        // Log the unpacked message
914        println!("--- UNPACKED CONTENT ---");
915        println!(
916            "{}",
917            serde_json::to_string_pretty(&plain_message)
918                .unwrap_or_else(|_| format!("{:?}", plain_message))
919        );
920        println!("------------------------");
921
922        // Get the message type from the unpacked message
923        let message_type = &plain_message.type_;
924
925        // Validate the message type
926        if message_type != T::message_type() {
927            println!(
928                "❌ Message type validation failed: expected {}, got {}",
929                T::message_type(),
930                message_type
931            );
932            return Err(Error::Validation(format!(
933                "Expected message type {} but got {}",
934                T::message_type(),
935                message_type
936            )));
937        }
938        println!("✅ Message type validation passed: {}", message_type);
939
940        // Deserialize the message body into the expected type
941        serde_json::from_value::<T>(plain_message.body.clone())
942            .map_err(|e| Error::Serialization(format!("Failed to deserialize message: {}", e)))
943    }
944}