use std::ops::Deref;
use serde::{Deserialize, Serialize};
use crate::{Extensions, Header, OperationError, validate_backlink};
#[derive(Clone, Copy, Debug, Default, Hash, Eq, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PruneFlag(bool);
impl PruneFlag {
pub fn new(flag: bool) -> Self {
Self(flag)
}
pub fn is_set(&self) -> bool {
self.0
}
pub fn is_not_set(&self) -> bool {
!self.0
}
}
impl From<bool> for PruneFlag {
fn from(value: bool) -> Self {
Self(value)
}
}
impl Deref for PruneFlag {
type Target = bool;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub fn validate_prunable_backlink<E>(
past_header: Option<&Header<E>>,
header: &Header<E>,
prune_flag: bool,
) -> Result<(), OperationError>
where
E: Extensions,
{
if header.seq_num > 0 {
if !prune_flag {
match past_header {
Some(past_header) => validate_backlink(past_header, header),
None => Err(OperationError::BacklinkMissing),
}
} else {
Ok(())
}
} else {
match past_header {
Some(past_header) => validate_backlink(past_header, header),
None => Ok(()),
}
}
}
#[cfg(test)]
mod tests {
use serde::{Deserialize, Serialize};
use crate::cbor::{decode_cbor, encode_cbor};
use crate::{Hash, Header, SigningKey};
use super::{PruneFlag, validate_prunable_backlink};
#[test]
fn validate_pruned_log() {
let signing_key = SigningKey::generate();
let mut header = Header::<()> {
verifying_key: signing_key.verifying_key(),
seq_num: 7,
backlink: Some(Hash::digest([1, 2, 3])),
..Default::default()
};
header.sign(&signing_key);
assert!(validate_prunable_backlink(None, &header, false).is_err());
assert!(validate_prunable_backlink(None, &header, true).is_ok());
}
#[test]
fn seq_num_zero() {
let signing_key = SigningKey::generate();
let mut header = Header::<()> {
verifying_key: signing_key.verifying_key(),
..Default::default()
};
header.sign(&signing_key);
assert!(validate_prunable_backlink(None, &header, false).is_ok());
assert!(validate_prunable_backlink(None, &header, true).is_ok());
}
#[test]
fn strictly_growing_log() {
let signing_key = SigningKey::generate();
let mut header_0 = Header::<()> {
verifying_key: signing_key.verifying_key(),
..Default::default()
};
header_0.sign(&signing_key);
let mut header_1 = Header::<()> {
verifying_key: signing_key.verifying_key(),
seq_num: 1,
backlink: Some(header_0.hash()),
..Default::default()
};
header_1.sign(&signing_key);
assert!(validate_prunable_backlink(Some(&header_0), &header_1, true).is_ok());
assert!(validate_prunable_backlink(Some(&header_1), &header_0, false).is_err());
}
#[test]
fn prune_flag_encoding_is_short() {
let prune_flag = PruneFlag::default();
let bytes = encode_cbor(&prune_flag).unwrap();
assert_eq!(bytes.len(), 1);
}
#[test]
fn prune_flag_deref() {
let prune_flag = PruneFlag::default();
if *prune_flag {
panic!("should be false!");
}
}
#[test]
fn prune_flag_can_be_optional() {
#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
struct Extensions {
#[serde(
rename = "p",
skip_serializing_if = "PruneFlag::is_not_set",
default = "PruneFlag::default"
)]
pub prune_flag: PruneFlag,
}
let extensions = Extensions::default();
let bytes = encode_cbor(&extensions).unwrap();
assert_eq!(bytes.len(), 1);
let decoded: Extensions = decode_cbor(&bytes[..]).unwrap();
assert_eq!(extensions, decoded);
let extensions = Extensions {
prune_flag: true.into(),
};
let bytes = encode_cbor(&extensions).unwrap();
assert_eq!(bytes.len(), 4);
let decoded: Extensions = decode_cbor(&bytes[..]).unwrap();
assert_eq!(extensions, decoded);
}
}