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}