1use core::fmt;
6use std::{borrow::Cow, collections::HashMap};
7
8use serde::{Deserialize, Serialize};
9use serde_json::Value;
10use ssi_jwk::JWK;
11use ssi_verification_methods_core::ProofPurpose;
12
13use crate::{
14 document::{self, DIDVerificationMethod, Service},
15 DIDBuf, DIDMethod, DIDURLBuf,
16};
17
18#[derive(Debug, Serialize, Deserialize, Clone)]
26#[serde(tag = "didDocumentOperation", content = "didDocument")]
27#[serde(rename_all = "camelCase")]
28#[allow(clippy::large_enum_variant)]
29pub enum DIDDocumentOperation {
30 SetDidDocument(document::Represented),
34
35 AddToDidDocument(HashMap<String, Value>),
39
40 RemoveFromDidDocument(Vec<String>),
44
45 SetVerificationMethod {
47 vmm: DIDVerificationMethod,
48 purposes: Vec<ProofPurpose>,
49 },
50
51 SetService(Service),
53
54 RemoveVerificationMethod(DIDURLBuf),
56
57 RemoveService(DIDURLBuf),
59}
60
61#[derive(Debug)]
62pub enum DIDDocumentOperationKind {
63 SetDidDocument,
64 AddToDidDocument,
65 RemoveFromDidDocument,
66 SetVerificationMethod,
67 SetService,
68 RemoveVerificationMethod,
69 RemoveService,
70}
71
72impl DIDDocumentOperationKind {
73 pub fn name(&self) -> &'static str {
74 match self {
75 Self::SetDidDocument => "setDidDocument",
76 Self::AddToDidDocument => "addToDidDocument",
77 Self::RemoveFromDidDocument => "removeFromDidDocument",
78 Self::SetVerificationMethod => "setVerificationMethod",
79 Self::SetService => "setService",
80 Self::RemoveVerificationMethod => "removeVerificationMethod",
81 Self::RemoveService => "removeService",
82 }
83 }
84}
85
86impl fmt::Display for DIDDocumentOperationKind {
87 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88 self.name().fmt(f)
89 }
90}
91
92pub struct DIDCreate {
96 pub update_key: Option<JWK>,
97 pub recovery_key: Option<JWK>,
98 pub verification_key: Option<JWK>,
99 pub options: HashMap<String, Value>,
100}
101
102pub struct DIDUpdate {
106 pub did: DIDBuf,
107 pub update_key: Option<JWK>,
108 pub new_update_key: Option<JWK>,
109 pub operation: DIDDocumentOperation,
110 pub options: HashMap<String, Value>,
111}
112
113pub struct DIDDeactivate {
117 pub did: DIDBuf,
118 pub key: Option<JWK>,
119 pub options: HashMap<String, Value>,
120}
121
122pub struct DIDRecover {
126 pub did: DIDBuf,
127 pub recovery_key: Option<JWK>,
128 pub new_update_key: Option<JWK>,
129 pub new_recovery_key: Option<JWK>,
130 pub new_verification_key: Option<JWK>,
131 pub options: HashMap<String, Value>,
132}
133
134#[derive(Debug, thiserror::Error)]
135pub enum DIDTransactionError {
136 #[error("unsupported DID method `{0}`")]
137 UnsupportedDIDMethod(String),
138
139 #[error("invalid transaction: {0}")]
140 InvalidTransaction(String),
141
142 #[error("transaction failed: {0}")]
143 Failed(String),
144}
145
146impl DIDTransactionError {
147 pub fn invalid(error: impl ToString) -> Self {
148 Self::InvalidTransaction(error.to_string())
149 }
150
151 pub fn failed(error: impl ToString) -> Self {
152 Self::Failed(error.to_string())
153 }
154}
155
156#[derive(Debug, thiserror::Error)]
157pub enum DIDTransactionCreationError {
158 #[error("unimplemented transaction `{0}`")]
159 UnimplementedTransaction(DIDTransactionKind),
160
161 #[error("unimplemented DID document operation: {0}")]
162 UnimplementedDocumentOperation(DIDDocumentOperationKind),
163
164 #[error("unsupported DID method `{0}`")]
165 UnsupportedDIDMethod(String),
166
167 #[error("unsupported option `{option}` for {operation} operation")]
168 UnsupportedOption {
169 operation: DIDTransactionKind,
170 option: String,
171 },
172
173 #[error("unsupported service property")]
174 UnsupportedServiceProperty,
175
176 #[error("missing required update key")]
177 MissingRequiredUpdateKey,
178
179 #[error("missing required new update key")]
180 MissingRequiredNewUpdateKey,
181
182 #[error("missing required recovery key")]
183 MissingRequiredRecoveryKey,
184
185 #[error("invalid update key")]
186 InvalidUpdateKey,
187
188 #[error("invalid recovery key")]
189 InvalidRecoveryKey,
190
191 #[error("invalid verification key")]
192 InvalidVerificationKey,
193
194 #[error("same update and recovery keys")]
195 SameUpdateAndRecoveryKeys,
196
197 #[error("update key unchanged")]
198 UpdateKeyUnchanged,
199
200 #[error("recovery key unchanged")]
201 RecoveryKeyUnchanged,
202
203 #[error("key generation failed")]
204 KeyGenerationFailed,
205
206 #[error("invalid DID")]
207 InvalidDID,
208
209 #[error("invalid DID URL")]
210 InvalidDIDURL,
211
212 #[error("invalid verification method")]
213 InvalidVerificationMethod,
214
215 #[error("signature failed")]
216 SignatureFailed,
217
218 #[error("missing service endpoint")]
219 MissingServiceEndpoint,
220
221 #[error("ambiguous service endpoint")]
222 AmbiguousServiceEndpoint,
223
224 #[error("ambiguous service type")]
225 AmbiguousServiceType,
226
227 #[error("unsupported service: {reason}")]
228 UnsupportedService { reason: Cow<'static, str> },
229
230 #[error("{0}")]
231 Internal(String),
232}
233
234impl DIDTransactionCreationError {
235 pub fn internal(e: impl ToString) -> Self {
236 Self::Internal(e.to_string())
237 }
238}
239
240#[derive(Debug)]
241pub enum DIDTransactionKind {
242 Create,
243 Update,
244 Deactivate,
245 Recover,
246}
247
248impl DIDTransactionKind {
249 pub fn name(&self) -> &'static str {
250 match self {
251 Self::Create => "create",
252 Self::Update => "update",
253 Self::Deactivate => "deactivate",
254 Self::Recover => "recover",
255 }
256 }
257}
258
259impl fmt::Display for DIDTransactionKind {
260 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
261 self.name().fmt(f)
262 }
263}
264
265pub struct DIDTransaction {
266 pub did_method: String,
268
269 pub value: Value,
271}
272
273impl DIDTransaction {
274 pub fn new(did_method: String, value: Value) -> Self {
275 Self { did_method, value }
276 }
277}
278
279pub trait DIDRegistry {
280 #[allow(async_fn_in_trait)]
281 async fn submit_transaction(
282 &self,
283 transaction: DIDTransaction,
284 ) -> Result<Value, DIDTransactionError>;
285
286 fn create(
288 &self,
289 _method: &str,
290 _create: DIDCreate,
291 ) -> Result<DIDTransaction, DIDTransactionCreationError> {
292 Err(DIDTransactionCreationError::UnimplementedTransaction(
293 DIDTransactionKind::Create,
294 ))
295 }
296
297 fn update(&self, _update: DIDUpdate) -> Result<DIDTransaction, DIDTransactionCreationError> {
299 Err(DIDTransactionCreationError::UnimplementedTransaction(
300 DIDTransactionKind::Update,
301 ))
302 }
303
304 fn deactivate(
306 &self,
307 _deactivate: DIDDeactivate,
308 ) -> Result<DIDTransaction, DIDTransactionCreationError> {
309 Err(DIDTransactionCreationError::UnimplementedTransaction(
310 DIDTransactionKind::Deactivate,
311 ))
312 }
313
314 fn recover(&self, _recover: DIDRecover) -> Result<DIDTransaction, DIDTransactionCreationError> {
316 Err(DIDTransactionCreationError::UnimplementedTransaction(
317 DIDTransactionKind::Recover,
318 ))
319 }
320}
321
322pub trait DIDMethodRegistry: DIDMethod {
323 #[allow(async_fn_in_trait)]
325 async fn submit_transaction(&self, transaction: Value) -> Result<Value, DIDTransactionError>;
326
327 fn create(&self, _create: DIDCreate) -> Result<Value, DIDTransactionCreationError> {
329 Err(DIDTransactionCreationError::UnimplementedTransaction(
330 DIDTransactionKind::Create,
331 ))
332 }
333
334 fn update(&self, _update: DIDUpdate) -> Result<Value, DIDTransactionCreationError> {
336 Err(DIDTransactionCreationError::UnimplementedTransaction(
337 DIDTransactionKind::Update,
338 ))
339 }
340
341 fn deactivate(&self, _deactivate: DIDDeactivate) -> Result<Value, DIDTransactionCreationError> {
343 Err(DIDTransactionCreationError::UnimplementedTransaction(
344 DIDTransactionKind::Deactivate,
345 ))
346 }
347
348 fn recover(&self, _recover: DIDRecover) -> Result<Value, DIDTransactionCreationError> {
350 Err(DIDTransactionCreationError::UnimplementedTransaction(
351 DIDTransactionKind::Recover,
352 ))
353 }
354}
355
356impl<M: DIDMethodRegistry> DIDRegistry for M {
357 #[allow(async_fn_in_trait)]
358 async fn submit_transaction(
359 &self,
360 transaction: DIDTransaction,
361 ) -> Result<Value, DIDTransactionError> {
362 if transaction.did_method == Self::DID_METHOD_NAME {
363 DIDMethodRegistry::submit_transaction(self, transaction.value).await
364 } else {
365 Err(DIDTransactionError::UnsupportedDIDMethod(
366 transaction.did_method,
367 ))
368 }
369 }
370
371 fn create(
373 &self,
374 method: &str,
375 create: DIDCreate,
376 ) -> Result<DIDTransaction, DIDTransactionCreationError> {
377 if method == Self::DID_METHOD_NAME {
378 Ok(DIDTransaction::new(
379 Self::DID_METHOD_NAME.to_owned(),
380 DIDMethodRegistry::create(self, create)?,
381 ))
382 } else {
383 Err(DIDTransactionCreationError::UnsupportedDIDMethod(
384 method.to_owned(),
385 ))
386 }
387 }
388
389 fn update(&self, update: DIDUpdate) -> Result<DIDTransaction, DIDTransactionCreationError> {
391 if update.did.method_name() == Self::DID_METHOD_NAME {
392 Ok(DIDTransaction::new(
393 Self::DID_METHOD_NAME.to_owned(),
394 DIDMethodRegistry::update(self, update)?,
395 ))
396 } else {
397 Err(DIDTransactionCreationError::UnsupportedDIDMethod(
398 update.did.method_name().to_owned(),
399 ))
400 }
401 }
402
403 fn deactivate(
405 &self,
406 deactivate: DIDDeactivate,
407 ) -> Result<DIDTransaction, DIDTransactionCreationError> {
408 if deactivate.did.method_name() == Self::DID_METHOD_NAME {
409 Ok(DIDTransaction::new(
410 Self::DID_METHOD_NAME.to_owned(),
411 DIDMethodRegistry::deactivate(self, deactivate)?,
412 ))
413 } else {
414 Err(DIDTransactionCreationError::UnsupportedDIDMethod(
415 deactivate.did.method_name().to_owned(),
416 ))
417 }
418 }
419
420 fn recover(&self, _recover: DIDRecover) -> Result<DIDTransaction, DIDTransactionCreationError> {
422 Err(DIDTransactionCreationError::UnimplementedTransaction(
423 DIDTransactionKind::Recover,
424 ))
425 }
426}