use super::{
CONTENT_RECLAIM_CLOCK_ATTESTATION_ID_VERSION, CONTENT_RECLAIM_CLOCK_EVIDENCE_DOMAIN,
CONTENT_RECLAIM_CLOCK_EVIDENCE_SHA256_TAG, ContentId, ContentReclaimGrace, Digest, Error,
ObjectStoreReclamationEvidenceDigest, Result, Sha256, StorageDomainId, fmt, write_hex,
};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ContentReclaimClockAttestationId([u8; 16]);
impl ContentReclaimClockAttestationId {
pub fn generate() -> Result<Self> {
let mut bytes = [0_u8; 16];
getrandom::fill(&mut bytes).map_err(|error| {
Error::runtime_busy(format!(
"content reclaim-clock attestation entropy: {error}"
))
})?;
bytes[0] = CONTENT_RECLAIM_CLOCK_ATTESTATION_ID_VERSION;
Ok(Self(bytes))
}
pub fn from_bytes(bytes: [u8; 16]) -> Result<Self> {
if bytes[0] != CONTENT_RECLAIM_CLOCK_ATTESTATION_ID_VERSION {
return Err(Error::UnsupportedFormat {
message: format!(
"unsupported content reclaim-clock attestation identity version {}",
bytes[0]
),
});
}
Ok(Self(bytes))
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 16] {
self.0
}
}
impl fmt::Debug for ContentReclaimClockAttestationId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("ContentReclaimClockAttestationId(")?;
write_hex(formatter, &self.0)?;
formatter.write_str(")")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ContentReclaimClockCoordinatorId([u8; 16]);
impl ContentReclaimClockCoordinatorId {
#[must_use]
pub const fn from_bytes(bytes: [u8; 16]) -> Self {
Self(bytes)
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 16] {
self.0
}
}
impl fmt::Debug for ContentReclaimClockCoordinatorId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("ContentReclaimClockCoordinatorId(")?;
write_hex(formatter, &self.0)?;
formatter.write_str(")")
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ContentReclaimClockEvidenceDigest([u8; 33]);
impl ContentReclaimClockEvidenceDigest {
#[must_use]
pub fn for_bytes(evidence: &[u8]) -> Self {
let mut hasher = Sha256::new();
hasher.update(CONTENT_RECLAIM_CLOCK_EVIDENCE_DOMAIN);
hasher.update(evidence);
let mut bytes = [0_u8; 33];
bytes[0] = CONTENT_RECLAIM_CLOCK_EVIDENCE_SHA256_TAG;
bytes[1..].copy_from_slice(&hasher.finalize());
Self(bytes)
}
pub fn from_bytes(bytes: [u8; 33]) -> Result<Self> {
if bytes[0] != CONTENT_RECLAIM_CLOCK_EVIDENCE_SHA256_TAG {
return Err(Error::UnsupportedFormat {
message: format!(
"unsupported content reclaim-clock evidence digest algorithm {}",
bytes[0]
),
});
}
Ok(Self(bytes))
}
#[must_use]
pub const fn to_bytes(self) -> [u8; 33] {
self.0
}
}
impl fmt::Debug for ContentReclaimClockEvidenceDigest {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("ContentReclaimClockEvidenceDigest(")?;
write_hex(formatter, &self.0)?;
formatter.write_str(")")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ContentReclaimClockAttestation {
pub(in crate::content::reclaim) storage_domain_id: StorageDomainId,
pub(in crate::content::reclaim) content_id: ContentId,
pub(in crate::content::reclaim) attestation_id: ContentReclaimClockAttestationId,
pub(in crate::content::reclaim) coordinator_id: ContentReclaimClockCoordinatorId,
pub(in crate::content::reclaim) evidence_digest: ContentReclaimClockEvidenceDigest,
pub(in crate::content::reclaim) grace_started_at: crate::ReadVersion,
pub(in crate::content::reclaim) observed_at_unix_ms: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ContentReclaimSweep {
pub(in crate::content::reclaim) storage_domain_id: StorageDomainId,
pub(in crate::content::reclaim) content_id: ContentId,
pub(in crate::content::reclaim) backend: ContentReclaimSweepBackend,
pub(in crate::content::reclaim) prepared_at: crate::ReadVersion,
pub(in crate::content::reclaim) reclaimed_at: Option<crate::ReadVersion>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContentReclaimSweepBackend {
NativeFilesystem,
WasiFilesystem,
BrowserStorage,
ObjectStore {
evidence_digest: ObjectStoreReclamationEvidenceDigest,
},
}
impl ContentReclaimSweep {
#[must_use]
pub const fn storage_domain_id(self) -> StorageDomainId {
self.storage_domain_id
}
#[must_use]
pub const fn content_id(self) -> ContentId {
self.content_id
}
#[must_use]
pub const fn backend(self) -> ContentReclaimSweepBackend {
self.backend
}
#[must_use]
pub const fn prepared_at(self) -> crate::ReadVersion {
self.prepared_at
}
#[must_use]
pub const fn reclaimed_at(self) -> Option<crate::ReadVersion> {
self.reclaimed_at
}
}
impl ContentReclaimClockAttestation {
pub fn new(
grace: ContentReclaimGrace,
attestation_id: ContentReclaimClockAttestationId,
coordinator_id: ContentReclaimClockCoordinatorId,
evidence_digest: ContentReclaimClockEvidenceDigest,
observed_at_unix_ms: u64,
) -> Result<Self> {
if observed_at_unix_ms < grace.not_before_unix_ms() {
return Err(Error::invalid_options(
"content reclaim-clock observation precedes durable grace deadline",
));
}
Ok(Self {
storage_domain_id: grace.storage_domain_id(),
content_id: grace.content_id(),
attestation_id,
coordinator_id,
evidence_digest,
grace_started_at: grace.started_at(),
observed_at_unix_ms,
})
}
#[must_use]
pub const fn storage_domain_id(self) -> StorageDomainId {
self.storage_domain_id
}
#[must_use]
pub const fn content_id(self) -> ContentId {
self.content_id
}
#[must_use]
pub const fn attestation_id(self) -> ContentReclaimClockAttestationId {
self.attestation_id
}
#[must_use]
pub const fn coordinator_id(self) -> ContentReclaimClockCoordinatorId {
self.coordinator_id
}
#[must_use]
pub const fn evidence_digest(self) -> ContentReclaimClockEvidenceDigest {
self.evidence_digest
}
#[must_use]
pub const fn grace_started_at(self) -> crate::ReadVersion {
self.grace_started_at
}
#[must_use]
pub const fn observed_at_unix_ms(self) -> u64 {
self.observed_at_unix_ms
}
}