use crate::action::NewEntryAction;
use crate::prelude::*;
use crate::warrant::WarrantOp;
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 serde::Deserialize;
use serde::Serialize;
use std::str::FromStr;
mod error;
pub use error::*;
#[cfg(test)]
mod tests;
#[derive(
Clone, Debug, Serialize, Deserialize, SerializedBytes, Eq, PartialEq, derive_more::From,
)]
pub enum DhtOp {
ChainOp(Box<ChainOp>),
WarrantOp(Box<WarrantOp>),
}
#[derive(
Clone, Debug, Serialize, Deserialize, SerializedBytes, Eq, PartialEq, derive_more::Display,
)]
pub enum ChainOp {
#[display("StoreRecord")]
StoreRecord(Signature, Action, RecordEntry),
#[display("StoreEntry")]
StoreEntry(Signature, NewEntryAction, Entry),
#[display("RegisterAgentActivity")]
RegisterAgentActivity(Signature, Action),
#[display("RegisterUpdatedContent")]
RegisterUpdatedContent(Signature, action::Update, RecordEntry),
#[display("RegisterUpdatedRecord")]
RegisterUpdatedRecord(Signature, action::Update, RecordEntry),
#[display("RegisterDeletedBy")]
RegisterDeletedBy(Signature, action::Delete),
#[display("RegisterDeletedEntryAction")]
RegisterDeletedEntryAction(Signature, action::Delete),
#[display("RegisterAddLink")]
RegisterAddLink(Signature, action::CreateLink),
#[display("RegisterRemoveLink")]
RegisterRemoveLink(Signature, action::DeleteLink),
}
impl From<ChainOp> for DhtOp {
fn from(op: ChainOp) -> Self {
DhtOp::ChainOp(Box::new(op))
}
}
impl From<WarrantOp> for DhtOp {
fn from(op: WarrantOp) -> Self {
DhtOp::WarrantOp(Box::new(op))
}
}
impl From<SignedWarrant> for DhtOp {
fn from(op: SignedWarrant) -> Self {
DhtOp::WarrantOp(Box::new(WarrantOp::from(op)))
}
}
#[allow(missing_docs)]
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, derive_more::From)]
pub enum DhtOpLite {
Chain(Box<ChainOpLite>),
Warrant(Box<WarrantOp>),
}
#[allow(missing_docs)]
#[derive(Clone, Debug, Serialize, Deserialize, derive_more::Display)]
pub enum ChainOpLite {
#[display("StoreRecord")]
StoreRecord(ActionHash, Option<EntryHash>, OpBasis),
#[display("StoreEntry")]
StoreEntry(ActionHash, EntryHash, OpBasis),
#[display("RegisterAgentActivity")]
RegisterAgentActivity(ActionHash, OpBasis),
#[display("RegisterUpdatedContent")]
RegisterUpdatedContent(ActionHash, EntryHash, OpBasis),
#[display("RegisterUpdatedRecord")]
RegisterUpdatedRecord(ActionHash, EntryHash, OpBasis),
#[display("RegisterDeletedBy")]
RegisterDeletedBy(ActionHash, OpBasis),
#[display("RegisterDeletedEntryAction")]
RegisterDeletedEntryAction(ActionHash, OpBasis),
#[display("RegisterAddLink")]
RegisterAddLink(ActionHash, OpBasis),
#[display("RegisterRemoveLink")]
RegisterRemoveLink(ActionHash, OpBasis),
}
impl From<ChainOpLite> for DhtOpLite {
fn from(op: ChainOpLite) -> Self {
DhtOpLite::Chain(Box::new(op))
}
}
impl From<WarrantOp> for DhtOpLite {
fn from(op: WarrantOp) -> Self {
DhtOpLite::Warrant(Box::new(op))
}
}
impl From<SignedWarrant> for DhtOpLite {
fn from(warrant: SignedWarrant) -> Self {
DhtOpLite::Warrant(Box::new(warrant.into()))
}
}
impl PartialEq for ChainOpLite {
fn eq(&self, other: &Self) -> bool {
self.get_type() == other.get_type() && self.action_hash() == other.action_hash()
}
}
impl Eq for ChainOpLite {}
impl std::hash::Hash for ChainOpLite {
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::From)]
pub enum DhtOpType {
Chain(ChainOpType),
Warrant(WarrantOpType),
}
impl ToSql for DhtOpType {
fn to_sql(
&self,
) -> holochain_sqlite::rusqlite::Result<holochain_sqlite::rusqlite::types::ToSqlOutput<'_>>
{
match self {
DhtOpType::Chain(op) => op.to_sql(),
DhtOpType::Warrant(op) => op.to_sql(),
}
}
}
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| {
ChainOpType::from_str(&string)
.map(DhtOpType::from)
.or_else(|_| WarrantOpType::from_str(&string).map(DhtOpType::from))
.map_err(|_| holochain_sqlite::rusqlite::types::FromSqlError::InvalidType)
})
}
}
impl DhtOp {
pub fn as_chain_op(&self) -> Option<&ChainOp> {
match self {
Self::ChainOp(op) => Some(op),
_ => None,
}
}
pub fn get_type(&self) -> DhtOpType {
match self {
Self::ChainOp(op) => DhtOpType::Chain(op.get_type()),
Self::WarrantOp(op) => DhtOpType::Warrant(op.get_type()),
}
}
pub fn dht_basis(&self) -> OpBasis {
match self {
Self::ChainOp(op) => op.as_unique_form().basis(),
Self::WarrantOp(op) => op.dht_basis(),
}
}
pub fn signature(&self) -> &Signature {
match self {
Self::ChainOp(op) => op.signature(),
Self::WarrantOp(op) => op.signature(),
}
}
fn to_order(&self) -> OpOrder {
match self {
Self::ChainOp(op) => OpOrder::new(op.get_type(), op.timestamp()),
Self::WarrantOp(op) => OpOrder::new(op.get_type(), op.timestamp()),
}
}
pub fn author(&self) -> AgentPubKey {
match self {
Self::ChainOp(op) => op.action().author().clone(),
Self::WarrantOp(op) => op.author.clone(),
}
}
pub fn timestamp(&self) -> Timestamp {
match self {
Self::ChainOp(op) => op.timestamp(),
Self::WarrantOp(op) => op.timestamp(),
}
}
pub fn to_lite(&self) -> DhtOpLite {
match self {
Self::ChainOp(op) => DhtOpLite::Chain(op.to_lite().into()),
Self::WarrantOp(op) => DhtOpLite::Warrant(op.clone()),
}
}
pub fn sys_validation_dependencies(&self) -> Vec<ActionHash> {
match self {
Self::ChainOp(op) => op.get_type().sys_validation_dependencies(&op.action()),
Self::WarrantOp(op) => match &op.proof {
WarrantProof::ChainIntegrity(w) => match w {
ChainIntegrityWarrant::InvalidChainOp {
action: action_hash,
..
} => vec![action_hash.0.clone()],
ChainIntegrityWarrant::ChainFork { action_pair, .. } => {
vec![action_pair.0 .0.clone()]
}
},
},
}
}
}
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 {
match self.to_order().cmp(&other.to_order()) {
std::cmp::Ordering::Equal => self.signature().cmp(other.signature()),
ordering => ordering,
}
}
}
impl ChainOp {
fn as_unique_form(&self) -> ChainOpUniqueForm<'_> {
match self {
Self::StoreRecord(_, action, _) => ChainOpUniqueForm::StoreRecord(action),
Self::StoreEntry(_, action, _) => ChainOpUniqueForm::StoreEntry(action),
Self::RegisterAgentActivity(_, action) => {
ChainOpUniqueForm::RegisterAgentActivity(action)
}
Self::RegisterUpdatedContent(_, action, _) => {
ChainOpUniqueForm::RegisterUpdatedContent(action)
}
Self::RegisterUpdatedRecord(_, action, _) => {
ChainOpUniqueForm::RegisterUpdatedRecord(action)
}
Self::RegisterDeletedBy(_, action) => ChainOpUniqueForm::RegisterDeletedBy(action),
Self::RegisterDeletedEntryAction(_, action) => {
ChainOpUniqueForm::RegisterDeletedEntryAction(action)
}
Self::RegisterAddLink(_, action) => ChainOpUniqueForm::RegisterAddLink(action),
Self::RegisterRemoveLink(_, action) => ChainOpUniqueForm::RegisterRemoveLink(action),
}
}
pub fn dht_basis(&self) -> OpBasis {
self.as_unique_form().basis()
}
pub fn signature(&self) -> &Signature {
match self {
Self::StoreRecord(s, _, _)
| Self::StoreEntry(s, _, _)
| Self::RegisterAgentActivity(s, _)
| Self::RegisterUpdatedContent(s, _, _)
| Self::RegisterUpdatedRecord(s, _, _)
| Self::RegisterDeletedBy(s, _)
| Self::RegisterDeletedEntryAction(s, _)
| Self::RegisterAddLink(s, _)
| Self::RegisterRemoveLink(s, _) => s,
}
}
pub fn to_lite(&self) -> ChainOpLite {
let basis = self.dht_basis();
match self {
Self::StoreRecord(_, a, _) => {
let e = a.entry_data().map(|(e, _)| e.clone());
let h = ActionHash::with_data_sync(a);
ChainOpLite::StoreRecord(h, e, basis)
}
Self::StoreEntry(_, a, _) => {
let e = a.entry().clone();
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
ChainOpLite::StoreEntry(h, e, basis)
}
Self::RegisterAgentActivity(_, a) => {
let h = ActionHash::with_data_sync(a);
ChainOpLite::RegisterAgentActivity(h, basis)
}
Self::RegisterUpdatedContent(_, a, _) => {
let e = a.entry_hash.clone();
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
ChainOpLite::RegisterUpdatedContent(h, e, basis)
}
Self::RegisterUpdatedRecord(_, a, _) => {
let e = a.entry_hash.clone();
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
ChainOpLite::RegisterUpdatedRecord(h, e, basis)
}
Self::RegisterDeletedBy(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
ChainOpLite::RegisterDeletedBy(h, basis)
}
Self::RegisterDeletedEntryAction(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
ChainOpLite::RegisterDeletedEntryAction(h, basis)
}
Self::RegisterAddLink(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
ChainOpLite::RegisterAddLink(h, basis)
}
Self::RegisterRemoveLink(_, a) => {
let h = ActionHash::with_data_sync(&Action::from(a.clone()));
ChainOpLite::RegisterRemoveLink(h, basis)
}
}
}
pub fn action(&self) -> Action {
match self {
Self::StoreRecord(_, a, _) => a.clone(),
Self::StoreEntry(_, a, _) => a.clone().into(),
Self::RegisterAgentActivity(_, a) => a.clone(),
Self::RegisterUpdatedContent(_, a, _) => a.clone().into(),
Self::RegisterUpdatedRecord(_, a, _) => a.clone().into(),
Self::RegisterDeletedBy(_, a) => a.clone().into(),
Self::RegisterDeletedEntryAction(_, a) => a.clone().into(),
Self::RegisterAddLink(_, a) => a.clone().into(),
Self::RegisterRemoveLink(_, a) => a.clone().into(),
}
}
pub fn signed_action(&self) -> SignedAction {
match self {
Self::StoreRecord(s, a, _) => SignedAction::new(a.clone(), s.clone()),
Self::StoreEntry(s, a, _) => SignedAction::new(a.clone().into(), s.clone()),
Self::RegisterAgentActivity(s, a) => SignedAction::new(a.clone(), s.clone()),
Self::RegisterUpdatedContent(s, a, _) => SignedAction::new(a.clone().into(), s.clone()),
Self::RegisterUpdatedRecord(s, a, _) => SignedAction::new(a.clone().into(), s.clone()),
Self::RegisterDeletedBy(s, a) => SignedAction::new(a.clone().into(), s.clone()),
Self::RegisterDeletedEntryAction(s, a) => {
SignedAction::new(a.clone().into(), s.clone())
}
Self::RegisterAddLink(s, a) => SignedAction::new(a.clone().into(), s.clone()),
Self::RegisterRemoveLink(s, a) => SignedAction::new(a.clone().into(), s.clone()),
}
}
pub fn is_genesis(&self) -> bool {
match self {
ChainOp::StoreRecord(_, a, _) => a.is_genesis(),
ChainOp::StoreEntry(_, a, _) => a.action_seq() < POST_GENESIS_SEQ_THRESHOLD,
ChainOp::RegisterAgentActivity(_, a) => a.is_genesis(),
ChainOp::RegisterUpdatedContent(_, a, _) => a.action_seq < POST_GENESIS_SEQ_THRESHOLD,
ChainOp::RegisterUpdatedRecord(_, a, _) => a.action_seq < POST_GENESIS_SEQ_THRESHOLD,
ChainOp::RegisterDeletedBy(_, a) => a.action_seq < POST_GENESIS_SEQ_THRESHOLD,
ChainOp::RegisterDeletedEntryAction(_, a) => a.action_seq < POST_GENESIS_SEQ_THRESHOLD,
ChainOp::RegisterAddLink(_, a) => a.action_seq < POST_GENESIS_SEQ_THRESHOLD,
ChainOp::RegisterRemoveLink(_, a) => a.action_seq < POST_GENESIS_SEQ_THRESHOLD,
}
}
pub fn entry(&self) -> RecordEntryRef<'_> {
match self {
Self::StoreRecord(_, _, e) => e.as_ref(),
Self::StoreEntry(_, _, e) => RecordEntry::Present(e),
Self::RegisterUpdatedContent(_, _, e) => e.as_ref(),
Self::RegisterUpdatedRecord(_, _, e) => e.as_ref(),
Self::RegisterAgentActivity(_, a) => RecordEntry::new(a.entry_visibility(), None),
Self::RegisterDeletedBy(_, _) => RecordEntry::NA,
Self::RegisterDeletedEntryAction(_, _) => RecordEntry::NA,
Self::RegisterAddLink(_, _) => RecordEntry::NA,
Self::RegisterRemoveLink(_, _) => RecordEntry::NA,
}
}
pub fn get_type(&self) -> ChainOpType {
match self {
Self::StoreRecord(_, _, _) => ChainOpType::StoreRecord,
Self::StoreEntry(_, _, _) => ChainOpType::StoreEntry,
Self::RegisterUpdatedContent(_, _, _) => ChainOpType::RegisterUpdatedContent,
Self::RegisterUpdatedRecord(_, _, _) => ChainOpType::RegisterUpdatedRecord,
Self::RegisterAgentActivity(_, _) => ChainOpType::RegisterAgentActivity,
Self::RegisterDeletedBy(_, _) => ChainOpType::RegisterDeletedBy,
Self::RegisterDeletedEntryAction(_, _) => ChainOpType::RegisterDeletedEntryAction,
Self::RegisterAddLink(_, _) => ChainOpType::RegisterAddLink,
Self::RegisterRemoveLink(_, _) => ChainOpType::RegisterRemoveLink,
}
}
pub fn from_type(
op_type: ChainOpType,
action: SignedAction,
entry: Option<Entry>,
) -> DhtOpResult<Self> {
let (action, signature) = action.into();
let entry = RecordEntry::new(action.entry_visibility(), entry);
let r = match op_type {
ChainOpType::StoreRecord => Self::StoreRecord(signature, action, entry),
ChainOpType::StoreEntry => {
let entry = entry
.into_option()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(Box::new(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())),
};
Self::StoreEntry(signature, action, entry)
}
ChainOpType::RegisterAgentActivity => Self::RegisterAgentActivity(signature, action),
ChainOpType::RegisterUpdatedContent => {
Self::RegisterUpdatedContent(signature, action.try_into()?, entry)
}
ChainOpType::RegisterUpdatedRecord => {
Self::RegisterUpdatedRecord(signature, action.try_into()?, entry)
}
ChainOpType::RegisterDeletedBy => {
Self::RegisterDeletedBy(signature, action.try_into()?)
}
ChainOpType::RegisterDeletedEntryAction => {
Self::RegisterDeletedEntryAction(signature, action.try_into()?)
}
ChainOpType::RegisterAddLink => Self::RegisterAddLink(signature, action.try_into()?),
ChainOpType::RegisterRemoveLink => {
Self::RegisterRemoveLink(signature, action.try_into()?)
}
};
Ok(r)
}
#[cfg(feature = "test_utils")]
pub fn normalized(self) -> DhtOpResult<Self> {
let action = self.signed_action();
let entry = if action.entry_hash().is_none() {
None
} else {
self.entry().into_option().cloned()
};
Self::from_type(self.get_type(), action, entry)
}
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
.first()
.map(|(pubkey, _)| pubkey)
} else {
None
}
} else {
None
}
}
pub fn timestamp(&self) -> Timestamp {
match self {
ChainOp::StoreRecord(_, a, _) => a.timestamp(),
ChainOp::StoreEntry(_, a, _) => a.timestamp(),
ChainOp::RegisterAgentActivity(_, a) => a.timestamp(),
ChainOp::RegisterUpdatedContent(_, a, _) => a.timestamp,
ChainOp::RegisterUpdatedRecord(_, a, _) => a.timestamp,
ChainOp::RegisterDeletedBy(_, a) => a.timestamp,
ChainOp::RegisterDeletedEntryAction(_, a) => a.timestamp,
ChainOp::RegisterAddLink(_, a) => a.timestamp,
ChainOp::RegisterRemoveLink(_, a) => a.timestamp,
}
}
pub fn author(&self) -> &AgentPubKey {
match self {
ChainOp::StoreRecord(_, a, _) => a.author(),
ChainOp::StoreEntry(_, a, _) => a.author(),
ChainOp::RegisterAgentActivity(_, a) => a.author(),
ChainOp::RegisterUpdatedContent(_, a, _) => &a.author,
ChainOp::RegisterUpdatedRecord(_, a, _) => &a.author,
ChainOp::RegisterDeletedBy(_, a) => &a.author,
ChainOp::RegisterDeletedEntryAction(_, a) => &a.author,
ChainOp::RegisterAddLink(_, a) => &a.author,
ChainOp::RegisterRemoveLink(_, a) => &a.author,
}
}
pub fn sys_validation_dependencies(&self) -> Vec<ActionHash> {
self.get_type().sys_validation_dependencies(&self.action())
}
pub fn map_entry(self, f: impl FnOnce(RecordEntry) -> RecordEntry) -> Self {
match self {
Self::StoreRecord(signature, action, entry) => {
Self::StoreRecord(signature, action, f(entry))
}
Self::RegisterUpdatedContent(signature, action, entry) => {
Self::RegisterUpdatedContent(signature, action, f(entry))
}
Self::RegisterUpdatedRecord(signature, action, entry) => {
Self::RegisterUpdatedRecord(signature, action, f(entry))
}
_ => self,
}
}
}
impl DhtOpLite {
pub fn dht_basis(&self) -> OpBasis {
match self {
Self::Chain(op) => op.dht_basis().clone(),
Self::Warrant(op) => op.dht_basis(),
}
}
pub fn as_chain_op(&self) -> Option<&ChainOpLite> {
match self {
Self::Chain(op) => Some(op),
_ => None,
}
}
pub fn get_type(&self) -> DhtOpType {
match self {
Self::Chain(op) => op.get_type().into(),
Self::Warrant(op) => op.get_type().into(),
}
}
}
impl ChainOpLite {
pub fn dht_basis(&self) -> &OpBasis {
match self {
ChainOpLite::StoreRecord(_, _, b)
| ChainOpLite::StoreEntry(_, _, b)
| ChainOpLite::RegisterAgentActivity(_, b)
| ChainOpLite::RegisterUpdatedContent(_, _, b)
| ChainOpLite::RegisterUpdatedRecord(_, _, b)
| ChainOpLite::RegisterDeletedBy(_, b)
| ChainOpLite::RegisterDeletedEntryAction(_, b)
| ChainOpLite::RegisterAddLink(_, b)
| ChainOpLite::RegisterRemoveLink(_, b) => b,
}
}
pub fn action_hash(&self) -> &ActionHash {
match self {
Self::StoreRecord(h, _, _)
| Self::StoreEntry(h, _, _)
| Self::RegisterAgentActivity(h, _)
| Self::RegisterUpdatedContent(h, _, _)
| Self::RegisterUpdatedRecord(h, _, _)
| Self::RegisterDeletedBy(h, _)
| Self::RegisterDeletedEntryAction(h, _)
| Self::RegisterAddLink(h, _)
| Self::RegisterRemoveLink(h, _) => h,
}
}
pub fn get_type(&self) -> ChainOpType {
match self {
Self::StoreRecord(_, _, _) => ChainOpType::StoreRecord,
Self::StoreEntry(_, _, _) => ChainOpType::StoreEntry,
Self::RegisterUpdatedContent(_, _, _) => ChainOpType::RegisterUpdatedContent,
Self::RegisterUpdatedRecord(_, _, _) => ChainOpType::RegisterUpdatedRecord,
Self::RegisterAgentActivity(_, _) => ChainOpType::RegisterAgentActivity,
Self::RegisterDeletedBy(_, _) => ChainOpType::RegisterDeletedBy,
Self::RegisterDeletedEntryAction(_, _) => ChainOpType::RegisterDeletedEntryAction,
Self::RegisterAddLink(_, _) => ChainOpType::RegisterAddLink,
Self::RegisterRemoveLink(_, _) => ChainOpType::RegisterRemoveLink,
}
}
pub fn from_type(
op_type: ChainOpType,
action_hash: ActionHash,
action: &Action,
) -> DhtOpResult<Self> {
let op = match op_type {
ChainOpType::StoreRecord => {
let entry_hash = action.entry_hash().cloned();
Self::StoreRecord(action_hash.clone(), entry_hash, action_hash.into())
}
ChainOpType::StoreEntry => {
let entry_hash = action
.entry_hash()
.cloned()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(Box::new(action.clone())))?;
Self::StoreEntry(action_hash, entry_hash.clone(), entry_hash.into())
}
ChainOpType::RegisterAgentActivity => {
Self::RegisterAgentActivity(action_hash, action.author().clone().into())
}
ChainOpType::RegisterUpdatedContent => {
let entry_hash = action
.entry_hash()
.cloned()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(Box::new(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())
}
ChainOpType::RegisterUpdatedRecord => {
let entry_hash = action
.entry_hash()
.cloned()
.ok_or_else(|| DhtOpError::ActionWithoutEntry(Box::new(action.clone())))?;
let basis = match action {
Action::Update(update) => update.original_action_address.clone(),
_ => return Err(DhtOpError::OpActionMismatch(op_type, action.action_type())),
};
Self::RegisterUpdatedRecord(action_hash, entry_hash, basis.into())
}
ChainOpType::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())
}
ChainOpType::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())
}
ChainOpType::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)
}
ChainOpType::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 ChainOpUniqueForm<'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> ChainOpUniqueForm<'a> {
fn basis(&'a self) -> OpBasis {
match self {
ChainOpUniqueForm::StoreRecord(action) => ActionHash::with_data_sync(*action).into(),
ChainOpUniqueForm::StoreEntry(action) => action.entry().clone().into(),
ChainOpUniqueForm::RegisterAgentActivity(action) => action.author().clone().into(),
ChainOpUniqueForm::RegisterUpdatedContent(action) => {
action.original_entry_address.clone().into()
}
ChainOpUniqueForm::RegisterUpdatedRecord(action) => {
action.original_action_address.clone().into()
}
ChainOpUniqueForm::RegisterDeletedBy(action) => action.deletes_address.clone().into(),
ChainOpUniqueForm::RegisterDeletedEntryAction(action) => {
action.deletes_entry_address.clone().into()
}
ChainOpUniqueForm::RegisterAddLink(action) => action.base_address.clone(),
ChainOpUniqueForm::RegisterRemoveLink(action) => action.base_address.clone(),
}
}
pub fn op_hash(op_type: ChainOpType, action: Action) -> DhtOpResult<(Action, DhtOpHash)> {
match op_type {
ChainOpType::StoreRecord => {
let hash = DhtOpHash::with_data_sync(&ChainOpUniqueForm::StoreRecord(&action));
Ok((action, hash))
}
ChainOpType::StoreEntry => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&ChainOpUniqueForm::StoreEntry(&action));
Ok((action.into(), hash))
}
ChainOpType::RegisterAgentActivity => {
let hash =
DhtOpHash::with_data_sync(&ChainOpUniqueForm::RegisterAgentActivity(&action));
Ok((action, hash))
}
ChainOpType::RegisterUpdatedContent => {
let action = action.try_into()?;
let hash =
DhtOpHash::with_data_sync(&ChainOpUniqueForm::RegisterUpdatedContent(&action));
Ok((action.into(), hash))
}
ChainOpType::RegisterUpdatedRecord => {
let action = action.try_into()?;
let hash =
DhtOpHash::with_data_sync(&ChainOpUniqueForm::RegisterUpdatedRecord(&action));
Ok((action.into(), hash))
}
ChainOpType::RegisterDeletedBy => {
let action = action.try_into()?;
let hash =
DhtOpHash::with_data_sync(&ChainOpUniqueForm::RegisterDeletedBy(&action));
Ok((action.into(), hash))
}
ChainOpType::RegisterDeletedEntryAction => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(
&ChainOpUniqueForm::RegisterDeletedEntryAction(&action),
);
Ok((action.into(), hash))
}
ChainOpType::RegisterAddLink => {
let action = action.try_into()?;
let hash = DhtOpHash::with_data_sync(&ChainOpUniqueForm::RegisterAddLink(&action));
Ok((action.into(), hash))
}
ChainOpType::RegisterRemoveLink => {
let action = action.try_into()?;
let hash =
DhtOpHash::with_data_sync(&ChainOpUniqueForm::RegisterRemoveLink(&action));
Ok((action.into(), hash))
}
}
}
}
pub fn produce_ops_from_record(record: &Record) -> DhtOpResult<Vec<ChainOp>> {
let op_lites = produce_op_lites_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_lites.len());
for op_light in op_lites {
let signature = signature.clone();
let action = action.clone();
let op = match op_light {
ChainOpLite::StoreRecord(_, _, _) => {
ChainOp::StoreRecord(signature, action, entry.clone())
}
ChainOpLite::StoreEntry(_, _, _) => {
let new_entry_action = action.clone().try_into()?;
let e = match entry.clone().into_option() {
Some(e) => e,
None => {
continue;
}
};
ChainOp::StoreEntry(signature, new_entry_action, e)
}
ChainOpLite::RegisterAgentActivity(_, _) => {
ChainOp::RegisterAgentActivity(signature, action)
}
ChainOpLite::RegisterUpdatedContent(_, _, _) => {
let entry_update = action.try_into()?;
ChainOp::RegisterUpdatedContent(signature, entry_update, entry.clone())
}
ChainOpLite::RegisterUpdatedRecord(_, _, _) => {
let entry_update = action.try_into()?;
ChainOp::RegisterUpdatedRecord(signature, entry_update, entry.clone())
}
ChainOpLite::RegisterDeletedEntryAction(_, _) => {
let record_delete = action.try_into()?;
ChainOp::RegisterDeletedEntryAction(signature, record_delete)
}
ChainOpLite::RegisterDeletedBy(_, _) => {
let record_delete = action.try_into()?;
ChainOp::RegisterDeletedBy(signature, record_delete)
}
ChainOpLite::RegisterAddLink(_, _) => {
let link_add = action.try_into()?;
ChainOp::RegisterAddLink(signature, link_add)
}
ChainOpLite::RegisterRemoveLink(_, _) => {
let link_remove = action.try_into()?;
ChainOp::RegisterRemoveLink(signature, link_remove)
}
};
ops.push(op);
}
Ok(ops)
}
pub fn produce_op_lites_from_records(actions: Vec<&Record>) -> DhtOpResult<Vec<ChainOpLite>> {
let actions_and_hashes = actions.into_iter().map(|e| {
(
e.action_address(),
e.action(),
e.action().entry_data().map(|(h, _)| h.clone()),
)
});
produce_op_lites_from_iter(actions_and_hashes)
}
pub fn produce_op_lites_from_iter<'a>(
iter: impl Iterator<Item = (&'a ActionHash, &'a Action, Option<EntryHash>)>,
) -> DhtOpResult<Vec<ChainOpLite>> {
let mut ops = Vec::new();
for (action_hash, action, maybe_entry_hash) in iter {
let op_lites = action_to_op_types(action)
.into_iter()
.filter_map(|op_type| {
let op_light = match (op_type, action) {
(ChainOpType::StoreRecord, _) => {
let store_record_basis = ChainOpUniqueForm::StoreRecord(action).basis();
ChainOpLite::StoreRecord(
action_hash.clone(),
maybe_entry_hash.clone(),
store_record_basis,
)
}
(ChainOpType::RegisterAgentActivity, _) => {
let register_activity_basis =
ChainOpUniqueForm::RegisterAgentActivity(action).basis();
ChainOpLite::RegisterAgentActivity(
action_hash.clone(),
register_activity_basis,
)
}
(ChainOpType::StoreEntry, Action::Create(create)) => ChainOpLite::StoreEntry(
action_hash.clone(),
maybe_entry_hash.clone()?,
ChainOpUniqueForm::StoreEntry(&NewEntryAction::Create(create.clone()))
.basis(),
),
(ChainOpType::StoreEntry, Action::Update(update)) => ChainOpLite::StoreEntry(
action_hash.clone(),
maybe_entry_hash.clone()?,
ChainOpUniqueForm::StoreEntry(&NewEntryAction::Update(update.clone()))
.basis(),
),
(ChainOpType::RegisterUpdatedContent, Action::Update(update)) => {
ChainOpLite::RegisterUpdatedContent(
action_hash.clone(),
maybe_entry_hash.clone()?,
ChainOpUniqueForm::RegisterUpdatedContent(update).basis(),
)
}
(ChainOpType::RegisterUpdatedRecord, Action::Update(update)) => {
ChainOpLite::RegisterUpdatedRecord(
action_hash.clone(),
maybe_entry_hash.clone()?,
ChainOpUniqueForm::RegisterUpdatedRecord(update).basis(),
)
}
(ChainOpType::RegisterDeletedBy, Action::Delete(delete)) => {
ChainOpLite::RegisterDeletedBy(
action_hash.clone(),
ChainOpUniqueForm::RegisterDeletedBy(delete).basis(),
)
}
(ChainOpType::RegisterDeletedEntryAction, Action::Delete(delete)) => {
ChainOpLite::RegisterDeletedEntryAction(
action_hash.clone(),
ChainOpUniqueForm::RegisterDeletedEntryAction(delete).basis(),
)
}
(ChainOpType::RegisterAddLink, Action::CreateLink(create_link)) => {
ChainOpLite::RegisterAddLink(
action_hash.clone(),
ChainOpUniqueForm::RegisterAddLink(create_link).basis(),
)
}
(ChainOpType::RegisterRemoveLink, Action::DeleteLink(delete_link)) => {
ChainOpLite::RegisterRemoveLink(
action_hash.clone(),
ChainOpUniqueForm::RegisterRemoveLink(delete_link).basis(),
)
}
_ => return None,
};
Some(op_light)
});
ops.extend(op_lites);
}
Ok(ops)
}
pub fn action_to_op_types(action: &Action) -> Vec<ChainOpType> {
match action {
Action::Dna(_)
| Action::OpenChain(_)
| Action::CloseChain(_)
| Action::AgentValidationPkg(_)
| Action::InitZomesComplete(_) => {
vec![ChainOpType::StoreRecord, ChainOpType::RegisterAgentActivity]
}
Action::CreateLink(_) => vec![
ChainOpType::StoreRecord,
ChainOpType::RegisterAgentActivity,
ChainOpType::RegisterAddLink,
],
Action::DeleteLink(_) => vec![
ChainOpType::StoreRecord,
ChainOpType::RegisterAgentActivity,
ChainOpType::RegisterRemoveLink,
],
Action::Create(_) => vec![
ChainOpType::StoreRecord,
ChainOpType::RegisterAgentActivity,
ChainOpType::StoreEntry,
],
Action::Update(_) => vec![
ChainOpType::StoreRecord,
ChainOpType::RegisterAgentActivity,
ChainOpType::StoreEntry,
ChainOpType::RegisterUpdatedContent,
ChainOpType::RegisterUpdatedRecord,
],
Action::Delete(_) => vec![
ChainOpType::StoreRecord,
ChainOpType::RegisterAgentActivity,
ChainOpType::RegisterDeletedBy,
ChainOpType::RegisterDeletedEntryAction,
],
}
}
impl<'a> TryFrom<&ChainOpUniqueForm<'a>> for SerializedBytes {
type Error = SerializedBytesError;
fn try_from(u: &ChainOpUniqueForm<'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>;
pub type ChainOpHashed = HoloHashed<ChainOp>;
impl HashableContent for DhtOp {
type HashType = hash_type::DhtOp;
fn hash_type(&self) -> Self::HashType {
hash_type::DhtOp
}
fn hashable_content(&self) -> HashableContentBytes {
match self {
DhtOp::ChainOp(op) => op.hashable_content(),
DhtOp::WarrantOp(op) => op.hashable_content(),
}
}
}
impl HashableContent for ChainOp {
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 ChainOpUniqueForm<'_> {
type HashType = hash_type::DhtOp;
fn hash_type(&self) -> Self::HashType {
hash_type::DhtOp
}
fn hashable_content(&self) -> HashableContentBytes {
HashableContentBytes::Content(
UnsafeBytes::from(
holochain_serialized_bytes::encode(self)
.expect("Could not serialize HashableContent"),
)
.into(),
)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, SerializedBytes)]
pub enum WireOps {
Entry(WireEntryOps),
Record(WireRecordOps),
Warrant(Box<WarrantOp>),
}
impl WireOps {
pub fn render(self) -> DhtOpResult<RenderedOps> {
match self {
WireOps::Entry(o) => o.render(),
WireOps::Record(o) => o.render(),
WireOps::Warrant(warrant) => Ok(RenderedOps {
entry: Default::default(),
ops: Default::default(),
warrant: Some(*warrant),
}),
}
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RenderedOp {
pub action: SignedActionHashed,
pub op_light: DhtOpLite,
pub op_hash: DhtOpHash,
pub validation_status: Option<ValidationStatus>,
}
impl RenderedOp {
pub fn new(
action: Action,
signature: Signature,
validation_status: Option<ValidationStatus>,
op_type: ChainOpType,
) -> DhtOpResult<Self> {
let (action, op_hash) = ChainOpUniqueForm::op_hash(op_type, action)?;
let action_hashed = ActionHashed::from_content_sync(action);
let action = SignedActionHashed::with_presigned(action_hashed, signature);
let op_light =
ChainOpLite::from_type(op_type, action.as_hash().clone(), action.action())?.into();
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>,
pub warrant: Option<WarrantOp>,
}
#[allow(missing_docs)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum OpNumericalOrder {
RegisterAgentActivity = 0,
StoreEntry,
StoreRecord,
RegisterUpdatedContent,
RegisterUpdatedRecord,
RegisterDeletedBy,
RegisterDeletedEntryAction,
RegisterAddLink,
RegisterRemoveLink,
ChainIntegrityWarrant,
}
#[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: impl Into<DhtOpType>,
timestamp: holochain_zome_types::timestamp::Timestamp,
) -> Self {
let order = match op_type.into() {
DhtOpType::Chain(ChainOpType::StoreRecord) => OpNumericalOrder::StoreRecord,
DhtOpType::Chain(ChainOpType::StoreEntry) => OpNumericalOrder::StoreEntry,
DhtOpType::Chain(ChainOpType::RegisterAgentActivity) => {
OpNumericalOrder::RegisterAgentActivity
}
DhtOpType::Chain(ChainOpType::RegisterUpdatedContent) => {
OpNumericalOrder::RegisterUpdatedContent
}
DhtOpType::Chain(ChainOpType::RegisterUpdatedRecord) => {
OpNumericalOrder::RegisterUpdatedRecord
}
DhtOpType::Chain(ChainOpType::RegisterDeletedBy) => OpNumericalOrder::RegisterDeletedBy,
DhtOpType::Chain(ChainOpType::RegisterDeletedEntryAction) => {
OpNumericalOrder::RegisterDeletedEntryAction
}
DhtOpType::Chain(ChainOpType::RegisterAddLink) => OpNumericalOrder::RegisterAddLink,
DhtOpType::Chain(ChainOpType::RegisterRemoveLink) => {
OpNumericalOrder::RegisterRemoveLink
}
DhtOpType::Warrant(WarrantOpType::ChainIntegrityWarrant) => {
OpNumericalOrder::ChainIntegrityWarrant
}
};
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(),
))
}
}