diff --git a/.gitignore b/.gitignore index 46dff00229..d93149ed08 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,5 @@ Cargo.lock !/Cargo.lock stress-test/passthrough-dna.dna.json + +.vscode/ diff --git a/crates/cli/src/main.rs b/crates/cli/src/main.rs index 6ed4bef125..9bbba78438 100755 --- a/crates/cli/src/main.rs +++ b/crates/cli/src/main.rs @@ -209,7 +209,7 @@ fn run() -> HolochainResult<()> { return Err(HolochainError::Default(format_err!( "Failed to parse properties argument as JSON: {:?}", e - ))) + ))); } } } diff --git a/crates/conductor_lib/src/conductor/admin.rs b/crates/conductor_lib/src/conductor/admin.rs index c5c10f5079..04d96856a1 100644 --- a/crates/conductor_lib/src/conductor/admin.rs +++ b/crates/conductor_lib/src/conductor/admin.rs @@ -222,7 +222,7 @@ impl ConductorAdmin for Conductor { return Err(HolochainError::ConfigError(format!( "Invalid storage option: {}", s - ))) + ))); } }; diff --git a/crates/core/src/action.rs b/crates/core/src/action.rs index b41913093c..e501cf2f70 100644 --- a/crates/core/src/action.rs +++ b/crates/core/src/action.rs @@ -4,7 +4,7 @@ use crate::{ network::{ direct_message::DirectMessage, entry_aspect::EntryAspect, - entry_with_header::EntryWithHeader, + header_with_its_entry::HeaderWithItsEntry, query::{GetLinksNetworkQuery, NetworkQueryResult}, state::NetworkState, }, @@ -127,7 +127,7 @@ pub enum Action { HoldAspect(EntryAspect), //action for updating crudstatus - CrudStatus((EntryWithHeader, CrudStatus)), + CrudStatus((HeaderWithItsEntry, CrudStatus)), // ---------------- // Network actions: diff --git a/crates/core/src/agent/state.rs b/crates/core/src/agent/state.rs index a5f7f19b6b..495f524e42 100644 --- a/crates/core/src/agent/state.rs +++ b/crates/core/src/agent/state.rs @@ -1,7 +1,7 @@ use crate::{ action::{Action, ActionWrapper, AgentReduceFn}, agent::chain_store::{ChainStore, ChainStoreIterator}, - network::entry_with_header::EntryWithHeader, + network::header_with_its_entry::HeaderWithItsEntry, state::State, }; use holochain_persistence_api::cas::content::{Address, AddressableContent, Content}; @@ -221,13 +221,13 @@ pub fn create_new_chain_header( )) } -/// Create an entry-with-header for a header. +/// Creates a `HeaderWithItsEntry` for a `ChainHeader`. /// Since published headers are treated as entries, the header must also /// have its own header! -pub fn create_entry_with_header_for_header( +pub fn create_header_with_its_entry_for_header( root_state: &StateWrapper, chain_header: ChainHeader, -) -> Result { +) -> Result { let timestamp = chain_header.timestamp().clone(); let entry = Entry::ChainHeader(chain_header); // This header entry needs its own header so we can publish it. @@ -251,7 +251,7 @@ pub fn create_entry_with_header_for_header( &None, ×tamp, ); - Ok(EntryWithHeader { entry, header }) + HeaderWithItsEntry::try_from_header_and_entry(header, entry) } /// Do a Commit Action against an agent state. diff --git a/crates/core/src/dht/dht_reducers.rs b/crates/core/src/dht/dht_reducers.rs index 24ab7de681..777f58dbc9 100644 --- a/crates/core/src/dht/dht_reducers.rs +++ b/crates/core/src/dht/dht_reducers.rs @@ -6,6 +6,7 @@ use crate::{ dht_store::DhtStore, pending_validations::{PendingValidationWithTimeout, ValidationTimeout}, }, + error::HolochainError, }; use std::sync::Arc; @@ -81,10 +82,22 @@ pub(crate) fn reduce_hold_aspect( match aspect { EntryAspect::Content(entry, header) => { match reduce_store_entry_inner(&mut new_store, entry) { - Ok(()) => { - new_store.add_header_for_entry(&entry, &header).ok()?; - Some(new_store) - } + Ok(()) => match new_store.add_header_for_entry(&entry, &header) { + Ok(()) => Some(new_store), + Err(err) => { + let err_msg = format!( + "Tried to add the header for entry to the new + store in reduce_hold_aspect, got error. {}", + err + ); + debug!( + "{}, entry:\n{:?}\nheader:\n{:?} \nnew_store:\n{:?}", + err_msg, entry, header, new_store + ); + error!("{}", err_msg); + None + } + }, Err(e) => { error!("{}", e); None @@ -191,7 +204,11 @@ pub fn reduce_prune(old_store: &DhtStore, _action_wrapper: &ActionWrapper) -> Op .unique_by(|p| { ( p.pending.workflow.clone(), - p.pending.entry_with_header.header.entry_address(), + p.pending + .header_with_its_entry + .header() + .entry_address() + .clone(), ) }) .cloned() @@ -251,15 +268,18 @@ pub mod tests { pending_validations::{PendingValidation, PendingValidationStruct, ValidatingWorkflow}, }, instance::tests::test_context, - network::entry_with_header::EntryWithHeader, + network::header_with_its_entry::HeaderWithItsEntry, state::test_store, }; use bitflags::_core::time::Duration; use holochain_core_types::{ agent::{test_agent_id, test_agent_id_with_name}, - chain_header::test_chain_header, + chain_header::{ + test_chain_header, test_chain_header_for_link_entry, test_chain_header_for_sys_entry, + ChainHeader, + }, eav::Attribute, - entry::{test_entry, test_sys_entry, Entry}, + entry::{test_entry, test_link_entry, test_sys_entry, Entry}, link::{link_data::LinkData, Link, LinkActionKind}, network::entry_aspect::EntryAspect, }; @@ -280,6 +300,7 @@ pub mod tests { #[test] fn reduce_hold_aspect_test() { + enable_logging_for_test(); let context = test_context("bob", None); let store = test_store(context); @@ -290,7 +311,7 @@ pub mod tests { &store.dht(), &ActionWrapper::new(Action::HoldAspect(EntryAspect::Content( sys_entry.clone(), - test_chain_header(), + test_chain_header_for_sys_entry(), ))), ) .expect("there should be a new store for committing a sys entry"); @@ -541,23 +562,47 @@ pub mod tests { assert_eq!(&entry, &result_entry,); } - fn create_pending_validation(entry: Entry, workflow: ValidatingWorkflow) -> PendingValidation { - let entry_with_header = EntryWithHeader { - entry: entry.clone(), - header: test_chain_header(), - }; - - Arc::new(PendingValidationStruct::new(entry_with_header, workflow)) + fn try_create_pending_validation( + entry: Entry, + header: ChainHeader, + workflow: ValidatingWorkflow, + ) -> Result { + match HeaderWithItsEntry::try_from_header_and_entry(header.clone(), entry.clone()) { + Ok(header_with_its_entry) => Arc::new(PendingValidationStruct::new( + header_with_its_entry, + workflow, + )), + Err(err) => { + let err_msg = format!( + "Tried to create a pending validation, got an error: {}", + err + ); + debug!( + "{}, entry:\n{:?}\nheader from test_chain_header():\n{:?}\n", + err_msg, entry, header + ); + Err(err_msg) + } + } } + // Causes a header and entry mismatch when calling try_create_pending_validation() + // -> try_from_header_and_entry(). + // Should be since link_entry doesn't match with test chain header #[test] pub fn test_holding_queue() { + enable_logging_for_test(); let context = test_context("test", None); let store = DhtStore::new(context.dht_storage.clone(), context.eav_storage.clone()); assert_eq!(store.queued_holding_workflows().len(), 0); let test_entry = test_entry(); - let hold = create_pending_validation(test_entry.clone(), ValidatingWorkflow::HoldEntry); + let test_header = test_chain_header(); + let hold = try_create_pending_validation( + test_entry.clone(), + test_header.clone(), + ValidatingWorkflow::HoldEntry, + ); let action = ActionWrapper::new(Action::QueueHoldingWorkflow(( hold.clone(), Some((SystemTime::now(), Duration::from_secs(10000))), @@ -567,23 +612,11 @@ pub mod tests { assert_eq!(store.queued_holding_workflows().len(), 1); assert!(store.has_exact_queued_holding_workflow(&hold)); - let test_link = String::from("test_link"); - let test_tag = String::from("test-tag"); - let link = Link::new( - &test_entry.address(), - &test_entry.address(), - &test_link.clone(), - &test_tag.clone(), + let hold_link = try_create_pending_validation( + test_link_entry(), + test_chain_header_for_link_entry(), + ValidatingWorkflow::HoldLink, ); - let link_data = LinkData::from_link( - &link, - LinkActionKind::ADD, - test_chain_header(), - test_agent_id(), - ); - - let link_entry = Entry::LinkAdd(link_data.clone()); - let hold_link = create_pending_validation(link_entry, ValidatingWorkflow::HoldLink); let action = ActionWrapper::new(Action::QueueHoldingWorkflow((hold_link.clone(), None))); let store = reduce_queue_holding_workflow(&store, &action).unwrap(); @@ -597,7 +630,11 @@ pub mod tests { let (next_pending, _) = store.next_queued_holding_workflow().unwrap(); assert_eq!(hold_link, next_pending); - let update = create_pending_validation(test_entry.clone(), ValidatingWorkflow::UpdateEntry); + let update = try_create_pending_validation( + test_entry.clone(), + test_header, + ValidatingWorkflow::UpdateEntry, + ); let action = ActionWrapper::new(Action::QueueHoldingWorkflow((update.clone(), None))); let store = reduce_queue_holding_workflow(&store, &action).unwrap(); diff --git a/crates/core/src/dht/dht_store.rs b/crates/core/src/dht/dht_store.rs index e26c9eb72b..face00b838 100644 --- a/crates/core/src/dht/dht_store.rs +++ b/crates/core/src/dht/dht_store.rs @@ -4,6 +4,7 @@ use crate::{ aspect_map::{AspectMap, AspectMapBare}, pending_validations::{PendingValidationWithTimeout, ValidationTimeout}, }, + network::header_with_its_entry::HeaderWithItsEntry, }; use holochain_core_types::{ chain_header::ChainHeader, @@ -230,14 +231,42 @@ impl DhtStore { entry: &Entry, header: &ChainHeader, ) -> Result<(), HolochainError> { - let eavi = EntityAttributeValueIndex::new( - &entry.address(), - &Attribute::EntryHeader, - &header.address(), - )?; - self.add(header)?; - self.meta_storage.write().unwrap().add_eavi(&eavi)?; - Ok(()) + match HeaderWithItsEntry::try_from_header_and_entry(header.clone(), entry.clone()) { + Ok(_header_with_its_entry) => { + let eavi = EntityAttributeValueIndex::new( + &entry.address(), + &Attribute::EntryHeader, + &header.address(), + )?; + self.add(header)?; + self.meta_storage.write().unwrap().add_eavi(&eavi)?; + Ok(()) + } + Err(err) => match err { + HolochainError::HeaderEntryMismatch( + mut err_msg, + header_entry_address, + entry_address, + ) => { + debug!( + "Tried to add entry:\n{:#?}\nand header:\n{:#?}\nto the CAS and EAV, respectively", + entry, header, + ); + err_msg = format!( + "Tried to add entry and header to the CAS and EAV,\n + respectively. See the debug log for further details\n + of header and entry. {}", + err_msg + ); + Err(HolochainError::HeaderEntryMismatch( + err_msg, + header_entry_address, + entry_address, + )) + } + _ => Err(err), + }, + } } pub fn mark_aspect_as_held(&mut self, aspect: &EntryAspect) { @@ -305,8 +334,8 @@ impl DhtStore { |PendingValidationWithTimeout { pending: current, .. }| { - current.entry_with_header.header.entry_address() - == pending.entry_with_header.header.entry_address() + current.header_with_its_entry.header().entry_address() + == pending.header_with_its_entry.header().entry_address() && current.workflow == pending.workflow }, ) @@ -327,7 +356,7 @@ where let unique_pending: HashSet
= pending .clone() .into_iter() - .map(|p| p.pending.entry_with_header.entry.address()) + .map(|p| p.pending.header_with_its_entry.entry().address()) .collect(); Box::new(move |p| { @@ -355,13 +384,14 @@ impl AddContent for DhtStore { #[cfg(test)] pub mod tests { use super::*; - use crate::{ - dht::pending_validations::{PendingValidationStruct, ValidatingWorkflow}, - network::entry_with_header::EntryWithHeader, - }; + use crate::dht::pending_validations::{PendingValidationStruct, ValidatingWorkflow}; use holochain_core_types::{ - chain_header::test_chain_header_with_sig, - entry::{test_entry, test_entry_a, test_entry_b, test_entry_c}, + chain_header::{ + test_chain_header_from_entry_with_sig_default_provs_time, test_chain_header_with_sig, + }, + entry::{ + entry_type::test_entry_type, test_entry, test_entry_a, test_entry_b, test_entry_c, + }, }; use holochain_persistence_api::{ @@ -385,24 +415,67 @@ pub mod tests { assert_eq!(headers, vec![header1, header2]); } - fn pending_validation_for_entry( + fn try_pending_validation_for_entry_header_and_deps( entry: Entry, + header: ChainHeader, dependencies: Vec
, - ) -> PendingValidationWithTimeout { - let header = test_chain_header_with_sig("sig1"); - let mut pending_struct = PendingValidationStruct::new( - EntryWithHeader { entry, header }, + ) -> Result { + match PendingValidationStruct::try_from_entry_and_header( + entry.clone(), + header.clone(), + EntryAspect::Content(entry, header), ValidatingWorkflow::HoldEntry, - ); - pending_struct.dependencies = dependencies; - PendingValidationWithTimeout::new(Arc::new(pending_struct.clone()), None) + ) { + Ok(mut pending_struct) => { + pending_struct.dependencies = dependencies; + Ok(PendingValidationWithTimeout::new( + Arc::new(pending_struct.clone()), + None, + )) + } + Err(err) => { + let err_msg = format!( + "Tried pending validation for entry from entry and header, got error: {}", + err + ); + Err(HolochainError::ErrorGeneric(err_msg)) + } + } + } + + // Convenience function + fn test_chain_header_for_entry_app(entry: Entry) -> ChainHeader { + test_chain_header_from_entry_with_sig_default_provs_time(entry, test_entry_type(), "sig1") + } + + // Convenience function, creates a header from the entry, no dependencies for app entry + fn try_pending_validation_for_app_entry_no_deps( + entry: Entry, + ) -> Result { + try_pending_validation_for_entry_header_and_deps( + entry.clone(), + test_chain_header_for_entry_app(entry), + Vec::new(), + ) + } + + // Convenience function, creates a header from the entry, with dependencies for app entry + fn try_pending_validation_for_app_entry_with_deps( + entry: Entry, + deps: Vec
, + ) -> Result { + try_pending_validation_for_entry_header_and_deps( + entry.clone(), + test_chain_header_for_entry_app(entry), + deps, + ) } #[test] - fn test_dependency_resolution_no_dependencies() { + fn test_dependency_resolution_no_dependencies() -> Result<(), HolochainError> { // A and B have no dependencies. Both should be free - let a = pending_validation_for_entry(test_entry_a(), Vec::new()); - let b = pending_validation_for_entry(test_entry_b(), Vec::new()); + let a = try_pending_validation_for_app_entry_no_deps(test_entry_a())?; + let b = try_pending_validation_for_app_entry_no_deps(test_entry_b())?; let pending_list = vec![a.clone(), b.clone()]; assert_eq!( pending_list @@ -412,14 +485,22 @@ pub mod tests { .collect::>(), vec![a, b] ); + Ok(()) } #[test] - fn test_dependency_resolution_chain() { + fn test_dependency_resolution_chain() -> Result<(), HolochainError> { // A depends on B and B depends on C. C should be free - let a = pending_validation_for_entry(test_entry_a(), vec![test_entry_b().address()]); - let b = pending_validation_for_entry(test_entry_b(), vec![test_entry_c().address()]); - let c = pending_validation_for_entry(test_entry_c(), vec![]); + let a = try_pending_validation_for_app_entry_with_deps( + test_entry_a(), + vec![test_entry_b().address()], + )?; + let b = try_pending_validation_for_app_entry_with_deps( + test_entry_b(), + vec![test_entry_c().address()], + )?; + let c = try_pending_validation_for_app_entry_no_deps(test_entry_c())?; + let pending_list = vec![a.clone(), b.clone(), c.clone()]; assert_eq!( pending_list @@ -429,17 +510,18 @@ pub mod tests { .collect::>(), vec![c] ); + Ok(()) } #[test] - fn test_dependency_resolution_tree() { + fn test_dependency_resolution_tree() -> Result<(), HolochainError> { // A depends on B and C. B and C should be free - let a = pending_validation_for_entry( + let a = try_pending_validation_for_app_entry_with_deps( test_entry_a(), vec![test_entry_b().address(), test_entry_c().address()], - ); - let b = pending_validation_for_entry(test_entry_b(), vec![]); - let c = pending_validation_for_entry(test_entry_c(), vec![]); + )?; + let b = try_pending_validation_for_app_entry_no_deps(test_entry_b())?; + let c = try_pending_validation_for_app_entry_no_deps(test_entry_c())?; let pending_list = vec![a.clone(), b.clone(), c.clone()]; assert_eq!( pending_list @@ -449,5 +531,7 @@ pub mod tests { .collect::>(), vec![b, c] ); + + Ok(()) } } diff --git a/crates/core/src/dht/pending_validations.rs b/crates/core/src/dht/pending_validations.rs index 6f7a5a995c..0c08c71e7c 100644 --- a/crates/core/src/dht/pending_validations.rs +++ b/crates/core/src/dht/pending_validations.rs @@ -1,8 +1,9 @@ use crate::{ entry::validation_dependencies::ValidationDependencies, - network::entry_with_header::EntryWithHeader, + network::header_with_its_entry::HeaderWithItsEntry, }; use holochain_core_types::{ + chain_header::ChainHeader, entry::{deletion_entry::DeletionEntry, Entry}, error::HolochainError, network::entry_aspect::EntryAspect, @@ -70,17 +71,17 @@ impl fmt::Display for ValidatingWorkflow { #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, DefaultJson)] pub struct PendingValidationStruct { - pub entry_with_header: EntryWithHeader, + pub header_with_its_entry: HeaderWithItsEntry, pub dependencies: Vec
, pub workflow: ValidatingWorkflow, uuid: ProcessUniqueId, } impl PendingValidationStruct { - pub fn new(entry_with_header: EntryWithHeader, workflow: ValidatingWorkflow) -> Self { - let dependencies = entry_with_header.get_validation_dependencies(); + pub fn new(header_with_its_entry: HeaderWithItsEntry, workflow: ValidatingWorkflow) -> Self { + let dependencies = header_with_its_entry.get_validation_dependencies(); Self { - entry_with_header, + header_with_its_entry, dependencies, workflow, uuid: ProcessUniqueId::new(), @@ -92,37 +93,73 @@ impl PendingValidationStruct { clone.uuid = ProcessUniqueId::new(); clone } + + /// Convenience function for returning a custom error in the context of validation. + pub fn try_from_entry_and_header( + entry: Entry, + header: ChainHeader, + entry_aspect: EntryAspect, + validating_workflow: ValidatingWorkflow, + ) -> Result { + match HeaderWithItsEntry::try_from_header_and_entry(header, entry) { + Ok(header_with_its_entry) => Ok(PendingValidationStruct::new( + header_with_its_entry, + validating_workflow, + )), + Err(error) => { + let error = format!( + "Tried to process {}; see the\n + debug output for further details of its contents. {}", + entry_aspect, error + ); + debug!("Tried to process {:?}", entry_aspect); + Err(HolochainError::ValidationFailed(error)) + } + } + } } impl TryFrom for PendingValidationStruct { type Error = HolochainError; fn try_from(aspect: EntryAspect) -> Result { match aspect { - EntryAspect::Content(entry, header) => Ok(PendingValidationStruct::new( - EntryWithHeader::try_from_entry_and_header(entry, header)?, - ValidatingWorkflow::HoldEntry, - )), + EntryAspect::Content(entry, header) => { + PendingValidationStruct::try_from_entry_and_header( + entry.clone(), + header.clone(), + EntryAspect::Content(entry, header), + ValidatingWorkflow::HoldEntry, + ) + } EntryAspect::Header(_header) => Err(HolochainError::NotImplemented(String::from( "EntryAspect::Header", ))), EntryAspect::LinkAdd(link_data, header) => { - let entry = Entry::LinkAdd(link_data); - Ok(PendingValidationStruct::new( - EntryWithHeader::try_from_entry_and_header(entry, header)?, + let entry = Entry::LinkAdd(link_data.clone()); + PendingValidationStruct::try_from_entry_and_header( + entry, + header.clone(), + EntryAspect::LinkAdd(link_data, header), ValidatingWorkflow::HoldLink, - )) + ) } EntryAspect::LinkRemove((link_data, links_to_remove), header) => { - let entry = Entry::LinkRemove((link_data, links_to_remove)); - Ok(PendingValidationStruct::new( - EntryWithHeader::try_from_entry_and_header(entry, header)?, + let entry = Entry::LinkRemove((link_data.clone(), links_to_remove.clone())); + PendingValidationStruct::try_from_entry_and_header( + entry, + header.clone(), + EntryAspect::LinkRemove((link_data, links_to_remove), header), ValidatingWorkflow::RemoveLink, - )) + ) + } + EntryAspect::Update(entry, header) => { + PendingValidationStruct::try_from_entry_and_header( + entry.clone(), + header.clone(), + EntryAspect::Update(entry, header), + ValidatingWorkflow::UpdateEntry, + ) } - EntryAspect::Update(entry, header) => Ok(PendingValidationStruct::new( - EntryWithHeader::try_from_entry_and_header(entry, header)?, - ValidatingWorkflow::UpdateEntry, - )), EntryAspect::Deletion(header) => { // reconstruct the deletion entry from the header. let deleted_entry_address = header.link_update_delete().ok_or_else(|| { @@ -132,10 +169,12 @@ impl TryFrom for PendingValidationStruct { })?; let entry = Entry::Deletion(DeletionEntry::new(deleted_entry_address)); - Ok(PendingValidationStruct::new( - EntryWithHeader::try_from_entry_and_header(entry, header)?, + PendingValidationStruct::try_from_entry_and_header( + entry, + header.clone(), + EntryAspect::Deletion(header), ValidatingWorkflow::RemoveEntry, - )) + ) } } } @@ -143,26 +182,20 @@ impl TryFrom for PendingValidationStruct { impl From for EntryAspect { fn from(pending: PendingValidationStruct) -> EntryAspect { + let entry = pending.header_with_its_entry.entry(); + let header = pending.header_with_its_entry.header(); match pending.workflow { - ValidatingWorkflow::HoldEntry => EntryAspect::Content( - pending.entry_with_header.entry.clone(), - pending.entry_with_header.header.clone(), - ), + ValidatingWorkflow::HoldEntry => EntryAspect::Content(entry, header), ValidatingWorkflow::HoldLink => { - let link_data = unwrap_to!(pending.entry_with_header.entry => Entry::LinkAdd); - EntryAspect::LinkAdd(link_data.clone(), pending.entry_with_header.header.clone()) + let link_data = unwrap_to!(entry => Entry::LinkAdd); + EntryAspect::LinkAdd(link_data.clone(), header) } ValidatingWorkflow::RemoveLink => { - let link_data = unwrap_to!(pending.entry_with_header.entry => Entry::LinkRemove); - EntryAspect::LinkRemove(link_data.clone(), pending.entry_with_header.header.clone()) - } - ValidatingWorkflow::UpdateEntry => EntryAspect::Update( - pending.entry_with_header.entry.clone(), - pending.entry_with_header.header, - ), - ValidatingWorkflow::RemoveEntry => { - EntryAspect::Deletion(pending.entry_with_header.header.clone()) + let link_data = unwrap_to!(entry => Entry::LinkRemove); + EntryAspect::LinkRemove(link_data.clone(), header) } + ValidatingWorkflow::UpdateEntry => EntryAspect::Update(entry, header), + ValidatingWorkflow::RemoveEntry => EntryAspect::Deletion(header), } } } diff --git a/crates/core/src/entry/validation_dependencies.rs b/crates/core/src/entry/validation_dependencies.rs index c4813884b9..16604654af 100644 --- a/crates/core/src/entry/validation_dependencies.rs +++ b/crates/core/src/entry/validation_dependencies.rs @@ -1,4 +1,4 @@ -use crate::network::entry_with_header::EntryWithHeader; +use crate::network::header_with_its_entry::HeaderWithItsEntry; use holochain_core_types::entry::Entry; use holochain_persistence_api::cas::content::Address; @@ -6,15 +6,15 @@ pub trait ValidationDependencies { fn get_validation_dependencies(&self) -> Vec
; } -impl ValidationDependencies for EntryWithHeader { +impl ValidationDependencies for HeaderWithItsEntry { fn get_validation_dependencies(&self) -> Vec
{ - match &self.entry { + match &self.entry() { Entry::App(_, _) => { // In the future an entry should be dependent its previous header but // for now it can require nothing by default. // There is also potential to add a WASM function for determining dependencies as a function // of the entry content. - match self.header.link_update_delete() { + match self.header().link_update_delete() { // If it is an update, require that the original entry is validated Some(entry_to_update) => vec![entry_to_update], None => Vec::new(), @@ -47,8 +47,10 @@ impl ValidationDependencies for EntryWithHeader { #[cfg(test)] pub mod tests { use super::*; + use crate::network::header_with_its_entry::HeaderWithItsEntry; use holochain_core_types::{ - agent::AgentId, chain_header::ChainHeader, link::link_data::LinkData, time::Iso8601, + agent::AgentId, chain_header::ChainHeader, error::HolochainError, + link::link_data::LinkData, time::Iso8601, }; use holochain_persistence_api::cas::content::AddressableContent; @@ -64,20 +66,26 @@ pub mod tests { ) } - fn entry_with_header_from_entry(entry: Entry) -> EntryWithHeader { + fn try_header_with_its_entry_from_entry( + entry: Entry, + ) -> Result { let header = test_header_for_entry(&entry); - EntryWithHeader::new(entry, header) + HeaderWithItsEntry::try_from_header_and_entry(header, entry) } #[test] - fn test_get_validation_dependencies_app_entry() { + fn test_get_validation_dependencies_app_entry() -> Result<(), HolochainError> { let entry = Entry::App("entry_type".into(), "content".into()); - let entry_wh = entry_with_header_from_entry(entry); - assert_eq!(entry_wh.get_validation_dependencies(), Vec::new(),) + try_header_with_its_entry_from_entry(entry).map(|header_with_its_entry| { + assert_eq!( + header_with_its_entry.get_validation_dependencies(), + Vec::new() + ) + }) } #[test] - fn test_get_validation_dependencies_link_add_entry() { + fn test_get_validation_dependencies_link_add_entry() -> Result<(), HolochainError> { let entry = Entry::LinkAdd(LinkData::new_add( &Address::from("QmBaseAddress"), &Address::from("QmTargetAddress"), @@ -86,19 +94,20 @@ pub mod tests { test_header_for_entry(&Entry::App("".into(), "".into())), AgentId::new("HcAgentId", "key".into()), )); - let entry_wh = entry_with_header_from_entry(entry); - assert_eq!( - entry_wh.get_validation_dependencies(), - vec![ - Address::from("QmBaseAddress"), - Address::from("QmTargetAddress") - ], - ) + try_header_with_its_entry_from_entry(entry).map(|header_with_its_entry| { + assert_eq!( + header_with_its_entry.get_validation_dependencies(), + vec![ + Address::from("QmBaseAddress"), + Address::from("QmTargetAddress") + ], + ) + }) } #[test] - fn test_get_validation_dependencies_header_entry() { - let header_entry_conent = ChainHeader::new( + fn test_get_validation_dependencies_header_entry() -> Result<(), HolochainError> { + let header_entry_content = ChainHeader::new( &"some type".into(), &Address::from("QmAddressOfEntry"), &Vec::new(), // provenences @@ -107,11 +116,12 @@ pub mod tests { &None, // link update/delete &Iso8601::from(0), ); - let entry = Entry::ChainHeader(header_entry_conent); - let entry_wh = entry_with_header_from_entry(entry); - assert_eq!( - entry_wh.get_validation_dependencies(), - vec![Address::from("QmPreviousHeaderAddress")], - ) + let entry = Entry::ChainHeader(header_entry_content); + try_header_with_its_entry_from_entry(entry).map(|header_with_its_entry| { + assert_eq!( + header_with_its_entry.get_validation_dependencies(), + vec![Address::from("QmPreviousHeaderAddress")], + ) + }) } } diff --git a/crates/core/src/network/entry_with_header.rs b/crates/core/src/network/entry_with_header.rs deleted file mode 100644 index f59d8a6569..0000000000 --- a/crates/core/src/network/entry_with_header.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::{ - agent::find_chain_header, - content_store::GetContent, - state::{State, StateWrapper}, -}; -use holochain_core_types::{chain_header::ChainHeader, entry::Entry, error::HolochainError}; -use holochain_persistence_api::cas::content::{Address, AddressableContent}; - -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] -pub struct EntryWithHeader { - pub entry: Entry, - pub header: ChainHeader, -} - -impl EntryWithHeader { - pub fn new(entry: Entry, header: ChainHeader) -> EntryWithHeader { - EntryWithHeader { entry, header } - } - - pub fn try_from_entry_and_header( - entry: Entry, - header: ChainHeader, - ) -> Result { - if entry.address() != *header.entry_address() { - Err(HolochainError::ValidationFailed(String::from( - "Entry/Header mismatch", - ))) - } else { - Ok(EntryWithHeader::new(entry, header)) - } - } -} - -pub fn fetch_entry_with_header( - address: &Address, - state: &State, -) -> Result { - let entry = state - .agent() - .chain_store() - .get(address)? - .ok_or_else(|| HolochainError::from("Entry not found"))?; - - let header = find_chain_header(&entry, &StateWrapper::from(state.clone())) - .ok_or_else(|| HolochainError::from("No header found for entry"))?; - - Ok(EntryWithHeader::new(entry, header)) -} diff --git a/crates/core/src/network/handler/lists.rs b/crates/core/src/network/handler/lists.rs index c5187ed950..0bbcb3ec90 100644 --- a/crates/core/src/network/handler/lists.rs +++ b/crates/core/src/network/handler/lists.rs @@ -1,6 +1,6 @@ use crate::{ action::{Action, ActionWrapper}, - agent::state::create_entry_with_header_for_header, + agent::state::create_header_with_its_entry_for_header, context::Context, dht::aspect_map::{AspectMap, AspectMapBare}, entry::CanPublish, @@ -77,7 +77,7 @@ fn create_authoring_map(context: Arc) -> AspectMap { // So we iterate over all our source chain headers for chain_header in context.state().unwrap().agent().iter_chain() { // Create an entry that represents the header - match create_entry_with_header_for_header(&state, chain_header.clone()) { + match create_header_with_its_entry_for_header(&state, chain_header.clone()) { Err(e) => { log_error!( context, @@ -86,11 +86,11 @@ fn create_authoring_map(context: Arc) -> AspectMap { ); continue; } - Ok(chain_entry_with_header) => { - let entry_hash = chain_entry_with_header.entry.address(); + Ok(header_with_its_entry) => { + let entry_hash = header_with_its_entry.entry().address(); let content_aspect = EntryAspect::Content( - chain_entry_with_header.entry, - chain_entry_with_header.header, + header_with_its_entry.entry(), + header_with_its_entry.header(), ); let aspect_hash = AspectHash::from(content_aspect.address()); address_map diff --git a/crates/core/src/network/handler/mod.rs b/crates/core/src/network/handler/mod.rs index fb5a33d342..77e1f9883f 100644 --- a/crates/core/src/network/handler/mod.rs +++ b/crates/core/src/network/handler/mod.rs @@ -1,4 +1,4 @@ -use crate::{agent::state::create_entry_with_header_for_header, content_store::GetContent}; +use crate::{agent::state::create_header_with_its_entry_for_header, content_store::GetContent}; use holochain_logging::prelude::*; pub mod fetch; pub mod lists; @@ -12,7 +12,6 @@ use crate::{ network::{ direct_message::DirectMessage, entry_aspect::EntryAspect, - entry_with_header::EntryWithHeader, handler::{ fetch::*, lists::{handle_get_authoring_list, handle_get_gossip_list}, @@ -20,6 +19,7 @@ use crate::{ send::*, store::*, }, + header_with_its_entry::HeaderWithItsEntry, }, workflows::get_entry_result::get_entry_with_meta_workflow, }; @@ -303,18 +303,19 @@ fn get_content_aspect( }); // If we have found a header for the requested entry in the chain... - let maybe_entry_with_header = match maybe_chain_header { - Some((header, true)) => Some(create_entry_with_header_for_header(&state, header)?), + let maybe_header_with_its_entry = match maybe_chain_header { + Some((header, true)) => Some(create_header_with_its_entry_for_header(&state, header)?), Some((header, false)) => { // ... we can just get the content from the chain CAS - Some(EntryWithHeader { - entry: state - .agent() - .chain_store() - .get(&header.entry_address())? - .expect("Could not find entry in chain CAS, but header is chain"), - header, - }) + let entry = state + .agent() + .chain_store() + .get(&header.entry_address())? + .expect("Could not find entry in chain CAS, but header is chain"); + match HeaderWithItsEntry::try_from_header_and_entry(header, entry) { + Ok(header_with_its_entry) => Some(header_with_its_entry), + Err(error) => return Err(error), + } } None => { // ... but if we didn't author that entry, let's see if we have it in the DHT cas: @@ -333,10 +334,13 @@ fn get_content_aspect( // TODO: this is just taking the first header.. // We should actually transform all headers into EntryAspect::Headers and just the first one // into an EntryAspect content (What about ordering? Using the headers timestamp?) - Some(EntryWithHeader { - entry, - header: headers[0].clone(), - }) + match HeaderWithItsEntry::try_from_header_and_entry(headers[0].clone(), entry) { + Ok(header_with_its_entry) => Some(header_with_its_entry), + Err(error) => { + log_error!(context, "{}", error); + None + } + } } else { debug!( "GET CONTENT ASPECT: entry found in cas, but then couldn't find a header" @@ -350,17 +354,18 @@ fn get_content_aspect( } }; - let entry_with_header = maybe_entry_with_header.ok_or(HolochainError::EntryNotFoundLocally)?; + let header_with_its_entry = + maybe_header_with_its_entry.ok_or(HolochainError::EntryNotFoundLocally)?; - let _ = entry_with_header - .entry + let _ = header_with_its_entry + .entry() .entry_type() .can_publish(&context) .ok_or(HolochainError::EntryIsPrivate)?; Ok(EntryAspect::Content( - entry_with_header.entry, - entry_with_header.header, + header_with_its_entry.entry(), + header_with_its_entry.header(), )) } diff --git a/crates/core/src/network/handler/store.rs b/crates/core/src/network/handler/store.rs index e0b0b55de3..26eaa76374 100644 --- a/crates/core/src/network/handler/store.rs +++ b/crates/core/src/network/handler/store.rs @@ -68,13 +68,13 @@ pub fn handle_store_meta(dht_meta_data: DhtMetaData, context: Arc) { log_debug!(context, "net/handle: HandleStoreMeta: got LINK. processing..."); // TODO: do a loop on content once links properly implemented assert_eq!(dht_meta_data.content_list.len(), 1); - let entry_with_header: EntryWithHeader = serde_json::from_str( + let header_with_its_entry: HeaderWithItsEntry = serde_json::from_str( &serde_json::to_string(&dht_meta_data.content_list[0]) - .expect("dht_meta_data should be EntryWithHeader"), + .expect("dht_meta_data should be HeaderWithItsEntry"), ) - .expect("dht_meta_data should be EntryWithHeader"); + .expect("dht_meta_data should be HeaderWithItsEntry"); thread::spawn(move || { - match context.block_on(hold_link_workflow(&entry_with_header, &context.clone())) { + match context.block_on(hold_link_workflow(&header_with_its_entry, &context.clone())) { Err(error) => log_error!(context, "net/dht: {}", error), _ => (), } @@ -83,15 +83,15 @@ pub fn handle_store_meta(dht_meta_data: DhtMetaData, context: Arc) { log_debug!(context, "net/handle: HandleStoreMeta: got LINK REMOVAL. processing..."); // TODO: do a loop on content once links properly implemented assert_eq!(dht_meta_data.content_list.len(), 1); - let entry_with_header: EntryWithHeader = serde_json::from_str( + let header_with_its_entry: HeaderWithItsEntry = serde_json::from_str( //should be careful doing slice access, it might panic &serde_json::to_string(&dht_meta_data.content_list[0]) - .expect("dht_meta_data should be EntryWithHader"), + .expect("dht_meta_data should be HeaderWithItsEntry"), ) .expect("dht_meta_data should be EntryWithHader"); thread::spawn(move || { if let Err(error) = - context.block_on(remove_link_workflow(&entry_with_header, &context.clone())) + context.block_on(remove_link_workflow(&header_with_its_entry, &context.clone())) { log_error!(context, "net/dht: {}", error) } @@ -102,15 +102,15 @@ pub fn handle_store_meta(dht_meta_data: DhtMetaData, context: Arc) { { log_debug!(context, "net/handle: HandleStoreMeta: got CRUD STATUS. processing..."); - let entry_with_header: EntryWithHeader = serde_json::from_str( + let header_with_its_entry: HeaderWithItsEntry = serde_json::from_str( //should be careful doing slice access, it might panic &serde_json::to_string(&dht_meta_data.content_list[0]) - .expect("dht_meta_data should be EntryWithHader"), + .expect("dht_meta_data should be HeaderWithItsEntry"), ) - .expect("dht_meta_data should be EntryWithHader"); + .expect("dht_meta_data should be HeaderWithItsEntry"); thread::spawn(move || { if let Err(error) = - context.block_on(hold_remove_workflow(entry_with_header, context.clone())) + context.block_on(hold_remove_workflow(header_with_its_entry, context.clone())) { log_error!(context, "net/dht: {}", error) } @@ -120,15 +120,15 @@ pub fn handle_store_meta(dht_meta_data: DhtMetaData, context: Arc) { == CrudStatus::Modified { log_debug!(context, "net/handle: HandleStoreMeta: got CRUD LINK. processing..."); - let entry_with_header: EntryWithHeader = serde_json::from_str( + let header_with_its_entry: HeaderWithItsEntry = serde_json::from_str( //should be careful doing slice access, it might panic &serde_json::to_string(&dht_meta_data.content_list[0]) - .expect("dht_meta_data should be EntryWithHader"), + .expect("dht_meta_data should be HeaderWithItsEntry"), ) - .expect("dht_meta_data should be EntryWithHader"); + .expect("dht_meta_data should be HeaderWithItsEntry"); thread::spawn(move || { if let Err(error) = - context.block_on(hold_update_workflow(entry_with_header, context.clone())) + context.block_on(hold_update_workflow(header_with_its_entry, context.clone())) { log_error!(context, "net/dht: {}", error) } diff --git a/crates/core/src/network/header_with_its_entry.rs b/crates/core/src/network/header_with_its_entry.rs new file mode 100644 index 0000000000..5646a8f0e9 --- /dev/null +++ b/crates/core/src/network/header_with_its_entry.rs @@ -0,0 +1,73 @@ +use crate::{ + agent::find_chain_header, + content_store::GetContent, + state::{State, StateWrapper}, +}; +use holochain_core_types::{chain_header::ChainHeader, entry::Entry, error::HolochainError}; +use holochain_persistence_api::cas::content::{Address, AddressableContent}; + +/// A `HeaderWithItsEntry` cannot be constructed unless the entry address in the +/// `ChainHeader` that is within the `HeaderWithItsEntry` is the same as the address +/// of the `Entry` that is also within the `HeaderWithItsEntry`. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +pub struct HeaderWithItsEntry(ChainHeader, Entry); + +impl HeaderWithItsEntry { + // It seems best to not have a new method, since stylistically it is expected to return Self, whereas constructing could fail. + pub fn try_from_header_and_entry( + header: ChainHeader, + entry: Entry, + ) -> Result { + let header_entry_address = header.entry_address(); + let entry_address = entry.address(); + if header_entry_address.clone() == entry_address { + Ok(HeaderWithItsEntry(header, entry)) + } else { + let basic_error_msg = "Tried to create a HeaderWithItsEntry, but got a + mismatch with the header's entry address and the entry's + address."; + let error_msg = format!( + "{} See the debug log output for data for the header and entry.", + basic_error_msg + ); + debug!( + "{}\nHeader:\n{:#?}\nEntry:{:#?}\nentry in header (i.e. header.entry()=\n", + basic_error_msg, header, entry + ); + Err(HolochainError::HeaderEntryMismatch( + error_msg, + header_entry_address.clone(), + entry_address, + )) + } + } + + pub fn header(&self) -> ChainHeader { + self.0.clone() + } + + pub fn entry(&self) -> Entry { + self.1.clone() + } + + pub fn fetch_header_with_its_entry( + address: &Address, + state: &State, + ) -> Result { + let entry = state + .agent() + .chain_store() + .get(address)? + .ok_or_else(|| HolochainError::from("Entry not found"))?; + + let header = + find_chain_header(&entry, &StateWrapper::from(state.clone())).ok_or_else(|| { + let error_msg = format!( + "No header found for the address:\n{}\nEntry:\n{:#?}\n", + address, entry + ); + HolochainError::from(error_msg) + })?; + HeaderWithItsEntry::try_from_header_and_entry(header, entry) + } +} diff --git a/crates/core/src/network/mod.rs b/crates/core/src/network/mod.rs index 2689daea63..0c0206d88a 100644 --- a/crates/core/src/network/mod.rs +++ b/crates/core/src/network/mod.rs @@ -1,7 +1,7 @@ pub mod actions; pub mod direct_message; -pub mod entry_with_header; pub mod handler; +pub mod header_with_its_entry; pub mod reducers; pub mod state; #[cfg(test)] @@ -133,6 +133,7 @@ pub mod tests { dht1.add(&entry).unwrap(); dht1.add_header_for_entry(&entry, &header2).unwrap(); } + cf } // Get it. diff --git a/crates/core/src/network/reducers/publish.rs b/crates/core/src/network/reducers/publish.rs index 1d7d88e64a..e23390e867 100644 --- a/crates/core/src/network/reducers/publish.rs +++ b/crates/core/src/network/reducers/publish.rs @@ -1,11 +1,8 @@ use crate::{ action::ActionWrapper, network::{ - actions::NetworkActionResponse, - entry_aspect::EntryAspect, - entry_with_header::{fetch_entry_with_header, EntryWithHeader}, - reducers::send, - state::NetworkState, + actions::NetworkActionResponse, entry_aspect::EntryAspect, + header_with_its_entry::HeaderWithItsEntry, reducers::send, state::NetworkState, }, state::State, }; @@ -40,7 +37,7 @@ pub fn entry_data_to_entry_aspect_data(ea: &EntryAspect) -> EntryAspectData { /// Send to network a PublishDhtData message fn publish_entry( network_state: &mut NetworkState, - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, ) -> Result<(), HolochainError> { send( network_state, @@ -48,10 +45,10 @@ fn publish_entry( space_address: network_state.dna_address.clone().unwrap().into(), provider_agent_id: network_state.agent_id.clone().unwrap().into(), entry: EntryData { - entry_address: entry_with_header.entry.address().into(), + entry_address: header_with_its_entry.entry().address().into(), aspect_list: vec![entry_data_to_entry_aspect_data(&EntryAspect::Content( - entry_with_header.entry.clone(), - entry_with_header.header.clone(), + header_with_its_entry.entry(), + header_with_its_entry.header(), ))], }, }), @@ -63,16 +60,16 @@ fn publish_update_delete_meta( network_state: &mut NetworkState, orig_entry_address: Address, crud_status: CrudStatus, - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, ) -> Result<(), HolochainError> { // publish crud-status let aspect = match crud_status { CrudStatus::Modified => EntryAspect::Update( - entry_with_header.entry.clone(), - entry_with_header.header.clone(), + header_with_its_entry.entry(), + header_with_its_entry.header(), ), - CrudStatus::Deleted => EntryAspect::Deletion(entry_with_header.header.clone()), + CrudStatus::Deleted => EntryAspect::Deletion(header_with_its_entry.header()), crud => { return Err(HolochainError::ErrorGeneric(format!( "Unexpeced CRUD variant {:?}", @@ -97,27 +94,24 @@ fn publish_update_delete_meta( Ok(()) } -/// Send to network a PublishMeta message holding a link metadata to `entry_with_header` +/// Send to network a PublishMeta message holding a link metadata to `header_with_its_entry` fn publish_link_meta( network_state: &mut NetworkState, - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, ) -> Result<(), HolochainError> { - let (base, aspect) = match entry_with_header.entry.clone() { + let (base, aspect) = match header_with_its_entry.entry() { Entry::LinkAdd(link_data) => ( link_data.link().base().clone(), - EntryAspect::LinkAdd(link_data, entry_with_header.header.clone()), + EntryAspect::LinkAdd(link_data, header_with_its_entry.header()), ), Entry::LinkRemove((link_data, links_to_remove)) => ( link_data.link().base().clone(), - EntryAspect::LinkRemove( - (link_data, links_to_remove), - entry_with_header.header.clone(), - ), + EntryAspect::LinkRemove((link_data, links_to_remove), header_with_its_entry.header()), ), _ => { return Err(HolochainError::ErrorGeneric(format!( "Received bad entry type. Expected Entry::LinkAdd/Remove received {:?}", - entry_with_header.entry, + header_with_its_entry.entry(), ))); } }; @@ -141,39 +135,42 @@ fn reduce_publish_inner( ) -> Result<(), HolochainError> { network_state.initialized()?; - let entry_with_header = fetch_entry_with_header(&address, root_state)?; + let header_with_its_entry = + HeaderWithItsEntry::fetch_header_with_its_entry(&address, root_state)?; - match entry_with_header.entry.entry_type() { - EntryType::AgentId => publish_entry(network_state, &entry_with_header), - EntryType::App(_) => publish_entry(network_state, &entry_with_header).and_then(|_| { - match entry_with_header.header.link_update_delete() { + match header_with_its_entry.entry().entry_type() { + EntryType::AgentId => publish_entry(network_state, &header_with_its_entry), + EntryType::App(_) => publish_entry(network_state, &header_with_its_entry).and_then(|_| { + match header_with_its_entry.header().link_update_delete() { Some(modified_entry) => publish_update_delete_meta( network_state, modified_entry, CrudStatus::Modified, - &entry_with_header.clone(), - ), - None => Ok(()), - } - }), - EntryType::LinkAdd => publish_entry(network_state, &entry_with_header) - .and_then(|_| publish_link_meta(network_state, &entry_with_header)), - EntryType::LinkRemove => publish_entry(network_state, &entry_with_header) - .and_then(|_| publish_link_meta(network_state, &entry_with_header)), - EntryType::Deletion => publish_entry(network_state, &entry_with_header).and_then(|_| { - match entry_with_header.header.link_update_delete() { - Some(modified_entry) => publish_update_delete_meta( - network_state, - modified_entry, - CrudStatus::Deleted, - &entry_with_header.clone(), + &header_with_its_entry.clone(), ), None => Ok(()), } }), + EntryType::LinkAdd => publish_entry(network_state, &header_with_its_entry) + .and_then(|_| publish_link_meta(network_state, &header_with_its_entry)), + EntryType::LinkRemove => publish_entry(network_state, &header_with_its_entry) + .and_then(|_| publish_link_meta(network_state, &header_with_its_entry)), + EntryType::Deletion => { + publish_entry(network_state, &header_with_its_entry).and_then(|_| { + match header_with_its_entry.header().link_update_delete() { + Some(modified_entry) => publish_update_delete_meta( + network_state, + modified_entry, + CrudStatus::Deleted, + &header_with_its_entry.clone(), + ), + None => Ok(()), + } + }) + } _ => Err(HolochainError::NotImplemented(format!( "reduce_publish_inner not implemented for {}", - entry_with_header.entry.entry_type() + header_with_its_entry.entry().entry_type() ))), } } diff --git a/crates/core/src/network/reducers/publish_header_entry.rs b/crates/core/src/network/reducers/publish_header_entry.rs index f36ecff0da..8f6fb9b949 100644 --- a/crates/core/src/network/reducers/publish_header_entry.rs +++ b/crates/core/src/network/reducers/publish_header_entry.rs @@ -1,10 +1,10 @@ use crate::{ action::ActionWrapper, - agent::state::create_entry_with_header_for_header, + agent::state::create_header_with_its_entry_for_header, network::{ actions::NetworkActionResponse, entry_aspect::EntryAspect, - entry_with_header::{fetch_entry_with_header, EntryWithHeader}, + header_with_its_entry::HeaderWithItsEntry, reducers::{publish::entry_data_to_entry_aspect_data, send}, state::NetworkState, }, @@ -26,8 +26,11 @@ fn publish_header( root_state: &State, chain_header: ChainHeader, ) -> Result<(), HolochainError> { - let EntryWithHeader { entry, header } = - create_entry_with_header_for_header(&StateWrapper::from(root_state.clone()), chain_header)?; + let header_with_its_entry = create_header_with_its_entry_for_header( + &StateWrapper::from(root_state.clone()), + chain_header, + )?; + let entry = header_with_its_entry.entry(); send( network_state, Lib3hClientProtocol::PublishEntry(ProvidedEntryData { @@ -36,7 +39,8 @@ fn publish_header( entry: EntryData { entry_address: entry.address().into(), aspect_list: vec![entry_data_to_entry_aspect_data(&EntryAspect::Content( - entry, header, + entry, + header_with_its_entry.header(), ))], }, }), @@ -49,8 +53,9 @@ fn reduce_publish_header_entry_inner( address: &Address, ) -> Result<(), HolochainError> { network_state.initialized()?; - let entry_with_header = fetch_entry_with_header(&address, root_state)?; - publish_header(network_state, root_state, entry_with_header.header) + let header_with_its_entry = + HeaderWithItsEntry::fetch_header_with_its_entry(&address, root_state)?; + publish_header(network_state, root_state, header_with_its_entry.header()) } pub fn reduce_publish_header_entry( diff --git a/crates/core/src/nucleus/reducers/queue_zome_function_call.rs b/crates/core/src/nucleus/reducers/queue_zome_function_call.rs index 216d98227e..c2c5a9b6e6 100644 --- a/crates/core/src/nucleus/reducers/queue_zome_function_call.rs +++ b/crates/core/src/nucleus/reducers/queue_zome_function_call.rs @@ -5,7 +5,7 @@ use crate::{ }; /// Reduce AddPendingValidation Action. -/// Inserts boxed EntryWithHeader and dependencies into state, referenced with +/// Inserts boxed HeaderWithItsEntry and dependencies into state, referenced with /// the entry's address. #[allow(unknown_lints)] #[allow(clippy::needless_pass_by_value)] diff --git a/crates/core/src/nucleus/validation/agent_entry.rs b/crates/core/src/nucleus/validation/agent_entry.rs index b9b20439a8..88575dca32 100644 --- a/crates/core/src/nucleus/validation/agent_entry.rs +++ b/crates/core/src/nucleus/validation/agent_entry.rs @@ -14,7 +14,7 @@ use holochain_core_types::{ use holochain_persistence_api::cas::content::AddressableContent; use holochain_wasm_utils::api_serialization::validation::AgentIdValidationArgs; -use futures::{future, future::FutureExt}; +use futures::future::{self, FutureExt}; use std::sync::Arc; pub async fn validate_agent_entry( diff --git a/crates/core/src/nucleus/validation/build_from_dht.rs b/crates/core/src/nucleus/validation/build_from_dht.rs index 596edb83eb..99c8fa2005 100644 --- a/crates/core/src/nucleus/validation/build_from_dht.rs +++ b/crates/core/src/nucleus/validation/build_from_dht.rs @@ -1,5 +1,5 @@ use crate::{ - context::Context, entry::CanPublish, network::entry_with_header::EntryWithHeader, + context::Context, entry::CanPublish, network::header_with_its_entry::HeaderWithItsEntry, workflows::get_entry_result::get_entry_with_meta_workflow, }; use holochain_core_types::{ @@ -85,16 +85,16 @@ async fn public_chain_entries_from_headers_dht( } pub(crate) async fn try_make_validation_package_dht( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, validation_package_definition: &ValidationPackageDefinition, context: Arc, ) -> Result { + let entry_header = header_with_its_entry.header(); log_debug!( context, "Constructing validation package from DHT for entry with address: {}", - entry_with_header.header.entry_address() + entry_header.clone().entry_address() ); - let entry_header = entry_with_header.header.clone(); log_debug!(context, "Retrieving chain headers..."); @@ -175,7 +175,7 @@ pub mod tests { } #[test] - fn test_validation_package_same_from_author_and_other_agent() { + fn test_validation_package_same_from_author_and_other_agent() -> Result<(), HolochainError> { let mut dna = test_dna(); dna.uuid = "test_validation_package_same_from_author_and_other_agent".to_string(); let netname = Some("test_validation_package_same_from_author_and_other_agent, the network"); @@ -204,12 +204,12 @@ pub mod tests { .next() .expect("Must be able to get header for just published entry"); - let entry_with_header = EntryWithHeader { entry, header }; + let header_with_its_entry = HeaderWithItsEntry::try_from_header_and_entry(header, entry)?; // jack (the author) retrieves a local validation package let local_validation_package = context2 .block_on(try_make_local_validation_package( - &entry_with_header, + &header_with_its_entry, &ValidationPackageDefinition::ChainFull, context2.clone(), )) @@ -218,7 +218,7 @@ pub mod tests { // jill reconstructs one from published headers let dht_validation_package = context1 .block_on(try_make_validation_package_dht( - &entry_with_header, + &header_with_its_entry, &ValidationPackageDefinition::ChainFull, context1.clone(), )) @@ -242,6 +242,7 @@ pub mod tests { 2 ); - assert_eq!(local_validation_package, dht_validation_package,) + assert_eq!(local_validation_package, dht_validation_package,); + Ok(()) } } diff --git a/crates/core/src/nucleus/validation/mod.rs b/crates/core/src/nucleus/validation/mod.rs index 74c95e2e67..d33c5ce993 100644 --- a/crates/core/src/nucleus/validation/mod.rs +++ b/crates/core/src/nucleus/validation/mod.rs @@ -136,12 +136,12 @@ pub fn entry_to_validation_data( match entry { Entry::App(_, _) => maybe_link_update_delete .map(|link_update| { - get_entry_with_header(context.clone(), &link_update) - .map(|entry_with_header| { + try_get_entry_with_meta_and_header_tuple(context.clone(), &link_update) + .map(|entry_with_meta_and_header_tuple| { Ok(EntryValidationData::Modify { - old_entry: entry_with_header.0.entry.clone(), + old_entry: entry_with_meta_and_header_tuple.0.entry.clone(), new_entry: entry.clone(), - old_entry_header: entry_with_header.1, + old_entry_header: entry_with_meta_and_header_tuple.1, validation_data: validation_data.clone(), }) }) @@ -159,11 +159,11 @@ pub fn entry_to_validation_data( }), Entry::Deletion(deletion_entry) => { let deletion_address = deletion_entry.deleted_entry_address().clone(); - get_entry_with_header(context, &deletion_address) - .map(|entry_with_header| { + try_get_entry_with_meta_and_header_tuple(context, &deletion_address) + .map(|entry_with_meta_and_header| { Ok(EntryValidationData::Delete { - old_entry: entry_with_header.0.entry.clone(), - old_entry_header: entry_with_header.1, + old_entry: entry_with_meta_and_header.0.entry.clone(), + old_entry_header: entry_with_meta_and_header.1, validation_data: validation_data.clone(), }) }) @@ -183,7 +183,8 @@ pub fn entry_to_validation_data( } } -fn get_entry_with_header( +// It's a long name but it avoids confusion with getting `EntryWithMetaAndHeader`, or avoiding confusion when searching. +fn try_get_entry_with_meta_and_header_tuple( context: Arc, address: &Address, ) -> Result<(EntryWithMeta, ChainHeader), HolochainError> { diff --git a/crates/core/src/scheduled_jobs/state_dump.rs b/crates/core/src/scheduled_jobs/state_dump.rs index 0f2c6406cc..1336528b44 100644 --- a/crates/core/src/scheduled_jobs/state_dump.rs +++ b/crates/core/src/scheduled_jobs/state_dump.rs @@ -53,8 +53,8 @@ pub fn state_dump(context: Arc) { format!( "<{}({})> {}: depends on : {:?}", pending.workflow.to_string(), - pending.entry_with_header.header.entry_type(), - pending.entry_with_header.entry.address(), + pending.header_with_its_entry.header().entry_type(), + pending.header_with_its_entry.entry().address(), pending .dependencies .iter() diff --git a/crates/core/src/workflows/hold_entry.rs b/crates/core/src/workflows/hold_entry.rs index 1e61ab7198..00d9377a8b 100644 --- a/crates/core/src/workflows/hold_entry.rs +++ b/crates/core/src/workflows/hold_entry.rs @@ -1,6 +1,6 @@ use crate::{ context::Context, dht::actions::hold_aspect::hold_aspect, - network::entry_with_header::EntryWithHeader, nucleus::validation::validate_entry, + network::header_with_its_entry::HeaderWithItsEntry, nucleus::validation::validate_entry, }; use crate::{nucleus::validation::ValidationError, workflows::validation_package}; @@ -15,11 +15,11 @@ use holochain_persistence_api::cas::content::AddressableContent; use std::sync::Arc; pub async fn hold_entry_workflow( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, context: Arc, ) -> Result<(), HolochainError> { // 1. Get hold of validation package - let maybe_validation_package = validation_package(&entry_with_header, context.clone()) + let maybe_validation_package = validation_package(&header_with_its_entry, context.clone()) .await .map_err(|err| { let message = "Could not get validation package from source! -> Add to pending..."; @@ -43,7 +43,7 @@ pub async fn hold_entry_workflow( // 3. Validate the entry validate_entry( - entry_with_header.entry.clone(), + header_with_its_entry.entry(), None, validation_data, &context @@ -51,13 +51,13 @@ pub async fn hold_entry_workflow( .map_err(|err| { if let ValidationError::UnresolvedDependencies(dependencies) = &err { log_debug!(context, "workflow/hold_entry: {} could not be validated due to unresolved dependencies and will be tried later. List of missing dependencies: {:?}", - entry_with_header.entry.address(), + header_with_its_entry.entry().address(), dependencies, ); HolochainError::ValidationPending } else { log_warn!(context, "workflow/hold_entry: Entry {} is NOT valid! Validation error: {:?}", - entry_with_header.entry.address(), + header_with_its_entry.entry().address(), err, ); HolochainError::from(err) @@ -67,20 +67,20 @@ pub async fn hold_entry_workflow( log_debug!( context, "workflow/hold_entry: is valid! {}", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); // 4. If valid store the entry aspect in the local DHT shard let aspect = EntryAspect::Content( - entry_with_header.entry.clone(), - entry_with_header.header.clone(), + header_with_its_entry.entry(), + header_with_its_entry.header(), ); hold_aspect(aspect, context.clone()).await?; log_debug!( context, "workflow/hold_entry: HOLDING: {}", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); Ok(()) @@ -130,10 +130,10 @@ pub mod tests { let header = agent1_state .get_most_recent_header_for_entry(&entry) .expect("There must be a header in the author's source chain after commit"); - let entry_with_header = EntryWithHeader { entry, header }; + let header_with_its_entry = HeaderWithItsEntry::try_from_header_and_entry(header, entry)?; // Call hold_entry_workflow on victim DHT node - let result = context2.block_on(hold_entry_workflow(&entry_with_header, &context2)); + let result = context2.block_on(hold_entry_workflow(&header_with_its_entry, &context2)); // ... and expect validation to fail with message defined in test WAT: assert!(result.is_err()); diff --git a/crates/core/src/workflows/hold_entry_remove.rs b/crates/core/src/workflows/hold_entry_remove.rs index ba9d96a9a1..b51d10ae5c 100644 --- a/crates/core/src/workflows/hold_entry_remove.rs +++ b/crates/core/src/workflows/hold_entry_remove.rs @@ -1,6 +1,6 @@ use crate::{ context::Context, dht::actions::hold_aspect::hold_aspect, - network::entry_with_header::EntryWithHeader, nucleus::validation::validate_entry, + network::header_with_its_entry::HeaderWithItsEntry, nucleus::validation::validate_entry, }; use crate::{nucleus::validation::ValidationError, workflows::validation_package}; @@ -12,11 +12,11 @@ use holochain_core_types::{ use std::sync::Arc; pub async fn hold_remove_workflow( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, context: Arc, ) -> Result<(), HolochainError> { // 1. Get hold of validation package - let maybe_validation_package = validation_package(entry_with_header, context.clone()) + let maybe_validation_package = validation_package(header_with_its_entry, context.clone()) .await .map_err(|err| { let message = "Could not get validation package from source! -> Add to pending..."; @@ -35,7 +35,7 @@ pub async fn hold_remove_workflow( // 3. Validate the entry validate_entry( - entry_with_header.entry.clone(), + header_with_its_entry.entry(), None, validation_data, &context @@ -46,7 +46,7 @@ pub async fn hold_remove_workflow( HolochainError::ValidationPending } else { log_warn!(context, "workflow/hold_remove: Entry removal {:?} is NOT valid! Validation error: {:?}", - entry_with_header.entry, + header_with_its_entry.entry(), err, ); HolochainError::from(err) @@ -55,7 +55,7 @@ pub async fn hold_remove_workflow( })?; // 4. If valid store the entry aspect in the local DHT shard - let aspect = EntryAspect::Deletion(entry_with_header.header.clone()); + let aspect = EntryAspect::Deletion(header_with_its_entry.header()); hold_aspect(aspect, context.clone()).await?; Ok(()) } diff --git a/crates/core/src/workflows/hold_entry_update.rs b/crates/core/src/workflows/hold_entry_update.rs index ff667d9625..7246b8dd78 100644 --- a/crates/core/src/workflows/hold_entry_update.rs +++ b/crates/core/src/workflows/hold_entry_update.rs @@ -1,7 +1,7 @@ use crate::{ context::Context, dht::actions::hold_aspect::hold_aspect, - network::entry_with_header::EntryWithHeader, + network::header_with_its_entry::HeaderWithItsEntry, nucleus::validation::{validate_entry, ValidationError}, workflows::validation_package, }; @@ -13,13 +13,14 @@ use holochain_core_types::{ use std::sync::Arc; pub async fn hold_update_workflow( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, context: Arc, ) -> Result<(), HolochainError> { - let EntryWithHeader { entry, header } = entry_with_header; + let entry = header_with_its_entry.entry(); + let header = header_with_its_entry.header(); // 1. Get hold of validation package - let maybe_validation_package = validation_package(&entry_with_header, context.clone()) + let maybe_validation_package = validation_package(&header_with_its_entry, context.clone()) .await .map_err(|err| { let message = "Could not get validation package from source! -> Add to pending..."; @@ -54,7 +55,7 @@ pub async fn hold_update_workflow( HolochainError::ValidationPending } else { log_warn!(context, "workflow/hold_update: Entry update {:?} is NOT valid! Validation error: {:?}", - entry_with_header.entry, + header_with_its_entry.entry(), err, ); HolochainError::from(err) @@ -64,8 +65,8 @@ pub async fn hold_update_workflow( // 4. If valid store the entry aspect in the local DHT shard let aspect = EntryAspect::Update( - entry_with_header.entry.clone(), - entry_with_header.header.clone(), + header_with_its_entry.entry(), + header_with_its_entry.header(), ); hold_aspect(aspect, context.clone()).await?; diff --git a/crates/core/src/workflows/hold_link.rs b/crates/core/src/workflows/hold_link.rs index 78da79ce69..c1a7ebd770 100644 --- a/crates/core/src/workflows/hold_link.rs +++ b/crates/core/src/workflows/hold_link.rs @@ -1,6 +1,6 @@ use crate::{ context::Context, dht::actions::hold_aspect::hold_aspect, - network::entry_with_header::EntryWithHeader, nucleus::validation::validate_entry, + network::header_with_its_entry::HeaderWithItsEntry, nucleus::validation::validate_entry, }; use crate::{ @@ -16,10 +16,11 @@ use holochain_core_types::{ use std::sync::Arc; pub async fn hold_link_workflow( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, context: Arc, ) -> Result<(), HolochainError> { - let link_add = match &entry_with_header.entry { + let entry = &header_with_its_entry.entry(); + let link_add = match entry { Entry::LinkAdd(link_add) => link_add, _ => Err(HolochainError::ErrorGeneric( "hold_link_workflow expects entry to be an Entry::LinkAdd".to_string(), @@ -30,7 +31,7 @@ pub async fn hold_link_workflow( log_debug!(context, "workflow/hold_link: {:?}", link); log_debug!(context, "workflow/hold_link: getting validation package..."); // 1. Get hold of validation package - let maybe_validation_package = validation_package(&entry_with_header, context.clone()) + let maybe_validation_package = validation_package(&header_with_its_entry, context.clone()) .await .map_err(|err| { let message = "Could not get validation package from source! -> Add to pending..."; @@ -54,7 +55,7 @@ pub async fn hold_link_workflow( // 3. Validate the entry log_debug!(context, "workflow/hold_link: validate..."); validate_entry( - entry_with_header.entry.clone(), + header_with_its_entry.entry(), None, validation_data, &context @@ -65,7 +66,7 @@ pub async fn hold_link_workflow( HolochainError::ValidationPending } else { log_warn!(context, "workflow/hold_link: Link {:?} is NOT valid! Validation error: {:?}", - entry_with_header.entry, + header_with_its_entry.entry(), err, ); HolochainError::from(err) @@ -75,17 +76,17 @@ pub async fn hold_link_workflow( log_debug!(context, "workflow/hold_link: is valid!"); // 3. If valid store the entry aspect in the local DHT shard - let aspect = EntryAspect::LinkAdd(link_add.clone(), entry_with_header.header.clone()); + let aspect = EntryAspect::LinkAdd(link_add.clone(), header_with_its_entry.header()); hold_aspect(aspect, context.clone()).await?; log_debug!(context, "workflow/hold_link: added! {:?}", link); //4. store link_add entry so we have all we need to respond to get links queries without any other network look-up - hold_entry_workflow(&entry_with_header, context.clone()).await?; + hold_entry_workflow(&header_with_its_entry, context.clone()).await?; log_debug!( context, "workflow/hold_entry: added! {:?}", - entry_with_header + header_with_its_entry ); //5. Link has been added to EAV and LinkAdd Entry has been stored on the dht @@ -150,13 +151,12 @@ pub mod tests { let header = agent1_state .get_most_recent_header_for_entry(&link_entry) .expect("There must be a header in the author's source chain after commit"); - let entry_with_header = EntryWithHeader { - entry: link_entry, - header, - }; + let header_with_its_entry = + HeaderWithItsEntry::try_from_entry_and_header(header, link_entry)?; // Call hold_entry_workflow on victim DHT node - let result = context2.block_on(hold_link_workflow(&entry_with_header, context2.clone())); + let result = + context2.block_on(hold_link_workflow(&header_with_its_entry, context2.clone())); // ... and expect validation to fail with message defined in test WAT: assert!(result.is_err()); diff --git a/crates/core/src/workflows/mod.rs b/crates/core/src/workflows/mod.rs index 53c64fe003..5faa7080f8 100644 --- a/crates/core/src/workflows/mod.rs +++ b/crates/core/src/workflows/mod.rs @@ -15,7 +15,8 @@ use crate::{ context::Context, dht::pending_validations::{PendingValidation, ValidatingWorkflow}, network::{ - actions::get_validation_package::get_validation_package, entry_with_header::EntryWithHeader, + actions::get_validation_package::get_validation_package, + header_with_its_entry::HeaderWithItsEntry, }, nucleus::{ actions::build_validation_package::build_validation_package, @@ -43,11 +44,11 @@ use std::sync::Arc; /// Checks the DNA's validation package definition for the given entry type. /// Fails if this entry type needs more than just the header for validation. pub(crate) async fn try_make_local_validation_package( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, validation_package_definition: &ValidationPackageDefinition, context: Arc, ) -> Result { - let entry_header = &entry_with_header.header; + let entry_header = &header_with_its_entry.header(); match validation_package_definition { ValidationPackageDefinition::Entry => { @@ -55,20 +56,16 @@ pub(crate) async fn try_make_local_validation_package( } _ => { let agent = context.state()?.agent().get_agent()?; - - let overlapping_provenance = entry_with_header - .header + let entry = &header_with_its_entry.entry(); + let header = header_with_its_entry.header(); + let overlapping_provenance = header .provenances() .iter() .find(|p| p.source() == agent.address()); if overlapping_provenance.is_some() { // We authored this entry, so lets build the validation package here and now: - build_validation_package( - &entry_with_header.entry, - context, - entry_with_header.header.provenances(), - ) + build_validation_package(entry, context, header.provenances()) } else { Err(HolochainError::ErrorGeneric(String::from( "Can't create validation package locally", @@ -80,33 +77,33 @@ pub(crate) async fn try_make_local_validation_package( /// Gets hold of the validation package for the given entry by trying several different methods. async fn validation_package( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, context: Arc, ) -> Result, HolochainError> { // 0. Call into the DNA to get the validation package definition for this entry // e.g. what data is needed to validate it (chain, entry, headers, etc) - let entry = &entry_with_header.entry; - let validation_package_definition = get_validation_package_definition(entry, context.clone()) + let entry = header_with_its_entry.entry(); + let validation_package_definition = get_validation_package_definition(&entry, context.clone()) .and_then(|callback_result| match callback_result { - CallbackResult::Fail(error_string) => Err(HolochainError::ErrorGeneric(error_string)), - CallbackResult::ValidationPackageDefinition(def) => Ok(def), - CallbackResult::NotImplemented(reason) => Err(HolochainError::ErrorGeneric(format!( - "ValidationPackage callback not implemented for {:?} ({})", - entry.entry_type(), - reason - ))), - _ => unreachable!(), - })?; + CallbackResult::Fail(error_string) => Err(HolochainError::ErrorGeneric(error_string)), + CallbackResult::ValidationPackageDefinition(def) => Ok(def), + CallbackResult::NotImplemented(reason) => Err(HolochainError::ErrorGeneric(format!( + "ValidationPackage callback not implemented for {:?} ({})", + entry.entry_type(), + reason + ))), + _ => unreachable!(), + })?; // 1. Try to construct it locally. // This will work if the entry doesn't need a chain to validate or if this agent is the author: log_debug!( context, "validation_package:{} - Trying to build locally", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); if let Ok(package) = try_make_local_validation_package( - &entry_with_header, + &header_with_its_entry, &validation_package_definition, context.clone(), ) @@ -115,7 +112,7 @@ async fn validation_package( log_debug!( context, "validation_package:{} - Successfully built locally", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); return Ok(Some(package)); } @@ -124,22 +121,22 @@ async fn validation_package( log_debug!( context, "validation_package:{} - Could not build locally. Trying to retrieve from author", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); - match get_validation_package(entry_with_header.header.clone(), &context).await { + match get_validation_package(header_with_its_entry.header(), &context).await { Ok(Some(package)) => { log_debug!( context, "validation_package:{} - Successfully retrieved from author", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); return Ok(Some(package)); } response => log_debug!( context, "validation_package:{} - Direct message to author responded: {:?}", - entry_with_header.entry.address(), + header_with_its_entry.entry().address(), response, ), } @@ -148,10 +145,10 @@ async fn validation_package( log_debug!( context, "validation_package:{} - Could not retrieve from author. Trying to build from published headers", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); if let Ok(package) = try_make_validation_package_dht( - &entry_with_header, + &header_with_its_entry, &validation_package_definition, context.clone(), ) @@ -160,7 +157,7 @@ async fn validation_package( log_debug!( context, "validation_package:{} - Successfully built from published headers", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); return Ok(Some(package)); } @@ -170,7 +167,7 @@ async fn validation_package( log_debug!( context, "validation_package:{} - Could not get validation package!!!", - entry_with_header.entry.address() + header_with_its_entry.entry().address() ); Err(HolochainError::ErrorGeneric( "Could not get validation package".to_string(), @@ -181,15 +178,15 @@ async fn validation_package( pub mod tests { use super::validation_package; use crate::{ - network::entry_with_header::EntryWithHeader, nucleus::actions::tests::*, + network::header_with_its_entry::HeaderWithItsEntry, nucleus::actions::tests::*, workflows::author_entry::author_entry, }; - use holochain_core_types::entry::Entry; + use holochain_core_types::{entry::Entry, error::HolochainError}; use holochain_json_api::json::JsonString; use std::{thread, time}; #[test] - fn test_simulate_packge_direct_from_author() { + fn test_simulate_packge_direct_from_author() -> Result<(), HolochainError> { let mut dna = test_dna(); dna.uuid = "test_simulate_packge_direct_from_author".to_string(); let netname = Some("test_simulate_packge_direct_from_author, the network"); @@ -217,20 +214,20 @@ pub mod tests { .next() .expect("Must be able to get header for just published entry"); - let entry_with_header = EntryWithHeader { entry, header }.clone(); - - let validation_package = context1 - .block_on(validation_package(&entry_with_header, context1.clone())) - .expect("Could not recover a validation package as the non-author"); + HeaderWithItsEntry::try_from_header_and_entry(header, entry).map(|header_with_its_entry| { + let validation_package = context1 + .block_on(validation_package(&header_with_its_entry, context1.clone())) + .expect("Could not recover a validation package as the non-author"); - assert_eq!( - validation_package - .unwrap() - .source_chain_headers - .unwrap() - .len(), - 2 - ); + assert_eq!( + validation_package + .unwrap() + .source_chain_headers + .unwrap() + .len(), + 2 + ); + }) } } @@ -242,19 +239,19 @@ pub async fn run_holding_workflow( ) -> Result<(), HolochainError> { match pending.workflow { ValidatingWorkflow::HoldLink => { - hold_link_workflow(&pending.entry_with_header, context.clone()).await + hold_link_workflow(&pending.header_with_its_entry, context.clone()).await } ValidatingWorkflow::HoldEntry => { - hold_entry_workflow(&pending.entry_with_header, context.clone()).await + hold_entry_workflow(&pending.header_with_its_entry, context.clone()).await } ValidatingWorkflow::RemoveLink => { - remove_link_workflow(&pending.entry_with_header, context.clone()).await + remove_link_workflow(&pending.header_with_its_entry, context.clone()).await } ValidatingWorkflow::UpdateEntry => { - hold_update_workflow(&pending.entry_with_header, context.clone()).await + hold_update_workflow(&pending.header_with_its_entry, context.clone()).await } ValidatingWorkflow::RemoveEntry => { - hold_remove_workflow(&pending.entry_with_header, context.clone()).await + hold_remove_workflow(&pending.header_with_its_entry, context.clone()).await } } } diff --git a/crates/core/src/workflows/remove_link.rs b/crates/core/src/workflows/remove_link.rs index efc83b6379..eb3cc9b70e 100644 --- a/crates/core/src/workflows/remove_link.rs +++ b/crates/core/src/workflows/remove_link.rs @@ -1,6 +1,6 @@ use crate::{ context::Context, dht::actions::hold_aspect::hold_aspect, - network::entry_with_header::EntryWithHeader, nucleus::validation::validate_entry, + network::header_with_its_entry::HeaderWithItsEntry, nucleus::validation::validate_entry, workflows::hold_entry::hold_entry_workflow, }; @@ -14,10 +14,11 @@ use holochain_core_types::{ use std::sync::Arc; pub async fn remove_link_workflow( - entry_with_header: &EntryWithHeader, + header_with_its_entry: &HeaderWithItsEntry, context: Arc, ) -> Result<(), HolochainError> { - let (link_data, links_to_remove) = match &entry_with_header.entry { + let entry = &header_with_its_entry.entry(); + let (link_data, links_to_remove) = match entry { Entry::LinkRemove(data) => data, _ => Err(HolochainError::ErrorGeneric( "remove_link_workflow expects entry to be an Entry::LinkRemove".to_string(), @@ -31,7 +32,7 @@ pub async fn remove_link_workflow( context, "workflow/remove_link: getting validation package..." ); - let maybe_validation_package = validation_package(&entry_with_header, context.clone()) + let maybe_validation_package = validation_package(&header_with_its_entry, context.clone()) .await .map_err(|err| { let message = "Could not get validation package from source! -> Add to pending..."; @@ -53,7 +54,7 @@ pub async fn remove_link_workflow( // 3. Validate the entry log_debug!(context, "workflow/remove_link: validate..."); validate_entry( - entry_with_header.entry.clone(), + header_with_its_entry.entry(), None, validation_data, &context @@ -64,7 +65,7 @@ pub async fn remove_link_workflow( HolochainError::ValidationPending } else { log_warn!(context, "workflow/remove_link: Link {:?} is NOT valid! Validation error: {:?}", - entry_with_header.entry, + header_with_its_entry.entry(), err, ); HolochainError::from(err) @@ -77,17 +78,17 @@ pub async fn remove_link_workflow( // 3. If valid store the entry aspect in the local DHT shard let aspect = EntryAspect::LinkRemove( (link_data.clone(), links_to_remove.clone()), - entry_with_header.header.clone(), + header_with_its_entry.header(), ); hold_aspect(aspect, context.clone()).await?; log_debug!(context, "workflow/remove_link: added! {:?}", link); //4. store link_remove entry so we have all we need to respond to get links queries without any other network look-up``` - hold_entry_workflow(&entry_with_header, context.clone()).await?; + hold_entry_workflow(&header_with_its_entry, context.clone()).await?; log_debug!( context, "workflow/hold_entry: added! {:?}", - entry_with_header + header_with_its_entry ); Ok(()) diff --git a/crates/core_types/src/chain_header.rs b/crates/core_types/src/chain_header.rs index 66e1565b1a..9cadca799c 100644 --- a/crates/core_types/src/chain_header.rs +++ b/crates/core_types/src/chain_header.rs @@ -5,8 +5,8 @@ use crate::{ agent::test_agent_id, entry::{ - entry_type::{test_entry_type, EntryType}, - test_entry, + entry_type::{test_entry_type, test_link_entry_type, test_sys_entry_type, EntryType}, + test_entry, test_link_entry, test_sys_entry, Entry, }, signature::{Provenance, Signature}, time::{test_iso_8601, Iso8601}, @@ -128,11 +128,38 @@ impl AddressableContent for ChainHeader { } } +// TODO: refactor these test_chain_headers with passing the test_entry /// returns a dummy header for use in tests pub fn test_chain_header() -> ChainHeader { test_chain_header_with_sig("sig") } +pub fn test_chain_header_for_sys_entry() -> ChainHeader { + test_chain_header_for_sys_entry_with_sig("sig") +} + +pub fn test_chain_header_for_link_entry() -> ChainHeader { + test_chain_header_for_link_entry_with_sig("sig") +} + +/// Creates a chain header from a test entry with a sig. +pub fn test_chain_header_from_entry_with_sig( + entry: Entry, + entry_type: EntryType, + provenances: Vec, + timestamp: Iso8601, +) -> ChainHeader { + ChainHeader::new( + &entry_type, + &entry.address(), + &provenances, + &None, + &None, + &None, + ×tamp, + ) +} + /// returns a dummy header for use in tests pub fn test_chain_header_with_sig(sig: &'static str) -> ChainHeader { ChainHeader::new( @@ -146,6 +173,42 @@ pub fn test_chain_header_with_sig(sig: &'static str) -> ChainHeader { ) } +/// Convenience function +pub fn test_chain_header_from_entry_with_sig_default_provs_time( + entry: Entry, + entry_type: EntryType, + sig: &'static str, +) -> ChainHeader { + test_chain_header_from_entry_with_sig(entry, entry_type, test_provenances(sig), test_iso_8601()) +} + +/// returns a sys header for use in tests. +// TODO: refactor test_chain_header_for_sys_entry_with_sig +// and test_chain_header_with_sig by passing an `Entry`. +pub fn test_chain_header_for_sys_entry_with_sig(sig: &'static str) -> ChainHeader { + ChainHeader::new( + &test_sys_entry_type(), + &test_sys_entry().address(), + &test_provenances(sig), + &None, + &None, + &None, + &test_iso_8601(), + ) +} + +pub fn test_chain_header_for_link_entry_with_sig(sig: &'static str) -> ChainHeader { + ChainHeader::new( + &test_link_entry_type(), + &test_link_entry().address(), + &test_provenances(sig), + &None, + &None, + &None, + &test_iso_8601(), + ) +} + pub fn test_provenances(sig: &'static str) -> Vec { vec![Provenance::new( test_agent_id().address(), diff --git a/crates/core_types/src/entry/entry_type.rs b/crates/core_types/src/entry/entry_type.rs index 13f997c195..a15a4207ac 100644 --- a/crates/core_types/src/entry/entry_type.rs +++ b/crates/core_types/src/entry/entry_type.rs @@ -168,6 +168,14 @@ pub fn test_entry_type() -> EntryType { EntryType::App(test_app_entry_type()) } +pub fn test_sys_entry_type() -> EntryType { + EntryType::AgentId +} + +pub fn test_link_entry_type() -> EntryType { + EntryType::LinkAdd +} + /// dummy entry type, same as test_type() #[cfg_attr(tarpaulin, skip)] pub fn test_app_entry_type_a() -> AppEntryType { diff --git a/crates/core_types/src/entry/mod.rs b/crates/core_types/src/entry/mod.rs index 728595a1b9..e105ef778f 100644 --- a/crates/core_types/src/entry/mod.rs +++ b/crates/core_types/src/entry/mod.rs @@ -12,7 +12,7 @@ use self::{ deletion_entry::DeletionEntry, }; use agent::{test_agent_id, AgentId}; -use chain_header::ChainHeader; +use chain_header::{test_chain_header, ChainHeader}; use chain_migrate::ChainMigrate; use crud_status::CrudStatus; use dna::Dna; @@ -22,7 +22,7 @@ use holochain_json_api::{ json::{JsonString, RawString}, }; use holochain_persistence_api::cas::content::{Address, AddressableContent, Content}; -use link::{link_data::LinkData, link_list::LinkList}; +use link::{link_data::LinkData, link_list::LinkList, Link, LinkActionKind}; use multihash::Hash; use serde::{ser::SerializeTuple, Deserialize, Deserializer, Serializer}; use snowflake; @@ -174,6 +174,7 @@ pub fn test_sys_entry_value() -> AgentId { pub fn test_entry() -> Entry { Entry::App(test_app_entry_type(), test_entry_value()) } + #[cfg_attr(tarpaulin, skip)] pub fn test_entry_with_value(value: &'static str) -> Entry { Entry::App(test_app_entry_type(), JsonString::from_json(&value)) @@ -213,6 +214,29 @@ pub fn test_entry_unique() -> Entry { ) } +pub fn test_link_entry() -> Entry { + let test_entry = test_entry(); + let test_link = String::from("test_link"); + let test_tag = String::from("test-tag"); + let link = Link::new( + &test_entry.address(), + &test_entry.address(), + &test_link, + &test_tag, + ); + let link_data = LinkData::from_link( + &link, + LinkActionKind::ADD, + test_chain_header(), + test_agent_id(), + ); + + let link_entry = Entry::LinkAdd(link_data); + + link_entry +} + +// TODO: refactor #[cfg_attr(tarpaulin, skip)] pub fn test_sys_entry() -> Entry { Entry::AgentId(test_sys_entry_value()) diff --git a/crates/core_types/src/error/mod.rs b/crates/core_types/src/error/mod.rs index 0ca2500451..b3ea09ed73 100644 --- a/crates/core_types/src/error/mod.rs +++ b/crates/core_types/src/error/mod.rs @@ -12,7 +12,7 @@ use holochain_json_api::{ json::*, }; use holochain_locksmith::LocksmithError; -use holochain_persistence_api::{error::PersistenceError, hash::HashString}; +use holochain_persistence_api::{cas::content::Address, error::PersistenceError, hash::HashString}; use lib3h_crypto_api::CryptoError; use serde_json::Error as SerdeError; @@ -115,6 +115,7 @@ pub enum HolochainError { InitializationFailed(String), LifecycleError(String), DnaHashMismatch(HashString, HashString), + HeaderEntryMismatch(String, Address, Address), EntryNotFoundLocally, EntryIsPrivate, List(Vec), @@ -162,6 +163,12 @@ impl fmt::Display for HolochainError { "Provided DNA hash does not match actual DNA hash! {} != {}", hash1, hash2 ), + HeaderEntryMismatch(err_msg, header_entry_address, entry_address) => write!( + f, + "Header/Entry mismatch. The entry address {} in the + header does not match the address {} of the entry. {}", + header_entry_address, entry_address, err_msg + ), EntryNotFoundLocally => write!(f, "The requested entry could not be found locally"), EntryIsPrivate => write!( f, diff --git a/crates/core_types/src/error/ribosome_error.rs b/crates/core_types/src/error/ribosome_error.rs index daf478ca3a..76cba8c03a 100644 --- a/crates/core_types/src/error/ribosome_error.rs +++ b/crates/core_types/src/error/ribosome_error.rs @@ -206,6 +206,7 @@ impl From for RibosomeErrorCode { HolochainError::InitializationFailed(_) => RibosomeErrorCode::Unspecified, HolochainError::LifecycleError(_) => RibosomeErrorCode::Unspecified, HolochainError::DnaHashMismatch(_, _) => RibosomeErrorCode::Unspecified, + HolochainError::HeaderEntryMismatch(_, _, _) => RibosomeErrorCode::Unspecified, HolochainError::EntryNotFoundLocally => RibosomeErrorCode::Unspecified, HolochainError::EntryIsPrivate => RibosomeErrorCode::Unspecified, HolochainError::List(_) => RibosomeErrorCode::Unspecified, diff --git a/crates/core_types/src/network/entry_aspect.rs b/crates/core_types/src/network/entry_aspect.rs index 907a6411f5..df8514024a 100644 --- a/crates/core_types/src/network/entry_aspect.rs +++ b/crates/core_types/src/network/entry_aspect.rs @@ -105,6 +105,23 @@ fn format_header(header: &ChainHeader) -> String { header.link_update_delete() ) } + +impl fmt::Display for EntryAspect { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + EntryAspect::Content(_, _) => write!(f, "EntryAspect::Content(Entry, ChainHeader)"), + EntryAspect::Header(_) => write!(f, "EntryAspect::Header(ChainHeader)"), + EntryAspect::LinkAdd(_, _) => write!(f, "EntryAspect::LinkAdd(LinkData, ChainHeader)"), + EntryAspect::LinkRemove(_, _) => write!( + f, + "EntryAspect::LinkRemove((LinkData, Vec
), ChainHeader)" + ), + EntryAspect::Update(_, _) => write!(f, "EntryAspect::Update(Entry, ChainHeader)"), + EntryAspect::Deletion(_) => write!(f, "EntryAspect::Deletion(ChainHeader)"), + } + } +} + impl fmt::Debug for EntryAspect { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { diff --git a/crates/in_stream/src/ws.rs b/crates/in_stream/src/ws.rs index 7d25987a92..6b0a72b180 100644 --- a/crates/in_stream/src/ws.rs +++ b/crates/in_stream/src/ws.rs @@ -235,7 +235,7 @@ impl InStreamWss { // ignore would-block errors on write // tungstenite queues them in pending, they'll get sent Err(tungstenite::error::Error::Io(e)) if e.would_block() => { - return Ok(()) + return Ok(()); } Err(tungstenite::error::Error::Io(_)) => { if let Err(tungstenite::error::Error::Io(e)) = res { @@ -248,7 +248,7 @@ impl InStreamWss { return Err(Error::new( ErrorKind::Other, format!("tungstenite error: {:?}", e), - )) + )); } } } else { @@ -342,7 +342,7 @@ impl InStream<&mut WsFrame, WsFrame> for InStreamWss { return Err(Error::new( ErrorKind::Other, format!("tungstenite error: {:?}", e), - )) + )); } } } diff --git a/crates/metrics/src/stats.rs b/crates/metrics/src/stats.rs index a866e9f757..cb27225404 100644 --- a/crates/metrics/src/stats.rs +++ b/crates/metrics/src/stats.rs @@ -6,8 +6,7 @@ use stats::Commute; use std::{ collections::HashMap, error::Error, - fmt, - fmt::{Display, Formatter}, + fmt::{self, Display, Formatter}, io, iter::FromIterator, }; @@ -634,7 +633,6 @@ mod tests { assert_eq!(size_stats.min(), 1.0); assert_eq!(size_stats.max(), 100.0); } - #[test] fn can_perform_stat_check() { let expected: StatsByMetric = StatsByMetric::from_iter( diff --git a/crates/sim1h/src/dht/bbdht/dynamodb/api/agent/inbox.rs b/crates/sim1h/src/dht/bbdht/dynamodb/api/agent/inbox.rs index bee923593f..bde4105f3d 100644 --- a/crates/sim1h/src/dht/bbdht/dynamodb/api/agent/inbox.rs +++ b/crates/sim1h/src/dht/bbdht/dynamodb/api/agent/inbox.rs @@ -215,7 +215,7 @@ pub fn item_to_direct_message_data(item: &Item) -> BbDhtResult<(DirectMessageDat return Err(BbDhtError::MissingData(format!( "message item missing content {:?}", &item - ))) + ))); } }; @@ -225,7 +225,7 @@ pub fn item_to_direct_message_data(item: &Item) -> BbDhtResult<(DirectMessageDat return Err(BbDhtError::MissingData(format!( "message item missing from {:?}", &item - ))) + ))); } }; @@ -235,7 +235,7 @@ pub fn item_to_direct_message_data(item: &Item) -> BbDhtResult<(DirectMessageDat return Err(BbDhtError::MissingData(format!( "message item missing to {:?}", &item - ))) + ))); } }; @@ -245,7 +245,7 @@ pub fn item_to_direct_message_data(item: &Item) -> BbDhtResult<(DirectMessageDat return Err(BbDhtError::MissingData(format!( "message item missing space_address {:?}", &item - ))) + ))); } }; @@ -255,7 +255,7 @@ pub fn item_to_direct_message_data(item: &Item) -> BbDhtResult<(DirectMessageDat return Err(BbDhtError::MissingData(format!( "message item missing request_id {:?}", &item - ))) + ))); } }; @@ -265,7 +265,7 @@ pub fn item_to_direct_message_data(item: &Item) -> BbDhtResult<(DirectMessageDat return Err(BbDhtError::MissingData(format!( "message item missing response flag {:?}", &item - ))) + ))); } }; @@ -317,7 +317,7 @@ pub fn request_ids_to_messages( return Err(BbDhtError::MissingData(format!( "missing message for request id: {:?}", &request_id - ))) + ))); } } } diff --git a/crates/sim1h/src/dht/bbdht/dynamodb/api/aspect/read.rs b/crates/sim1h/src/dht/bbdht/dynamodb/api/aspect/read.rs index df845baeae..b79033c2bf 100644 --- a/crates/sim1h/src/dht/bbdht/dynamodb/api/aspect/read.rs +++ b/crates/sim1h/src/dht/bbdht/dynamodb/api/aspect/read.rs @@ -40,7 +40,7 @@ fn try_aspect_from_item(item: Item) -> BbDhtResult { return Err(BbDhtError::MissingData(format!( "Missing aspect_hash: {:?}", item - ))) + ))); } }; @@ -50,7 +50,7 @@ fn try_aspect_from_item(item: Item) -> BbDhtResult { return Err(BbDhtError::MissingData(format!( "Missing aspect: {:?}", item - ))) + ))); } }; @@ -60,7 +60,7 @@ fn try_aspect_from_item(item: Item) -> BbDhtResult { return Err(BbDhtError::MissingData(format!( "Missing publish_ts: {:?}", item - ))) + ))); } }; @@ -70,7 +70,7 @@ fn try_aspect_from_item(item: Item) -> BbDhtResult { return Err(BbDhtError::MissingData(format!( "Missing type_hint: {:?}", item - ))) + ))); } }; @@ -89,7 +89,7 @@ pub fn try_aspect_list_from_item(item: Item) -> BbDhtResult> { return Err(BbDhtError::MissingData(format!( "Missing aspect_list: {:?}", item - ))) + ))); } }; @@ -132,7 +132,7 @@ pub fn get_entry_aspects( return Err(BbDhtError::MissingData(format!( "Missing entry aspect data: {:?}", &aspect_address - ))) + ))); } Err(err) => return Err(err), }, diff --git a/crates/sim1h/src/ghost_actor/mod.rs b/crates/sim1h/src/ghost_actor/mod.rs index 4993920c33..dda5b8906b 100644 --- a/crates/sim1h/src/ghost_actor/mod.rs +++ b/crates/sim1h/src/ghost_actor/mod.rs @@ -254,7 +254,7 @@ impl<'engine> /// process after the subconscious process items have run fn process_concrete(&mut self) -> GhostResult { // always run the endpoint process loop - detach_run!(&mut self.lib3h_endpoint, |cs| { cs.process(self) })?; + detach_run!(&mut self.lib3h_endpoint, |cs| cs.process(self))?; let mut work_was_done = false; // process any messages from the client to us