1use super::{
2 InputValue, field_to_signed_hex, parse_integer_to_signed, parse_str_to_field,
3 parse_str_to_signed,
4};
5use crate::{Abi, AbiType, MAIN_RETURN_NAME, errors::InputParserError};
6use acvm::{AcirField, FieldElement};
7use iter_extended::{try_btree_map, try_vecmap};
8use serde::{Deserialize, Serialize};
9use std::collections::BTreeMap;
10
11pub fn parse_json(
12 input_string: &str,
13 abi: &Abi,
14) -> Result<BTreeMap<String, InputValue>, InputParserError> {
15 let data: BTreeMap<String, JsonTypes> = serde_json::from_str(input_string)?;
17
18 let mut parsed_inputs = try_btree_map(abi.to_btree_map(), |(arg_name, abi_type)| {
20 let value = data
22 .get(&arg_name)
23 .ok_or_else(|| InputParserError::MissingArgument(arg_name.clone()))?;
24
25 InputValue::try_from_json(value.clone(), &abi_type, &arg_name)
26 .map(|input_value| (arg_name, input_value))
27 })?;
28
29 if let (Some(return_type), Some(json_return_value)) =
32 (&abi.return_type, data.get(MAIN_RETURN_NAME))
33 {
34 let return_value = InputValue::try_from_json(
35 json_return_value.clone(),
36 &return_type.abi_type,
37 MAIN_RETURN_NAME,
38 )?;
39 parsed_inputs.insert(MAIN_RETURN_NAME.to_owned(), return_value);
40 }
41
42 Ok(parsed_inputs)
43}
44
45pub fn serialize_to_json(
46 input_map: &BTreeMap<String, InputValue>,
47 abi: &Abi,
48) -> Result<String, InputParserError> {
49 let mut json_map = try_btree_map(abi.to_btree_map(), |(key, param_type)| {
50 JsonTypes::try_from_input_value(&input_map[&key], ¶m_type)
51 .map(|value| (key.to_owned(), value))
52 })?;
53
54 if let (Some(return_type), Some(return_value)) =
55 (&abi.return_type, input_map.get(MAIN_RETURN_NAME))
56 {
57 let return_value = JsonTypes::try_from_input_value(return_value, &return_type.abi_type)?;
58 json_map.insert(MAIN_RETURN_NAME.to_owned(), return_value);
59 }
60
61 let json_string = serde_json::to_string(&json_map)?;
62
63 Ok(json_string)
64}
65
66#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
67#[serde(untagged)]
68pub enum JsonTypes {
69 String(String),
72 Integer(i64),
78 Bool(bool),
80 Array(Vec<JsonTypes>),
82 Table(BTreeMap<String, JsonTypes>),
84}
85
86impl JsonTypes {
87 pub fn try_from_input_value(
88 value: &InputValue,
89 abi_type: &AbiType,
90 ) -> Result<JsonTypes, InputParserError> {
91 let json_value = match (value, abi_type) {
92 (InputValue::Field(f), AbiType::Integer { sign: crate::Sign::Signed, width }) => {
93 JsonTypes::String(field_to_signed_hex(*f, *width))
94 }
95 (InputValue::Field(f), AbiType::Field | AbiType::Integer { .. }) => {
96 JsonTypes::String(Self::format_field_string(*f))
97 }
98 (InputValue::Field(f), AbiType::Boolean) => JsonTypes::Bool(f.is_one()),
99
100 (InputValue::Vec(vector), AbiType::Array { typ, .. }) => {
101 let array =
102 try_vecmap(vector, |value| JsonTypes::try_from_input_value(value, typ))?;
103 JsonTypes::Array(array)
104 }
105
106 (InputValue::String(s), AbiType::String { .. }) => JsonTypes::String(s.to_string()),
107
108 (InputValue::Struct(map), AbiType::Struct { fields, .. }) => {
109 let map_with_json_types = try_btree_map(fields, |(key, field_type)| {
110 JsonTypes::try_from_input_value(&map[key], field_type)
111 .map(|json_value| (key.to_owned(), json_value))
112 })?;
113 JsonTypes::Table(map_with_json_types)
114 }
115
116 (InputValue::Vec(vector), AbiType::Tuple { fields }) => {
117 let fields = try_vecmap(vector.iter().zip(fields), |(value, typ)| {
118 JsonTypes::try_from_input_value(value, typ)
119 })?;
120 JsonTypes::Array(fields)
121 }
122
123 _ => {
124 return Err(InputParserError::AbiTypeMismatch(
125 format!("{value:?}"),
126 abi_type.clone(),
127 ));
128 }
129 };
130 Ok(json_value)
131 }
132
133 fn format_field_string(field: FieldElement) -> String {
138 if field.is_zero() {
139 return "0x00".to_owned();
140 }
141 let mut trimmed_field = field.to_hex().trim_start_matches('0').to_owned();
142 if trimmed_field.len() % 2 != 0 {
143 trimmed_field = "0".to_owned() + &trimmed_field;
144 }
145 "0x".to_owned() + &trimmed_field
146 }
147}
148
149impl InputValue {
150 pub fn try_from_json(
151 value: JsonTypes,
152 param_type: &AbiType,
153 arg_name: &str,
154 ) -> Result<InputValue, InputParserError> {
155 let input_value = match (value, param_type) {
156 (JsonTypes::String(string), AbiType::String { .. }) => InputValue::String(string),
157 (JsonTypes::String(string), AbiType::Integer { sign: crate::Sign::Signed, width }) => {
158 InputValue::Field(parse_str_to_signed(&string, *width, arg_name)?)
159 }
160 (
161 JsonTypes::String(string),
162 AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean,
163 ) => InputValue::Field(parse_str_to_field(&string, arg_name)?),
164
165 (
166 JsonTypes::Integer(integer),
167 AbiType::Integer { sign: crate::Sign::Signed, width },
168 ) => {
169 let new_value = parse_integer_to_signed(integer as i128, *width, arg_name)?;
170 InputValue::Field(new_value)
171 }
172
173 (
174 JsonTypes::Integer(integer),
175 AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean,
176 ) => {
177 let new_value = FieldElement::from(i128::from(integer));
178
179 InputValue::Field(new_value)
180 }
181
182 (JsonTypes::Bool(boolean), AbiType::Boolean) => InputValue::Field(boolean.into()),
183
184 (JsonTypes::Array(array), AbiType::Array { typ, .. }) => {
185 let mut index = 0;
186 let array_elements = try_vecmap(array, |value| {
187 let sub_name = format!("{arg_name}[{index}]");
188 let value = InputValue::try_from_json(value, typ, &sub_name);
189 index += 1;
190 value
191 })?;
192 InputValue::Vec(array_elements)
193 }
194
195 (JsonTypes::Table(table), AbiType::Struct { fields, .. }) => {
196 let native_table = try_btree_map(fields, |(field_name, abi_type)| {
197 let field_id = format!("{arg_name}.{field_name}");
199 let value = table
200 .get(field_name)
201 .ok_or_else(|| InputParserError::MissingArgument(field_id.clone()))?;
202 InputValue::try_from_json(value.clone(), abi_type, &field_id)
203 .map(|input_value| (field_name.to_string(), input_value))
204 })?;
205
206 InputValue::Struct(native_table)
207 }
208
209 (JsonTypes::Array(array), AbiType::Tuple { fields }) => {
210 let mut index = 0;
211 let tuple_fields = try_vecmap(array.into_iter().zip(fields), |(value, typ)| {
212 let sub_name = format!("{arg_name}[{index}]");
213 let value = InputValue::try_from_json(value, typ, &sub_name);
214 index += 1;
215 value
216 })?;
217 InputValue::Vec(tuple_fields)
218 }
219
220 (value, _) => {
221 return Err(InputParserError::AbiTypeMismatch(
222 format!("{value:?}"),
223 param_type.clone(),
224 ));
225 }
226 };
227
228 Ok(input_value)
229 }
230}
231
232#[cfg(test)]
233mod test {
234 use acvm::FieldElement;
235 use proptest::prelude::*;
236
237 use crate::{
238 AbiType,
239 arbitrary::arb_abi_and_input_map,
240 input_parser::{InputValue, arbitrary::arb_signed_integer_type_and_value, json::JsonTypes},
241 };
242
243 use super::{parse_json, serialize_to_json};
244
245 proptest! {
246 #[test]
247 fn serializing_and_parsing_returns_original_input((abi, input_map) in arb_abi_and_input_map()) {
248 let json = serialize_to_json(&input_map, &abi).expect("should be serializable");
249 let parsed_input_map = parse_json(&json, &abi).expect("should be parsable");
250
251 prop_assert_eq!(parsed_input_map, input_map);
252 }
253
254 #[test]
255 fn signed_integer_serialization_roundtrip((typ, value) in arb_signed_integer_type_and_value()) {
256 let string_input = JsonTypes::String(value.to_string());
257 let input_value = InputValue::try_from_json(string_input, &typ, "foo").expect("should be parsable");
258 let JsonTypes::String(output_string) = JsonTypes::try_from_input_value(&input_value, &typ).expect("should be serializable") else {
259 panic!("wrong type output");
260 };
261 let output_number = if let Some(output_string) = output_string.strip_prefix("-0x") {
262 -i64::from_str_radix(output_string, 16).unwrap()
263 } else {
264 i64::from_str_radix(output_string.strip_prefix("0x").unwrap(), 16).unwrap()
265 };
266 prop_assert_eq!(output_number, value);
267 }
268 }
269
270 #[test]
271 fn errors_on_integer_to_signed_integer_overflow() {
272 let typ = AbiType::Integer { sign: crate::Sign::Signed, width: 8 };
273 let input = JsonTypes::Integer(128);
274 assert!(InputValue::try_from_json(input, &typ, "foo").is_err());
275
276 let typ = AbiType::Integer { sign: crate::Sign::Signed, width: 16 };
277 let input = JsonTypes::Integer(32768);
278 assert!(InputValue::try_from_json(input, &typ, "foo").is_err());
279 }
280
281 #[test]
282 fn try_from_json_negative_integer() {
283 let typ = AbiType::Integer { sign: crate::Sign::Signed, width: 8 };
284 let input = JsonTypes::Integer(-1);
285 let InputValue::Field(field) = InputValue::try_from_json(input, &typ, "foo").unwrap()
286 else {
287 panic!("Expected field");
288 };
289 assert_eq!(field, FieldElement::from(255_u128));
290 }
291}