use crate::prelude::*;
use arbitrary::{Arbitrary, Unstructured};
use contrafact::*;
use holo_hash::*;
#[derive(Default)]
struct ValidChainFact {
hash: Option<ActionHash>,
seq: u32,
}
impl Fact<Action> for ValidChainFact {
fn check(&self, action: &Action) -> Check {
let action_hash = ActionHash::with_data_sync(action);
let result = match (action.prev_action(), self.hash.as_ref()) {
(Some(prev), Some(stored)) => {
if prev == stored {
Check::pass()
} else {
vec![format!("Hashes don't match: {} != {}", prev, stored)].into()
}
}
(None, None) => Check::pass(),
(None, Some(_)) => vec![format!(
"Found Dna in position other than beginning of the chain. Hash: {}",
action_hash
)]
.into(),
(Some(_), None) => vec![format!(
"First action must be of type Dna, but instead got type {:?}",
action.action_type()
)]
.into(),
};
result
}
fn mutate(&self, action: &mut Action, u: &mut Unstructured<'static>) {
if let Some(stored_hash) = self.hash.as_ref() {
while action.prev_action().is_none() {
*action = Action::arbitrary(u).unwrap();
}
*action.prev_action_mut().unwrap() = stored_hash.clone();
*action.action_seq_mut().unwrap() = self.seq;
} else {
*action = Action::Dna(Dna::arbitrary(u).unwrap());
}
}
fn advance(&mut self, action: &Action) {
self.hash = Some(ActionHash::with_data_sync(action));
self.seq += 1;
}
}
pub fn is_of_type(action_type: ActionType) -> Facts<'static, Action> {
facts![brute("action is of type", move |h: &Action| h
.action_type()
== action_type)]
}
pub fn is_new_entry_action() -> Facts<'static, Action> {
facts![or(
"is NewEntryAction",
is_of_type(ActionType::Create),
is_of_type(ActionType::Update)
)]
}
pub fn valid_chain() -> Facts<'static, Action> {
facts![ValidChainFact::default(),]
}
pub fn new_entry_action() -> Facts<'static, Action> {
facts![brute("Is a NewEntryAction", |h: &Action| {
matches!(h.action_type(), ActionType::Create | ActionType::Update)
}),]
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_chain_fact() {
let mut u = Unstructured::new(&crate::NOISE);
let chain = build_seq(&mut u, 5, valid_chain());
check_seq(chain.as_slice(), valid_chain()).unwrap();
let hashes: Vec<_> = chain.iter().map(ActionHash::with_data_sync).collect();
let backlinks: Vec<_> = chain
.iter()
.filter_map(|h| h.prev_action())
.cloned()
.collect();
let action_seqs: Vec<_> = chain.iter().map(|h| h.action_seq()).collect();
assert_eq!(hashes[0..chain.len() - 1], backlinks[..]);
assert_eq!(action_seqs, vec![0, 1, 2, 3, 4]);
}
}
pub trait ActionRefMut {
fn author_mut(&mut self) -> &mut AgentPubKey;
fn action_seq_mut(&mut self) -> Option<&mut u32>;
fn prev_action_mut(&mut self) -> Option<&mut ActionHash>;
fn entry_data_mut(&mut self) -> Option<(&mut EntryHash, &mut EntryType)>;
fn timestamp_mut(&mut self) -> &mut Timestamp;
}
impl ActionRefMut for Action {
fn author_mut(&mut self) -> &mut AgentPubKey {
match *self {
Self::Dna(Dna { ref mut author, .. })
| Self::AgentValidationPkg(AgentValidationPkg { ref mut author, .. })
| Self::InitZomesComplete(InitZomesComplete { ref mut author, .. })
| Self::CreateLink(CreateLink { ref mut author, .. })
| Self::DeleteLink(DeleteLink { ref mut author, .. })
| Self::Delete(Delete { ref mut author, .. })
| Self::CloseChain(CloseChain { ref mut author, .. })
| Self::OpenChain(OpenChain { ref mut author, .. })
| Self::Create(Create { ref mut author, .. })
| Self::Update(Update { ref mut author, .. }) => author,
}
}
fn timestamp_mut(&mut self) -> &mut Timestamp {
match *self {
Self::Dna(Dna {
ref mut timestamp, ..
})
| Self::AgentValidationPkg(AgentValidationPkg {
ref mut timestamp, ..
})
| Self::InitZomesComplete(InitZomesComplete {
ref mut timestamp, ..
})
| Self::CreateLink(CreateLink {
ref mut timestamp, ..
})
| Self::DeleteLink(DeleteLink {
ref mut timestamp, ..
})
| Self::Delete(Delete {
ref mut timestamp, ..
})
| Self::CloseChain(CloseChain {
ref mut timestamp, ..
})
| Self::OpenChain(OpenChain {
ref mut timestamp, ..
})
| Self::Create(Create {
ref mut timestamp, ..
})
| Self::Update(Update {
ref mut timestamp, ..
}) => timestamp,
}
}
fn action_seq_mut(&mut self) -> Option<&mut u32> {
match *self {
Self::Dna(Dna { .. }) => None,
Self::AgentValidationPkg(AgentValidationPkg {
ref mut action_seq, ..
})
| Self::InitZomesComplete(InitZomesComplete {
ref mut action_seq, ..
})
| Self::CreateLink(CreateLink {
ref mut action_seq, ..
})
| Self::DeleteLink(DeleteLink {
ref mut action_seq, ..
})
| Self::Delete(Delete {
ref mut action_seq, ..
})
| Self::CloseChain(CloseChain {
ref mut action_seq, ..
})
| Self::OpenChain(OpenChain {
ref mut action_seq, ..
})
| Self::Create(Create {
ref mut action_seq, ..
})
| Self::Update(Update {
ref mut action_seq, ..
}) => Some(action_seq),
}
}
fn prev_action_mut(&mut self) -> Option<&mut ActionHash> {
match self {
Self::Dna(Dna { .. }) => None,
Self::AgentValidationPkg(AgentValidationPkg {
ref mut prev_action,
..
}) => Some(prev_action),
Self::InitZomesComplete(InitZomesComplete {
ref mut prev_action,
..
}) => Some(prev_action),
Self::CreateLink(CreateLink {
ref mut prev_action,
..
}) => Some(prev_action),
Self::DeleteLink(DeleteLink {
ref mut prev_action,
..
}) => Some(prev_action),
Self::Delete(Delete {
ref mut prev_action,
..
}) => Some(prev_action),
Self::CloseChain(CloseChain {
ref mut prev_action,
..
}) => Some(prev_action),
Self::OpenChain(OpenChain {
ref mut prev_action,
..
}) => Some(prev_action),
Self::Create(Create {
ref mut prev_action,
..
}) => Some(prev_action),
Self::Update(Update {
ref mut prev_action,
..
}) => Some(prev_action),
}
}
fn entry_data_mut(&mut self) -> Option<(&mut EntryHash, &mut EntryType)> {
match self {
Self::Create(Create {
ref mut entry_hash,
ref mut entry_type,
..
}) => Some((entry_hash, entry_type)),
Self::Update(Update {
ref mut entry_hash,
ref mut entry_type,
..
}) => Some((entry_hash, entry_type)),
_ => None,
}
}
}