tap_node/resolver/
mod.rs

1//! DID resolution for TAP Node
2//!
3//! This module provides DID resolution capabilities for the TAP Node.
4
5use std::collections::HashMap;
6use std::sync::Arc;
7
8use base58::ToBase58;
9use serde_json::json;
10use sha2::{Digest, Sha256};
11use tap_agent::did::SyncDIDResolver;
12use tokio::sync::RwLock;
13
14use crate::error::{Error, Result};
15
16/// Generate a hash of a DID for testing purposes
17fn hash_did(did: &str) -> Result<String> {
18    let mut hasher = Sha256::new();
19    hasher.update(did.as_bytes());
20    let result = hasher.finalize();
21    Ok(result.to_vec().to_base58())
22}
23
24/// Node DID resolver
25///
26/// This resolver combines multiple DID resolvers for different DID methods,
27/// providing a unified interface for resolving DIDs across various methods.
28///
29/// The resolver supports:
30/// - did:key method, which is used for keys represented as DIDs
31/// - Multi-resolver, which combines multiple method-specific resolvers
32///
33/// # Resolution Process
34///
35/// When a DID is received for resolution:
36///
37/// 1. The method is extracted from the DID (e.g., "key" from "did:key:z6Mk...")
38/// 2. The appropriate resolver for that method is selected
39/// 3. The resolver processes the DID and returns a DID Document
40/// 4. The DID Document provides cryptographic material and service endpoints
41///
42/// # Thread Safety
43///
44/// The NodeResolver is thread-safe and can be safely shared across threads
45/// using `Arc<NodeResolver>`. All mutable state is protected by RwLock.
46#[derive(Default)]
47pub struct NodeResolver {
48    /// Resolvers for different DID methods
49    resolvers: RwLock<HashMap<String, Arc<dyn SyncDIDResolver>>>,
50}
51
52impl NodeResolver {
53    /// Create a new node resolver with default resolvers
54    pub fn new() -> Self {
55        // Create a HashMap to store our resolvers
56        let mut resolvers_map = HashMap::new();
57
58        // Create a MultiResolver that can handle multiple methods
59        let multi_resolver = tap_agent::did::MultiResolver::default();
60        resolvers_map.insert(
61            "multi".to_string(),
62            Arc::new(multi_resolver) as Arc<dyn SyncDIDResolver>,
63        );
64
65        // Initialize the resolvers RwLock with our map
66        let resolvers = RwLock::new(resolvers_map);
67
68        Self { resolvers }
69    }
70
71    /// Add a resolver for a DID method
72    pub async fn add_resolver(&self, method: String, resolver: Arc<dyn SyncDIDResolver>) {
73        let mut resolvers = self.resolvers.write().await;
74        resolvers.insert(method, resolver);
75    }
76
77    /// Get a resolver for a DID method
78    pub async fn get_resolver(&self, did: &str) -> Option<Arc<dyn SyncDIDResolver>> {
79        // Extract the method from the DID
80        let parts: Vec<&str> = did.split(':').collect();
81        if parts.len() < 3 || parts[0] != "did" {
82            return None;
83        }
84
85        let method = parts[1].to_string();
86
87        // Get the resolver for this method
88        let resolvers = self.resolvers.read().await;
89        resolvers.get(&method).cloned()
90    }
91
92    /// Resolve a DID to a DID Document
93    ///
94    /// This method takes a DID and returns the corresponding DID Document.
95    /// The DID Document contains the cryptographic material and service endpoints
96    /// associated with the DID.
97    ///
98    /// # Parameters
99    ///
100    /// * `did` - The DID to resolve
101    ///
102    /// # Returns
103    ///
104    /// The DID Document as a JSON Value
105    ///
106    /// # Errors
107    ///
108    /// Returns an error if:
109    /// - No resolver is found for the DID method
110    /// - The DID resolution fails
111    /// - The DID Document cannot be serialized to JSON
112    pub async fn resolve(&self, did: &str) -> Result<serde_json::Value> {
113        // First try to use a method-specific resolver
114        let method_parts: Vec<&str> = did.split(':').collect();
115        if method_parts.len() >= 3 && method_parts[0] == "did" {
116            let _method = method_parts[1];
117
118            // Get a resolver for this method
119            let resolver = self
120                .get_resolver(did)
121                .await
122                .ok_or_else(|| Error::Resolver(format!("No resolver found for DID: {}", did)))?;
123
124            // Resolve the DID
125            let did_doc_option = resolver
126                .resolve(did)
127                .await
128                .map_err(|e| Error::Resolver(format!("Failed to resolve DID {}: {}", did, e)))?;
129
130            // Check if we got a DID Document
131            if let Some(did_doc) = did_doc_option {
132                // Serialize the DID Document to JSON
133                return serde_json::to_value(did_doc).map_err(Error::Serialization);
134            }
135        }
136
137        // If we couldn't resolve with a method-specific resolver or the DID format was invalid,
138        // fall back to a simple hash-based approach for testing/development
139        let hash = hash_did(did)?;
140
141        // Create a simple DID Document
142        serde_json::to_value(json!({
143            "id": did,
144            "verificationMethod": [
145                {
146                    "id": format!("{}#keys-1", did),
147                    "type": "Ed25519VerificationKey2018",
148                    "controller": did,
149                    "publicKeyBase58": hash
150                }
151            ],
152            "authentication": [
153                format!("{}#keys-1", did)
154            ],
155            "service": []
156        }))
157        .map_err(Error::Serialization)
158    }
159}