diff --git a/contracts/pairing/.gitignore b/contracts/pairing/.gitignore index 030c863..2bc361c 100644 --- a/contracts/pairing/.gitignore +++ b/contracts/pairing/.gitignore @@ -1,2 +1,2 @@ /target -.snfoundry_cache +.snfoundry_cache \ No newline at end of file diff --git a/contracts/pairing/src/lib.cairo b/contracts/pairing/src/lib.cairo index 38e49f5..3ff2053 100644 --- a/contracts/pairing/src/lib.cairo +++ b/contracts/pairing/src/lib.cairo @@ -61,5 +61,4 @@ mod pairing { res } } -} - +} \ No newline at end of file diff --git a/contracts/scripts/README.md b/contracts/scripts/README.md new file mode 100644 index 0000000..afb4adc --- /dev/null +++ b/contracts/scripts/README.md @@ -0,0 +1,78 @@ +# Code Limit + +A Python script for analyzing the size and bytecode length of Sierra contract class files. This tool helps developers understand the size characteristics of their smart contracts by providing both the total bytecode size in bytes and the number of field elements (felts) in the program. + +#### ⚠️ Important Note on Size Calculation + +Please be aware: The bytecode size calculation provided by this tool should be used as a rough estimate only. Even when the tool reports a bytecode size around 49,000 felts, you may still encounter "code size limit" errors when attempting to declare the contract class. + +## Prerequisites + +- Python 3.x +- `jq` command-line tool installed on your system +- JSON-formatted contract class files + +## Installation + +1. Clone this repository or download `code_limit.py` +2. Ensure you have `jq` installed: + - For Ubuntu/Debian: `sudo apt-get install jq` + - For macOS: `brew install jq` + - For Windows: Download from the [official jq website](https://stedolan.github.io/jq/download/) + +## Usage + +Run it directly from the command line: + +```bash +python code_limit.py +``` + +Before running, make sure to modify the `path_filename` variable in the `main()` function to point to your contract class JSON file: + +```python +path_filename = "path/to/your/contract_class.json" +``` + +## Functions + +### get_sierra_size(path_filename) + +Calculates the total size in bytes of the Sierra program bytecode. + +Parameters: + +- `path_filename` (str): Path to the contract class JSON file + +Returns: + +- `int`: Total size in bytes, or `None` if an error occurs + +### get_bytecode_length_with_jq(path_filename) + +Uses `jq` to calculate the number of field elements in the Sierra program. + +Parameters: + +- `path_filename` (str): Path to the contract class JSON file + +Returns: + +- `int`: Number of field elements, or `None` if an error occurs + +## Error Handling + +The script handles various error cases: + +- File not found +- Invalid JSON format +- Missing 'sierra_program' field +- JQ command failures +- General exceptions + +## Example Output + +``` +Contract class size: 1,234 bytes +Contract Bytecode size (Number of felts in the program): 567 +``` diff --git a/contracts/scripts/code_limit.py b/contracts/scripts/code_limit.py new file mode 100644 index 0000000..a1bf893 --- /dev/null +++ b/contracts/scripts/code_limit.py @@ -0,0 +1,77 @@ +import json +import subprocess + +def calculate_bytecode_size(hex_strings): + total_bytes = 0 + + for hex_str in hex_strings: + hex_str = hex_str.replace('0x', '') + num_bytes = len(hex_str) // 2 + total_bytes += num_bytes + + return total_bytes + +def get_sierra_size(path_filename): + try: + with open(path_filename, 'r') as file: + data = json.load(file) + + # Extract sierra_program array + if 'sierra_program' not in data: + raise KeyError("No 'sierra_program' found in JSON file") + + sierra_program = data['sierra_program'] + + # Calculate total size + size = calculate_bytecode_size(sierra_program) + return size + + except FileNotFoundError: + print(f"Error: File '{path_filename}' not found") + except json.JSONDecodeError: + print(f"Error: File '{path_filename}' is not valid JSON") + except KeyError as e: + print(f"Error: {str(e)}") + except Exception as e: + print(f"Unexpected error: {str(e)}") + + return None + +def get_bytecode_length_with_jq(path_filename): + """Call the jq command to get the bytecode length.""" + try: + # Run the jq command to get sierra_program length + result = subprocess.run( + ['jq', '.sierra_program | length', path_filename], + capture_output=True, + text=True, + check=True + ) + + bytecode_length = int(result.stdout.strip()) + return bytecode_length + + except subprocess.CalledProcessError as e: + print(f"Error running jq: {e}") + except ValueError: + print("Error: Unable to parse jq output as an integer") + except Exception as e: + print(f"Unexpected error: {str(e)}") + + return None + +def main(): + # Path to JSON file + path_filename = "path/to/contract_class.json" + + size = get_sierra_size(path_filename) + if size is not None: + print(f"Contract class size: {size:,} bytes") + + # Calculate size using jq + bytecode_length = get_bytecode_length_with_jq(path_filename) + if bytecode_length is not None: + print(f"Contract Bytecode size (Number of felts in the program): {bytecode_length}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/plonk-verifier/src/fields/fq_2.cairo b/plonk-verifier/src/fields/fq_2.cairo index 591b2ca..3d694bc 100644 --- a/plonk-verifier/src/fields/fq_2.cairo +++ b/plonk-verifier/src/fields/fq_2.cairo @@ -101,18 +101,19 @@ impl Fq2Utils of FieldUtils { impl Fq2Ops of FieldOps { // #[inline(always)] fn add(self: Fq2, rhs: Fq2, m: CircuitModulus) -> Fq2 { - let (c0, c1) = add_circuit(); - - let outputs = match (c0, c1) - .new_inputs() - .next(self.c0.c0) - .next(self.c1.c0) - .next(rhs.c0.c0) - .next(rhs.c1.c0) - .done() - .eval(m) { - Result::Ok(outputs) => { outputs }, - Result::Err(_) => { panic!("Expected success") } + let (c0, c1) = add_circuit(); + + let outputs = + match (c0, c1) + .new_inputs() + .next(self.c0.c0) + .next(self.c1.c0) + .next(rhs.c0.c0) + .next(rhs.c1.c0) + .done() + .eval(m) { + Result::Ok(outputs) => { outputs }, + Result::Err(_) => { panic!("Expected success") } }; fq2(outputs.get_output(c0), outputs.get_output(c1)) @@ -120,18 +121,19 @@ impl Fq2Ops of FieldOps { // #[inline(always)] fn sub(self: Fq2, rhs: Fq2, m: CircuitModulus) -> Fq2 { - let (c0, c1) = sub_circuit(); - - let outputs = match (c0, c1) - .new_inputs() - .next(self.c0.c0) - .next(self.c1.c0) - .next(rhs.c0.c0) - .next(rhs.c1.c0) - .done() - .eval(m) { - Result::Ok(outputs) => { outputs }, - Result::Err(_) => { panic!("Expected success") } + let (c0, c1) = sub_circuit(); + + let outputs = + match (c0, c1) + .new_inputs() + .next(self.c0.c0) + .next(self.c1.c0) + .next(rhs.c0.c0) + .next(rhs.c1.c0) + .done() + .eval(m) { + Result::Ok(outputs) => { outputs }, + Result::Err(_) => { panic!("Expected success") } }; fq2(outputs.get_output(c0), outputs.get_output(c1)) @@ -139,18 +141,19 @@ impl Fq2Ops of FieldOps { // #[inline(always)] fn mul(self: Fq2, rhs: Fq2, m: CircuitModulus) -> Fq2 { - let (c0, c1) = mul_circuit(); - - let outputs = match (c0, c1) - .new_inputs() - .next(self.c0.c0) - .next(self.c1.c0) - .next(rhs.c0.c0) - .next(rhs.c1.c0) - .done() - .eval(m) { - Result::Ok(outputs) => { outputs }, - Result::Err(_) => { panic!("Expected success") } + let (c0, c1) = mul_circuit(); + + let outputs = + match (c0, c1) + .new_inputs() + .next(self.c0.c0) + .next(self.c1.c0) + .next(rhs.c0.c0) + .next(rhs.c1.c0) + .done() + .eval(m) { + Result::Ok(outputs) => { outputs }, + Result::Err(_) => { panic!("Expected success") } }; fq2(outputs.get_output(c0), outputs.get_output(c1)) @@ -158,18 +161,19 @@ impl Fq2Ops of FieldOps { // #[inline(always)] fn div(self: Fq2, rhs: Fq2, m: CircuitModulus) -> Fq2 { - let (c0, c1) = div_circuit(); - - let outputs = match (c0, c1) - .new_inputs() - .next(self.c0.c0) - .next(self.c1.c0) - .next(rhs.c0.c0) - .next(rhs.c1.c0) - .done() - .eval(m) { - Result::Ok(outputs) => { outputs }, - Result::Err(_) => { panic!("Expected success") } + let (c0, c1) = div_circuit(); + + let outputs = + match (c0, c1) + .new_inputs() + .next(self.c0.c0) + .next(self.c1.c0) + .next(rhs.c0.c0) + .next(rhs.c1.c0) + .done() + .eval(m) { + Result::Ok(outputs) => { outputs }, + Result::Err(_) => { panic!("Expected success") } }; fq2(outputs.get_output(c0), outputs.get_output(c1)) @@ -177,16 +181,11 @@ impl Fq2Ops of FieldOps { // #[inline(always)] fn neg(self: Fq2, m: CircuitModulus) -> Fq2 { - let (c0, c1) = neg_circuit(); - - let outputs = match (c0, c1) - .new_inputs() - .next(self.c0.c0) - .next(self.c1.c0) - .done() - .eval(m) { - Result::Ok(outputs) => { outputs }, - Result::Err(_) => { panic!("Expected success") } + let (c0, c1) = neg_circuit(); + + let outputs = match (c0, c1).new_inputs().next(self.c0.c0).next(self.c1.c0).done().eval(m) { + Result::Ok(outputs) => { outputs }, + Result::Err(_) => { panic!("Expected success") } }; fq2(outputs.get_output(c0), outputs.get_output(c1)) @@ -194,16 +193,11 @@ impl Fq2Ops of FieldOps { // #[inline(always)] fn sqr(self: Fq2, m: CircuitModulus) -> Fq2 { - let (c0, c1) = sqr_circuit(); - - let outputs = match (c0, c1) - .new_inputs() - .next(self.c0.c0) - .next(self.c1.c0) - .done() - .eval(m) { - Result::Ok(outputs) => { outputs }, - Result::Err(_) => { panic!("Expected success") } + let (c0, c1) = sqr_circuit(); + + let outputs = match (c0, c1).new_inputs().next(self.c0.c0).next(self.c1.c0).done().eval(m) { + Result::Ok(outputs) => { outputs }, + Result::Err(_) => { panic!("Expected success") } }; fq2(outputs.get_output(c0), outputs.get_output(c1)) @@ -211,16 +205,11 @@ impl Fq2Ops of FieldOps { // #[inline(always)] fn inv(self: Fq2, m: CircuitModulus) -> Fq2 { - let (c0, c1) = inv_circuit(); - - let outputs = match (c0, c1) - .new_inputs() - .next(self.c0.c0) - .next(self.c1.c0) - .done() - .eval(m) { - Result::Ok(outputs) => { outputs }, - Result::Err(_) => { panic!("Expected success") } + let (c0, c1) = inv_circuit(); + + let outputs = match (c0, c1).new_inputs().next(self.c0.c0).next(self.c1.c0).done().eval(m) { + Result::Ok(outputs) => { outputs }, + Result::Err(_) => { panic!("Expected success") } }; fq2(outputs.get_output(c0), outputs.get_output(c1))