From f1363c21ccde00bc1155a4ec86e79a50c3f70022 Mon Sep 17 00:00:00 2001 From: Zeek Date: Fri, 11 Oct 2024 02:58:33 -0400 Subject: [PATCH] migrate v1 --- Cargo.lock | 123 ++++++ Cargo.toml | 10 +- crates/mews_types/src/lib.rs | 6 + dnas/mewsfeed/workdir/dna.yaml | 9 + .../zomes/coordinator/follows/Cargo.toml | 7 +- .../follows/src/follower_to_creators.rs | 159 ++++--- .../zomes/coordinator/mews/Cargo.toml | 1 + .../zomes/coordinator/mews/src/all_mews.rs | 129 ++++++ .../coordinator/mews/src/hashtag_to_mews.rs | 116 ++++- .../coordinator/mews/src/mew_with_context.rs | 146 +++++-- .../zomes/coordinator/trust_atom/Cargo.toml | 16 + .../zomes/coordinator/trust_atom/src/lib.rs | 1 + .../trust_atom/tests/trust_atom_tests.rs | 400 ++++++++++++++++++ .../zomes/integrity/mews/src/all_mews.rs | 6 + dnas/mewsfeed/zomes/integrity/mews/src/lib.rs | 9 +- .../integrity/trust_atom_integrity/Cargo.toml | 15 + .../integrity/trust_atom_integrity/src/lib.rs | 1 + 17 files changed, 1030 insertions(+), 124 deletions(-) create mode 100644 dnas/mewsfeed/zomes/coordinator/trust_atom/Cargo.toml create mode 100644 dnas/mewsfeed/zomes/coordinator/trust_atom/src/lib.rs create mode 100644 dnas/mewsfeed/zomes/coordinator/trust_atom/tests/trust_atom_tests.rs create mode 100644 dnas/mewsfeed/zomes/integrity/trust_atom_integrity/Cargo.toml create mode 100644 dnas/mewsfeed/zomes/integrity/trust_atom_integrity/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index dad39c06..ebc0f0b5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -188,6 +188,30 @@ dependencies = [ "generic-array", ] +[[package]] +name = "borsh" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6362ed55def622cddc70a4746a68554d7b687713770de539e59a739b249f8ed" +dependencies = [ + "borsh-derive", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3ef8005764f53cd4dca619f5bf64cafd4664dada50ece25e4d81de54c80cc0b" +dependencies = [ + "once_cell", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.75", + "syn_derive", +] + [[package]] name = "bumpalo" version = "3.16.0" @@ -287,6 +311,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.38" @@ -796,9 +826,12 @@ version = "0.0.1" dependencies = [ "follows_integrity", "follows_types", + "hc_call_utils", "hc_link_pagination", "hdk", + "mews_types", "serde", + "trust_atom_types", ] [[package]] @@ -1576,6 +1609,7 @@ dependencies = [ "rand", "regex", "serde", + "trust_atom 0.0.1", ] [[package]] @@ -1730,6 +1764,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -1990,6 +2033,22 @@ dependencies = [ "serde", ] +[[package]] +name = "rust_decimal" +version = "1.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b082d80e3e3cc52b2ed634388d436fe1f4de6af5786cc2de9ba9737527bdf555" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -2345,6 +2404,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "syn_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1329189c02ff984e9736652b1631330da25eaa6bc639089ed4915d25446cbe7b" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.75", +] + [[package]] name = "tap" version = "1.0.1" @@ -2558,6 +2629,58 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683ba5022fe6dbd7133cad150478ccf51bdb6d861515181e5fc6b4323d4fa424" +[[package]] +name = "trust_atom" +version = "0.0.1" +dependencies = [ + "hdk", + "rust_decimal", + "serde", + "trust_atom 0.1.32-dev", +] + +[[package]] +name = "trust_atom" +version = "0.1.32-dev" +source = "git+https://github.com/trustgraph/trustgraph-holochain.git?branch=chore/hdk-0.3.2-using-builder#be2d3e016e7225bfceeddd381465079c956e454a" +dependencies = [ + "hdk", + "rust_decimal", + "serde", + "trust_atom_integrity 0.1.32-dev", + "trust_atom_types", +] + +[[package]] +name = "trust_atom_integrity" +version = "0.0.1" +dependencies = [ + "hdk", + "rust_decimal", + "serde", + "trust_atom_integrity 0.1.32-dev", +] + +[[package]] +name = "trust_atom_integrity" +version = "0.1.32-dev" +source = "git+https://github.com/trustgraph/trustgraph-holochain.git?branch=chore/hdk-0.3.2-using-builder#be2d3e016e7225bfceeddd381465079c956e454a" +dependencies = [ + "hdi", + "rust_decimal", + "serde", + "trust_atom_types", +] + +[[package]] +name = "trust_atom_types" +version = "0.1.32-dev" +source = "git+https://github.com/trustgraph/trustgraph-holochain.git?branch=chore/hdk-0.3.2-using-builder#be2d3e016e7225bfceeddd381465079c956e454a" +dependencies = [ + "hdk", + "serde", +] + [[package]] name = "typenum" version = "1.17.0" diff --git a/Cargo.toml b/Cargo.toml index eced64bc..c1995c53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,13 +5,13 @@ resolver = "2" [workspace.dependencies] hdi = "=0.4.2" hdk = "=0.3.2" +# holochain_integrity_types = "=0.2.2" serde = "1.0" paste = "1.0" rand = "0.8.5" hc_prefix_index = "0.12.0" regex = "1.10.3" -hc_zome_profiles_coordinator = { git = "https://github.com/holochain-open-dev/profiles.git", rev = "a1a487c8d6a8fd9910ba9b3f26e47df0bf0d09ae" } -hc_zome_profiles_integrity = { git = "https://github.com/holochain-open-dev/profiles.git", rev = "a1a487c8d6a8fd9910ba9b3f26e47df0bf0d09ae" } +trust_atom_types = { git = "https://github.com/trustgraph/trustgraph-holochain.git", package = "trust_atom_types", branch = "chore/hdk-0.3.2-using-builder" } [workspace.dependencies.agent_pins] path = "dnas/mewsfeed/zomes/coordinator/agent_pins" @@ -57,3 +57,9 @@ path = "crates/hc_call_utils" [workspace.dependencies.hc_link_pagination] path = "crates/hc_link_pagination" + +[workspace.dependencies.trust_atom] +path = "dnas/mewsfeed/zomes/coordinator/trust_atom" + +[workspace.dependencies.trust_atom_integrity] +path = "dnas/mewsfeed/zomes/integrity/trust_atom_integrity" \ No newline at end of file diff --git a/crates/mews_types/src/lib.rs b/crates/mews_types/src/lib.rs index 837a0a69..4d4feaec 100644 --- a/crates/mews_types/src/lib.rs +++ b/crates/mews_types/src/lib.rs @@ -2,6 +2,8 @@ use hc_link_pagination::Timestamped; use hdk::prelude::*; use std::collections::BTreeMap; +pub const FOLLOW_TOPIC: &str = "__DEFAULT__"; + #[derive(Serialize, Deserialize, SerializedBytes, Debug, Clone, PartialEq, Eq)] pub enum LinkTarget { Mention(AgentPubKey), @@ -42,6 +44,7 @@ pub struct Profile { #[derive(Serialize, Deserialize, SerializedBytes, Debug, Clone)] pub struct FeedMew { + // Mew with context pub mew: Mew, pub action: Action, pub action_hash: ActionHash, @@ -57,6 +60,9 @@ pub struct FeedMew { pub is_replied: bool, pub is_quoted: bool, pub original_mew: Option, + // introduced with TrustAtoms + pub weight: Option, + pub topic: Option, } #[derive(Serialize, Deserialize, SerializedBytes, Debug, Clone)] diff --git a/dnas/mewsfeed/workdir/dna.yaml b/dnas/mewsfeed/workdir/dna.yaml index f03367da..0f893120 100644 --- a/dnas/mewsfeed/workdir/dna.yaml +++ b/dnas/mewsfeed/workdir/dna.yaml @@ -28,6 +28,10 @@ integrity: hash: ~ bundled: "../../../target/wasm32-unknown-unknown/release/agent_pins_integrity.wasm" dependencies: ~ + - name: trust_atom_integrity + hash: ~ + bundled: "../../../target/wasm32-unknown-unknown/release/trust_atom_integrity_zome.wasm" + dependencies: ~ coordinator: zomes: - name: profiles @@ -60,3 +64,8 @@ coordinator: hash: ~ bundled: "../../../target/wasm32-unknown-unknown/release/ping.wasm" dependencies: [] + - name: trust_atom + hash: ~ + bundled: "../../../target/wasm32-unknown-unknown/release/trust_atom_zome.wasm" + dependencies: + - name: trust_atom_integrity \ No newline at end of file diff --git a/dnas/mewsfeed/zomes/coordinator/follows/Cargo.toml b/dnas/mewsfeed/zomes/coordinator/follows/Cargo.toml index b6942d0c..457ed52b 100644 --- a/dnas/mewsfeed/zomes/coordinator/follows/Cargo.toml +++ b/dnas/mewsfeed/zomes/coordinator/follows/Cargo.toml @@ -8,8 +8,11 @@ crate-type = ["cdylib", "rlib"] name = "follows" [dependencies] +hc_call_utils = { workspace = true } +hc_link_pagination = { workspace = true } hdk = { workspace = true } -serde = { workspace = true } follows_integrity = { workspace = true } -hc_link_pagination = { workspace = true } follows_types = { workspace = true } +mews_types ={ workspace = true} +serde = { workspace = true } +trust_atom_types ={ workspace = true} \ No newline at end of file diff --git a/dnas/mewsfeed/zomes/coordinator/follows/src/follower_to_creators.rs b/dnas/mewsfeed/zomes/coordinator/follows/src/follower_to_creators.rs index 26bbae6a..3b785977 100644 --- a/dnas/mewsfeed/zomes/coordinator/follows/src/follower_to_creators.rs +++ b/dnas/mewsfeed/zomes/coordinator/follows/src/follower_to_creators.rs @@ -1,62 +1,94 @@ -use follows_integrity::*; +use follows_integrity::LinkTypes; +// use follows_integrity::*; use follows_types::*; +use hc_call_utils::call_local_zome; use hc_link_pagination::paginate_by_agentpubkey; +// use hc_link_pagination::paginate_by_agentpubkey; use hdk::prelude::*; +use mews_types::FOLLOW_TOPIC; +use trust_atom_types::{DeleteReport, QueryInput, TrustAtom, TrustAtomInput}; + +#[derive(Debug, Serialize, Deserialize, SerializedBytes)] +pub struct FollowInput { + pub agent: AgentPubKey, + pub follow_topics: Vec, +} + +#[derive(Debug, Serialize, Deserialize, SerializedBytes)] +pub struct FollowTopicInput { + pub topic: String, + pub weight: String, +} + +#[derive(Debug, Serialize, Deserialize, SerializedBytes)] +pub struct TrustedFeedInput { + pub agent: AgentPubKey, + pub topic: String, + pub weight: String, +} #[hdk_extern] pub fn add_creator_for_follower(input: AddCreatorForFollowerInput) -> ExternResult<()> { - create_link( - input.base_follower.clone(), - input.target_creator.clone(), - LinkTypes::FollowerToCreators, - (), - )?; - create_link( - input.target_creator, - input.base_follower, - LinkTypes::CreatorToFollowers, - (), - )?; - - Ok(()) + call_local_zome( + "trust_atom", + "create_trust_atom", + TrustAtomInput { + target: AnyLinkableHash::from(input.target_creator), + content: Some(String::from(FOLLOW_TOPIC)), + value: None, + extra: None, + }, + ) } #[hdk_extern] pub fn get_creators_for_follower( input: GetCreatorsForFollowerInput, ) -> ExternResult> { - let links = get_links( - GetLinksInputBuilder::try_new( - input.follower.clone(), - LinkTypes::FollowerToCreators.try_into_filter()?, - )? - .build(), + let links_from_follower_to_creators: Vec = call_local_zome( + "trust_atom", + "query", + QueryInput { + source: Some(AnyLinkableHash::from(input.follower)), + target: None, + content_full: Some(String::from(FOLLOW_TOPIC)), + content_starts_with: None, + content_not_starts_with: None, + value_starts_with: None, + }, )?; - let links_page = paginate_by_agentpubkey(links, input.page)?; - - let agents: Vec = links_page + let creators: Vec = links_from_follower_to_creators .into_iter() - .filter_map(|link| EntryHash::try_from(link.target).ok()) + .filter_map(|link| link.target_hash.into_entry_hash()) .map(AgentPubKey::from) .collect(); - Ok(agents) + Ok(creators) } #[hdk_extern] -pub fn get_followers_for_creator( - input: GetFollowersForCreatorInput, -) -> ExternResult> { - let links = get_follower_links_for_creator(input)?; +pub fn get_followers_for_creator(creator: AgentPubKey) -> ExternResult> { + let links_from_followers_to_creator: Vec = call_local_zome( + "trust_atom", + "query", + QueryInput { + source: None, + target: Some(AnyLinkableHash::from(creator)), + content_full: Some(String::from(FOLLOW_TOPIC)), + content_starts_with: None, + content_not_starts_with: None, + value_starts_with: None, + }, + )?; - let agents: Vec = links + let followers: Vec = links_from_followers_to_creator .into_iter() - .filter_map(|link| EntryHash::try_from(link.target).ok()) + .filter_map(|link| link.source_hash.into_entry_hash()) .map(AgentPubKey::from) .collect(); - Ok(agents) + Ok(followers) } #[hdk_extern] @@ -107,47 +139,40 @@ pub fn get_follower_link_details_for_creator(creator: AgentPubKey) -> ExternResu #[hdk_extern] pub fn remove_creator_for_follower(input: RemoveCreatorForFollowerInput) -> ExternResult<()> { - let links = get_links( - GetLinksInputBuilder::try_new( - input.base_follower.clone(), - LinkTypes::FollowerToCreators.try_into_filter()?, - )? - .build(), - )?; - - for link in links { - let entry_hash = - EntryHash::try_from(link.target.clone()).map_err(|err| wasm_error!(err))?; - if AgentPubKey::from(entry_hash).eq(&input.target_creator) { - delete_link(link.create_link_hash)?; - } - } - - let links = get_links( - GetLinksInputBuilder::try_new( - input.target_creator.clone(), - LinkTypes::CreatorToFollowers.try_into_filter()?, - )? - .build(), + let _deleted_link_count: DeleteReport = call_local_zome( + "trust_atom", + "delete_trust_atoms", + AnyLinkableHash::from(input.target_creator), )?; - for link in links { - let entry_hash = - EntryHash::try_from(link.target.clone()).map_err(|err| wasm_error!(err))?; - if AgentPubKey::from(entry_hash).eq(&input.base_follower) { - delete_link(link.create_link_hash)?; - } - } - Ok(()) } #[hdk_extern] -pub fn follow(agent: AgentPubKey) -> ExternResult<()> { +pub fn follow(input: FollowInput) -> ExternResult<()> { + let agent_pubkey = agent_info()?.agent_initial_pubkey; + if input.agent == agent_pubkey { + return Err(wasm_error!("You cannot follow yourself")); + } + add_creator_for_follower(AddCreatorForFollowerInput { - base_follower: agent_info()?.agent_initial_pubkey, - target_creator: agent, - }) + base_follower: agent_pubkey, + target_creator: input.agent.clone(), + })?; + + for follow_topic in input.follow_topics { + let _: TrustAtom = call_local_zome( + "trust_atom", + "create_trust_atom", + TrustAtomInput { + target: AnyLinkableHash::from(input.agent.clone()), + content: Some(follow_topic.topic), + value: Some(follow_topic.weight), + extra: None, + }, + )?; + } + Ok(()) } #[hdk_extern] diff --git a/dnas/mewsfeed/zomes/coordinator/mews/Cargo.toml b/dnas/mewsfeed/zomes/coordinator/mews/Cargo.toml index 2d75a308..6c423c38 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/Cargo.toml +++ b/dnas/mewsfeed/zomes/coordinator/mews/Cargo.toml @@ -19,3 +19,4 @@ rand = { workspace = true } hc_call_utils = { workspace = true } hc_link_pagination = { workspace = true } follows_types = { workspace = true } +trust_atom = { workspace = true } \ No newline at end of file diff --git a/dnas/mewsfeed/zomes/coordinator/mews/src/all_mews.rs b/dnas/mewsfeed/zomes/coordinator/mews/src/all_mews.rs index f8055249..8f244292 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/src/all_mews.rs +++ b/dnas/mewsfeed/zomes/coordinator/mews/src/all_mews.rs @@ -1,5 +1,19 @@ use hdk::prelude::*; use mews_integrity::*; +use crate::mew_with_context::get_batch_mews_with_context; + +#[hdk_extern] +pub fn get_all_mews(_: ()) -> ExternResult> { + let hashes = get_all_mew_hashes()?; + let get_input: Vec = hashes + .into_iter() + .map(|hash| GetInput::new(hash.into(), GetOptions::default())) + .collect(); + let records = HDK.with(|hdk| hdk.borrow().get(get_input))?; + let records: Vec = records.into_iter().flatten().collect(); + + Ok(records) +} pub fn get_all_mew_hashes() -> ExternResult> { let path = Path::from("all_mews"); @@ -18,3 +32,118 @@ pub fn get_all_mew_hashes() -> ExternResult> { Ok(hashes) } + +#[hdk_extern] +pub fn get_all_mews_with_context(_: ()) -> ExternResult> { + let hashes = get_all_mew_hashes()?; + + get_batch_mews_with_context(hashes) +} + +// #[hdk_extern] +// pub fn get_trusted_mews_with_context(input: RecommendedInput) -> ExternResult> { +// let _oldest_mew_seconds = input.oldest_mew_seconds.unwrap_or(60 * 60 * 24 * 7 * 2); + +// // get all TrustAtoms -- topic/author combos "rated" by this agent +// let trust_atoms: Vec = call_local_zome( +// "trust_atom", +// "query_mine", +// QueryMineInput { +// target: None, +// content_full: None, +// content_starts_with: None, +// // content_not_starts_with: Some(String::from("__")), // TODO use this or manually filter +// value_starts_with: None, +// }, +// )?; + +// let topics_by_author: Vec = trust_atoms +// .into_iter() +// .filter(|atom| match atom.content.clone() { +// Some(content) => content != FOLLOW_TOPIC, +// None => true, +// }) +// .collect(); + +// // debug!("topics_by_author: {:#?}", topics_by_author); + +// // filter for those TrustAtoms above a weight threshold (>= 0) +// let recomended_topics_by_author = +// topics_by_author +// .into_iter() +// .filter_map(|atom| match atom.value.clone() { +// Some(value_string) => { +// let value_float: Result = value_string.parse(); +// match value_float { +// Ok(value_float) => { +// if value_float >= 0f32 { +// // let key = format!( +// // "{}{}", +// // atom.target_hash.clone(), +// // atom.content.clone().unwrap_or(String::from("")) +// // ); +// Some(atom) +// } else { +// None +// } +// } +// _ => None, +// } +// } +// None => None, // null value/weight is allowed in TrustAtom lib, but not in MewsFeed +// }); + +// debug!( +// "recomended_topics_by_author: {:#?}", +// recomended_topics_by_author, +// ); + +// // get all mews by those authors +// let trust_feed_mews: Vec = recomended_topics_by_author +// .flat_map(|atom| { +// let followed_author = atom.target_hash.clone().into_agent_pub_key(); +// if let Some(pubkey) = followed_author { +// match atom.content.clone() { +// None => vec![], // TODO get all mews by this author +// Some(content) => { +// let feed_mews_result = get_mews_for_hashtag_by_author_with_context( +// format!("#{}", content), // add # (hash) to make it a hashtag +// pubkey, +// ); +// // debug!("feed_mews_result: {:#?}", feed_mews_result); +// match feed_mews_result { +// Ok(feed_mews) => feed_mews +// .into_iter() +// .map(|feed_mew| FeedMew { +// weight: Some( +// atom.value +// .clone() +// .unwrap_or_else(|| String::from("0")) +// .parse::() +// .unwrap_or(0.0), +// ), +// topic: atom.content.clone(), +// ..feed_mew +// }) +// .collect(), +// Err(_) => vec![], +// } +// } +// } +// } else { +// vec![] +// } +// }) +// .collect(); + +// debug!( +// "trust_feed_mews: {:#?}", +// trust_feed_mews +// .clone() +// .into_iter() +// .map(|feed_mew| feed_mew.clone().mew.text) +// .collect::>() +// ); + +// Ok(trust_feed_mews) +// } \ No newline at end of file diff --git a/dnas/mewsfeed/zomes/coordinator/mews/src/hashtag_to_mews.rs b/dnas/mewsfeed/zomes/coordinator/mews/src/hashtag_to_mews.rs index e88886b0..2977b5d4 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/src/hashtag_to_mews.rs +++ b/dnas/mewsfeed/zomes/coordinator/mews/src/hashtag_to_mews.rs @@ -1,4 +1,4 @@ -use crate::tag_to_mews::*; +use crate::{mew_with_context::get_mew_with_context, tag_to_mews::*}; use hc_link_pagination::HashPagination; use hdk::prelude::*; use mews_integrity::*; @@ -8,9 +8,22 @@ pub struct AddHashtagForMewInput { pub base_hashtag: String, pub target_mew_hash: ActionHash, } + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetMewsForHashtagWithContextInput { + hashtag: String, + page: Option, +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct RemoveHashtagForMewInput { + pub base_hashtag: String, + pub target_mew_hash: ActionHash, +} + #[hdk_extern] pub fn add_hashtag_for_mew(input: AddHashtagForMewInput) -> ExternResult<()> { - // Add cashtag to prefix index + // Add hashtag to prefix index let tag_text = make_tag_text(input.base_hashtag.clone()); let prefix_index = make_tag_prefix_index()?; let path = prefix_index.add_result_with_label(tag_text, input.base_hashtag.clone())?; @@ -23,14 +36,93 @@ pub fn add_hashtag_for_mew(input: AddHashtagForMewInput) -> ExternResult<()> { LinkTag(input.base_hashtag.as_bytes().to_vec()), )?; + add_hashtag_by_author_for_mew(input)?; + Ok(()) } -#[derive(Serialize, Deserialize, Debug)] -pub struct RemoveHashtagForMewInput { - pub base_hashtag: String, - pub target_mew_hash: ActionHash, +pub fn add_hashtag_by_author_for_mew(input: AddHashtagForMewInput) -> ExternResult<()> { + let tag_text = make_hashtag_text(input.base_hashtag.clone()); + let prefix_index = make_tag_prefix_index()?; + + let me = agent_info()?.agent_latest_pubkey; + let path_text = format!("{}.{}", tag_text, me); + // debug!("path_text on create --- {}", path_text); + + let path = prefix_index.add_result(path_text)?; + + // Link from hashtag to mew_hash + create_link( + path.path_entry_hash()?, + input.target_mew_hash, + LinkTypes::HashtagByAuthorToMews, + LinkTag(input.base_hashtag.as_bytes().to_vec()), + )?; + + Ok(()) } + +#[hdk_extern] +pub fn get_mews_for_hashtag_with_context( + input: GetMewsForHashtagWithContextInput, +) -> ExternResult> { + // Get links from hashtag to mew + let tag = make_hashtag_text(input.hashtag.clone()); + let prefix_index = make_tag_prefix_index()?; + let result_path: Path = prefix_index.make_result_path(tag, Some(input.hashtag))?; + + let links = get_links( + GetLinksInputBuilder::try_new( + result_path.path_entry_hash()?, + LinkTypes::HashtagByAuthorToMews.try_into_filter()?, + )? + .build(), + )?; + + // Get mews with context + let feedmews: Vec = links + .into_iter() + .filter_map(|l| l.target.into_action_hash()) + .filter_map(|ah| get_mew_with_context(ah).ok()) + .collect(); + + Ok(feedmews) +} + +pub fn get_mews_for_hashtag_by_author_with_context( + hashtag: String, + agent: AgentPubKey, +) -> ExternResult> { + // Get links from hashtag to mew + let tag = make_hashtag_text(hashtag.clone()); + let prefix_index = make_tag_prefix_index()?; + + let path_text = format!("{}.{}", tag, agent); + // debug!("path_text --- {}", path_text); + + let result_path: Path = prefix_index.make_result_path(path_text, None)?; + + let links = get_links( + GetLinksInputBuilder::try_new( + result_path.path_entry_hash()?, + LinkTypes::HashtagByAuthorToMews.try_into_filter()?, + )? + .build(), + )?; + + // Get mews with context + let feedmews: Vec = links + .into_iter() + .filter_map(|l| l.target.into_action_hash()) + .filter_map(|ah| get_mew_with_context(ah).ok()) + .collect(); + Ok(feedmews) +} + +fn make_hashtag_text(text: String) -> String { + text.split('#').nth(1).unwrap_or(&text).to_string() +} + #[hdk_extern] pub fn remove_hashtag_for_mew(input: RemoveHashtagForMewInput) -> ExternResult<()> { let tag = make_tag_text(input.base_hashtag.clone()); @@ -53,15 +145,3 @@ pub fn remove_hashtag_for_mew(input: RemoveHashtagForMewInput) -> ExternResult<( Ok(()) } - -#[derive(Serialize, Deserialize, Debug)] -pub struct GetMewsForHashtagWithContextInput { - hashtag: String, - page: Option, -} -#[hdk_extern] -pub fn get_mews_for_hashtag_with_context( - input: GetMewsForHashtagWithContextInput, -) -> ExternResult> { - get_mews_for_tag_with_context(input.hashtag, LinkTypes::HashtagToMews, input.page) -} diff --git a/dnas/mewsfeed/zomes/coordinator/mews/src/mew_with_context.rs b/dnas/mewsfeed/zomes/coordinator/mews/src/mew_with_context.rs index 5cbbee75..95792706 100644 --- a/dnas/mewsfeed/zomes/coordinator/mews/src/mew_with_context.rs +++ b/dnas/mewsfeed/zomes/coordinator/mews/src/mew_with_context.rs @@ -6,6 +6,119 @@ use hdk::prelude::*; use mews_integrity::*; use mews_types::Profile; +fn get_agent_profile(agent_pub_key: AgentPubKey) -> ExternResult> { + let maybe_record = call_local_zome::, AgentPubKey>( + "profiles", + "get_agent_profile", + agent_pub_key, + )?; + + match maybe_record { + Some(record) => { + let profile: Profile = record + .entry() + .to_app_option() + .map_err(|e| wasm_error!(WasmErrorInner::Guest(e.into())))? + .ok_or(wasm_error!(WasmErrorInner::Guest(String::from( + "Malformed Profile" + ))))?; + + Ok(Some(profile)) + } + None => Ok(None), + } +} + +#[hdk_extern] +pub fn get_batch_mews_with_context(hashes: Vec) -> ExternResult> { + hashes + .into_iter() + .map(get_mew_with_context) + .collect::>>() +} + +#[hdk_extern] +pub fn get_batch_mews_with_context_based_on_topic_and_weight_threshold( + input: TrustedFeedInput, +) -> ExternResult> { + let topic = input.topic; + let min_weight = input.weight.parse::(); + + if let Ok(weight) = min_weight { + let trust_atoms_by_topic: Vec = call_local_zome( + "trust_atom", + "query", + QueryInput { + source: Some(AnyLinkableHash::from(input.agent)), // ?TODO: handle potential conversion error + target: None, + content_full: Some(topic.clone()), // query by topic + content_starts_with: None, + value_starts_with: None, + }, + )?; + + // debug!("trust_atoms: {:#?}", trust_atoms_by_topic.clone()); + + let weighted_filter: Vec = trust_atoms_by_topic + .clone() + .into_iter() + .filter_map(|atom| match atom.value.clone() { + Some(value_string) => { + let value_float: Result = value_string.parse::(); // TODO: find way to escape iterator with an error + if let Ok(value) = value_float { + if value >= weight { + Some(atom) + } else { + None + } + } else { + None + } + } + None => None, + }) + .collect(); + + // debug!("weighted_filter: {:#?}", weighted_filter.clone()); + + let mut weighted_trust_feed_mews: Vec = Vec::new(); + + for atom in weighted_filter.clone() { + let agent = atom.target_hash.into_agent_pub_key(); + if let Some(pubkey) = agent { + let mut feed_mews = + get_mews_for_hashtag_by_author_with_context(topic.clone(), pubkey)?; + for feed_mew in &mut feed_mews { + feed_mew.topic = atom.content.clone(); + feed_mew.weight = atom.value.clone(); + } + weighted_trust_feed_mews.append(&mut feed_mews); + } else { + return Err(wasm_error!( + "error converting target hash, should be an agent pubkey" + )); + } + } + + weighted_trust_feed_mews.sort_by(|a, b| b.weight.cmp(&a.weight)); + + // debug!("weighted feed: {:#?}", weighted_trust_feed_mews.clone()); + + debug!( + "trust_feed_mews: {:#?}", + weighted_trust_feed_mews + .clone() + .into_iter() + .map(|feed_mew| feed_mew.clone().mew.text) + .collect::>() + ); + + Ok(weighted_trust_feed_mews.clone()) + } else { + Err(wasm_error!("could not parse weight")) + } +} + #[hdk_extern] pub fn get_mew_with_context(original_mew_hash: ActionHash) -> ExternResult { let response = get_details(original_mew_hash.clone(), GetOptions::default())?.ok_or( @@ -84,6 +197,8 @@ pub fn get_mew_with_context(original_mew_hash: ActionHash) -> ExternResult ExternResult) -> ExternResult> { - hashes - .into_iter() - .map(get_mew_with_context) - .collect::>>() -} - #[hdk_extern] pub fn get_responses_for_mew_with_context( input: GetResponsesForMewInput, @@ -163,26 +270,3 @@ pub fn get_responses_for_mew_with_context( get_batch_mews_with_context(response_hashes) } - -fn get_agent_profile(agent_pub_key: AgentPubKey) -> ExternResult> { - let maybe_record = call_local_zome::, AgentPubKey>( - "profiles", - "get_agent_profile", - agent_pub_key, - )?; - - match maybe_record { - Some(record) => { - let profile: Profile = record - .entry() - .to_app_option() - .map_err(|e| wasm_error!(WasmErrorInner::Guest(e.into())))? - .ok_or(wasm_error!(WasmErrorInner::Guest(String::from( - "Malformed Profile" - ))))?; - - Ok(Some(profile)) - } - None => Ok(None), - } -} diff --git a/dnas/mewsfeed/zomes/coordinator/trust_atom/Cargo.toml b/dnas/mewsfeed/zomes/coordinator/trust_atom/Cargo.toml new file mode 100644 index 00000000..a51c1dcc --- /dev/null +++ b/dnas/mewsfeed/zomes/coordinator/trust_atom/Cargo.toml @@ -0,0 +1,16 @@ +[package] +edition = "2021" +name = "trust_atom" +version = "0.0.1" + +[lib] +crate-type = ["cdylib", "rlib"] +name = "trust_atom" + +[dependencies] +trust_atom = { git = "https://github.com/trustgraph/trustgraph-holochain.git", package = "trust_atom", branch = "chore/hdk-0.3.2-using-builder" } + +hdk = { workspace = true } +serde = { workspace = true } +rust_decimal = "1" + diff --git a/dnas/mewsfeed/zomes/coordinator/trust_atom/src/lib.rs b/dnas/mewsfeed/zomes/coordinator/trust_atom/src/lib.rs new file mode 100644 index 00000000..c8ebc9f6 --- /dev/null +++ b/dnas/mewsfeed/zomes/coordinator/trust_atom/src/lib.rs @@ -0,0 +1 @@ +pub extern crate trust_atom; diff --git a/dnas/mewsfeed/zomes/coordinator/trust_atom/tests/trust_atom_tests.rs b/dnas/mewsfeed/zomes/coordinator/trust_atom/tests/trust_atom_tests.rs new file mode 100644 index 00000000..d1c78990 --- /dev/null +++ b/dnas/mewsfeed/zomes/coordinator/trust_atom/tests/trust_atom_tests.rs @@ -0,0 +1,400 @@ +#![warn(warnings)] + +use futures::future::join_all; +use serial_test::serial; + +use hdk::prelude::*; +use holochain::conductor::config::ConductorConfig; +use holochain::sweettest::{ + SweetAgents, SweetAppBatch, SweetCell, SweetConductor, SweetConductorBatch, SweetDnaFile, + SweetZome, +}; +use holochain::test_utils::consistency_10s; + +use trust_atom_types::{QueryInput, TrustAtom}; + +use follows::follower_to_creators::{FollowInput, FollowTopicInput, TrustedFeedInput}; +use mews_types::{FeedMew, Mew, MewType}; + +const DNA_FILEPATH: &str = "../../../workdir/mewsfeed.dna"; +const MEWS_ZOME_NAME: &str = "mews"; +const FOLLOWS_ZOME_NAME: &str = "follows"; +const TRUST_ATOM_ZOME_NAME: &str = "trust_atom"; + +// Map of Follows for convenience calculations: +// Ann -> Bob in HC 0.5 +// Ann -> Cat in HC 1.0 +// Ann -> Dave in HC 0.25 +// Ann -> Bob in BC 0.1 +// Ann -> Cat in BC 0 +// Ann -> Dave in BC 0.55 +// +// Feed order should be: +// HC +// Cat,Bob,Dave +// BC +// Dave,Bob (Cat excluded because of 0 value) + +#[tokio::test(flavor = "multi_thread")] +#[serial] +async fn trusted_feed_based_on_follow_topics_ordered_by_weight() { + let mut agent_group = setup().await; + let agents = agent_group.create_agents().await; + + let ann = &agents[0]; // Ann is the testing agent + let bob = &agents[1]; + let cat = &agents[2]; + let dave = &agents[3]; + // let emma = &agents[4]; + // let frank = &agents[5]; + + // FOLLOWS // + + // #Holochain + + ann.follow(FollowInput { + agent: bob.pubkey.clone(), + follow_topics: vec![FollowTopicInput { + topic: String::from("holochain"), + weight: String::from("0.5"), + }], + }) + .await; + + ann.follow(FollowInput { + agent: cat.pubkey.clone(), + follow_topics: vec![FollowTopicInput { + topic: String::from("holochain"), + weight: String::from("1.0"), + }], + }) + .await; + + ann.follow(FollowInput { + agent: dave.pubkey.clone(), + follow_topics: vec![FollowTopicInput { + topic: String::from("holochain"), + weight: String::from("0.25"), + }], + }) + .await; + + // ann.follow(FollowInput { + // agent: emma.pubkey.clone(), + // follow_topics: vec![FollowTopicInput { + // topic: String::from("holochain"), + // weight: String::from("0.75"), + // }], + // }) + // .await; + + // ann.follow(FollowInput { + // agent: frank.pubkey.clone(), + // follow_topics: vec![FollowTopicInput { + // topic: String::from("holochain"), + // weight: String::from("-1"), // Negative indicates spam or otherwise matierial to be thrown out + // }], + // }) + // .await; + + // #Blockchain + + ann.follow(FollowInput { + agent: bob.pubkey.clone(), + follow_topics: vec![FollowTopicInput { + topic: String::from("blockchain"), + weight: String::from("0.1"), + }], + }) + .await; + + ann.follow(FollowInput { + agent: cat.pubkey.clone(), + follow_topics: vec![FollowTopicInput { + topic: String::from("blockchain"), + weight: String::from("0"), + }], + }) + .await; + + ann.follow(FollowInput { + agent: dave.pubkey.clone(), + follow_topics: vec![FollowTopicInput { + topic: String::from("blockchain"), + weight: String::from("0.55"), + }], + }) + .await; + + // ann.follow(FollowInput { + // agent: emma.pubkey.clone(), + // follow_topics: vec![FollowTopicInput { + // topic: String::from("blockchain"), + // weight: String::from("0.33"), + // }], + // }) + // .await; + + // ann.follow(FollowInput { + // agent: frank.pubkey.clone(), + // follow_topics: vec![FollowTopicInput { + // topic: String::from("blockchain"), + // weight: String::from("0.9"), + // }], + // }) + // .await; + + // MEWS // + + // #Holochain + + bob.create_mew(Mew { + mew_type: MewType::Original, + text: String::from("#holochain from bob, weight 0.5"), + links: vec![], + }) + .await; + + cat.create_mew(Mew { + mew_type: MewType::Original, + text: String::from("#holochain from cat, weight 1.0"), + links: vec![], + }) + .await; + + dave.create_mew(Mew { + mew_type: MewType::Original, + text: String::from("#holochain from dave, weight 0.25"), + links: vec![], + }) + .await; + + // emma.create_mew(Mew { + // mew_type: MewType::Original, + // text: String::from("#holochain from emma, weight 0.75"), + // links: vec![], + // }) + // .await; + + // frank + // .create_mew(Mew { + // mew_type: MewType::Original, + // text: String::from("#holochain from frank, weight -1 should not be seen"), + // links: vec![], + // }) + // .await; + + // #Blockchain + + bob.create_mew(Mew { + mew_type: MewType::Original, + text: String::from("#blockchain from bob, weight 0.1"), + links: vec![], + }) + .await; + + cat.create_mew(Mew { + mew_type: MewType::Original, + text: String::from("#blockchain from cat, weight 0 should not be seen"), + links: vec![], + }) + .await; + + dave.create_mew(Mew { + mew_type: MewType::Original, + text: String::from("#blockchain from dave, weight 0.55"), + links: vec![], + }) + .await; + + // emma.create_mew(Mew { + // mew_type: MewType::Original, + // text: String::from("#blockchain from emma, weight 0.33"), + // links: vec![], + // }) + // .await; + + // frank + // .create_mew(Mew { + // mew_type: MewType::Original, + // text: String::from("#blockchain from frank, weight 0.9"), + // links: vec![], + // }) + // .await; + + consistency_10s([ + &(ann.cell.clone()), + &(bob.cell.clone()), + &(cat.cell.clone()), + &(dave.cell.clone()), + // &(emma.cell.clone()), + // &(frank.cell.clone()), + ]) + .await; + + // Show all mews on holochain topic above 0 threshold + let trusted_feed_holochain_topic_positive = ann + .trusted_feed_weighted(TrustedFeedInput { + agent: ann.pubkey.clone(), + topic: "holochain".to_string(), + weight: "0.000001".to_string(), + }) + .await; + + println!( + "trusted feed: {:#?}", + trusted_feed_holochain_topic_positive.clone() + ); + + assert!(trusted_feed_holochain_topic_positive.len() == 3); + assert_eq!( + trusted_feed_holochain_topic_positive[0].mew.text, + String::from("#holochain from cat, weight 1.0") + ); + assert_eq!( + trusted_feed_holochain_topic_positive[1].mew.text, + String::from("#holochain from bob, weight 0.5") + ); + assert_eq!( + trusted_feed_holochain_topic_positive[2].mew.text, + String::from("#holochain from dave, weight 0.25") + ); + + // Show all mews on blockchain topic above 0 threshold + let trusted_feed_blockchain_topic_positive = ann + .trusted_feed_weighted(TrustedFeedInput { + agent: ann.pubkey.clone(), + topic: "blockchain".to_string(), + weight: "0.000001".to_string(), + }) + .await; + + println!( + "trusted feed: {:#?}", + trusted_feed_blockchain_topic_positive.clone() + ); + + assert!(trusted_feed_blockchain_topic_positive.len() == 2); + assert_eq!( + trusted_feed_blockchain_topic_positive[0].mew.text, + String::from("#blockchain from dave, weight 0.55") + ); + assert_eq!( + trusted_feed_blockchain_topic_positive[1].mew.text, + String::from("#blockchain from bob, weight 0.1") + ); +} + +// +// ^^^ TESTS: ^^^ +// +// vvv TEST HELPERS: vvv +// + +pub struct Agent<'a> { + pub cell: SweetCell, + pub conductor: &'a SweetConductor, + pub pubkey: AgentPubKey, + pub mews_zome: SweetZome, + pub follows_zome: SweetZome, + pub trust_atom_zome: SweetZome, +} + +impl Agent<'_> { + // pub async fn follow(&self, input: FollowInput) { + // self.conductor + // .call(&self.follows_zome, "follow", input) + // .await + // } + + // pub async fn create_mew(&self, input: Mew) -> ActionHash { + // self.conductor + // .call(&self.mews_zome, "create_mew", input) + // .await + // } + + // // pub async fn recommended(&self, input: RecommendedInput) -> Vec { + // // self.conductor.call(&self.zome, "recommended", input).await + // // } + + // pub async fn trusted_feed_weighted(&self, input: TrustedFeedInput) -> Vec { + // self.conductor + // .call( + // &self.mews_zome, + // "get_batch_mews_with_context_based_on_topic_and_weight_threshold", + // input, + // ) + // .await + // } +} + +pub struct AgentGroup { + conductors: SweetConductorBatch, +} + +impl AgentGroup { + #[allow(clippy::needless_lifetimes)] + pub async fn create_agents<'a>(&'a mut self) -> Vec> { + let dna_path = std::env::current_dir().unwrap().join(DNA_FILEPATH); + let dna = SweetDnaFile::from_bundle(&dna_path).await.unwrap(); + + let apps = self.conductors.setup_app("mewsfeed", &[dna]).await.unwrap(); + self.conductors.exchange_peer_info().await; + + let ((ann_cell,), (bob_cell,), (cat_cell,), (dave_cell,)) = apps.into_tuples(); + + let ann = Agent { + cell: ann_cell.clone(), + conductor: self.conductors.get(0).unwrap(), + pubkey: ann_cell.agent_pubkey().clone(), + mews_zome: ann_cell.zome(MEWS_ZOME_NAME), + follows_zome: ann_cell.zome(FOLLOWS_ZOME_NAME), + trust_atom_zome: ann_cell.zome(TRUST_ATOM_ZOME_NAME), + }; + let bob = Agent { + cell: bob_cell.clone(), + conductor: self.conductors.get(1).unwrap(), + pubkey: bob_cell.agent_pubkey().clone(), + mews_zome: bob_cell.zome(MEWS_ZOME_NAME), + follows_zome: bob_cell.zome(FOLLOWS_ZOME_NAME), + trust_atom_zome: bob_cell.zome(TRUST_ATOM_ZOME_NAME), + }; + let cat = Agent { + cell: cat_cell.clone(), + conductor: self.conductors.get(2).unwrap(), + pubkey: cat_cell.agent_pubkey().clone(), + mews_zome: cat_cell.zome(MEWS_ZOME_NAME), + follows_zome: cat_cell.zome(FOLLOWS_ZOME_NAME), + trust_atom_zome: cat_cell.zome(TRUST_ATOM_ZOME_NAME), + }; + let dave = Agent { + cell: dave_cell.clone(), + conductor: self.conductors.get(3).unwrap(), + pubkey: dave_cell.agent_pubkey().clone(), + mews_zome: dave_cell.zome(MEWS_ZOME_NAME), + follows_zome: dave_cell.zome(FOLLOWS_ZOME_NAME), + trust_atom_zome: dave_cell.zome(TRUST_ATOM_ZOME_NAME), + }; + // let emma = Agent { + // cell: emma_cell.clone(), + // conductor: self.conductors.get(4).unwrap(), + // pubkey: emma_cell.agent_pubkey().clone(), + // mews_zome: emma_cell.zome(MEWS_ZOME_NAME), + // follows_zome: emma_cell.zome(FOLLOWS_ZOME_NAME), + // }; + // let frank = Agent { + // cell: frank_cell.clone(), + // conductor: self.conductors.get(5).unwrap(), + // pubkey: frank_cell.agent_pubkey().clone(), + // mews_zome: frank_cell.zome(MEWS_ZOME_NAME), + // follows_zome: frank_cell.zome(FOLLOWS_ZOME_NAME), + // }; + + vec![ann, bob, cat, dave] + } +} + +pub async fn setup() -> AgentGroup { + let conductors = SweetConductorBatch::from_config(4, ConductorConfig::default()).await; + AgentGroup { conductors } +} diff --git a/dnas/mewsfeed/zomes/integrity/mews/src/all_mews.rs b/dnas/mewsfeed/zomes/integrity/mews/src/all_mews.rs index b70fc7c0..08cee493 100644 --- a/dnas/mewsfeed/zomes/integrity/mews/src/all_mews.rs +++ b/dnas/mewsfeed/zomes/integrity/mews/src/all_mews.rs @@ -1,6 +1,12 @@ use hdi::prelude::*; use hdk::prelude::Path; +#[derive(Serialize, Deserialize, SerializedBytes, Debug, Clone)] +pub struct RecommendedInput { + pub now: hdi::prelude::Timestamp, + pub oldest_mew_seconds: Option, +} + pub fn validate_create_link_all_mews( _action: CreateLink, base_address: AnyLinkableHash, diff --git a/dnas/mewsfeed/zomes/integrity/mews/src/lib.rs b/dnas/mewsfeed/zomes/integrity/mews/src/lib.rs index 4b4cd884..4e64a34a 100644 --- a/dnas/mewsfeed/zomes/integrity/mews/src/lib.rs +++ b/dnas/mewsfeed/zomes/integrity/mews/src/lib.rs @@ -45,6 +45,7 @@ pub enum LinkTypes { MewToResponses, MentionToMews, HashtagToMews, + HashtagByAuthorToMews, CashtagToMews, } @@ -190,7 +191,7 @@ pub fn validate(op: Op) -> ExternResult { LinkTypes::MentionToMews => { validate_create_link_mention_to_mews(action, base_address, target_address, tag) } - LinkTypes::HashtagToMews => { + LinkTypes::HashtagToMews | LinkTypes::HashtagByAuthorToMews => { validate_create_link_hashtag_to_mews(action, base_address, target_address, tag) } LinkTypes::CashtagToMews => { @@ -257,7 +258,7 @@ pub fn validate(op: Op) -> ExternResult { target_address, tag, ), - LinkTypes::HashtagToMews => validate_delete_link_hashtag_to_mews( + LinkTypes::HashtagToMews | LinkTypes::HashtagByAuthorToMews => validate_delete_link_hashtag_to_mews( action, original_action, base_address, @@ -411,7 +412,7 @@ pub fn validate(op: Op) -> ExternResult { LinkTypes::MentionToMews => { validate_create_link_mention_to_mews(action, base_address, target_address, tag) } - LinkTypes::HashtagToMews => { + LinkTypes::HashtagToMews | LinkTypes::HashtagByAuthorToMews => { validate_create_link_hashtag_to_mews(action, base_address, target_address, tag) } LinkTypes::CashtagToMews => { @@ -496,7 +497,7 @@ pub fn validate(op: Op) -> ExternResult { create_link.target_address, create_link.tag, ), - LinkTypes::HashtagToMews => validate_delete_link_hashtag_to_mews( + LinkTypes::HashtagToMews | LinkTypes::HashtagByAuthorToMews => validate_delete_link_hashtag_to_mews( action, create_link.clone(), base_address, diff --git a/dnas/mewsfeed/zomes/integrity/trust_atom_integrity/Cargo.toml b/dnas/mewsfeed/zomes/integrity/trust_atom_integrity/Cargo.toml new file mode 100644 index 00000000..3ecca16b --- /dev/null +++ b/dnas/mewsfeed/zomes/integrity/trust_atom_integrity/Cargo.toml @@ -0,0 +1,15 @@ +[package] +edition = "2021" +name = "trust_atom_integrity" +version = "0.0.1" + +[lib] +crate-type = ["cdylib", "rlib"] +name = "trust_atom_integrity" + +[dependencies] +trust_atom_integrity = { git = "https://github.com/trustgraph/trustgraph-holochain.git", package = "trust_atom_integrity", branch = "chore/hdk-0.3.2-using-builder" } + +hdk = { workspace = true } +serde = { workspace = true } +rust_decimal = "1" \ No newline at end of file diff --git a/dnas/mewsfeed/zomes/integrity/trust_atom_integrity/src/lib.rs b/dnas/mewsfeed/zomes/integrity/trust_atom_integrity/src/lib.rs new file mode 100644 index 00000000..c5d8cc20 --- /dev/null +++ b/dnas/mewsfeed/zomes/integrity/trust_atom_integrity/src/lib.rs @@ -0,0 +1 @@ +pub extern crate trust_atom_integrity;