use std::{collections::HashMap, fs, path::Path, sync::RwLock};
use ed25519_dalek::SigningKey;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{InMemory, TreeHeightsCache, TreeTipsCache};
use crate::{
Error, Result,
auth::crypto::ED25519_PRIVATE_KEY_SIZE,
backend::{VerificationStatus, errors::BackendError},
entry::{Entry, ID},
};
#[derive(Serialize, Deserialize)]
struct SerializableDatabase {
entries: HashMap<ID, Entry>,
#[serde(default)]
verification_status: HashMap<ID, VerificationStatus>,
#[serde(default)]
private_keys_bytes: HashMap<String, [u8; ED25519_PRIVATE_KEY_SIZE]>,
#[serde(default)]
cache: HashMap<String, String>,
#[serde(default)]
heights: HashMap<ID, TreeHeightsCache>,
#[serde(default)]
tips: HashMap<ID, TreeTipsCache>,
}
impl Serialize for InMemory {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
let entries = self.entries.read().unwrap().clone();
let verification_status = self.verification_status.read().unwrap().clone();
let private_keys = self.private_keys.read().unwrap();
let private_keys_bytes = private_keys
.iter()
.map(|(k, v)| (k.clone(), v.to_bytes()))
.collect();
let cache = self.cache.read().unwrap().clone();
let heights = self.heights.read().unwrap().clone();
let tips = self.tips.read().unwrap().clone();
let serializable = SerializableDatabase {
entries,
verification_status,
private_keys_bytes,
cache,
heights,
tips,
};
serializable.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for InMemory {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let serializable = SerializableDatabase::deserialize(deserializer)?;
let private_keys = serializable
.private_keys_bytes
.into_iter()
.map(|(k, bytes)| {
let signing_key = SigningKey::from_bytes(&bytes);
(k, signing_key)
})
.collect();
Ok(InMemory {
entries: RwLock::new(serializable.entries),
verification_status: RwLock::new(serializable.verification_status),
private_keys: RwLock::new(private_keys),
cache: RwLock::new(serializable.cache),
heights: RwLock::new(serializable.heights),
tips: RwLock::new(serializable.tips),
})
}
}
pub(crate) fn save_to_file<P: AsRef<Path>>(backend: &InMemory, path: P) -> Result<()> {
let json = serde_json::to_string_pretty(backend)
.map_err(|e| -> Error { BackendError::SerializationFailed { source: e }.into() })?;
fs::write(path, json).map_err(|e| -> Error { BackendError::FileIo { source: e }.into() })
}
pub(crate) fn load_from_file<P: AsRef<Path>>(path: P) -> Result<InMemory> {
if !path.as_ref().exists() {
return Ok(InMemory::new());
}
let json = fs::read_to_string(path)
.map_err(|e| -> Error { BackendError::FileIo { source: e }.into() })?;
let database: InMemory = serde_json::from_str(&json)
.map_err(|e| -> Error { BackendError::DeserializationFailed { source: e }.into() })?;
Ok(database)
}