Skip to content

Commit

Permalink
validate inputs
Browse files Browse the repository at this point in the history
  • Loading branch information
olehmisar committed Jan 27, 2025
1 parent f7fdec1 commit c232886
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 1 deletion.
4 changes: 3 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ jobs:
toolchain: ${{ matrix.toolchain }}

- name: Run Noir tests
run: nargo test
run: |
nargo test
cd tests && nargo test
format:
runs-on: ubuntu-latest
Expand Down
2 changes: 2 additions & 0 deletions src/lib.nr
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ mod solidity;
mod string;
mod tables;
mod array;
mod validate_inputs;

pub use array::pack_bytes;
pub use hash::{keccak256, pedersen, poseidon2, sha256};
pub use math::{clamp, div_ceil, sqrt::sqrt};
pub use string::{field_to_hex, ord, str_to_u64, to_hex_string_bytes};
pub use validate_inputs::{validate_inputs, ValidateInput};

pub trait ArrayExtensions<T, let N: u32> {
fn slice<let L: u32>(self, start: u32) -> [T; L];
Expand Down
90 changes: 90 additions & 0 deletions src/validate_inputs.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
pub comptime fn validate_inputs(f: FunctionDefinition) {
let validated_inputs = f
.parameters()
.map(|(name, _typ): (Quoted, Type)| quote {{ nodash::ValidateInput::validate($name); }})
.join(quote {;});
let checks_body = quote {{ $validated_inputs }}.as_expr().expect(
f"failed to parse ValidateInput checks code",
); // should never fail

let old_body = f.body();
let checked_body = quote {{
$checks_body;
$old_body
}};
f.set_body(checked_body.as_expr().expect(f"failed to concatenate body with checks"));
}

pub trait ValidateInput {
fn validate(self);
}

impl ValidateInput for u8 {
fn validate(self) {}
}

impl ValidateInput for u16 {
fn validate(self) {}
}

impl ValidateInput for u32 {
fn validate(self) {}
}

impl ValidateInput for u64 {
fn validate(self) {}
}

impl ValidateInput for i8 {
fn validate(self) {}
}
impl ValidateInput for i16 {
fn validate(self) {}
}
impl ValidateInput for i32 {
fn validate(self) {}
}

impl ValidateInput for i64 {
fn validate(self) {}
}

impl ValidateInput for Field {
fn validate(self) {}
}

impl ValidateInput for bool {
fn validate(self) {}
}

impl ValidateInput for U128 {
fn validate(self) {}
}

impl<let N: u32> ValidateInput for str<N> {
fn validate(self) {}
}

impl<T, let N: u32> ValidateInput for [T; N]
where
T: ValidateInput,
{
fn validate(mut self) {
for i in 0..N {
self[i].validate();
}
}
}

impl<T, let MaxLen: u32> ValidateInput for BoundedVec<T, MaxLen>
where
T: ValidateInput,
{
fn validate(mut self) {
for i in 0..MaxLen {
if i < self.len() {
self.get_unchecked(i).validate()
}
}
}
}
6 changes: 6 additions & 0 deletions tests/Nargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[package]
name = "tests"
type = "lib"

[dependencies]
nodash = { path = "../" }
1 change: 1 addition & 0 deletions tests/src/lib.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
mod validate_inputs;
79 changes: 79 additions & 0 deletions tests/src/validate_inputs.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#[nodash::validate_inputs]
fn my_main(a: Field, b: u64, _s: GoodStruct) -> Field {
a + b as Field
}

#[test]
fn test_validate_inputs() {
let result = my_main(1, 2, GoodStruct {});
assert(result == 3);
}

#[nodash::validate_inputs]
fn main_collections(a: [U120; 1], b: BoundedVec<U120, 2>) -> Field {
a[0].inner + b.get(0).inner
}

#[test]
fn test_validate_collections() {
let result = main_collections(
[U120::new(1)],
BoundedVec::from_parts([U120::new(2), U120 { inner: 2.pow_32(120) }], 1),
);
assert(result == 3);
}

#[test(should_fail_with = "call to assert_max_bit_size")]
fn test_validate_array_fail() {
let _ = main_collections([U120 { inner: 2.pow_32(120) }], BoundedVec::new());
}

#[test(should_fail_with = "call to assert_max_bit_size")]
fn test_validate_bounded_vec_fail() {
let _ = main_collections(
[U120::new(1)],
BoundedVec::from_parts([U120::new(2), U120 { inner: 2.pow_32(120) }], 2),
);
}

#[nodash::validate_inputs]
fn main_u120(a: U120) -> Field {
a.inner
}

#[test]
fn test_validate_u120() {
let inner = 2.pow_32(120) - 1;
let result = main_u120(U120 { inner });
assert(result == inner);
}

#[test(should_fail_with = "call to assert_max_bit_size")]
fn test_validate_u120_fail() {
let inner = 2.pow_32(120);
let result = main_u120(U120 { inner });
assert(result == inner);
}

struct GoodStruct {}

impl nodash::ValidateInput for GoodStruct {
fn validate(self) {}
}

struct U120 {
inner: Field,
}

impl U120 {
fn new(inner: Field) -> Self {
inner.assert_max_bit_size::<120>();
Self { inner }
}
}

impl nodash::ValidateInput for U120 {
fn validate(self) {
let _ = U120::new(self.inner);
}
}

0 comments on commit c232886

Please sign in to comment.