use std::str::FromStr;
use crate::action::NewEntryAction;
use crate::prelude::*;
use crate::record::RecordGroup;
use error::DhtOpError;
use error::DhtOpResult;
use holo_hash::*;
use holochain_sqlite::rusqlite::types::FromSql;
use holochain_sqlite::rusqlite::ToSql;
use holochain_zome_types::action;
use holochain_zome_types::prelude::*;
use kitsune_p2p_dht::region::RegionData;
use kitsune_p2p_dht::Loc;
use serde::Deserialize;
use serde::Serialize;
#[allow(missing_docs)]
pub mod error;
#[cfg(test)]
pub mod tests;
#[cfg(feature = "test_utils")]
pub mod facts;
#[derive(
Clone, Debug, Serialize, Deserialize, SerializedBytes, Eq, PartialEq, Hash, derive_more::Display,
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum DhtOp {
#[display(fmt = "StoreRecord")]
StoreRecord(Signature, Action, RecordEntry),
#[display(fmt = "StoreEntry")]
StoreEntry(Signature, NewEntryAction, Entry),
#[display(fmt = "RegisterAgentActivity")]
RegisterAgentActivity(Signature, Action),
#[display(fmt = "RegisterUpdatedContent")]
RegisterUpdatedContent(Signature, action::Update, RecordEntry),
#[display(fmt = "RegisterUpdatedRecord")]
RegisterUpdatedRecord(Signature, action::Update, RecordEntry),
#[display(fmt = "RegisterDeletedBy")]
RegisterDeletedBy(Signature, action::Delete),
#[display(fmt = "RegisterDeletedEntryAction")]
RegisterDeletedEntryAction(Signature, action::Delete),
#[display(fmt = "RegisterAddLink")]
RegisterAddLink(Signature, action::CreateLink),
#[display(fmt = "RegisterRemoveLink")]
RegisterRemoveLink(Signature, action::DeleteLink),
}
impl kitsune_p2p_dht::prelude::OpRegion for DhtOp {
fn loc(&self) -> Loc {
self.dht_basis().get_loc()
}
fn timestamp(&self) -> Timestamp {
self.timestamp()
}
fn region_data(&self) -> RegionData {
unimplemented!()
}
fn bound(_timestamp: Timestamp, _loc: kitsune_p2p_dht::Loc) -> Self {
unimplemented!()
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, Serialize, Deserialize, derive_more::Display)]
pub enum DhtOpLight {
#[display(fmt = "StoreRecord")]
StoreRecord(ActionHash, Option<EntryHash>, OpBasis),
#[display(fmt = "StoreEntry")]
StoreEntry(ActionHash, EntryHash, OpBasis),
#[display(fmt = "RegisterAgentActivity")]
RegisterAgentActivity(ActionHash, OpBasis),
#[display(fmt = "RegisterUpdatedContent")]
RegisterUpdatedContent(ActionHash, EntryHash, OpBasis),
#[display(fmt = "RegisterUpdatedRecord")]
RegisterUpdatedRecord(ActionHash, EntryHash, OpBasis),
#[display(fmt = "RegisterDeletedBy")]
RegisterDeletedBy(ActionHash, OpBasis),
#[display(fmt = "RegisterDeletedEntryAction")]
RegisterDeletedEntryAction(ActionHash, OpBasis),
#[display(fmt = "RegisterAddLink")]
RegisterAddLink(ActionHash, OpBasis),
#[display(fmt = "RegisterRemoveLink")]
RegisterRemoveLink(ActionHash, OpBasis),
}
impl PartialEq for DhtOpLight {
fn eq(&self, other: &Self) -> bool {
self.get_type() == other.get_type() && self.action_hash() == other.action_hash()
}
}
impl Eq for DhtOpLight {}
impl std::hash::Hash for DhtOpLight {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.get_type().hash(state);
self.action_hash().hash(state);
}
}
#[allow(missing_docs)]
#[derive(
Clone,
Copy,
Debug,
Serialize,
Deserialize,
Eq,
PartialEq,
Hash,
derive_more::Display,
strum_macros::EnumString,
)]
pub enum DhtOpType {
#[display(fmt = "StoreRecord")]
StoreRecord,
#[display(fmt = "StoreEntry")]
StoreEntry,
#[display(fmt = "RegisterAgentActivity")]
RegisterAgentActivity,
#[display(fmt = "RegisterUpdatedContent")]
RegisterUpdatedContent,
#[display(fmt = "RegisterUpdatedRecord")]
RegisterUpdatedRecord,
#[display(fmt = "RegisterDeletedBy")]
RegisterDeletedBy,
#[display(fmt = "RegisterDeletedEntryAction")]
RegisterDeletedEntryAction,
#[display(fmt = "RegisterAddLink")]
RegisterAddLink,
#[display(fmt = "RegisterRemoveLink")]
RegisterRemoveLink,
}
impl ToSql for DhtOpType {
fn to_sql(
&self,
) -> holochain_sqlite::rusqlite::Result<holochain_sqlite::rusqlite::types::ToSqlOutput> {
Ok(holochain_sqlite::rusqlite::types::ToSqlOutput::Owned(
format!("{}", self).into(),
))
}
}
impl FromSql for DhtOpType {
fn column_result(
value: holochain_sqlite::rusqlite::types::ValueRef<'_>,
) -> holochain_sqlite::rusqlite::types::FromSqlResult<Self> {
String::column_result(value).and_then(|string| {
DhtOpType::from_str(&string)
.map_err(|_| holochain_sqlite::rusqlite::types::FromSqlError::InvalidType)
})
}
}
impl DhtOp {
fn as_unique_form(&self) -> UniqueForm<'_> {
match self {
Self::StoreRecord(_, action, _) => UniqueForm::StoreRecord(action),
Self::StoreEntry(_, action, _) => UniqueForm::StoreEntry(action),
Self::RegisterAgentActivity(_, action) => UniqueForm::RegisterAgentActivity(action),
Self::RegisterUpdatedContent(_, action, _) => {
UniqueForm::RegisterUpdatedContent(action)
}
Self::RegisterUpdatedRecord(_, action, _) => UniqueForm::RegisterUpdatedRecord(action),
Self::RegisterDeletedBy(_, action) => UniqueForm::RegisterDeletedBy(action),
Self::RegisterDeletedEntryAction(_, action) => {
UniqueForm::RegisterDeletedEntryAction(action)
}
Self::RegisterAddLink(_, action) => UniqueForm::RegisterAddLink(action),
Self::RegisterRemoveLink(_, action) => UniqueForm::RegisterRemoveLink(action),
}
}
pub fn dht_basis(&self) -> OpBasis {
self.as_unique_form().basis()
}
pub fn to_light(
&self,
) -> DhtOpLight {
let basis = self.dht_basis();
match self {
DhtOp::StoreRecord(_, a, _) => {
let e = a.entry_data().map(|(e, _)| e.clone());
let h = ActionHash::with_data_sync(a);
DhtOpLight::StoreRecord(h, e, basis)
}
DhtOp::StoreEntry(_, a, _) => {
let e = a.entry().clone();
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
DhtOpLight::StoreEntry(h, e, basis)
}
DhtOp::RegisterAgentActivity(_, a) => {
let h = ActionHash::with_data_sync(a);
DhtOpLight::RegisterAgentActivity(h, basis)
}
DhtOp::RegisterUpdatedContent(_, a, _) => {
let e = a.entry_hash.clone();
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
DhtOpLight::RegisterUpdatedContent(h, e, basis)
}
DhtOp::RegisterUpdatedRecord(_, a, _) => {
let e = a.entry_hash.clone();
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
DhtOpLight::RegisterUpdatedRecord(h, e, basis)
}
DhtOp::RegisterDeletedBy(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
DhtOpLight::RegisterDeletedBy(h, basis)
}
DhtOp::RegisterDeletedEntryAction(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
DhtOpLight::RegisterDeletedEntryAction(h, basis)
}
DhtOp::RegisterAddLink(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
DhtOpLight::RegisterAddLink(h, basis)
}
DhtOp::RegisterRemoveLink(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
DhtOpLight::RegisterRemoveLink(h, basis)
}
}
}
pub fn signature(&self) -> &Signature {
match self {
DhtOp::StoreRecord(s, _, _)
| DhtOp::StoreEntry(s, _, _)
| DhtOp::RegisterAgentActivity(s, _)
| DhtOp::RegisterUpdatedContent(s, _, _)
| DhtOp::RegisterUpdatedRecord(s, _, _)
| DhtOp::RegisterDeletedBy(s, _)
| DhtOp::RegisterDeletedEntryAction(s, _)
| DhtOp::RegisterAddLink(s, _)
| DhtOp::RegisterRemoveLink(s, _) => s,
}
}
pub fn action(&self) -> Action {
match self {
DhtOp::StoreRecord(_, a, _) => a.clone(),
DhtOp::StoreEntry(_, a, _) => a.clone().into(),
DhtOp::RegisterAgentActivity(_, a) => a.clone(),
DhtOp::RegisterUpdatedContent(_, a, _) => a.clone().into(),
DhtOp::RegisterUpdatedRecord(_, a, _) => a.clone().into(),
DhtOp::RegisterDeletedBy(_, a) => a.clone().into(),
DhtOp::RegisterDeletedEntryAction(_, a) => a.clone().into(),
DhtOp::RegisterAddLink(_, a) => a.clone().into(),
DhtOp::RegisterRemoveLink(_, a) => a.clone().into(),
}
}
pub fn entry(&self) -> RecordEntryRef {
match self {
DhtOp::StoreRecord(_, _, e) => e.as_ref(),
DhtOp::StoreEntry(_, _, e) => RecordEntry::Present(e),
DhtOp::RegisterUpdatedContent(_, _, e) => e.as_ref(),
DhtOp::RegisterUpdatedRecord(_, _, e) => e.as_ref(),
DhtOp::RegisterAgentActivity(_, a) => RecordEntry::new(a.entry_visibility(), None),
DhtOp::RegisterDeletedBy(_, _) => RecordEntry::NA,
DhtOp::RegisterDeletedEntryAction(_, _) => RecordEntry::NA,
DhtOp::RegisterAddLink(_, _) => RecordEntry::NA,
DhtOp::RegisterRemoveLink(_, _) => RecordEntry::NA,
}
}
pub fn get_type(&self) -> DhtOpType {
match self {
DhtOp::StoreRecord(_, _, _) => DhtOpType::StoreRecord,
DhtOp::StoreEntry(_, _, _) => DhtOpType::StoreEntry,
DhtOp::RegisterUpdatedContent(_, _, _) => DhtOpType::RegisterUpdatedContent,
DhtOp::RegisterUpdatedRecord(_, _, _) => DhtOpType::RegisterUpdatedRecord,
DhtOp::RegisterAgentActivity(_, _) => DhtOpType::RegisterAgentActivity,
DhtOp::RegisterDeletedBy(_, _) => DhtOpType::RegisterDeletedBy,
DhtOp::RegisterDeletedEntryAction(_, _) => DhtOpType::RegisterDeletedEntryAction,
DhtOp::RegisterAddLink(_, _) => DhtOpType::RegisterAddLink,
DhtOp::RegisterRemoveLink(_, _) => DhtOpType::RegisterRemoveLink,
}
}
pub fn from_type(
op_type: DhtOpType,
action: SignedAction,
entry: Option<Entry>,
) -> DhtOpResult<Self> {
let SignedAction(action, signature) = action;
let entry = RecordEntry::new(action.entry_visibility(), entry);
let r = match op_type {
DhtOpType::StoreRecord => DhtOp::StoreRecord(signature, action, entry),
DhtOpType::StoreEntry => {
let entry = entry
.into_option()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(action.clone()))?;
let action = match action {
Action::Create(c) => NewEntryAction::Create(c),
Action::Update(c) => NewEntryAction::Update(c),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
DhtOp::StoreEntry(signature, action, entry)
}
DhtOpType::RegisterAgentActivity => DhtOp::RegisterAgentActivity(signature, action),
DhtOpType::RegisterUpdatedContent => {
DhtOp::RegisterUpdatedContent(signature, action.try_into()?, entry)
}
DhtOpType::RegisterUpdatedRecord => {
DhtOp::RegisterUpdatedRecord(signature, action.try_into()?, entry)
}
DhtOpType::RegisterDeletedBy => DhtOp::RegisterDeletedBy(signature, action.try_into()?),
DhtOpType::RegisterDeletedEntryAction => {
DhtOp::RegisterDeletedEntryAction(signature, action.try_into()?)
}
DhtOpType::RegisterAddLink => DhtOp::RegisterAddLink(signature, action.try_into()?),
DhtOpType::RegisterRemoveLink => {
DhtOp::RegisterRemoveLink(signature, action.try_into()?)
}
};
Ok(r)
}
fn to_order(&self) -> OpOrder {
OpOrder::new(self.get_type(), self.timestamp())
}
pub fn enzymatic_countersigning_enzyme(&self) -> Option<&AgentPubKey> {
if let Some(Entry::CounterSign(session_data, _)) = self.entry().into_option() {
if session_data.preflight_request().enzymatic {
session_data
.preflight_request()
.signing_agents
.get(0)
.map(|(pubkey, _)| pubkey)
} else {
None
}
} else {
None
}
}
}
impl PartialOrd for DhtOp {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for DhtOp {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.to_order().cmp(&other.to_order())
}
}
impl DhtOpLight {
pub fn dht_basis(&self) -> &OpBasis {
match self {
DhtOpLight::StoreRecord(_, _, b)
| DhtOpLight::StoreEntry(_, _, b)
| DhtOpLight::RegisterAgentActivity(_, b)
| DhtOpLight::RegisterUpdatedContent(_, _, b)
| DhtOpLight::RegisterUpdatedRecord(_, _, b)
| DhtOpLight::RegisterDeletedBy(_, b)
| DhtOpLight::RegisterDeletedEntryAction(_, b)
| DhtOpLight::RegisterAddLink(_, b)
| DhtOpLight::RegisterRemoveLink(_, b) => b,
}
}
pub fn action_hash(&self) -> &ActionHash {
match self {
DhtOpLight::StoreRecord(h, _, _)
| DhtOpLight::StoreEntry(h, _, _)
| DhtOpLight::RegisterAgentActivity(h, _)
| DhtOpLight::RegisterUpdatedContent(h, _, _)
| DhtOpLight::RegisterUpdatedRecord(h, _, _)
| DhtOpLight::RegisterDeletedBy(h, _)
| DhtOpLight::RegisterDeletedEntryAction(h, _)
| DhtOpLight::RegisterAddLink(h, _)
| DhtOpLight::RegisterRemoveLink(h, _) => h,
}
}
pub fn get_type(&self) -> DhtOpType {
match self {
DhtOpLight::StoreRecord(_, _, _) => DhtOpType::StoreRecord,
DhtOpLight::StoreEntry(_, _, _) => DhtOpType::StoreEntry,
DhtOpLight::RegisterUpdatedContent(_, _, _) => DhtOpType::RegisterUpdatedContent,
DhtOpLight::RegisterUpdatedRecord(_, _, _) => DhtOpType::RegisterUpdatedRecord,
DhtOpLight::RegisterAgentActivity(_, _) => DhtOpType::RegisterAgentActivity,
DhtOpLight::RegisterDeletedBy(_, _) => DhtOpType::RegisterDeletedBy,
DhtOpLight::RegisterDeletedEntryAction(_, _) => DhtOpType::RegisterDeletedEntryAction,
DhtOpLight::RegisterAddLink(_, _) => DhtOpType::RegisterAddLink,
DhtOpLight::RegisterRemoveLink(_, _) => DhtOpType::RegisterRemoveLink,
}
}
pub fn from_type(
op_type: DhtOpType,
action_hash: ActionHash,
action: &Action,
) -> DhtOpResult<Self> {
let op = match op_type {
DhtOpType::StoreRecord => {
let entry_hash = action.entry_hash().cloned();
Self::StoreRecord(action_hash.clone(), entry_hash, action_hash.into())
}
DhtOpType::StoreEntry => {
let entry_hash = action
.entry_hash()
.cloned()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(action.clone()))?;
Self::StoreEntry(action_hash, entry_hash.clone(), entry_hash.into())
}
DhtOpType::RegisterAgentActivity => {
Self::RegisterAgentActivity(action_hash, action.author().clone().into())
}
DhtOpType::RegisterUpdatedContent => {
let entry_hash = action
.entry_hash()
.cloned()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(action.clone()))?;
let basis = match action {
Action::Update(update) => update.original_entry_address.clone(),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
Self::RegisterUpdatedContent(action_hash, entry_hash, basis.into())
}
DhtOpType::RegisterUpdatedRecord => {
let entry_hash = action
.entry_hash()
.cloned()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(action.clone()))?;
let basis = match action {
Action::Update(update) => update.original_entry_address.clone(),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
Self::RegisterUpdatedRecord(action_hash, entry_hash, basis.into())
}
DhtOpType::RegisterDeletedBy => {
let basis = match action {
Action::Delete(delete) => delete.deletes_address.clone(),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
Self::RegisterDeletedBy(action_hash, basis.into())
}
DhtOpType::RegisterDeletedEntryAction => {
let basis = match action {
Action::Delete(delete) => delete.deletes_entry_address.clone(),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
Self::RegisterDeletedEntryAction(action_hash, basis.into())
}
DhtOpType::RegisterAddLink => {
let basis = match action {
Action::CreateLink(create_link) => create_link.base_address.clone(),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
Self::RegisterAddLink(action_hash, basis)
}
DhtOpType::RegisterRemoveLink => {
let basis = match action {
Action::DeleteLink(delete_link) => delete_link.base_address.clone(),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
Self::RegisterRemoveLink(action_hash, basis)
}
};
Ok(op)
}
}
#[allow(missing_docs)]
#[derive(Serialize, Debug)]
pub enum UniqueForm<'a> {
StoreRecord(&'a Action),
StoreEntry(&'a NewEntryAction),
RegisterAgentActivity(&'a Action),
RegisterUpdatedContent(&'a action::Update),
RegisterUpdatedRecord(&'a action::Update),
RegisterDeletedBy(&'a action::Delete),
RegisterDeletedEntryAction(&'a action::Delete),
RegisterAddLink(&'a action::CreateLink),
RegisterRemoveLink(&'a action::DeleteLink),
}
impl<'a> UniqueForm<'a> {
fn basis(&'a self) -> OpBasis {
match self {
UniqueForm::StoreRecord(action) => ActionHash::with_data_sync(*action).into(),
UniqueForm::StoreEntry(action) => action.entry().clone().into(),
UniqueForm::RegisterAgentActivity(action) => action.author().clone().into(),
UniqueForm::RegisterUpdatedContent(action) => {
action.original_entry_address.clone().into()
}
UniqueForm::RegisterUpdatedRecord(action) => {
action.original_action_address.clone().into()
}
UniqueForm::RegisterDeletedBy(action) => action.deletes_address.clone().into(),
UniqueForm::RegisterDeletedEntryAction(action) => {
action.deletes_entry_address.clone().into()
}
UniqueForm::RegisterAddLink(action) => action.base_address.clone(),
UniqueForm::RegisterRemoveLink(action) => action.base_address.clone(),
}
}
pub fn op_hash(op_type: DhtOpType, action: Action) -> DhtOpResult<(Action, DhtOpHash)> {
match op_type {
DhtOpType::StoreRecord => {
let hash = DhtOpHash::with_data_sync(&UniqueForm::StoreRecord(&action));
Ok((action, hash))
}
DhtOpType::StoreEntry => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&UniqueForm::StoreEntry(&action));
Ok((action.into(), hash))
}
DhtOpType::RegisterAgentActivity => {
let hash = DhtOpHash::with_data_sync(&UniqueForm::RegisterAgentActivity(&action));
Ok((action, hash))
}
DhtOpType::RegisterUpdatedContent => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&UniqueForm::RegisterUpdatedContent(&action));
Ok((action.into(), hash))
}
DhtOpType::RegisterUpdatedRecord => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&UniqueForm::RegisterUpdatedRecord(&action));
Ok((action.into(), hash))
}
DhtOpType::RegisterDeletedBy => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&UniqueForm::RegisterDeletedBy(&action));
Ok((action.into(), hash))
}
DhtOpType::RegisterDeletedEntryAction => {
let action = action.try_into()?;
let hash =
DhtOpHash::with_data_sync(&UniqueForm::RegisterDeletedEntryAction(&action));
Ok((action.into(), hash))
}
DhtOpType::RegisterAddLink => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&UniqueForm::RegisterAddLink(&action));
Ok((action.into(), hash))
}
DhtOpType::RegisterRemoveLink => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&UniqueForm::RegisterRemoveLink(&action));
Ok((action.into(), hash))
}
}
}
}
pub fn produce_ops_from_record(record: &Record) -> DhtOpResult<Vec<DhtOp>> {
let op_lights = produce_op_lights_from_records(vec![record])?;
let (shh, entry) = record.clone().into_inner();
let SignedActionHashed {
hashed: ActionHashed {
content: action, ..
},
signature,
} = shh;
let mut ops = Vec::with_capacity(op_lights.len());
for op_light in op_lights {
let signature = signature.clone();
let action = action.clone();
let op = match op_light {
DhtOpLight::StoreRecord(_, _, _) => {
DhtOp::StoreRecord(signature, action, entry.clone())
}
DhtOpLight::StoreEntry(_, _, _) => {
let new_entry_action = action.clone().try_into()?;
let e = match entry.clone().into_option() {
Some(e) => e,
None => {
continue;
}
};
DhtOp::StoreEntry(signature, new_entry_action, e)
}
DhtOpLight::RegisterAgentActivity(_, _) => {
DhtOp::RegisterAgentActivity(signature, action)
}
DhtOpLight::RegisterUpdatedContent(_, _, _) => {
let entry_update = action.try_into()?;
DhtOp::RegisterUpdatedContent(signature, entry_update, entry.clone())
}
DhtOpLight::RegisterUpdatedRecord(_, _, _) => {
let entry_update = action.try_into()?;
DhtOp::RegisterUpdatedRecord(signature, entry_update, entry.clone())
}
DhtOpLight::RegisterDeletedEntryAction(_, _) => {
let record_delete = action.try_into()?;
DhtOp::RegisterDeletedEntryAction(signature, record_delete)
}
DhtOpLight::RegisterDeletedBy(_, _) => {
let record_delete = action.try_into()?;
DhtOp::RegisterDeletedBy(signature, record_delete)
}
DhtOpLight::RegisterAddLink(_, _) => {
let link_add = action.try_into()?;
DhtOp::RegisterAddLink(signature, link_add)
}
DhtOpLight::RegisterRemoveLink(_, _) => {
let link_remove = action.try_into()?;
DhtOp::RegisterRemoveLink(signature, link_remove)
}
};
ops.push(op);
}
Ok(ops)
}
pub fn produce_op_lights_from_records(actions: Vec<&Record>) -> DhtOpResult<Vec<DhtOpLight>> {
let actions_and_hashes = actions.into_iter().map(|e| {
(
e.action_address(),
e.action(),
e.action().entry_data().map(|(h, _)| h.clone()),
)
});
produce_op_lights_from_iter(actions_and_hashes)
}
pub fn produce_op_lights_from_record_group(
records: &RecordGroup<'_>,
) -> DhtOpResult<Vec<DhtOpLight>> {
let actions_and_hashes = records.actions_and_hashes();
let maybe_entry_hash = Some(records.entry_hash());
produce_op_lights_from_parts(actions_and_hashes, maybe_entry_hash)
}
fn produce_op_lights_from_parts<'a>(
actions_and_hashes: impl Iterator<Item = (&'a ActionHash, &'a Action)>,
maybe_entry_hash: Option<&EntryHash>,
) -> DhtOpResult<Vec<DhtOpLight>> {
let iter = actions_and_hashes.map(|(head, hash)| (head, hash, maybe_entry_hash.cloned()));
produce_op_lights_from_iter(iter)
}
pub fn produce_op_lights_from_iter<'a>(
iter: impl Iterator<Item = (&'a ActionHash, &'a Action, Option<EntryHash>)>,
) -> DhtOpResult<Vec<DhtOpLight>> {
let mut ops = Vec::new();
for (action_hash, action, maybe_entry_hash) in iter {
let op_lights = action_to_op_types(action)
.into_iter()
.filter_map(|op_type| {
let op_light = match (op_type, action) {
(DhtOpType::StoreRecord, _) => {
let store_record_basis = UniqueForm::StoreRecord(action).basis();
DhtOpLight::StoreRecord(
action_hash.clone(),
maybe_entry_hash.clone(),
store_record_basis,
)
}
(DhtOpType::RegisterAgentActivity, _) => {
let register_activity_basis =
UniqueForm::RegisterAgentActivity(action).basis();
DhtOpLight::RegisterAgentActivity(
action_hash.clone(),
register_activity_basis,
)
}
(DhtOpType::StoreEntry, Action::Create(create)) => DhtOpLight::StoreEntry(
action_hash.clone(),
maybe_entry_hash.clone()?,
UniqueForm::StoreEntry(&NewEntryAction::Create(create.clone())).basis(),
),
(DhtOpType::StoreEntry, Action::Update(update)) => DhtOpLight::StoreEntry(
action_hash.clone(),
maybe_entry_hash.clone()?,
UniqueForm::StoreEntry(&NewEntryAction::Update(update.clone())).basis(),
),
(DhtOpType::RegisterUpdatedContent, Action::Update(update)) => {
DhtOpLight::RegisterUpdatedContent(
action_hash.clone(),
maybe_entry_hash.clone()?,
UniqueForm::RegisterUpdatedContent(update).basis(),
)
}
(DhtOpType::RegisterUpdatedRecord, Action::Update(update)) => {
DhtOpLight::RegisterUpdatedRecord(
action_hash.clone(),
maybe_entry_hash.clone()?,
UniqueForm::RegisterUpdatedRecord(update).basis(),
)
}
(DhtOpType::RegisterDeletedBy, Action::Delete(delete)) => {
DhtOpLight::RegisterDeletedBy(
action_hash.clone(),
UniqueForm::RegisterDeletedBy(delete).basis(),
)
}
(DhtOpType::RegisterDeletedEntryAction, Action::Delete(delete)) => {
DhtOpLight::RegisterDeletedEntryAction(
action_hash.clone(),
UniqueForm::RegisterDeletedEntryAction(delete).basis(),
)
}
(DhtOpType::RegisterAddLink, Action::CreateLink(create_link)) => {
DhtOpLight::RegisterAddLink(
action_hash.clone(),
UniqueForm::RegisterAddLink(create_link).basis(),
)
}
(DhtOpType::RegisterRemoveLink, Action::DeleteLink(delete_link)) => {
DhtOpLight::RegisterRemoveLink(
action_hash.clone(),
UniqueForm::RegisterRemoveLink(delete_link).basis(),
)
}
_ => return None,
};
Some(op_light)
});
ops.extend(op_lights);
}
Ok(ops)
}
pub fn action_to_op_types(action: &Action) -> Vec<DhtOpType> {
match action {
Action::Dna(_)
| Action::OpenChain(_)
| Action::CloseChain(_)
| Action::AgentValidationPkg(_)
| Action::InitZomesComplete(_) => {
vec![DhtOpType::StoreRecord, DhtOpType::RegisterAgentActivity]
}
Action::CreateLink(_) => vec![
DhtOpType::StoreRecord,
DhtOpType::RegisterAgentActivity,
DhtOpType::RegisterAddLink,
],
Action::DeleteLink(_) => vec![
DhtOpType::StoreRecord,
DhtOpType::RegisterAgentActivity,
DhtOpType::RegisterRemoveLink,
],
Action::Create(_) => vec![
DhtOpType::StoreRecord,
DhtOpType::RegisterAgentActivity,
DhtOpType::StoreEntry,
],
Action::Update(_) => vec![
DhtOpType::StoreRecord,
DhtOpType::RegisterAgentActivity,
DhtOpType::StoreEntry,
DhtOpType::RegisterUpdatedContent,
DhtOpType::RegisterUpdatedRecord,
],
Action::Delete(_) => vec![
DhtOpType::StoreRecord,
DhtOpType::RegisterAgentActivity,
DhtOpType::RegisterDeletedBy,
DhtOpType::RegisterDeletedEntryAction,
],
}
}
impl<'a> TryFrom<&UniqueForm<'a>> for SerializedBytes {
type Error = SerializedBytesError;
fn try_from(u: &UniqueForm<'a>) -> Result<Self, Self::Error> {
match holochain_serialized_bytes::encode(u) {
Ok(v) => Ok(SerializedBytes::from(
holochain_serialized_bytes::UnsafeBytes::from(v),
)),
Err(e) => Err(SerializedBytesError::Serialize(e.to_string())),
}
}
}
pub type DhtOpHashed = HoloHashed<DhtOp>;
impl HashableContent for DhtOp {
type HashType = hash_type::DhtOp;
fn hash_type(&self) -> Self::HashType {
hash_type::DhtOp
}
fn hashable_content(&self) -> HashableContentBytes {
HashableContentBytes::Content(
(&self.as_unique_form())
.try_into()
.expect("Could not serialize HashableContent"),
)
}
}
impl HashableContent for UniqueForm<'_> {
type HashType = hash_type::DhtOp;
fn hash_type(&self) -> Self::HashType {
hash_type::DhtOp
}
fn hashable_content(&self) -> HashableContentBytes {
HashableContentBytes::Content(
self.try_into()
.expect("Could not serialize HashableContent"),
)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub enum WireOps {
Entry(WireEntryOps),
Record(WireRecordOps),
}
impl WireOps {
pub fn render(self) -> DhtOpResult<RenderedOps> {
match self {
WireOps::Entry(o) => o.render(),
WireOps::Record(o) => o.render(),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RenderedOp {
pub action: SignedActionHashed,
pub op_light: DhtOpLight,
pub op_hash: DhtOpHash,
pub validation_status: Option<ValidationStatus>,
}
impl RenderedOp {
pub fn new(
action: Action,
signature: Signature,
validation_status: Option<ValidationStatus>,
op_type: DhtOpType,
) -> DhtOpResult<Self> {
let (action, op_hash) = UniqueForm::op_hash(op_type, action)?;
let action_hashed = ActionHashed::from_content_sync(action);
let action = SignedActionHashed::with_presigned(action_hashed, signature);
let op_light = DhtOpLight::from_type(op_type, action.as_hash().clone(), action.action())?;
Ok(Self {
action,
op_light,
op_hash,
validation_status,
})
}
}
#[derive(Debug, PartialEq, Eq, Clone, Default)]
pub struct RenderedOps {
pub entry: Option<EntryHashed>,
pub ops: Vec<RenderedOp>,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum OpNumericalOrder {
RegisterAgentActivity = 0,
StoreEntry,
StoreRecord,
RegisterUpdatedContent,
RegisterUpdatedRecord,
RegisterDeletedBy,
RegisterDeletedEntryAction,
RegisterAddLink,
RegisterRemoveLink,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub struct OpOrder {
order: OpNumericalOrder,
timestamp: holochain_zome_types::timestamp::Timestamp,
}
impl std::fmt::Display for OpOrder {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}{:019}",
self.order as u8,
i64::max(0, self.timestamp.as_micros())
)
}
}
impl OpOrder {
pub fn new(op_type: DhtOpType, timestamp: holochain_zome_types::timestamp::Timestamp) -> Self {
let order = match op_type {
DhtOpType::StoreRecord => OpNumericalOrder::StoreRecord,
DhtOpType::StoreEntry => OpNumericalOrder::StoreEntry,
DhtOpType::RegisterAgentActivity => OpNumericalOrder::RegisterAgentActivity,
DhtOpType::RegisterUpdatedContent => OpNumericalOrder::RegisterUpdatedContent,
DhtOpType::RegisterUpdatedRecord => OpNumericalOrder::RegisterUpdatedRecord,
DhtOpType::RegisterDeletedBy => OpNumericalOrder::RegisterDeletedBy,
DhtOpType::RegisterDeletedEntryAction => OpNumericalOrder::RegisterDeletedEntryAction,
DhtOpType::RegisterAddLink => OpNumericalOrder::RegisterAddLink,
DhtOpType::RegisterRemoveLink => OpNumericalOrder::RegisterRemoveLink,
};
Self { order, timestamp }
}
}
impl holochain_sqlite::rusqlite::ToSql for OpOrder {
fn to_sql(
&self,
) -> holochain_sqlite::rusqlite::Result<holochain_sqlite::rusqlite::types::ToSqlOutput> {
Ok(holochain_sqlite::rusqlite::types::ToSqlOutput::Owned(
self.to_string().into(),
))
}
}