Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: build validation page #486

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/pages/build/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,5 @@ This Build Guide organizes everything you need to know about developing Holochai

* [Overview](/build/working-with-data/) --- general concepts related to working with data in Holochain
* [Entries](/build/entries/) --- creating, reading, updating, and deleting
* [Validation](/build/validation/) --- validating changes to DHT state
:::
166 changes: 166 additions & 0 deletions src/pages/build/validation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
---
title: "Validation"
---

::: intro
**Validation** is how your app defined what kinds of Entries and Links can be published, and how they can be altered. The **validate** callback is a function defined in every integrity zome which defines allowed changes to the Entry Types and Link Types declared in that zome.
:::


### Validate Callback
The validate callback runs on **Ops**, not **Actions** (TODO: link to more info about ops vs actions). The hdi provides a helper to transform an `Op` into a data structure more suitable for use in validation: a `FlatOp`


The validate callback can return:
- `ValidateCallbackResult::Valid`
- `ValidateCallbackResult::Invalid`
- `ValidateCallbackResult::MissingDependencies`
- TODO: The validation callback will be scheduled to run again. If subsequent runs never complete, then the Op will never be intergrated and is effectively ignored.

Check warning on line 18 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (intergrated)

Check warning on line 18 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (intergrated)
- an Error
- TODO: The validation callback will be scheduled to run again. If subsequent runs never complete, then the Op will never be intergrated and is effectively ignored.

Check warning on line 20 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (intergrated)

Check warning on line 20 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (intergrated)

Because the validate callback itself can become quite large and unwiedly, we recommend using it only for matching by Action, Entry Type and Link Type, and then keeping your actual validation logic in a standalone function. This is how the scaffolder tool sets up your validate callback.

Check warning on line 22 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (unwiedly)

Check warning on line 22 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (unwiedly)

```rust
#[hdk_entry_type]
struct Post {
title: String,
body: String,
tags: Vec<String>
}

#[hdk_entry_types]
enum EntryTypes {
Post(Post)
}

/// PLEASE LET THE SCAFFOLDER GENERATE THIS FUNCTION FOR YOU
/// IT IS TOO LARGE AND UNWIEDLY TO INCLUDE HERE IN FULL

Check warning on line 38 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (UNWIEDLY)

Check warning on line 38 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (UNWIEDLY)
pub fn validate(op: Op) -> ExternResult<ValidateCallbackResult> {
match op.to_flat_op() {
...
}
}

pub fn validate_create_post(action: Action, post: Post) -> ExternResult<ValidateCallbackResult> {
// Post body must be at least 5 characters
if post.body.trim().len() < 5 {
return Ok(ValidateCallbackResult::Invalid("Post body must be at least 5 characters"));
}

// Post title cannot be blank
if post.title.trim().len() == 0 {
return Ok(ValidateCallbackResult::Invalid("Post title cannot be blank"));
}

// Post can have a maximum of 3 tags
if post.tags.len() > 3 {
return Ok(ValidateCallbackResult::Invalid("Post cannot have more than 3 tags"));
}

Ok(ValidateCallbackResult::Valid)
}

pub fn validate_update_post(action: Action, post: Post, original_post: Post, original_action: Action) -> ExternResult<ValidateCallbackResult> {
// Only the original author can update their Post
if original_action.author != action.author {
return Ok(ValidateCallbackResult::Invalid("Only the original author can update a Post"));
}

// The Post title cannot be updated
if original_post.title != post.title {
return Ok(ValidateCallbackResult::Invalid("Post title cannot be updated"));
}

Ok(ValidateCallbackResult::Valid)
}

pub fn validate_delete_post(action: Action, post: Post, original_post: Post, original_action: Action) -> ExternResult<ValidateCallbackResult> {
// Only the original author can delete their Post
if original_action.author != action.author {
return Ok(ValidateCallbackResult::Invalid("Only the original author can delete a Post"));
}

Ok(ValidateCallbackResult::Valid)
}
```

### Determanism

Check warning on line 88 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Determanism)

Check warning on line 88 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (Determanism)

The validate callback function must be **determanistic**. That is, no matter when or by whom the validation is executed, it always arrives at the same outcome: either Valid or Invalid.

Check warning on line 90 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (determanistic)

Check warning on line 90 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (determanistic)

For this reason you cannot:
- Use current machine timestamps in validation
- Use your own agent info in validation


### Using DHT Data

To allow validation to depend fetching some other data from the DHT, the `hdi` provides a `must_get_*` functions:

- `must_get_valid_record`
- The only one that checks for validation receipts/warrants because an entry is only valid/invalid in the context of an action
- It trusts the claim of the agent it fetched from (1-of-n validation)
- If you want to run the validation yourself, then you must run validate on the received record.

- `must_get_entry`
- `must_get_action`


Note that you **cannot** depend on links from the DHT. There is no `must_get_links` function in the hdi because that would not be determanistic, as different agents may have a different perspective on all the links available. There is also no `must_get_link` function in the hdi currently, as links are only hashed by their base hash.

Check warning on line 110 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (determanistic)

Check warning on line 110 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (determanistic)


### What Can Be Validated?

- membrane proof
- entry structure (entry_def macro gives you deserialisation and error short-circuiting for free with ? operator)

Check warning on line 116 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (deserialisation)

Check warning on line 116 in src/pages/build/validation.md

View workflow job for this annotation

GitHub Actions / spellcheck

Unknown word (deserialisation)
- permission
- rate limiting with weight field
- dependencies, incl source chain history
- Inductive validation for costly dep trees (pattern?)

### What to Validate and When?

TODO: Considerations re: what should be validated on each op (IOW, what authorities should be responsible for what things)


### Limitations

- Cannot must_get links or actions on a base
- Cannot must_get a single link
- Cannot currently co-validate multiple actions (can only validate an action based on prior valid actions)
- Cannot validate the non-existence of something, because that can always change
- (future) source chain restructured to atomic bundle of actions, co-validated
- (future) link have another basis of their base,target,tag and can be must_get by that hash


### Validate Under-the-hood
TODO: Lifecycle of a validation
At author time
At publish time
At gossip time


Membrane proof
Genesis self-check -- not a 'true' validation function, just a way to guard yourself against copy/paste mistakes and other things that can permanently hose your chance of joining a network
(future) Handled specially -- restricts/grants access to a network; validated at handshake time (turns out this is not currently implemented, and there are questions about how to implement it in a way that doesn't carry a performance hit with each new peer connection -- and there may be lots of them in a big heavily sharded DHT)
AgentValidationPkg is the only action for which an honest person can get warranted, because they try to join the network and publish it before they're able to fetch deps

### Sys Validation

Some validation is always run by the Holochain Conductor, without requiring any changes to your app.

TODO: It includes:


## Reference

* [hdi::prelude::must_get_entry](https://docs.rs/hdi/latest/hdi/entry/fn.must_get_entry.html)
* [hdi::prelude::must_get_action](https://docs.rs/hdi/latest/hdi/entry/fn.must_get_action.html)
* [hdi::prelude::must_get_valid_record](https://docs.rs/hdi/latest/hdi/entry/fn.must_get_valid_record.html)
* [hdi::prelude::ValidateCallbackResult](https://docs.rs/hdi/latest/hdi/prelude/enum.ValidateCallbackResult.html)
* [hdi::prelude::FlatOp](https://docs.rs/hdi/latest/hdi/flat_op/enum.FlatOp.html)

## Further reading

* [Core Concepts: Validation](/concepts/7_validation/)
Loading