use crate::prelude::*;
use ::contrafact::*;
use holochain_keystore::MetaLairClient;
use holochain_zome_types::facts::ActionRefMut;
pub fn valid_dht_op(
keystore: MetaLairClient,
author: AgentPubKey,
must_be_public: bool,
) -> impl Fact<'static, DhtOp> {
facts![
brute(
"Action type matches Entry existence, and is public if exists",
move |op: &DhtOp| {
let action = op.action();
let h = action.entry_data();
let e = op.entry();
match (h, e) {
(
Some((_entry_hash, entry_type)),
RecordEntry::Present(_) | RecordEntry::NotStored,
) => {
!must_be_public || entry_type.visibility().is_public()
}
(None, RecordEntry::Present(_)) => false,
(None, _) => true,
_ => false,
}
}
),
lambda_unit(
"If there is entry data, the action must point to it",
|g, op: DhtOp| {
if let Some(entry) = op.entry().into_option() {
prism(
"action's entry hash",
|op: &mut DhtOp| op.action_entry_data_mut().map(|(hash, _)| hash),
eq(EntryHash::with_data_sync(entry)),
)
.mutate(g, op)
} else {
Ok(op)
}
}
),
lens1(
"The author is the one specified",
DhtOp::author_mut,
eq(author)
),
lambda_unit("The Signature matches the Action", move |g, op: DhtOp| {
use holochain_keystore::AgentPubKeyExt;
let action = op.action();
let agent = action.author();
let actual = tokio_helper::block_forever_on(agent.sign(&keystore, &action))
.expect("Can sign the action");
lens1("signature", DhtOp::signature_mut, eq(actual)).mutate(g, op)
})
]
}
#[cfg(test)]
mod tests {
use arbitrary::Arbitrary;
use super::*;
use holochain_zome_types::facts;
#[tokio::test(flavor = "multi_thread")]
async fn test_valid_dht_op() {
let mut gg = Generator::from(unstructured_noise());
let g = &mut gg;
let keystore = holochain_keystore::spawn_test_keystore().await.unwrap();
let agent = AgentPubKey::new_random(&keystore).await.unwrap();
let e = Entry::arbitrary(g).unwrap();
let mut a0 = facts::is_not_entry_action().build(g);
*a0.author_mut() = agent.clone();
let mut a1 = facts::is_new_entry_action().build(g);
*a1.entry_data_mut().unwrap().0 = EntryHash::with_data_sync(&e);
let mut a1 = Action::from(a1);
*a1.author_mut() = agent.clone();
let sn = agent.sign(&keystore, &a0).await.unwrap();
let se = agent.sign(&keystore, &a1).await.unwrap();
let op0a = DhtOp::StoreRecord(sn.clone(), a0.clone(), RecordEntry::Present(e.clone()));
let op0b = DhtOp::StoreRecord(sn.clone(), a0.clone(), RecordEntry::Hidden);
let op0c = DhtOp::StoreRecord(sn.clone(), a0.clone(), RecordEntry::NA);
let op0d = DhtOp::StoreRecord(sn.clone(), a0.clone(), RecordEntry::NotStored);
let op1a = DhtOp::StoreRecord(se.clone(), a1.clone(), RecordEntry::Present(e.clone()));
let op1b = DhtOp::StoreRecord(se.clone(), a1.clone(), RecordEntry::Hidden);
let op1c = DhtOp::StoreRecord(se.clone(), a1.clone(), RecordEntry::NA);
let op1d = DhtOp::StoreRecord(se.clone(), a1.clone(), RecordEntry::NotStored);
let fact = valid_dht_op(keystore, agent, false);
assert!(fact.clone().check(&op0a).is_err());
fact.clone().check(&op0b).unwrap();
fact.clone().check(&op0c).unwrap();
fact.clone().check(&op0d).unwrap();
fact.clone().check(&op1a).unwrap();
assert!(fact.clone().check(&op1b).is_err());
assert!(fact.clone().check(&op1c).is_err());
fact.clone().check(&op1d).unwrap();
}
}
impl DhtOp {
pub fn author_mut(&mut self) -> &mut AgentPubKey {
match self {
DhtOp::StoreRecord(_, h, _) => h.author_mut(),
DhtOp::StoreEntry(_, h, _) => h.author_mut(),
DhtOp::RegisterAgentActivity(_, h) => h.author_mut(),
DhtOp::RegisterUpdatedContent(_, h, _) => &mut h.author,
DhtOp::RegisterUpdatedRecord(_, h, _) => &mut h.author,
DhtOp::RegisterDeletedBy(_, h) => &mut h.author,
DhtOp::RegisterDeletedEntryAction(_, h) => &mut h.author,
DhtOp::RegisterAddLink(_, h) => &mut h.author,
DhtOp::RegisterRemoveLink(_, h) => &mut h.author,
}
}
pub fn timestamp_mut(&mut self) -> &mut Timestamp {
match self {
DhtOp::StoreRecord(_, h, _) => h.timestamp_mut(),
DhtOp::StoreEntry(_, h, _) => h.timestamp_mut(),
DhtOp::RegisterAgentActivity(_, h) => h.timestamp_mut(),
DhtOp::RegisterUpdatedContent(_, h, _) => &mut h.timestamp,
DhtOp::RegisterUpdatedRecord(_, h, _) => &mut h.timestamp,
DhtOp::RegisterDeletedBy(_, h) => &mut h.timestamp,
DhtOp::RegisterDeletedEntryAction(_, h) => &mut h.timestamp,
DhtOp::RegisterAddLink(_, h) => &mut h.timestamp,
DhtOp::RegisterRemoveLink(_, h) => &mut h.timestamp,
}
}
pub fn signature_mut(&mut self) -> &mut Signature {
match self {
DhtOp::StoreRecord(s, _, _) => s,
DhtOp::StoreEntry(s, _, _) => s,
DhtOp::RegisterAgentActivity(s, _) => s,
DhtOp::RegisterUpdatedContent(s, _, _) => s,
DhtOp::RegisterUpdatedRecord(s, _, _) => s,
DhtOp::RegisterDeletedBy(s, _) => s,
DhtOp::RegisterDeletedEntryAction(s, _) => s,
DhtOp::RegisterAddLink(s, _) => s,
DhtOp::RegisterRemoveLink(s, _) => s,
}
}
pub fn action_seq_mut(&mut self) -> Option<&mut u32> {
match self {
DhtOp::StoreRecord(_, ref mut h, _) => h.action_seq_mut(),
DhtOp::StoreEntry(_, ref mut h, _) => h.action_seq_mut(),
DhtOp::RegisterAgentActivity(_, ref mut h) => h.action_seq_mut(),
DhtOp::RegisterUpdatedContent(_, ref mut h, _) => Some(&mut h.action_seq),
DhtOp::RegisterUpdatedRecord(_, ref mut h, _) => Some(&mut h.action_seq),
DhtOp::RegisterDeletedBy(_, ref mut h) => Some(&mut h.action_seq),
DhtOp::RegisterDeletedEntryAction(_, ref mut h) => Some(&mut h.action_seq),
DhtOp::RegisterAddLink(_, ref mut h) => Some(&mut h.action_seq),
DhtOp::RegisterRemoveLink(_, ref mut h) => Some(&mut h.action_seq),
}
}
pub fn action_entry_data_mut(&mut self) -> Option<(&mut EntryHash, &mut EntryType)> {
match self {
DhtOp::StoreRecord(_, ref mut h, _) => h.entry_data_mut(),
DhtOp::StoreEntry(_, ref mut h, _) => h.entry_data_mut(),
DhtOp::RegisterAgentActivity(_, ref mut h) => h.entry_data_mut(),
DhtOp::RegisterUpdatedContent(_, ref mut h, _) => {
Some((&mut h.entry_hash, &mut h.entry_type))
}
DhtOp::RegisterUpdatedRecord(_, ref mut h, _) => {
Some((&mut h.entry_hash, &mut h.entry_type))
}
_ => None,
}
}
}
impl ActionRefMut for NewEntryAction {
fn author_mut(&mut self) -> &mut AgentPubKey {
match self {
Self::Create(Create { ref mut author, .. }) => author,
Self::Update(Update { ref mut author, .. }) => author,
}
}
fn action_seq_mut(&mut self) -> Option<&mut u32> {
Some(match self {
Self::Create(Create {
ref mut action_seq, ..
}) => action_seq,
Self::Update(Update {
ref mut action_seq, ..
}) => action_seq,
})
}
fn prev_action_mut(&mut self) -> Option<&mut ActionHash> {
todo!()
}
fn entry_data_mut(&mut self) -> Option<(&mut EntryHash, &mut EntryType)> {
Some(match self {
Self::Create(Create {
ref mut entry_hash,
ref mut entry_type,
..
}) => (entry_hash, entry_type),
Self::Update(Update {
ref mut entry_hash,
ref mut entry_type,
..
}) => (entry_hash, entry_type),
})
}
fn timestamp_mut(&mut self) -> &mut Timestamp {
match self {
Self::Create(Create {
ref mut timestamp, ..
}) => timestamp,
Self::Update(Update {
ref mut timestamp, ..
}) => timestamp,
}
}
}
impl NewEntryAction {
pub fn entry_hash_mut(&mut self) -> &mut EntryHash {
self.entry_data_mut().unwrap().0
}
}