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

feat(abi): Tuples as inputs/outputs to main #2899

Merged
merged 14 commits into from
Sep 29, 2023
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "tuple_inputs"
type = "bin"
authors = [""]
compiler_version = "0.14.1"

[dependencies]
12 changes: 12 additions & 0 deletions tooling/nargo_cli/tests/execution_success/tuple_inputs/Prover.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
pair = [1, 0]
x = [[0, 1, 2], [3, 4, 5]]

[[struct_pair]]
a = "1"
b = ["2", "3", "20"]

[struct_pair.bar]
inner = ["100", "101", "102"]

[[struct_pair]]
inner = ["103", "104", "105"]
37 changes: 37 additions & 0 deletions tooling/nargo_cli/tests/execution_success/tuple_inputs/src/main.nr
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
struct Bar {
inner: [Field; 3],
}

struct Foo {
a: Field,
b: [Field; 3],
bar: Bar,
}

fn main(pair : (Field, Field), x: [(u8, u8, u8); 2], struct_pair: (Foo, Bar)) -> pub (Field, Field) {
assert(pair.0 == 1);
assert(pair.1 == 0);

let mut start_val = 0;
for i in 0..2 {
assert(x[i].0 == start_val);
assert(x[i].1 == start_val + 1);
assert(x[i].2 == start_val + 2);
start_val += 3;
}

assert(struct_pair.0.a == 1);
assert(struct_pair.0.b == [2, 3, 20]);
assert(struct_pair.0.bar.inner == [100, 101, 102]);
assert(struct_pair.1.inner == [103, 104, 105]);

let (u, v) = if pair.0 as u32 < 1 {
(pair.0, pair.0 + 1)
} else {
(pair.0 + 1, pair.0)
};
assert(u == pair.0 + 1);
assert(v == pair.0);

(u, v)
}
15 changes: 15 additions & 0 deletions tooling/noirc_abi/src/input_parser/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ impl JsonTypes {
JsonTypes::Table(map_with_json_types)
}

(InputValue::Vec(vector), AbiType::Tuple { fields }) => {
let fields = try_vecmap(vector.iter().zip(fields.iter()), |(value, typ)| {
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
JsonTypes::try_from_input_value(value, typ)
})?;
JsonTypes::Array(fields)
}

_ => return Err(InputParserError::AbiTypeMismatch(abi_type.clone())),
};
Ok(json_value)
Expand Down Expand Up @@ -169,6 +176,14 @@ impl InputValue {
InputValue::Struct(native_table)
}

(JsonTypes::Array(array), AbiType::Tuple { fields }) => {
let tuple_fields =
try_vecmap(array.into_iter().zip(fields.iter()), |(value, typ)| {
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
InputValue::try_from_json(value, typ, arg_name)
})?;
InputValue::Vec(tuple_fields)
}

(_, _) => return Err(InputParserError::AbiTypeMismatch(param_type.clone())),
};

Expand Down
11 changes: 11 additions & 0 deletions tooling/noirc_abi/src/input_parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,17 @@
})
}

(InputValue::Vec(vec_elements), AbiType::Tuple { fields }) => {
if vec_elements.len() != fields.len() {
return false;
}

vec_elements
.iter()
.zip(fields.iter())
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
.all(|(input_value, abi_param)| input_value.matches_abi(abi_param))
}

// All other InputValue-AbiType combinations are fundamentally incompatible.
_ => false,
}
Expand Down Expand Up @@ -201,7 +212,7 @@
}
}

fn parse_str_to_signed(value: &str, witdh: u32) -> Result<FieldElement, InputParserError> {

Check warning on line 215 in tooling/noirc_abi/src/input_parser/mod.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (witdh)
if value.starts_with("0x") {
FieldElement::from_hex(value).ok_or_else(|| InputParserError::ParseHexStr(value.to_owned()))
} else {
Expand All @@ -210,7 +221,7 @@
.and_then(|bigint| {
let modulus: BigInt = FieldElement::modulus().into();
let bigint = if bigint.sign() == num_bigint::Sign::Minus {
BigInt::from(2).pow(witdh) + bigint

Check warning on line 224 in tooling/noirc_abi/src/input_parser/mod.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (witdh)
} else {
bigint
};
Expand Down Expand Up @@ -282,9 +293,9 @@
}

#[test]
fn rejects_noncanonical_fields() {

Check warning on line 296 in tooling/noirc_abi/src/input_parser/mod.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (noncanonical)
let noncanonical_field = FieldElement::modulus().to_string();

Check warning on line 297 in tooling/noirc_abi/src/input_parser/mod.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (noncanonical)
let parsed_field = parse_str_to_field(&noncanonical_field);

Check warning on line 298 in tooling/noirc_abi/src/input_parser/mod.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (noncanonical)
println!("{parsed_field:?}");
}
}
15 changes: 15 additions & 0 deletions tooling/noirc_abi/src/input_parser/toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,13 @@ impl TomlTypes {
TomlTypes::Table(map_with_toml_types)
}

(InputValue::Vec(vector), AbiType::Tuple { fields }) => {
let fields = try_vecmap(vector.iter().zip(fields.iter()), |(value, typ)| {
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
TomlTypes::try_from_input_value(value, typ)
})?;
TomlTypes::Array(fields)
}

_ => return Err(InputParserError::AbiTypeMismatch(abi_type.clone())),
};
Ok(toml_value)
Expand Down Expand Up @@ -156,6 +163,14 @@ impl InputValue {
InputValue::Struct(native_table)
}

(TomlTypes::Array(array), AbiType::Tuple { fields }) => {
let tuple_fields =
try_vecmap(array.into_iter().zip(fields.iter()), |(value, typ)| {
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
InputValue::try_from_toml(value, typ, arg_name)
})?;
InputValue::Vec(tuple_fields)
}

(_, _) => return Err(InputParserError::AbiTypeMismatch(param_type.clone())),
};

Expand Down
24 changes: 23 additions & 1 deletion tooling/noirc_abi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ pub enum AbiType {
)]
fields: Vec<(String, AbiType)>,
},
Tuple {
fields: Vec<AbiType>,
},
String {
length: u64,
},
Expand Down Expand Up @@ -164,7 +167,10 @@ impl AbiType {
context.fully_qualified_struct_path(context.root_crate_id(), struct_type.id);
Self::Struct { fields, path }
}
Type::Tuple(_) => todo!("AbiType::from_type not yet implemented for tuple types"),
Type::Tuple(fields) => {
let fields = vecmap(fields, |typ| Self::from_type(context, typ));
Self::Tuple { fields }
}
Type::TypeVariable(_, _) => unreachable!(),
Type::NamedGeneric(..) => unreachable!(),
Type::Forall(..) => unreachable!(),
Expand All @@ -182,6 +188,9 @@ impl AbiType {
AbiType::Struct { fields, .. } => {
fields.iter().fold(0, |acc, (_, field_type)| acc + field_type.field_count())
}
AbiType::Tuple { fields } => {
fields.iter().fold(0, |acc, field_typ| acc + field_typ.field_count())
}
AbiType::String { length } => *length as u32,
}
}
Expand Down Expand Up @@ -370,6 +379,11 @@ impl Abi {
encoded_value.extend(Self::encode_value(object[field].clone(), typ)?);
}
}
(InputValue::Vec(vec_elements), AbiType::Tuple { fields }) => {
for (i, typ) in fields.iter().enumerate() {
encoded_value.extend(Self::encode_value(vec_elements[i].clone(), typ)?);
}
vezenovm marked this conversation as resolved.
Show resolved Hide resolved
}
_ => unreachable!("value should have already been checked to match abi type"),
}
Ok(encoded_value)
Expand Down Expand Up @@ -462,6 +476,14 @@ fn decode_value(

InputValue::Struct(struct_map)
}
AbiType::Tuple { fields } => {
let mut tuple_elements = Vec::with_capacity(fields.len());
for field_typ in fields {
tuple_elements.push(decode_value(field_iterator, field_typ)?);
}

InputValue::Vec(tuple_elements)
}
};

Ok(value)
Expand Down