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

Feature/validation #84

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

36 changes: 2 additions & 34 deletions zomes/trust_atom/src/trust_atom.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
#![allow(clippy::module_name_repetitions)]

use hdk::prelude::*;
use rust_decimal::prelude::*;
use trust_atom_integrity::validation::normalize_value;

use std::collections::BTreeMap;
use trust_atom_integrity::entries::{EntryTypes, Extra};
use trust_atom_integrity::headers::{
Expand Down Expand Up @@ -91,39 +92,6 @@ pub fn calc_extra_hash(input: Extra) -> ExternResult<EntryHash> {
Ok(hash)
}

fn normalize_value(value_str: Option<String>) -> ExternResult<Option<String>> {
match value_str {
Some(value_str) => match Decimal::from_str(value_str.as_str()) {
Ok(value_decimal) => {
match value_decimal.round_sf_with_strategy(9, RoundingStrategy::MidpointAwayFromZero) {
Some(value_decimal) => {
if value_decimal == Decimal::ONE {
Ok(Some(".999999999".to_string()))
} else if value_decimal == Decimal::NEGATIVE_ONE {
Ok(Some("-.999999999".to_string()))
} else if value_decimal > Decimal::NEGATIVE_ONE && value_decimal < Decimal::ONE {
let value_zero_stripped = value_decimal.to_string().replace("0.", ".");
Ok(Some(value_zero_stripped))
} else {
Err(wasm_error!(
"Value must be in the range -1..1, but got: `{}`",
value_str
))
}
}
None => Err(wasm_error!("Value could not be processed: `{}`", value_str)),
}
}
Err(error) => Err(wasm_error!(
"Value could not be processed: `{}`. Error: `{}`",
value_str,
error
)),
},
None => Ok(None),
}
}

fn create_link_tag(link_direction: &LinkDirection, chunk_options: &[Option<String>]) -> LinkTag {
let mut chunks: Vec<String> = vec![];

Expand Down
1 change: 1 addition & 0 deletions zomes/trust_atom_integrity/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
hdi = "0.3.8"
hdk = "0.2.8"
rust_decimal = "1"
serde = "1"
trust_atom_types = { path = "../trust_atom_types" }
193 changes: 193 additions & 0 deletions zomes/trust_atom_integrity/src/validation.rs
Original file line number Diff line number Diff line change
@@ -1 +1,194 @@
use crate::LinkTypes;
use hdi::prelude::*;
use hdk::prelude::agent_info;
use rust_decimal::prelude::*;
use std::str;

pub const UNICODE_NUL_STR: &str = "\u{0}"; // Unicode NUL character
pub const LINK_TAG_HEADER: [u8; 2] = [197, 166]; // Unicode "Ŧ" // hex bytes: [0xC5][0xA6]
pub const LINK_TAG_ARROW_FORWARD: [u8; 3] = [226, 134, 146]; // Unicode "→" // hex bytes: [0xE2][0x86][0x92]
pub const LINK_TAG_ARROW_REVERSE: [u8; 3] = [226, 134, 169]; // Unicode "↩" // hex bytes: [0xE2][0x86][0xA9]

#[hdk_extern]
pub fn validate(op: Op) -> ExternResult<ValidateCallbackResult> {
match op.flattened::<(), LinkTypes>()? {
FlatOp::RegisterCreateLink {
base_address,
target_address,
tag,
link_type,
action,
} => {
if base_address != agent_info()?.agent_initial_pubkey.into() {
return Ok(ValidateCallbackResult::Invalid(
"Creator must be same as base_address".to_string(),
));
}
if base_address != AnyLinkableHash::from(action.author) {
return Ok(ValidateCallbackResult::Invalid(
"Creator must be same as base_address".to_string(),
));
}

let link_tag = match String::from_utf8(tag.clone().into_inner()) {
Ok(link_tag) => link_tag,
Err(_) => {
return Err(wasm_error!(
"Link tag is not valid UTF-8 -- found: {:?}",
String::from_utf8_lossy(&tag.into_inner())
))
}
};

let chunks: Vec<&str> = link_tag.split(UNICODE_NUL_STR).collect();

if chunks.len() != 4 {
return Ok(ValidateCallbackResult::Invalid(format!(
"Invalid link tag! Expected 4 NUL bytes, got {} NUL bytes",
chunks.len().to_string(),
)));
}

// debug!("{:?}", chunks);
if chunks[0].as_bytes() != build_forward_header()
&& chunks[0].as_bytes() != build_reverse_header()
{
return Err(wasm_error!(
"LinkTag format error - header bytes incorrect, must be either {:?}, or {:?}, but got {}",
convert_bytes_to_string(&LINK_TAG_ARROW_FORWARD),
convert_bytes_to_string(&LINK_TAG_ARROW_REVERSE),
chunks[0]
));
}

if chunks[2].chars().count() > 12 {
return Err(wasm_error!(
"LinkTag format error - rating must be <= 12 chars"
));
}

let mut bucket_iter = chunks[3].chars();
if bucket_iter.clone().count() != 9 {
return Err(wasm_error!(
"LinkTag format error - must have 9 char bucket"
));
}
if !bucket_iter.all(|c| c.is_ascii_digit()) {
return Err(wasm_error!(
"LinkTag format error - all char must be digit 0-9"
));
} else {
return Ok(ValidateCallbackResult::Valid);
}
}

// match link_tag.matches(UNICODE_NUL_STR).count() {
// 4 => { let chunks: Vec<&str> = link_tag.split(UNICODE_NUL_STR).collect();
// // debug!("{:?}", chunks);
// if chunks[0].as_bytes() != build_forward_header() && chunks[0].as_bytes() != build_reverse_header() {
// return Err(wasm_error!(WasmErrorInner::Guest("LinkTag format error - prefix symbol incorrect".to_string())))
// }
// if chunks[1].as_bytes().len() > 900 {
// if chunks[4] == "" {
// return Err(wasm_error!(WasmErrorInner::Guest("LinkTag format error - content > 900 bytes must have extra entry hash".to_string())))
// }
// }
// if chunks[2].chars().count() > 12 {
// return Err(wasm_error!(WasmErrorInner::Guest("LinkTag format error - rating must be <= 12 chars".to_string())))
// }
// if chunks[3].chars().count() != 9 {
// return Err(wasm_error!(WasmErrorInner::Guest("LinkTag format error - must have 9 char bucket".to_string())))
// }
// else {
// return Ok(ValidateCallbackResult::Valid)
// }
// },
// _ => return Ok(ValidateCallbackResult::Invalid(String::from(
// "Invalid link tag! Expected 4 null bytes.",
// ))),
// };
FlatOp::RegisterDeleteLink {
link_type,
base_address,
target_address,
tag,
original_action,
action,
} => {
if action.author != original_action.author {
return Ok(ValidateCallbackResult::Invalid(
"Only the original author can delete Trust Atom".to_string(),
));
}
if base_address != AnyLinkableHash::from(agent_info()?.agent_initial_pubkey) {
return Ok(ValidateCallbackResult::Invalid(
"Base address of Trust Atom must be original author".to_string(),
));
}

Ok(ValidateCallbackResult::Valid)
}
_ => Ok(ValidateCallbackResult::Valid), // TODO: Exhuast remaining match options
}
}

fn build_forward_header() -> Vec<u8> {
let mut forward_bytes = vec![];
forward_bytes.extend_from_slice(&LINK_TAG_HEADER);
forward_bytes.extend_from_slice(&LINK_TAG_ARROW_FORWARD);

forward_bytes
}

fn build_reverse_header() -> Vec<u8> {
let mut reverse_bytes = vec![];
reverse_bytes.extend_from_slice(&LINK_TAG_HEADER);
reverse_bytes.extend_from_slice(&LINK_TAG_ARROW_REVERSE);

reverse_bytes
}

fn convert_bytes_to_string(byte_array: &[u8]) -> ExternResult<String> {
let conversion = String::from_utf8(byte_array.to_vec());

match conversion {
Ok(string_slice) => Ok(string_slice),
Err(e) => Err(wasm_error!(
"Failed to convert byte slice to string slice: {:?}",
e
)),
}
}

pub fn normalize_value(value_str: Option<String>) -> ExternResult<Option<String>> {
match value_str {
Some(value_str) => match Decimal::from_str(value_str.as_str()) {
Ok(value_decimal) => {
match value_decimal.round_sf_with_strategy(9, RoundingStrategy::MidpointAwayFromZero) {
Some(value_decimal) => {
if value_decimal == Decimal::ONE {
Ok(Some(".999999999".to_string()))
} else if value_decimal == Decimal::NEGATIVE_ONE {
Ok(Some("-.999999999".to_string()))
} else if value_decimal > Decimal::NEGATIVE_ONE && value_decimal < Decimal::ONE {
let value_zero_stripped = value_decimal.to_string().replace("0.", ".");
Ok(Some(value_zero_stripped))
} else {
Err(wasm_error!(
"Value must be in the range -1..1, but got: `{}`",
value_str
))
}
}
None => Err(wasm_error!("Value could not be processed: `{}`", value_str)),
}
}
Err(error) => Err(wasm_error!(
"Value could not be processed: `{}`. Error: `{}`",
value_str,
error
)),
},
None => Ok(None),
}
}
Loading