fix: prevent tokio runtime deadlock in credential issue/verify
The credential issuance and verification handlers used Handle::block_on() directly inside the tokio runtime, causing a deadlock. Wrapped with block_in_place() to properly yield the runtime thread. Also completed full feature verification across all 25 test groups (~175 checks) on live server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
5ce8b7965c
commit
e3aa95a103
@@ -0,0 +1,150 @@
|
||||
use super::RpcHandler;
|
||||
use crate::credentials;
|
||||
use crate::identity_manager::IdentityManager;
|
||||
use anyhow::Result;
|
||||
|
||||
impl RpcHandler {
|
||||
/// Issue a Verifiable Credential from one of the user's identities.
|
||||
pub(super) async fn handle_identity_issue_credential(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
let issuer_id = params
|
||||
.get("issuer_id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing issuer_id"))?;
|
||||
let subject_did = params
|
||||
.get("subject_did")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing subject_did"))?;
|
||||
let credential_type = params
|
||||
.get("type")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("VerifiableCredential");
|
||||
let claims = params
|
||||
.get("claims")
|
||||
.cloned()
|
||||
.unwrap_or(serde_json::json!({}));
|
||||
let expires_at = params.get("expires_at").and_then(|v| v.as_str());
|
||||
|
||||
let manager = IdentityManager::new(&self.config.data_dir).await?;
|
||||
let issuer_record = manager.get(issuer_id).await?;
|
||||
let issuer_did = issuer_record.did.clone();
|
||||
|
||||
// Capture identity_id for the signing closure
|
||||
let data_dir = self.config.data_dir.clone();
|
||||
let sign_id = issuer_id.to_string();
|
||||
|
||||
let vc = credentials::issue_credential(
|
||||
&self.config.data_dir,
|
||||
&issuer_did,
|
||||
subject_did,
|
||||
credential_type,
|
||||
claims,
|
||||
expires_at,
|
||||
|bytes| {
|
||||
// Use block_in_place to avoid deadlocking the tokio runtime
|
||||
let hex_msg = hex::encode(bytes);
|
||||
tokio::task::block_in_place(|| {
|
||||
let rt = tokio::runtime::Handle::current();
|
||||
rt.block_on(async {
|
||||
let mgr = IdentityManager::new(&data_dir).await?;
|
||||
mgr.sign(&sign_id, hex_msg.as_bytes()).await
|
||||
})
|
||||
})
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"id": vc.id,
|
||||
"issuer": vc.issuer,
|
||||
"subject": vc.subject,
|
||||
"type": vc.credential_type,
|
||||
"issued_at": vc.issued_at,
|
||||
"status": vc.status,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Verify a credential by its ID.
|
||||
pub(super) async fn handle_identity_verify_credential(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
let credential_id = params
|
||||
.get("id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing id"))?;
|
||||
|
||||
let store = credentials::load_credentials(&self.config.data_dir).await?;
|
||||
let vc = store
|
||||
.credentials
|
||||
.iter()
|
||||
.find(|c| c.id == credential_id)
|
||||
.ok_or_else(|| anyhow::anyhow!("Credential not found"))?;
|
||||
|
||||
let data_dir = self.config.data_dir.clone();
|
||||
let valid = credentials::verify_credential(vc, |did, bytes, signature| {
|
||||
let hex_msg = hex::encode(bytes);
|
||||
tokio::task::block_in_place(|| {
|
||||
let rt = tokio::runtime::Handle::current();
|
||||
rt.block_on(async {
|
||||
let mgr = IdentityManager::new(&data_dir).await?;
|
||||
mgr.verify(did, hex_msg.as_bytes(), signature).await
|
||||
})
|
||||
})
|
||||
})?;
|
||||
|
||||
Ok(serde_json::json!({
|
||||
"id": vc.id,
|
||||
"valid": valid,
|
||||
"status": vc.status,
|
||||
}))
|
||||
}
|
||||
|
||||
/// List all credentials, optionally filtered by DID.
|
||||
pub(super) async fn handle_identity_list_credentials(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let filter_did = params
|
||||
.as_ref()
|
||||
.and_then(|p| p.get("did"))
|
||||
.and_then(|v| v.as_str());
|
||||
|
||||
let creds = credentials::list_credentials(&self.config.data_dir, filter_did).await?;
|
||||
let items: Vec<serde_json::Value> = creds
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
serde_json::json!({
|
||||
"id": c.id,
|
||||
"issuer": c.issuer,
|
||||
"subject": c.subject,
|
||||
"type": c.credential_type,
|
||||
"claims": c.claims,
|
||||
"issued_at": c.issued_at,
|
||||
"expires_at": c.expires_at,
|
||||
"status": c.status,
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
Ok(serde_json::json!({ "credentials": items }))
|
||||
}
|
||||
|
||||
/// Revoke a credential.
|
||||
pub(super) async fn handle_identity_revoke_credential(
|
||||
&self,
|
||||
params: Option<serde_json::Value>,
|
||||
) -> Result<serde_json::Value> {
|
||||
let params = params.ok_or_else(|| anyhow::anyhow!("Missing params"))?;
|
||||
let id = params
|
||||
.get("id")
|
||||
.and_then(|v| v.as_str())
|
||||
.ok_or_else(|| anyhow::anyhow!("Missing id"))?;
|
||||
|
||||
credentials::revoke_credential(&self.config.data_dir, id).await?;
|
||||
Ok(serde_json::json!({ "ok": true }))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user