-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
6cd58e4
commit 7e3fc86
Showing
9 changed files
with
337 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
dnas/trusted/zomes/coordinator/trusted/src/key_collection.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
use hdk::prelude::*; | ||
use trusted_integrity::prelude::*; | ||
|
||
#[hdk_extern] | ||
pub fn create_key_collection(key_collection: KeyCollection) -> ExternResult<Record> { | ||
check_key_collection_create(&key_collection)?; | ||
|
||
let entry = EntryTypes::KeyCollection(key_collection); | ||
let action_hash = create_entry(entry)?; | ||
|
||
let record = get(action_hash.clone(), GetOptions::content())?.ok_or(wasm_error!( | ||
WasmErrorInner::Guest(String::from( | ||
"Could not find the newly created KeyCollection" | ||
)) | ||
))?; | ||
|
||
let entry_hash = hash_entry( | ||
record | ||
.entry() | ||
.as_option() | ||
.ok_or_else(|| wasm_error!(WasmErrorInner::Guest(String::from("Missing entry hash"))))? | ||
.clone(), | ||
)?; | ||
let my_agent_info = agent_info()?; | ||
create_link( | ||
my_agent_info.agent_latest_pubkey, | ||
entry_hash, | ||
LinkTypes::KeyCollection, | ||
(), | ||
)?; | ||
|
||
Ok(record) | ||
} | ||
|
||
fn check_key_collection_create(key_collection: &KeyCollection) -> ExternResult<()> { | ||
let existing_key_collections = query( | ||
ChainQueryFilter::default() | ||
.entry_type(EntryType::App(UnitEntryTypes::KeyCollection.try_into()?)), | ||
)?; | ||
|
||
// This is enforced by validation, but checked here for faster feedback | ||
if existing_key_collections.len() >= KEY_COLLECTION_LIMIT { | ||
return Err(wasm_error!(WasmErrorInner::Guest(String::from( | ||
"Exceeded the maximum number of key collections", | ||
)))); | ||
} | ||
|
||
let names: HashSet<_> = existing_key_collections.into_iter().filter_map(|kc| match kc.entry().as_option().and_then(|e| e.as_app_entry()) { | ||
Some(entry_bytes) => { | ||
let key_collection: KeyCollection = entry_bytes.clone().into_sb().try_into().ok()?; | ||
Some(key_collection.name) | ||
} | ||
None => None | ||
}).collect(); | ||
|
||
// Not checked by validation, other users do not care about your key collection names being unique. The entries are private | ||
// so they can't actually see them! | ||
if names.contains(&key_collection.name) { | ||
return Err(wasm_error!(WasmErrorInner::Guest(String::from( | ||
"Key collection with the same name already exists", | ||
)))); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
#[hdk_extern] | ||
pub fn my_key_collections(_: ()) -> ExternResult<Vec<Record>> { | ||
let my_agent_info = agent_info()?; | ||
let key_collection_links = get_links(GetLinksInputBuilder::try_new(my_agent_info.agent_latest_pubkey, LinkTypes::KeyCollection)?.build())?; | ||
|
||
let mut records = Vec::with_capacity(key_collection_links.len()); | ||
for link in key_collection_links { | ||
let entry_hash: EntryHash = link.target.clone().try_into().map_err(|_| { | ||
wasm_error!(WasmErrorInner::Guest(String::from("Could not convert link target to EntryHash"))) | ||
})?; | ||
let record = get(entry_hash, GetOptions::content())?.ok_or(wasm_error!( | ||
WasmErrorInner::Guest(String::from("Could not find the KeyCollection")) | ||
))?; | ||
// No need to type check these records, validation ensures they are all KeyCollections | ||
records.push(record); | ||
} | ||
|
||
Ok(records) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
mod gpg_key_dist; | ||
mod key_collection; | ||
mod gpg_util; | ||
|
||
use hdk::prelude::*; | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
dnas/trusted/zomes/integrity/trusted/src/key_collection.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use hdi::prelude::*; | ||
|
||
use crate::LinkTypes; | ||
use crate::UnitEntryTypes; | ||
|
||
pub const KEY_COLLECTION_LIMIT: usize = 10; | ||
pub const KEY_COLLECTION_NAME_MIN_LENGTH: usize = 3; | ||
|
||
#[hdk_entry_helper] | ||
pub struct KeyCollection { | ||
pub name: String, | ||
} | ||
|
||
pub fn validate_create_key_collection( | ||
create_action: EntryCreationAction, | ||
key_collection: KeyCollection, | ||
) -> ExternResult<ValidateCallbackResult> { | ||
if key_collection.name.len() < KEY_COLLECTION_NAME_MIN_LENGTH { | ||
return Ok(ValidateCallbackResult::Invalid( | ||
format!( | ||
"Key collection name must be at least {} characters long", | ||
KEY_COLLECTION_NAME_MIN_LENGTH | ||
) | ||
.to_string(), | ||
)); | ||
} | ||
|
||
let entry_def: AppEntryDef = UnitEntryTypes::KeyCollection.try_into()?; | ||
|
||
let action: Action = create_action.clone().into(); | ||
let action_hash = hash_action(action.clone())?; | ||
let activity = must_get_agent_activity(action.author().clone(), ChainFilter::new(action_hash))?; | ||
|
||
// Find all key collection creates | ||
let mut key_collection_creates: HashSet<_> = activity | ||
.iter() | ||
.filter_map(|activity| match activity.action.action() { | ||
Action::Create(Create { | ||
entry_type: EntryType::App(app_entry), | ||
entry_hash, | ||
.. | ||
}) if app_entry == &entry_def => Some(entry_hash), | ||
_ => None, | ||
}) | ||
.collect(); | ||
|
||
// Run through every delete and grab the entry hash that it deletes, then remove those entries from the key collection set | ||
activity | ||
.iter() | ||
.filter_map(|activity| match activity.action.action() { | ||
Action::Delete(Delete { | ||
deletes_entry_address, | ||
.. | ||
}) => Some(deletes_entry_address), | ||
_ => None, | ||
}) | ||
.for_each(|entry_address| { | ||
key_collection_creates.remove(&entry_address); | ||
}); | ||
|
||
// Now check the remaining number of key collections is under the limit | ||
// Note that being at the limit is allowed because the newly created key collection is already in the agent activity. | ||
if key_collection_creates.len() > KEY_COLLECTION_LIMIT { | ||
return Ok(ValidateCallbackResult::Invalid( | ||
"Exceeded the maximum number of key collections".to_string(), | ||
)); | ||
} | ||
|
||
Ok(ValidateCallbackResult::Valid) | ||
} | ||
|
||
pub fn validate_key_collection_link( | ||
action: CreateLink, | ||
base_address: AnyLinkableHash, | ||
target_address: AnyLinkableHash, | ||
link_type: LinkTypes, | ||
) -> ExternResult<ValidateCallbackResult> { | ||
let action_author_anylinkable: AnyLinkableHash = action.author.clone().into(); | ||
|
||
if action_author_anylinkable != base_address { | ||
return Ok(ValidateCallbackResult::Invalid( | ||
"Key collection must be linked from the author".to_string(), | ||
)); | ||
} | ||
|
||
let entry_hash = match target_address.clone().try_into() { | ||
Ok(entry_hash) => entry_hash, | ||
Err(_) => { | ||
return Ok(ValidateCallbackResult::Invalid(format!( | ||
"The target address for {:?} must be an entry hash", | ||
link_type | ||
))); | ||
} | ||
}; | ||
let entry = must_get_entry(entry_hash)?; | ||
match entry.as_app_entry() { | ||
Some(app_entry) => { | ||
match <SerializedBytes as TryInto<crate::key_collection::KeyCollection>>::try_into(app_entry.clone().into_sb()) { | ||
Ok(_) => Ok(ValidateCallbackResult::Valid), | ||
Err(_) => Ok(ValidateCallbackResult::Invalid(format!( | ||
"The target for {:?} must be a {}", | ||
link_type, | ||
std::any::type_name::<crate::key_collection::KeyCollection>() | ||
))), | ||
} | ||
} | ||
None => Ok(ValidateCallbackResult::Invalid(format!( | ||
"The target for {:?} must be an app entry", | ||
link_type | ||
))), | ||
} | ||
} |
Oops, something went wrong.