Skip to content

Commit c232886

Browse files
committed
validate inputs
1 parent f7fdec1 commit c232886

File tree

6 files changed

+181
-1
lines changed

6 files changed

+181
-1
lines changed

.github/workflows/test.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ jobs:
2727
toolchain: ${{ matrix.toolchain }}
2828

2929
- name: Run Noir tests
30-
run: nargo test
30+
run: |
31+
nargo test
32+
cd tests && nargo test
3133
3234
format:
3335
runs-on: ubuntu-latest

src/lib.nr

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ mod solidity;
44
mod string;
55
mod tables;
66
mod array;
7+
mod validate_inputs;
78

89
pub use array::pack_bytes;
910
pub use hash::{keccak256, pedersen, poseidon2, sha256};
1011
pub use math::{clamp, div_ceil, sqrt::sqrt};
1112
pub use string::{field_to_hex, ord, str_to_u64, to_hex_string_bytes};
13+
pub use validate_inputs::{validate_inputs, ValidateInput};
1214

1315
pub trait ArrayExtensions<T, let N: u32> {
1416
fn slice<let L: u32>(self, start: u32) -> [T; L];

src/validate_inputs.nr

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
pub comptime fn validate_inputs(f: FunctionDefinition) {
2+
let validated_inputs = f
3+
.parameters()
4+
.map(|(name, _typ): (Quoted, Type)| quote {{ nodash::ValidateInput::validate($name); }})
5+
.join(quote {;});
6+
let checks_body = quote {{ $validated_inputs }}.as_expr().expect(
7+
f"failed to parse ValidateInput checks code",
8+
); // should never fail
9+
10+
let old_body = f.body();
11+
let checked_body = quote {{
12+
$checks_body;
13+
$old_body
14+
}};
15+
f.set_body(checked_body.as_expr().expect(f"failed to concatenate body with checks"));
16+
}
17+
18+
pub trait ValidateInput {
19+
fn validate(self);
20+
}
21+
22+
impl ValidateInput for u8 {
23+
fn validate(self) {}
24+
}
25+
26+
impl ValidateInput for u16 {
27+
fn validate(self) {}
28+
}
29+
30+
impl ValidateInput for u32 {
31+
fn validate(self) {}
32+
}
33+
34+
impl ValidateInput for u64 {
35+
fn validate(self) {}
36+
}
37+
38+
impl ValidateInput for i8 {
39+
fn validate(self) {}
40+
}
41+
impl ValidateInput for i16 {
42+
fn validate(self) {}
43+
}
44+
impl ValidateInput for i32 {
45+
fn validate(self) {}
46+
}
47+
48+
impl ValidateInput for i64 {
49+
fn validate(self) {}
50+
}
51+
52+
impl ValidateInput for Field {
53+
fn validate(self) {}
54+
}
55+
56+
impl ValidateInput for bool {
57+
fn validate(self) {}
58+
}
59+
60+
impl ValidateInput for U128 {
61+
fn validate(self) {}
62+
}
63+
64+
impl<let N: u32> ValidateInput for str<N> {
65+
fn validate(self) {}
66+
}
67+
68+
impl<T, let N: u32> ValidateInput for [T; N]
69+
where
70+
T: ValidateInput,
71+
{
72+
fn validate(mut self) {
73+
for i in 0..N {
74+
self[i].validate();
75+
}
76+
}
77+
}
78+
79+
impl<T, let MaxLen: u32> ValidateInput for BoundedVec<T, MaxLen>
80+
where
81+
T: ValidateInput,
82+
{
83+
fn validate(mut self) {
84+
for i in 0..MaxLen {
85+
if i < self.len() {
86+
self.get_unchecked(i).validate()
87+
}
88+
}
89+
}
90+
}

tests/Nargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
[package]
2+
name = "tests"
3+
type = "lib"
4+
5+
[dependencies]
6+
nodash = { path = "../" }

tests/src/lib.nr

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
mod validate_inputs;

tests/src/validate_inputs.nr

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
#[nodash::validate_inputs]
2+
fn my_main(a: Field, b: u64, _s: GoodStruct) -> Field {
3+
a + b as Field
4+
}
5+
6+
#[test]
7+
fn test_validate_inputs() {
8+
let result = my_main(1, 2, GoodStruct {});
9+
assert(result == 3);
10+
}
11+
12+
#[nodash::validate_inputs]
13+
fn main_collections(a: [U120; 1], b: BoundedVec<U120, 2>) -> Field {
14+
a[0].inner + b.get(0).inner
15+
}
16+
17+
#[test]
18+
fn test_validate_collections() {
19+
let result = main_collections(
20+
[U120::new(1)],
21+
BoundedVec::from_parts([U120::new(2), U120 { inner: 2.pow_32(120) }], 1),
22+
);
23+
assert(result == 3);
24+
}
25+
26+
#[test(should_fail_with = "call to assert_max_bit_size")]
27+
fn test_validate_array_fail() {
28+
let _ = main_collections([U120 { inner: 2.pow_32(120) }], BoundedVec::new());
29+
}
30+
31+
#[test(should_fail_with = "call to assert_max_bit_size")]
32+
fn test_validate_bounded_vec_fail() {
33+
let _ = main_collections(
34+
[U120::new(1)],
35+
BoundedVec::from_parts([U120::new(2), U120 { inner: 2.pow_32(120) }], 2),
36+
);
37+
}
38+
39+
#[nodash::validate_inputs]
40+
fn main_u120(a: U120) -> Field {
41+
a.inner
42+
}
43+
44+
#[test]
45+
fn test_validate_u120() {
46+
let inner = 2.pow_32(120) - 1;
47+
let result = main_u120(U120 { inner });
48+
assert(result == inner);
49+
}
50+
51+
#[test(should_fail_with = "call to assert_max_bit_size")]
52+
fn test_validate_u120_fail() {
53+
let inner = 2.pow_32(120);
54+
let result = main_u120(U120 { inner });
55+
assert(result == inner);
56+
}
57+
58+
struct GoodStruct {}
59+
60+
impl nodash::ValidateInput for GoodStruct {
61+
fn validate(self) {}
62+
}
63+
64+
struct U120 {
65+
inner: Field,
66+
}
67+
68+
impl U120 {
69+
fn new(inner: Field) -> Self {
70+
inner.assert_max_bit_size::<120>();
71+
Self { inner }
72+
}
73+
}
74+
75+
impl nodash::ValidateInput for U120 {
76+
fn validate(self) {
77+
let _ = U120::new(self.inner);
78+
}
79+
}

0 commit comments

Comments
 (0)