use crate::{DIDWebVHError, SCID_HOLDER, log_entry::LogEntry, parameters::Parameters};
use affinidi_data_integrity::{DataIntegrityProof, SigningDocument};
use affinidi_secrets_resolver::secrets::Secret;
use chrono::Utc;
use serde_json::Value;
impl LogEntry {
pub async fn create_first_entry(
version_time: Option<String>,
document: &Value,
parameters: &Parameters,
secret: &Secret,
) -> Result<LogEntry, DIDWebVHError> {
let now = Utc::now();
let mut parameters = parameters.clone();
parameters.scid = Some(SCID_HOLDER.to_string());
let vm_id = if let Some(Some(value)) = ¶meters.update_keys {
if let Some(key) = value.iter().next() {
["did:key:", key, "#", key].concat()
} else {
return Err(DIDWebVHError::SCIDError(
"No update keys provided in parameters".to_string(),
));
}
} else {
return Err(DIDWebVHError::SCIDError(
"No update keys provided in parameters".to_string(),
));
};
if secret.id != vm_id {
return Err(DIDWebVHError::SCIDError(format!(
"Secret key ID {} does not match VerificationMethod ID {}",
secret.id, vm_id
)));
}
let log_entry = LogEntry {
version_id: SCID_HOLDER.to_string(),
version_time: version_time
.unwrap_or_else(|| now.to_rfc3339_opts(chrono::SecondsFormat::Secs, true)),
parameters,
state: document.clone(),
proof: None,
};
let scid = log_entry.generate_scid()?;
let le_str = serde_json::to_string(&log_entry).map_err(|e| {
DIDWebVHError::SCIDError(format!(
"Couldn't serialize LogEntry to JSON. Reason: {}",
e
))
})?;
let mut log_entry: LogEntry = serde_json::from_str(&le_str.replace(SCID_HOLDER, &scid))
.map_err(|e| {
DIDWebVHError::SCIDError(format!(
"Couldn't deserialize LogEntry from SCID conversion. Reason: {}",
e
))
})?;
let entry_hash = log_entry.generate_log_entry_hash().map_err(|e| {
DIDWebVHError::SCIDError(format!(
"Couldn't generate entryHash for first LogEntry. Reason: {}",
e
))
})?;
log_entry.version_id = ["1-", &entry_hash].concat();
let mut log_entry_unsigned: SigningDocument = (&log_entry).try_into()?;
DataIntegrityProof::sign_jcs_data(&mut log_entry_unsigned, secret).map_err(|e| {
DIDWebVHError::SCIDError(format!(
"Couldn't generate Data Integrity Proof for LogEntry. Reason: {}",
e
))
})?;
log_entry.proof = log_entry_unsigned.proof;
Ok(log_entry)
}
pub async fn create_new_log_entry(
previous_log_entry: &LogEntry,
version_time: Option<String>,
document: &Value,
parameters: &Parameters,
secret: &Secret,
) -> Result<LogEntry, DIDWebVHError> {
let mut new_entry = LogEntry {
version_id: previous_log_entry.version_id.clone(),
version_time: version_time
.unwrap_or_else(|| Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true)),
parameters: parameters.clone(),
state: document.clone(),
proof: None,
};
let entry_hash = new_entry.generate_log_entry_hash().map_err(|e| {
DIDWebVHError::SCIDError(format!(
"Couldn't generate entryHash for LogEntry. Reason: {}",
e
))
})?;
let (current_id, _) = LogEntry::get_version_id_fields(&new_entry.version_id)?;
new_entry.version_id = [&(current_id + 1).to_string(), "-", &entry_hash].concat();
let mut log_entry_unsigned: SigningDocument = (&new_entry).try_into()?;
DataIntegrityProof::sign_jcs_data(&mut log_entry_unsigned, secret).map_err(|e| {
DIDWebVHError::SCIDError(format!(
"Couldn't generate Data Integrity Proof for LogEntry. Reason: {}",
e
))
})?;
new_entry.proof = log_entry_unsigned.proof;
Ok(new_entry)
}
}