diff --git a/agent1.keystore b/agent1.keystore
new file mode 100644
index 0000000..c3506df
--- /dev/null
+++ b/agent1.keystore
@@ -0,0 +1 @@
+{"passphrase_check":"eyJzYWx0IjpbNzUsODIsMTk5LDE0NCwxODQsMzcsMTUxLDQ3LDEyNiwyMzAsMjQ0LDE3OCwxMTQsMTI4LDE3NSwxMjhdLCJub25jZSI6Wzg2LDQ2LDIwNywyNTIsODEsMTM0LDIyLDE1MywyMjEsNzUsMjAyLDExOSwyNywxNjAsMjQzLDMxLDkyLDczLDIyOCwxMjgsMzYsMTkxLDIxNCwxNl0sImNpcGhlciI6WzEwOCw4NCwxNiw0OCwxNjcsMTUyLDcxLDE4NiwxNDUsMTk3LDEyNywyMDksMTIwLDI0OCwxNTEsMTkxLDYwLDExNywzNCwyMDcsOTUsMTcwLDE4MCwxMCw2OSwxNDYsNTksMTg1LDUsMjEyLDIxLDI0LDI0MywxMTUsMjE1LDE3MiwxNTMsMTUwLDE4Nyw5OSwxMDksMjIzLDExNCwxNzAsMTA5LDE0NywxODgsMTg2LDIxNCwyMTIsMjM0LDIxOCwyMCwyMDcsNjgsMTY1XX0=","secrets":{"primary_keybundle:enc_key":{"blob_type":"EncryptingKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbMjQ5LDIyOCw1MCwyNDcsNDQsMTI4LDkyLDEyNiwxNSw1MSwyMjIsMTA0LDIyNCwzNCwyMzQsMTNdLCJub25jZSI6WzMwLDIwMywxMzYsMTMsNjAsMTEsMTAsNTMsMTI2LDUyLDE5OCwxOTcsNDYsNDksMSwyMzUsMTksMTcsMTUyLDEzLDEzMiwzNCw5MSwxOTRdLCJjaXBoZXIiOlsyMzYsMTMsMTMzLDE2NSw4OCw1LDc3LDU3LDE5NSw5NywxMjUsNCw0OSwxNTgsMzIsMiwxNDAsMjI1LDIyNywxNTQsMTA0LDE3NSwyMCwxNTcsNjEsMzAsMSw3MCwyLDE5MiwxMTIsMzcsMTI2LDE5MCw5NSwzNiwxMjksNTIsMzEsMjQ5LDE2NywyMzYsNDUsNTQsOTYsMzIsMjIwLDIwLDE5OSwyMiwyMDAsMTc0LDI0MCwyMjAsMjI3LDYyLDIxMiwyNTMsNjUsMTkwLDE1MSwxODUsNTQsMTQwLDc3LDc0LDE4MCwyMyw2NywxMjEsMTQ2LDc1LDEzOSwxNTQsMTgyLDE4NSwyNTQsODcsMTQzLDI0OSwxMzMsNSwxODIsODYsMTUsMTc3LDc5LDE3MF19"},"primary_keybundle:sign_key":{"blob_type":"SigningKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbNTEsODMsMTU3LDI0OSw2MSwxMzYsMjUwLDQ5LDE0MiwxNCw2MywxNzgsMjI1LDIxMSwxNTAsNjJdLCJub25jZSI6WzE4MCwxMzQsMjQ0LDIxOCwxMzQsMSwyNTEsMTkyLDIyOSwzNywxNzksMjQzLDEwMiwyMTcsMTQsMTY4LDEyOCwxNTcsMTIsMjEyLDgyLDEzNSwxNjUsMjI2XSwiY2lwaGVyIjpbMTMyLDE4Nyw0NiwyMDgsODksMTI5LDIzNywxNDQsNTEsMTg5LDE3OCw3LDkyLDYxLDE2NSwzNSwxNTEsMjI1LDExMiwxNzcsMjM0LDE4OSwxNjQsMjAxLDEyMywxMTYsMTEsMjEsMTQ0LDIwNSwyMzksMTc0LDIxNSwxNDIsMTE3LDg2LDIxNCwyNyw5NiwyMDAsMTQyLDE5MywyOSwyMzIsMjEzLDY3LDYwLDg3LDEzNiwyNTQsNDAsNjcsMTYwLDIzLDkyLDIyOCw3MywxOTksMTUzLDE4OCwyNDgsMjI5LDkwLDExLDUyLDkyLDE2NCwyMjMsMTkxLDEyNSwxMTMsNzgsMjE5LDYxLDc1LDIyNCwxNTgsNTgsMzgsMzMsMjE5LDI0LDE2NSwxNzIsMTkxLDUsMTk0LDcsMjEzLDMxLDI1NSwxMTEsMTY0LDMzLDMxLDIxMywzMywxNDAsOSwxMTAsMTA4LDYxLDIxOSwxNDIsMTgwLDEwOCwyNTIsNjIsMTU0LDE3NywxMDIsNCwyMDYsMTc2LDIyNCwxMywxMTYsMTU3LDk3LDk5XX0="},"root_seed":{"blob_type":"Seed","seed_type":"OneShot","hint":"","data":"eyJzYWx0IjpbMTE2LDI3LDE1MSw4MywxMzQsNzIsMTYyLDE2NSwxNTQsMjA0LDE1NCwxNTksMTkyLDEzNCwxMzYsMjQyXSwibm9uY2UiOlsyMzIsNDAsMjQyLDIwNCwyMjksMTk5LDMwLDIxMiwxNTUsOTQsMTY1LDYsMTczLDIzNSw5Niw3MCwxODEsNjQsMzYsNiwxMTQsMTI3LDE3NSwxMzhdLCJjaXBoZXIiOlsxNTIsMTI3LDM0LDE4OSw1NCwxNjQsNTAsMjA4LDYzLDkxLDY4LDIzNSwzNiwxODcsNzMsMzksMTQ4LDE2LDEyOSwxMzIsMTkxLDExNiwyMTIsMjExLDE0NywxNzEsMjM2LDE2MCw3NCwxNjQsMTksMjQwLDI1NSwzOSwxOTUsNDcsMjIsMTQwLDc5LDIzNCwyMTUsMTMxLDIxNywyMDQsMTg2LDU1LDI0OSwxNjldfQ=="}}}
\ No newline at end of file
diff --git a/agent2.keystore b/agent2.keystore
new file mode 100644
index 0000000..23ee2e0
--- /dev/null
+++ b/agent2.keystore
@@ -0,0 +1 @@
+{"passphrase_check":"eyJzYWx0IjpbMjA2LDE2Nyw3OSw1OSw1NiwxNzgsMTQ2LDM0LDI0MywyMjYsMTAzLDI3LDQyLDE1OSwxNjMsMjI3XSwibm9uY2UiOlsxNzAsNzAsMTY1LDE3Niw5MiwxNjEsMTE0LDM2LDEzMSw0Miw3NSwzNSwxNTgsMjUxLDIzNiwxNTYsMjExLDE1OCwxNDQsMTAxLDYsMjM4LDE1MywxMjVdLCJjaXBoZXIiOlsyNTEsMTA4LDczLDEyNCwxODUsNzgsMjA0LDkwLDExMyw3NywyMDksMTc4LDEzLDQ0LDE1OSw0OSwxLDE5NiwxMTAsNTQsMTU1LDExOSw5Nyw1MSwxMDcsMTUwLDE2LDI4LDc4LDE3MiwyMTEsMTM5LDk1LDg3LDEzNywxNDUsNjgsNTUsNjEsNzcsMTcsMTUyLDE1NywyNiwxNTcsMjMzLDI0OCwyMjEsODksMTIxLDkyLDkwLDEzNCwyNiwyMzksNzFdfQ==","secrets":{"primary_keybundle:enc_key":{"blob_type":"EncryptingKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbOTgsMTE1LDIzNSwxMzksMTM5LDIyNywxOTYsMTY4LDIxNSwxOTIsMTg5LDI1MywxMDksMjAsNDgsMTQwXSwibm9uY2UiOlsyMzEsMjMsMzAsMTg4LDI1MCwxMCwyMjgsMTMxLDcsMTgsMjQ0LDEwLDgxLDEyNCw4OCwxMzQsMTM1LDE3OCw0OSwyLDE0MywzNSwyNDAsMTUzXSwiY2lwaGVyIjpbMzYsMjQ1LDI0MCwxNzUsMjA4LDgwLDY4LDE3MiwxNzgsMjEsOTksNjcsMjAwLDI0NCw5NywyMTQsMTA1LDMxLDcsMTIxLDE2NSwyMTksMjQ5LDE5OSwxNTYsNDksNDksMTkzLDE1LDk4LDE4LDIwNiwyMDMsMzEsMTE1LDEyMSwxMzEsMTcxLDM2LDIzMiw0NCwxMjcsMTExLDIwMiwyMDIsMjUzLDIyNiwyMywyMzAsMTc0LDQxLDE0LDE3NSw2NywyNCwyMzAsMjIwLDY0LDIxNiw1MSwxNjMsOTksMjI2LDM2LDI0NywzMSwxODMsMTkxLDIzNywzOCwyMDEsMjIyLDE0MywyMDYsMTQ5LDEwMCwxNjQsMTQ3LDExMiwyMzYsMTI5LDIzLDI1NSwzLDEzMCw5MiwyMzEsOTNdfQ=="},"primary_keybundle:sign_key":{"blob_type":"SigningKey","seed_type":"Mock","hint":"","data":"eyJzYWx0IjpbMTAwLDcsMzgsOTQsMjUsMjI3LDE3Myw3MCwxMzMsOTMsMjE3LDksMjUwLDE4NSwxNjEsMjM2XSwibm9uY2UiOlsxNDcsMTU0LDE4OCwxNSwyNDMsMTAxLDQwLDExMCwxOSwxMDQsMjUzLDMsMjE1LDEyOSw0NiwxMDQsMTcsMTQ4LDIxNiwzOSw3LDYwLDIwNiwyMDRdLCJjaXBoZXIiOlsyNDYsNDMsMTgsOTcsMTI4LDMwLDE3OCw4MCwxMzUsMTQ4LDIzNCwxMiwzMSwxNDIsMTc2LDU5LDk5LDI0NSwyMDUsMTQwLDE1NCwyMTksMjA5LDQ5LDE0OCwxMiwxOTUsOTEsMTEyLDIwOCwxMDAsMTc1LDIyMiw2NywxNzAsMzEsMjA0LDUwLDIxMSw0NSwxNyw4NSw0NSw1NSwyNiwzMCwxMzAsNDQsNCw1NiwxMyw5MywzOSwyMzksMTM5LDIyNywyNTAsNywyNTQsNzMsMTQ5LDE1NiwyMDEsMTA3LDc5LDEyNCwyMzIsMCwxNzMsMjEwLDM0LDEsMTIzLDE0MywyNTIsMzgsMTEzLDEyOSwyMTAsMTU2LDIzNSw2Nyw2LDc0LDQ5LDI1MywxNSwyMzMsMTA2LDE0MiwyMjEsNjksMTU3LDI0Nyw5NiwxNzEsMTk5LDE4LDE3LDI0OCwxMzEsMjMyLDkwLDg5LDY4LDEwMiwxODMsMzksMjEwLDI1MCw0NywyMDcsMjMyLDE4NSwzNiwxOTgsMjMsMyw0LDEwOV19"},"root_seed":{"blob_type":"Seed","seed_type":"OneShot","hint":"","data":"eyJzYWx0IjpbOTAsMywxNTgsMTg4LDE2OCwyMTUsMTEwLDQ5LDEyMiwyMjAsNzYsMjE1LDE4OCwxMDcsMTAyLDc5XSwibm9uY2UiOls1NywxMSwxOTEsMTIsOTYsMTg2LDYxLDI0OSwxMDEsMTM0LDEyNywyNDAsMjQ1LDI1NSwxMDIsMTk1LDE1OSwyNTIsNjEsMzAsNTksMTA5LDE1OCwxMTddLCJjaXBoZXIiOlsyNDAsMjQ2LDE0LDE2NywxMTMsMjA5LDY0LDYsMjI5LDYzLDE4Niw5NiwxOTcsNDIsMTMzLDE1MCw0NSw0MSw0OCwyMjcsMjU1LDE3MywyNDIsNzgsOTgsMTc2LDE1OCwyMCwxMjEsOTYsMTc0LDE5NywyMjUsNTQsMTA4LDI0LDcsNDAsMSwxNiwxMDUsOCwyMywxMDcsMjQsMjI4LDExNSwxODddfQ=="}}}
\ No newline at end of file
diff --git a/conductor-config.toml b/conductor-config.toml
new file mode 100644
index 0000000..1b57345
--- /dev/null
+++ b/conductor-config.toml
@@ -0,0 +1,51 @@
+[[dnas]]
+id = "marketplace-dna"
+file = "./dist/holo-barcelona-hackathon.dna.json"
+hash = "QmDontCheck"
+
+
+
+
+[[agents]]
+id = "test_agent1"
+name = "HoloTester1"
+public_address = "HcScjcgKqXC5pmfvka9DmtEJwVr548yd86UPtJGGoue9ynuikuRTN7oE5zcjgbi"
+keystore_file = "./agent1.keystore"
+
+[[agents]]
+id = "test_agent2"
+name = "HoloTester2"
+public_address = "HcScidPSdAT43q9qirJwt5rHJYjjsvougV3jgSBwdJujszw3bBu5Mktr74Rgnea"
+keystore_file = "./agent2.keystore"
+
+
+
+
+[[instances]]
+id = "instance1"
+dna = "marketplace-dna"
+agent = "test_agent1"
+[instances.storage]
+type = "memory"
+path = "tmp-storage"
+
+[[instances]]
+id = "instance2"
+dna = "marketplace-dna"
+agent = "test_agent2"
+[instances.storage]
+type = "memory"
+path = "tmp-storage"
+
+
+
+
+[[interfaces]]
+id = "http-interface1"
+[interfaces.driver]
+type = "http"
+port = 3000
+[[interfaces.instances]]
+id = "instance1"
+[[interfaces.instances]]
+id = "instance2"
diff --git a/zomes/main/code/src/lib.rs b/zomes/main/code/src/lib.rs
index 403b479..f470d41 100644
--- a/zomes/main/code/src/lib.rs
+++ b/zomes/main/code/src/lib.rs
@@ -1,3 +1,4 @@
+#![feature(vec_remove_item)]
#![feature(try_from, proc_macro_hygiene)]
#[macro_use]
extern crate hdk;
@@ -9,13 +10,20 @@ extern crate serde_json;
#[macro_use]
extern crate holochain_json_derive;
+mod marketplace;
+use marketplace::{TradeState, ActionType};
+
+mod matchmaking;
+mod trade;
+mod trade_action;
+
+use matchmaking::{TradeProposal};
+use trade::{Trade};
+
+
use hdk::{
- AGENT_ADDRESS,
entry_definition::ValidatingEntryType,
error::ZomeApiResult,
- holochain_json_api::{
- error::JsonError, json::JsonString,
- },
holochain_persistence_api::{
cas::content::{AddressableContent, Address},
},
@@ -27,28 +35,12 @@ use hdk::{
},
};
-
use hdk_proc_macros::zome;
// see https://developer.holochain.org/api/0.0.18-alpha1/hdk/ for info on using the hdk library
// This is a sample zome that defines an entry type "MyEntry" that can be committed to the
// agent's chain via the exposed function create_my_entry
-
-#[derive(Serialize, Deserialize, Debug, DefaultJson,Clone)]
-pub struct TradeProposal {
- seller: Address,
- name_of_item: String,
- description: String,
-}
-
-#[derive(Serialize, Deserialize, Debug, DefaultJson,Clone)]
-pub struct Trade {
- seller: Address,
- buyer: Address,
- created_at: u32,
-}
-
#[zome]
mod main {
@@ -87,64 +79,17 @@ mod main {
#[zome_fn("hc_public")]
fn create_trade_proposal(name_of_item: String, description: String) -> ZomeApiResult
{
- let trade_proposal_data = TradeProposal {
- seller: AGENT_ADDRESS.to_string().into(),
- name_of_item,
- description,
- };
-
- let entry = Entry::App("trade_proposal".into(), trade_proposal_data.into());
- let trade_proposal_address = hdk::commit_entry(&entry)?;
-
- let anchor_entry = Entry::App(
- "anchor".into(),
- // NOTE: check naming
- // NOTE: into?
- "trade_proposals".into(),
- );
- let anchor_address = hdk::commit_entry(&anchor_entry)?;
-
- hdk::link_entries(
- &anchor_address,
- &trade_proposal_address,
- "has_trade_proposal",
- "",
- )?;
-
- Ok(trade_proposal_address)
+ matchmaking::handle_create_trade_proposal(name_of_item, description)
}
#[zome_fn("hc_public")]
fn accept_trade_proposal(trade_proposal_address: Address, created_at: u32) -> ZomeApiResult {
- let trade_proposal: TradeProposal = hdk::utils::get_as_type(trade_proposal_address.clone())?;
-
- let trade_data = Trade {
- seller: trade_proposal.seller,
- buyer: AGENT_ADDRESS.to_string().into(),
- created_at,
- };
-
- let trade_entry = Entry::App(
- "trade".into(),
- trade_data.into()
- );
-
- let trade_address = hdk::commit_entry(&trade_entry)?;
-
- hdk::link_entries(
- &trade_proposal_address,
- &trade_address,
- "from_trade_proposal",
- ""
- )?;
-
- Ok(trade_address)
+ matchmaking::handle_accept_trade_proposal(trade_proposal_address, created_at)
}
#[zome_fn("hc_public")]
- // NOTE: WTF?
pub fn check_responses(trade_proposal_address: Address) -> ZomeApiResult> {
- hdk::utils::get_links_and_load_type(&trade_proposal_address, LinkMatch::Exactly("from_trade_proposal".into()), LinkMatch::Any)
+ matchmaking::handle_check_responses(trade_proposal_address)
}
#[zome_fn("hc_public")]
diff --git a/zomes/main/code/src/marketplace/actions.rs b/zomes/main/code/src/marketplace/actions.rs
new file mode 100644
index 0000000..1f21741
--- /dev/null
+++ b/zomes/main/code/src/marketplace/actions.rs
@@ -0,0 +1,17 @@
+use hdk::holochain_json_api::{
+ error::JsonError, json::JsonString,
+};
+
+#[derive(Clone, Debug, Serialize, Deserialize, DefaultJson, PartialEq)]
+pub enum ActionType {
+ Buy
+}
+
+impl ActionType {
+ pub fn describe() -> Vec {
+ vec![
+ ActionType::Buy,
+ ]
+ }
+}
+
diff --git a/zomes/main/code/src/marketplace/mod.rs b/zomes/main/code/src/marketplace/mod.rs
new file mode 100644
index 0000000..83e5b2d
--- /dev/null
+++ b/zomes/main/code/src/marketplace/mod.rs
@@ -0,0 +1,12 @@
+pub mod state;
+pub mod validation;
+pub mod actions;
+
+pub use self::{
+ state::{
+ TradeState,
+ },
+ actions::{
+ ActionType,
+ },
+};
diff --git a/zomes/main/code/src/marketplace/state.rs b/zomes/main/code/src/marketplace/state.rs
new file mode 100644
index 0000000..3d0f384
--- /dev/null
+++ b/zomes/main/code/src/marketplace/state.rs
@@ -0,0 +1,49 @@
+use hdk::holochain_json_api::{
+ error::JsonError, json::JsonString,
+};
+
+use crate::trade_action::Action;
+use crate::trade::Trade;
+use super::{
+ // Actions::Piece,
+ ActionType,
+ validation::{get_current_role},
+};
+
+#[derive(Clone, Debug, Serialize, Deserialize, DefaultJson)]
+pub struct TradeState {
+ pub actions: Vec,
+ // pub buyer: Address,
+ // pub seller: Address,
+ pub sold: bool,
+}
+
+impl TradeState {
+ pub fn initial() -> Self {
+ TradeState {
+ actions: Vec::new(),
+ sold: false,
+ }
+ }
+
+ pub fn render(&self) -> String {
+ "".to_string()
+ }
+
+ pub fn evolve(&self, trade: Trade, next_action: &Action) -> Self {
+ let _current_role = get_current_role(&trade, &next_action.author).unwrap();
+ let mut actions = self.clone().actions.clone();
+ actions.push(next_action.to_owned());
+
+ match &next_action.action_type {
+ ActionType::Buy => {
+ TradeState {
+ actions,
+ sold: true,
+ ..self.clone()
+ }
+ }
+ }
+
+ }
+}
diff --git a/zomes/main/code/src/marketplace/validation.rs b/zomes/main/code/src/marketplace/validation.rs
new file mode 100644
index 0000000..9db2604
--- /dev/null
+++ b/zomes/main/code/src/marketplace/validation.rs
@@ -0,0 +1,54 @@
+use hdk::holochain_persistence_api::{
+ cas::content::Address,
+};
+
+use crate::Trade;
+use crate::trade_action::Action;
+use super::{
+ TradeState,
+ ActionType,
+};
+
+pub enum Role {
+ Buyer,
+ Seller,
+}
+
+pub fn get_current_role(trade: &Trade, user_address: &Address) -> Result {
+ // FIXME
+ match (user_address == &trade.buyer, user_address == &trade.seller) {
+ (true, true) => return Err("Buyer cannot be seller".into()),
+ (true, false) => Ok(Role::Buyer),
+ (false, true) => Ok(Role::Seller),
+ (false, false) => return Err("User is not participant of the trade!".into()),
+ }
+}
+
+impl Action {
+ pub fn is_valid(&self, trade: Trade, trade_state: TradeState) -> Result<(), String> {
+ hdk::debug(format!("{:?}", trade_state)).unwrap();
+ let _current_role = get_current_role(&trade, &self.author)?;
+
+ // move type specific validation
+ match &self.action_type {
+ ActionType::Buy => {
+ // TODO: Check is not bought
+ is_sold(&trade_state)?;
+ // from.is_in_bounds()?;
+ // to.is_in_bounds()?;
+ // from.is_piece_beloning_to_player(¤t_role, &trade_state)?;
+ // to.is_empty(&trade_state)?;
+ // from.can_move_to(to, ¤t_role, &trade_state)?;
+ hdk::debug("Validation Success!").unwrap();
+ Ok(())
+ }
+ }
+ }
+}
+
+fn is_sold(trade_state: &TradeState) -> Result {
+ match trade_state.sold {
+ false => Ok(true),
+ true => Err("Item is sold".to_string()),
+ }
+}
diff --git a/zomes/main/code/src/matchmaking.rs b/zomes/main/code/src/matchmaking.rs
new file mode 100644
index 0000000..f1d3fbc
--- /dev/null
+++ b/zomes/main/code/src/matchmaking.rs
@@ -0,0 +1,94 @@
+use hdk::{
+ AGENT_ADDRESS,
+ error::ZomeApiResult,
+ holochain_persistence_api::{
+ cas::content::{Address},
+ },
+ holochain_json_api::{
+ error::JsonError, json::{JsonString, default_to_json},
+ },
+ holochain_core_types::{
+ entry::Entry,
+ link::LinkMatch,
+ }
+};
+
+use serde::Serialize;
+use std::fmt::Debug;
+
+use crate::trade::Trade;
+
+#[derive(Serialize, Deserialize, Debug, DefaultJson,Clone)]
+pub struct TradeProposal {
+ pub seller: Address,
+ pub name_of_item: String,
+ pub description: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone)]
+pub struct GetResponse {
+ pub entry: T,
+ pub address: Address
+}
+
+impl + Debug + Serialize> From> for JsonString {
+ fn from(u: GetResponse) -> JsonString {
+ default_to_json(u)
+ }
+}
+
+pub fn handle_create_trade_proposal(name_of_item: String, description: String) -> ZomeApiResult {
+ let trade_proposal_data = TradeProposal {
+ seller: AGENT_ADDRESS.to_string().into(),
+ name_of_item,
+ description,
+ };
+
+ let entry = Entry::App("trade_proposal".into(), trade_proposal_data.into());
+ let trade_proposal_address = hdk::commit_entry(&entry)?;
+
+ let anchor_entry = Entry::App(
+ "anchor".into(),
+ "trade_proposals".into(),
+ );
+ let anchor_address = hdk::commit_entry(&anchor_entry)?;
+
+ hdk::link_entries(
+ &anchor_address,
+ &trade_proposal_address,
+ "has_trade_proposal",
+ "",
+ )?;
+
+ Ok(trade_proposal_address)
+}
+
+pub fn handle_accept_trade_proposal(trade_proposal_address: Address, created_at: u32) -> ZomeApiResult {
+ let trade_proposal: TradeProposal = hdk::utils::get_as_type(trade_proposal_address.clone())?;
+
+ let trade_data = Trade {
+ seller: trade_proposal.seller,
+ buyer: AGENT_ADDRESS.to_string().into(),
+ created_at,
+ };
+
+ let trade_entry = Entry::App(
+ "trade".into(),
+ trade_data.into()
+ );
+
+ let trade_address = hdk::commit_entry(&trade_entry)?;
+
+ hdk::link_entries(
+ &trade_proposal_address,
+ &trade_address,
+ "from_trade_proposal",
+ ""
+ )?;
+
+ Ok(trade_address)
+}
+
+pub fn handle_check_responses(trade_proposal_address: Address) -> ZomeApiResult> {
+ hdk::utils::get_links_and_load_type(&trade_proposal_address, LinkMatch::Exactly("from_trade_proposal".into()), LinkMatch::Any)
+}
diff --git a/zomes/main/code/src/trade.rs b/zomes/main/code/src/trade.rs
new file mode 100644
index 0000000..36704bd
--- /dev/null
+++ b/zomes/main/code/src/trade.rs
@@ -0,0 +1,108 @@
+use std::convert::TryFrom;
+use hdk::{
+ error::{ZomeApiResult, ZomeApiError},
+ holochain_persistence_api::{
+ cas::content::{AddressableContent, Address},
+ },
+ holochain_json_api::{
+ error::JsonError, json::JsonString,
+ },
+ holochain_core_types::{
+ entry::Entry,
+ link::LinkMatch,
+ }
+};
+
+use crate::trade_action::Action;
+use crate::TradeState;
+
+#[derive(Serialize, Deserialize, Debug, DefaultJson,Clone)]
+pub struct Trade {
+ pub seller: Address,
+ pub buyer: Address,
+ pub created_at: u32,
+}
+
+
+/*=====================================
+= DHT Functions =
+=====================================*/
+
+/// Traverse the linked list rooted at a trade to find all the actions
+pub fn get_actions(trade_address: &Address) -> ZomeApiResult> {
+ match hdk::get_links(trade_address, LinkMatch::Any, LinkMatch::Any)?.addresses().into_iter().next() {
+ Some(first_action) => {
+ let mut action_addresses = vec![first_action];
+ let mut more = true;
+ while more {
+ more = match hdk::get_links(action_addresses.last().unwrap(), LinkMatch::Any, LinkMatch::Any)?.addresses().into_iter().next() {
+ Some(address) => {
+ action_addresses.push(address.clone());
+ true
+ },
+ None => {
+ false
+ },
+ }
+ }
+ let actions: Vec = action_addresses.iter().map(|address| {
+ let action_entry = hdk::get_entry(address).unwrap().unwrap();
+ if let Entry::App(_, action_struct) = action_entry {
+ Action::try_from(action_struct).expect("Entry at address is type other than Action")
+ } else {
+ panic!("Not an app entry!")
+ }
+ }).collect();
+ Ok(actions)
+ },
+ None => {
+ Ok(Vec::new())
+ }
+ }
+}
+
+pub fn get_state_local_chain(local_chain: Vec, trade_address: &Address) -> ZomeApiResult {
+ let actions = get_actions_local_chain(local_chain.clone(), trade_address)?;
+ let trade = get_trade_local_chain(local_chain, trade_address)?;
+ let new_state = actions.iter().fold(TradeState::initial(), move |state, new_action| state.evolve(trade.clone(), new_action));
+ Ok(new_state)
+}
+
+pub fn get_actions_local_chain(local_chain: Vec, trade_address: &Address) -> ZomeApiResult> {
+ Ok(local_chain
+ .iter()
+ .filter_map(|entry| {
+ if let Entry::App(entry_type, entry_data) = entry {
+ if entry_type.to_string() == "action" {
+ Some(Action::try_from(entry_data.clone()).unwrap())
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })
+ .filter(|trade_action| {
+ trade_action.trade == trade_address.to_owned()
+ })
+ .rev()
+ .collect())
+}
+
+
+pub fn get_trade_local_chain(local_chain: Vec, trade_address: &Address) -> ZomeApiResult {
+ local_chain
+ .iter()
+ .filter(|entry| {
+ entry.address() == trade_address.to_owned()
+ })
+ .filter_map(|entry| {
+ if let Entry::App(_, entry_data) = entry {
+ Some(Trade::try_from(entry_data.clone()).unwrap())
+ } else {
+ None
+ }
+ })
+ .next()
+ .ok_or(ZomeApiError::HashNotFound)
+}
diff --git a/zomes/main/code/src/trade_action.rs b/zomes/main/code/src/trade_action.rs
new file mode 100644
index 0000000..780ecfa
--- /dev/null
+++ b/zomes/main/code/src/trade_action.rs
@@ -0,0 +1,87 @@
+use hdk::{
+ entry_definition::ValidatingEntryType,
+ holochain_persistence_api::{
+ cas::content::{Address},
+ },
+ holochain_json_api::{
+ error::JsonError, json::JsonString,
+ },
+ holochain_core_types::{
+ dna::entry_types::Sharing,
+ validation::EntryValidationData,
+ entry::Entry,
+ }
+};
+
+use crate::trade::{get_state_local_chain, get_trade_local_chain};
+
+use crate::ActionType;
+
+#[derive(Clone, Debug, Serialize, Deserialize, DefaultJson, PartialEq)]
+pub struct Action {
+ pub trade: Address,
+ pub author: Address,
+ pub action_type: ActionType,
+ pub previous_action: Address,
+ pub timestamp: u32,
+}
+
+pub fn definition() -> ValidatingEntryType {
+ entry!(
+ name: "action",
+ description: "An action by an agent in a trade",
+ sharing: Sharing::Public,
+ validation_package: || {
+ hdk::ValidationPackageDefinition::ChainFull
+ },
+
+ validation: | validation_data: hdk::EntryValidationData| {
+ match validation_data {
+ EntryValidationData::Create{entry, validation_data} => {
+ let mut local_chain = validation_data.package.source_chain_entries
+ .ok_or("Could not retrieve source chain")?;
+ hdk::debug(format!("{:?}", local_chain))?;
+
+ let new_action = Action::from(entry);
+
+ // Sometimes the validating entry is already in the chain when validation runs,
+ // To make our state reduction work correctly this must be removed
+ local_chain.remove_item(&Entry::App("action".into() , new_action.clone().into()));
+
+ let state = get_state_local_chain(local_chain.clone(), &new_action.trade)
+ .map_err(|_| "Could not load state during validation")?;
+ let trade = get_trade_local_chain(local_chain, &new_action.trade)
+ .map_err(|_| "Could not load trade during validation")?;
+
+ new_action.is_valid(trade, state)
+ },
+ _ => {
+ Err("Cannot modify or delete a action".into())
+ }
+ }
+ },
+
+ links: [
+ from!(
+ "trade",
+ link_type: "",
+ validation_package: || {
+ hdk::ValidationPackageDefinition::Entry
+ },
+ validation: | _validation_data: hdk::LinkValidationData| {
+ Ok(())
+ }
+ ),
+ from!(
+ "action",
+ link_type: "",
+ validation_package: || {
+ hdk::ValidationPackageDefinition::Entry
+ },
+ validation: | _validation_data: hdk::LinkValidationData| {
+ Ok(())
+ }
+ )
+ ]
+ )
+}