Skip to content

Commit 182d017

Browse files
phatedvezenovm
andauthored
chore: Decouple noirc_abi from frontend by introducing PrintableType (#2373)
Co-authored-by: Maxim Vezenov <mvezenov@gmail.com>
1 parent 3771e52 commit 182d017

File tree

18 files changed

+511
-245
lines changed

18 files changed

+511
-245
lines changed

Cargo.lock

+15-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ members = [
55
"crates/noirc_frontend",
66
"crates/noirc_errors",
77
"crates/noirc_driver",
8+
"crates/noirc_printable_type",
89
"crates/nargo",
910
"crates/nargo_cli",
1011
"crates/nargo_toml",
@@ -38,6 +39,7 @@ noirc_driver = { path = "crates/noirc_driver" }
3839
noirc_errors = { path = "crates/noirc_errors" }
3940
noirc_evaluator = { path = "crates/noirc_evaluator" }
4041
noirc_frontend = { path = "crates/noirc_frontend" }
42+
noirc_printable_type = { path = "crates/noirc_printable_type" }
4143
noir_wasm = { path = "crates/wasm" }
4244
cfg-if = "1.0.0"
4345
clap = { version = "4.3.19", features = ["derive"] }

crates/nargo/Cargo.toml

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,9 @@ fm.workspace = true
1616
noirc_abi.workspace = true
1717
noirc_driver.workspace = true
1818
noirc_frontend.workspace = true
19+
noirc_printable_type.workspace = true
1920
iter-extended.workspace = true
2021
serde.workspace = true
21-
serde_json.workspace = true
2222
thiserror.workspace = true
2323
noirc_errors.workspace = true
2424
base64.workspace = true
25-
regex = "1.9.1"

crates/nargo/src/errors.rs

+1-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use acvm::pwg::OpcodeResolutionError;
2-
use noirc_abi::errors::{AbiError, InputParserError};
2+
use noirc_printable_type::ForeignCallError;
33
use thiserror::Error;
44

55
#[derive(Debug, Error)]
@@ -15,17 +15,3 @@ pub enum NargoError {
1515
#[error(transparent)]
1616
ForeignCallError(#[from] ForeignCallError),
1717
}
18-
19-
#[derive(Debug, Error)]
20-
pub enum ForeignCallError {
21-
#[error("Foreign call inputs needed for execution are missing")]
22-
MissingForeignCallInputs,
23-
24-
/// ABI encoding/decoding error
25-
#[error(transparent)]
26-
AbiError(#[from] AbiError),
27-
28-
/// Input parsing error
29-
#[error(transparent)]
30-
InputParserError(#[from] InputParserError),
31-
}

crates/nargo/src/ops/foreign_calls.rs

+6-88
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@ use acvm::{
33
pwg::ForeignCallWaitInfo,
44
};
55
use iter_extended::vecmap;
6-
use noirc_abi::{decode_string_value, input_parser::InputValueDisplay, AbiType};
7-
use regex::{Captures, Regex};
6+
use noirc_printable_type::PrintableValueDisplay;
87

9-
use crate::errors::ForeignCallError;
8+
use crate::NargoError;
109

1110
/// This enumeration represents the Brillig foreign calls that are natively supported by nargo.
1211
/// After resolution of a foreign call, nargo will restart execution of the ACVM
@@ -43,7 +42,7 @@ impl ForeignCall {
4342
pub(crate) fn execute(
4443
foreign_call: &ForeignCallWaitInfo,
4544
show_output: bool,
46-
) -> Result<ForeignCallResult, ForeignCallError> {
45+
) -> Result<ForeignCallResult, NargoError> {
4746
let foreign_call_name = foreign_call.function.as_str();
4847
match Self::lookup(foreign_call_name) {
4948
Some(ForeignCall::Println) => {
@@ -78,90 +77,9 @@ impl ForeignCall {
7877
}
7978
}
8079

81-
fn execute_println(foreign_call_inputs: &[Vec<Value>]) -> Result<(), ForeignCallError> {
82-
let (is_fmt_str, foreign_call_inputs) =
83-
foreign_call_inputs.split_last().ok_or(ForeignCallError::MissingForeignCallInputs)?;
84-
85-
let output_string = if is_fmt_str[0].to_field().is_one() {
86-
convert_fmt_string_inputs(foreign_call_inputs)?
87-
} else {
88-
convert_string_inputs(foreign_call_inputs)?
89-
};
90-
println!("{output_string}");
80+
fn execute_println(foreign_call_inputs: &[Vec<Value>]) -> Result<(), NargoError> {
81+
let display_values: PrintableValueDisplay = foreign_call_inputs.try_into()?;
82+
println!("{display_values}");
9183
Ok(())
9284
}
9385
}
94-
95-
fn convert_string_inputs(foreign_call_inputs: &[Vec<Value>]) -> Result<String, ForeignCallError> {
96-
// Fetch the abi type from the foreign call input
97-
// The remaining input values should hold what is to be printed
98-
let (abi_type_as_values, input_values) =
99-
foreign_call_inputs.split_last().ok_or(ForeignCallError::MissingForeignCallInputs)?;
100-
let abi_type = fetch_abi_type(abi_type_as_values)?;
101-
102-
// We must use a flat map here as each value in a struct will be in a separate input value
103-
let mut input_values_as_fields =
104-
input_values.iter().flat_map(|values| vecmap(values, |value| value.to_field()));
105-
106-
let input_value_display =
107-
InputValueDisplay::try_from_fields(&mut input_values_as_fields, abi_type)?;
108-
109-
Ok(input_value_display.to_string())
110-
}
111-
112-
fn convert_fmt_string_inputs(
113-
foreign_call_inputs: &[Vec<Value>],
114-
) -> Result<String, ForeignCallError> {
115-
let (message_as_values, input_and_abi_values) =
116-
foreign_call_inputs.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?;
117-
118-
let message_as_fields = vecmap(message_as_values, |value| value.to_field());
119-
let message_as_string = decode_string_value(&message_as_fields);
120-
121-
let (num_values, input_and_abi_values) =
122-
input_and_abi_values.split_first().ok_or(ForeignCallError::MissingForeignCallInputs)?;
123-
124-
let mut output_strings = Vec::new();
125-
let num_values = num_values[0].to_field().to_u128() as usize;
126-
127-
let mut abi_types = Vec::new();
128-
for abi_values in input_and_abi_values.iter().skip(input_and_abi_values.len() - num_values) {
129-
let abi_type = fetch_abi_type(abi_values)?;
130-
abi_types.push(abi_type);
131-
}
132-
133-
for i in 0..num_values {
134-
let abi_type = &abi_types[i];
135-
let type_size = abi_type.field_count() as usize;
136-
137-
let mut input_values_as_fields = input_and_abi_values[i..(i + type_size)]
138-
.iter()
139-
.flat_map(|values| vecmap(values, |value| value.to_field()));
140-
141-
let input_value_display =
142-
InputValueDisplay::try_from_fields(&mut input_values_as_fields, abi_type.clone())?;
143-
144-
output_strings.push(input_value_display.to_string());
145-
}
146-
147-
let mut output_strings_iter = output_strings.into_iter();
148-
let re = Regex::new(r"\{([a-zA-Z0-9_]+)\}")
149-
.expect("ICE: an invalid regex pattern was used for checking format strings");
150-
151-
let formatted_str = re.replace_all(&message_as_string, |_: &Captures| {
152-
output_strings_iter
153-
.next()
154-
.expect("ICE: there are more regex matches than fields supplied to the format string")
155-
});
156-
157-
Ok(formatted_str.into_owned())
158-
}
159-
160-
fn fetch_abi_type(abi_type_as_values: &[Value]) -> Result<AbiType, ForeignCallError> {
161-
let abi_type_as_fields = vecmap(abi_type_as_values, |value| value.to_field());
162-
let abi_type_as_string = decode_string_value(&abi_type_as_fields);
163-
let abi_type: AbiType = serde_json::from_str(&abi_type_as_string)
164-
.map_err(|err| ForeignCallError::InputParserError(err.into()))?;
165-
166-
Ok(abi_type)
167-
}

crates/noirc_abi/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ edition.workspace = true
99
[dependencies]
1010
acvm.workspace = true
1111
iter-extended.workspace = true
12+
noirc_frontend.workspace = true
1213
toml.workspace = true
1314
serde_json = "1.0"
1415
serde.workspace = true

crates/noirc_abi/src/errors.rs

-4
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ pub enum InputParserError {
1010
ParseStr(String),
1111
#[error("Could not parse hex value {0}")]
1212
ParseHexStr(String),
13-
#[error("duplicate variable name {0}")]
14-
DuplicateVariableName(String),
1513
#[error("cannot parse value into {0:?}")]
1614
AbiTypeMismatch(AbiType),
1715
#[error("Expected argument `{0}`, but none was found")]
@@ -38,8 +36,6 @@ impl From<serde_json::Error> for InputParserError {
3836

3937
#[derive(Debug, Error)]
4038
pub enum AbiError {
41-
#[error("{0}")]
42-
Generic(String),
4339
#[error("Received parameters not expected by ABI: {0:?}")]
4440
UnexpectedParams(Vec<String>),
4541
#[error("The parameter {} is expected to be a {:?} but found incompatible value {value:?}", .param.name, .param.typ)]

crates/noirc_abi/src/input_parser/json.rs

-10
Original file line numberDiff line numberDiff line change
@@ -175,13 +175,3 @@ impl InputValue {
175175
Ok(input_value)
176176
}
177177
}
178-
179-
impl std::fmt::Display for JsonTypes {
180-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181-
// From the docs: https://doc.rust-lang.org/std/fmt/struct.Error.html
182-
// This type does not support transmission of an error other than that an error
183-
// occurred. Any extra information must be arranged to be transmitted through
184-
// some other means.
185-
write!(f, "{}", serde_json::to_string(&self).map_err(|_| std::fmt::Error)?)
186-
}
187-
}

crates/noirc_abi/src/input_parser/mod.rs

+2-31
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use std::collections::BTreeMap;
66
use acvm::FieldElement;
77
use serde::Serialize;
88

9-
use crate::errors::{AbiError, InputParserError};
10-
use crate::{decode_value, Abi, AbiType};
9+
use crate::errors::InputParserError;
10+
use crate::{Abi, AbiType};
1111
/// This is what all formats eventually transform into
1212
/// For example, a toml file will parse into TomlTypes
1313
/// and those TomlTypes will be mapped to Value
@@ -67,35 +67,6 @@ impl InputValue {
6767
}
6868
}
6969

70-
/// In order to display an `InputValue` we need an `AbiType` to accurately
71-
/// convert the value into a human-readable format.
72-
pub struct InputValueDisplay {
73-
input_value: InputValue,
74-
abi_type: AbiType,
75-
}
76-
77-
impl InputValueDisplay {
78-
pub fn try_from_fields(
79-
field_iterator: &mut impl Iterator<Item = FieldElement>,
80-
abi_type: AbiType,
81-
) -> Result<Self, AbiError> {
82-
let input_value = decode_value(field_iterator, &abi_type)?;
83-
Ok(InputValueDisplay { input_value, abi_type })
84-
}
85-
}
86-
87-
impl std::fmt::Display for InputValueDisplay {
88-
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89-
// From the docs: https://doc.rust-lang.org/std/fmt/struct.Error.html
90-
// This type does not support transmission of an error other than that an error
91-
// occurred. Any extra information must be arranged to be transmitted through
92-
// some other means.
93-
let json_value = json::JsonTypes::try_from_input_value(&self.input_value, &self.abi_type)
94-
.map_err(|_| std::fmt::Error)?;
95-
write!(f, "{}", serde_json::to_string(&json_value).map_err(|_| std::fmt::Error)?)
96-
}
97-
}
98-
9970
/// The different formats that are supported when parsing
10071
/// the initial witness values
10172
#[cfg_attr(test, derive(strum_macros::EnumIter))]

0 commit comments

Comments
 (0)