From 3fb0344ce038b3a68cae897c403d1f561cfe8da7 Mon Sep 17 00:00:00 2001 From: Julian Gonzalez Calderon Date: Fri, 4 Oct 2024 12:34:34 -0300 Subject: [PATCH] Missing dynamic layout features (#1838) * Fix Zero segment location. * Fixed has_zero_segment naming * Fix prover input. * Fixed version reading when no version is supplied * Added change to changelog. * fix test_from_serializable() * fix panic_impl error * fix cairo version * Add dummy changelog * Pin wasm-bindgen * Register change in CHANGELOG * Update Cargo.lock * Remove changes from CHANGELOG * Add argument parsing for layout params file * Add dynamic support (no implement) * Add cairo_layout_params_file.example.json * Implement dynamic layout creation * Update CHANGELOG * Add cli dynamic support for cairo 1 * Make wasm compatible * Use public_memory_fraction = 4 vy default * Deserialize bool from int * Add comparison with python-vm (failing) * Rebuild .rs files in makefile * Use 8 as dynamic public_memory_fraction The same value is used in python-vm * Use None ratio for dynamic unused builtins * Add rangecheck96 to private inputs * Make dyn py files depend on params_file * Use cpu_component_step=1 by default * Fix typo in private inputs * Add range check value to air private input test * Fix zero segment location * Use zero builtin instead of None * Add debug scripts * Remove dup makefile recipes * remove outdated test * Enable ensure-no_std on test * Fix tests * Add correct test * Rename tset * Add comment * Add debugging document * Update cairo layout params file * Remove duplicated range check * Remove dup * Add support for dynamic memory units per step * Add changelog * Add support for negative log units per step * Add LowRatio struct to RangeCheck and Mod builtins * Fix test * Use ratio_den in get_allocated_instances * Remove debugging and scrippts (moveed to another branch) * Add comment * Add tests * Add dynamic test to cairo-vm-cli * Add parse test * Remove compare all dynamic * Add script for comparing with dynamic layouts * Add tests to workflow * Delete logic changes They are going to be moved to another branch * Delete more logic changes * Reorder fields * Remove ref * Add modulo tests with all_cairo * Add tests * Update rust.yml * Rename compare_outputs_dynamic_layout.sh to compare_outputs_dynamic_layouts.sh * Update test script * Remove tests * Enforce prover constraints in add, sub, and mul * Remove debug prints * Add tests * Update CHANGELOG.md * Fix serialization * Comment failing test * Uncomment test * Add fractional units per step test * Change safe_div for div_ceil (bug) * Add ratio_den tests * Fix tests * Remove operation struct and use builtin type instead * Add unit tests to modulo builtin operations * Fix security error message * Add mod_builtin to coverage * Test custom serde impl * Upload mod_builtin coverage * Test custom serde impl * Upload mod_builtin coverage * Add Debug scripts (#1839) * Add debug scripts * Add debugging document * Add changelog * Revert changelog * Improve script usage code blocks * Remove extra space * Add cpu_component_step * Add comments * Add cairo pie tests * Restore zip * Use .rs.pie.zip * Update Changelog * Add dynamic layout documentation * Update CHANGELOG.md --------- Co-authored-by: Alon Titelman Co-authored-by: Yuval Goldberg Co-authored-by: Omri Eshhar Co-authored-by: Pedro Fontana --- .github/workflows/rust.yml | 14 +- CHANGELOG.md | 8 +- README.md | 9 ++ .../tests/compare_outputs_dynamic_layouts.sh | 133 ++++++++++++++++++ .../builtins_instance_def.rs | 18 ++- .../diluted_pool_instance_def.rs | 11 ++ vm/src/types/instance_definitions/mod.rs | 24 ++++ .../instance_definitions/mod_instance_def.rs | 25 +++- .../range_check_instance_def.rs | 20 ++- vm/src/types/layout.rs | 93 ++++++++---- vm/src/vm/runners/builtin_runner/mod.rs | 100 +++++++++++-- vm/src/vm/runners/builtin_runner/modulo.rs | 6 +- .../vm/runners/builtin_runner/range_check.rs | 22 ++- vm/src/vm/runners/cairo_runner.rs | 32 +++-- 14 files changed, 443 insertions(+), 72 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 600b0e52ca..18cdb5653d 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -757,7 +757,7 @@ jobs: compare-outputs-dynamic-layouts: name: Compare outputs with dynamic layouts - needs: [ build-programs, build-release ] + needs: [ build-programs, build-release, run-cairo-release ] runs-on: ubuntu-22.04 steps: - name: Checkout @@ -791,6 +791,18 @@ jobs: key: cairo_proof_programs-cache-${{ hashFiles('cairo_programs/**/*.cairo', 'examples/wasm-demo/src/array_sum.cairo') }} fail-on-cache-miss: true + - name: Fetch pie + uses: actions/cache/restore@v3 + with: + path: | + cairo_programs/**/*.memory + cairo_programs/**/*.trace + cairo_programs/**/*.air_public_input + cairo_programs/**/*.air_private_input + cairo_programs/**/*.pie.zip + key: cairo_test_programs-release-trace-cache-${{ github.sha }} + fail-on-cache-miss: true + - name: Run script run: ./vm/src/tests/compare_outputs_dynamic_layouts.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f4001b5a7..4e19803711 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,14 +2,14 @@ #### Upcoming Changes -* fix: [#1841](https://github.com/lambdaclass/cairo-vm/pull/1841): - * Fix modulo builtin to comply with prover constraints - -* feat(BREAKING): [#1824](https://github.com/lambdaclass/cairo-vm/pull/1824): +* feat(BREAKING): [#1824](https://github.com/lambdaclass/cairo-vm/pull/1824)[#1838](https://github.com/lambdaclass/cairo-vm/pull/1838): * Add support for dynamic layout * CLI change(BREAKING): The flag `cairo_layout_params_file` must be specified when using dynamic layout. * Signature change(BREAKING): Both `CairoRunner::new` and `CairoRunner::new_v2` now receive an `Option`, used only with dynamic layout. +* fix: [#1841](https://github.com/lambdaclass/cairo-vm/pull/1841): + * Fix modulo builtin to comply with prover constraints + * chore: bump pip `cairo-lang` 0.13.2 [#1827](https://github.com/lambdaclass/cairo-vm/pull/1827) * chore: bump `cairo-lang-` dependencies to 2.8.0 [#1833](https://github.com/lambdaclass/cairo-vm/pull/1833/files) diff --git a/README.md b/README.md index a7ecf920dc..ffae28ccdf 100644 --- a/README.md +++ b/README.md @@ -266,6 +266,15 @@ Now that you have the dependencies necessary to run the test suite you can run: make test ``` +### Using a Dynamic Layout + +A dynamic layout must be specified with a dynamic params file. You can find an example in: `vm/src/tests/cairo_layout_params_file.json`. + +To run cairo 0 or 1 programs with a dynamic layout, you must use `--layout dynamic` and the `--cairo_layout_params_file` flag pointing a dynamic params file. For example, run: +```bash +cargo run --bin cairo-vm-cli cairo_programs/fibonacci.json --layout dynamic --cairo_layout_params_file vm/src/tests/cairo_layout_params_file.json +``` + ### Tracer Cairo-vm offers a tracer which gives you a visualization of how your memory and registers change line after line as the VM executes the code. You can read more about it [here](./docs/tracer/README.md) diff --git a/vm/src/tests/compare_outputs_dynamic_layouts.sh b/vm/src/tests/compare_outputs_dynamic_layouts.sh index 2bc512e3a1..41c7bf0f17 100755 --- a/vm/src/tests/compare_outputs_dynamic_layouts.sh +++ b/vm/src/tests/compare_outputs_dynamic_layouts.sh @@ -68,6 +68,70 @@ cat < "$TEMP_FOLDER/double_all_cairo.json" } EOF +cat < "$TEMP_FOLDER/fractional_units_per_step.json" +{ + "rc_units": 4, + "log_diluted_units_per_step": -2, + "cpu_component_step": 8, + "memory_units_per_step": 8, + "uses_pedersen_builtin": false, + "pedersen_ratio": 0, + "uses_range_check_builtin": false, + "range_check_ratio": 0, + "uses_ecdsa_builtin": false, + "ecdsa_ratio": 0, + "uses_bitwise_builtin": false, + "bitwise_ratio": 0, + "uses_ec_op_builtin": false, + "ec_op_ratio": 0, + "uses_keccak_builtin": false, + "keccak_ratio": 0, + "uses_poseidon_builtin": false, + "poseidon_ratio": 0, + "uses_range_check96_builtin": false, + "range_check96_ratio": 0, + "range_check96_ratio_den": 1, + "uses_add_mod_builtin": false, + "add_mod_ratio": 0, + "add_mod_ratio_den": 1, + "uses_mul_mod_builtin": false, + "mul_mod_ratio": 0, + "mul_mod_ratio_den": 1 +} +EOF + +cat < "$TEMP_FOLDER/ratio_den.json" +{ + "rc_units": 4, + "log_diluted_units_per_step": 4, + "cpu_component_step": 8, + "memory_units_per_step": 512, + "uses_pedersen_builtin": false, + "pedersen_ratio": 0, + "uses_range_check_builtin": false, + "range_check_ratio": 0, + "uses_ecdsa_builtin": false, + "ecdsa_ratio": 0, + "uses_bitwise_builtin": false, + "bitwise_ratio": 0, + "uses_ec_op_builtin": false, + "ec_op_ratio": 0, + "uses_keccak_builtin": false, + "keccak_ratio": 0, + "uses_poseidon_builtin": false, + "poseidon_ratio": 0, + "uses_range_check96_builtin": true, + "range_check96_ratio": 1, + "range_check96_ratio_den": 2, + "uses_add_mod_builtin": true, + "add_mod_ratio": 1, + "add_mod_ratio_den": 2, + "uses_mul_mod_builtin": true, + "mul_mod_ratio": 1, + "mul_mod_ratio_den": 2 +} +EOF + # Build cases to execute CASES=( "cairo_programs/proof_programs/factorial.json;all_cairo" @@ -82,9 +146,33 @@ CASES=( "cairo_programs/proof_programs/sha256.json;double_all_cairo" "cairo_programs/proof_programs/keccak.json;all_cairo" "cairo_programs/proof_programs/keccak.json;double_all_cairo" + # Mod builtin feature "cairo_programs/mod_builtin_feature/proof/mod_builtin.json;all_cairo" "cairo_programs/mod_builtin_feature/proof/mod_builtin_failure.json;all_cairo" "cairo_programs/mod_builtin_feature/proof/apply_poly.json;all_cairo" + # Fractional units per step + "cairo_programs/proof_programs/factorial.json;fractional_units_per_step" + "cairo_programs/proof_programs/fibonacci.json;fractional_units_per_step" + # Ratio den + "cairo_programs/mod_builtin_feature/proof/mod_builtin.json;ratio_den" + "cairo_programs/mod_builtin_feature/proof/mod_builtin_failure.json;ratio_den" + "cairo_programs/mod_builtin_feature/proof/apply_poly.json;ratio_den" +) + +# Build pie cases to execute +PIE_CASES=( + "cairo_programs/fibonacci.rs.pie.zip;all_cairo" + "cairo_programs/fibonacci.rs.pie.zip;double_all_cairo" + "cairo_programs/factorial.rs.pie.zip;all_cairo" + "cairo_programs/factorial.rs.pie.zip;double_all_cairo" + "cairo_programs/bigint.rs.pie.zip;all_cairo" + "cairo_programs/bigint.rs.pie.zip;double_all_cairo" + "cairo_programs/dict.rs.pie.zip;all_cairo" + "cairo_programs/dict.rs.pie.zip;double_all_cairo" + "cairo_programs/sha256.rs.pie.zip;all_cairo" + "cairo_programs/sha256.rs.pie.zip;double_all_cairo" + "cairo_programs/keccak.rs.pie.zip;all_cairo" + "cairo_programs/keccak.rs.pie.zip;double_all_cairo" ) passed_tests=0 @@ -155,6 +243,51 @@ for case in "${CASES[@]}"; do rm program_py.* done + +for case in "${PIE_CASES[@]}"; do + IFS=";" read -r program layout <<< "$case" + + full_program="$program" + full_layout="$TEMP_FOLDER/$layout.json" + + # Run cairo-vm + echo "Running cairo-vm with case: $case" + cargo run -p cairo-vm-cli --features mod_builtin --release -- "$full_program" \ + --layout "dynamic" --cairo_layout_params_file "$full_layout" --run_from_cairo_pie \ + --trace_file program_rs.trace --memory_file program_rs.memory + + # Run cairo-lang + echo "Running cairo-lang with case: $case" + cairo-run --run_from_cairo_pie "$full_program" \ + --layout "dynamic" --cairo_layout_params_file "$full_layout" \ + --trace_file program_py.trace --memory_file program_py.memory + + # Compare trace + echo "Running trace comparison for case: $case" + if ! diff -q program_rs.trace program_py.trace; then + echo "Trace differs for case: $case" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + + # Compare memory + echo "Running memory comparison for case: $case" + if ! ./vm/src/tests/memory_comparator.py program_rs.memory program_py.memory; then + echo "Memory differs for case: $case" + exit_code=1 + failed_tests=$((failed_tests + 1)) + else + passed_tests=$((passed_tests + 1)) + fi + + # Clean files generated by the script + echo "Cleaning files" + rm program_rs.* + rm program_py.* +done + if test $failed_tests != 0; then echo "Comparisons: $failed_tests failed, $passed_tests passed, $((failed_tests + passed_tests)) total" elif test $passed_tests = 0; then diff --git a/vm/src/types/instance_definitions/builtins_instance_def.rs b/vm/src/types/instance_definitions/builtins_instance_def.rs index cf7d420636..bc33233af2 100644 --- a/vm/src/types/instance_definitions/builtins_instance_def.rs +++ b/vm/src/types/instance_definitions/builtins_instance_def.rs @@ -1,6 +1,7 @@ use crate::types::layout::CairoLayoutParams; use super::mod_instance_def::ModInstanceDef; +use super::LowRatio; use super::{ bitwise_instance_def::BitwiseInstanceDef, ec_op_instance_def::EcOpInstanceDef, ecdsa_instance_def::EcdsaInstanceDef, keccak_instance_def::KeccakInstanceDef, @@ -199,7 +200,7 @@ impl BuiltinsInstanceDef { ratio: Some(params.pedersen_ratio), }); let range_check = Some(RangeCheckInstanceDef { - ratio: Some(params.range_check_ratio), + ratio: Some(LowRatio::new_int(params.range_check_ratio)), }); let ecdsa = Some(EcdsaInstanceDef { ratio: Some(params.ecdsa_ratio), @@ -217,17 +218,26 @@ impl BuiltinsInstanceDef { ratio: Some(params.poseidon_ratio), }); let range_check96 = Some(RangeCheckInstanceDef { - ratio: Some(params.range_check96_ratio), + ratio: Some(LowRatio::new( + params.range_check96_ratio, + params.range_check96_ratio_den, + )), }); #[cfg(feature = "mod_builtin")] let add_mod = Some(ModInstanceDef { - ratio: Some(params.add_mod_ratio), + ratio: Some(LowRatio::new( + params.add_mod_ratio, + params.add_mod_ratio_den, + )), word_bit_len: 96, batch_size: 1, }); #[cfg(feature = "mod_builtin")] let mul_mod = Some(ModInstanceDef { - ratio: Some(params.mul_mod_ratio), + ratio: Some(LowRatio::new( + params.mul_mod_ratio, + params.mul_mod_ratio_den, + )), word_bit_len: 96, batch_size: 1, }); diff --git a/vm/src/types/instance_definitions/diluted_pool_instance_def.rs b/vm/src/types/instance_definitions/diluted_pool_instance_def.rs index 9be73a835d..d3c109b27d 100644 --- a/vm/src/types/instance_definitions/diluted_pool_instance_def.rs +++ b/vm/src/types/instance_definitions/diluted_pool_instance_def.rs @@ -3,6 +3,7 @@ use serde::Serialize; #[derive(Serialize, Debug, PartialEq)] pub(crate) struct DilutedPoolInstanceDef { pub(crate) units_per_step: u32, // 2 ^ log_units_per_step (for cairo_lang comparison) + pub(crate) fractional_units_per_step: bool, // true when log_units_per_step is negative pub(crate) spacing: u32, pub(crate) n_bits: u32, } @@ -11,6 +12,7 @@ impl DilutedPoolInstanceDef { pub(crate) fn default() -> Self { DilutedPoolInstanceDef { units_per_step: 16, + fractional_units_per_step: false, spacing: 4, n_bits: 16, } @@ -21,6 +23,15 @@ impl DilutedPoolInstanceDef { units_per_step, spacing, n_bits, + ..Self::default() + } + } + + pub(crate) fn from_log_units_per_step(log_units_per_step: i32) -> Self { + DilutedPoolInstanceDef { + units_per_step: 2_u32.pow(log_units_per_step.unsigned_abs()), + fractional_units_per_step: log_units_per_step.is_negative(), + ..DilutedPoolInstanceDef::default() } } } diff --git a/vm/src/types/instance_definitions/mod.rs b/vm/src/types/instance_definitions/mod.rs index 8f1bba2198..7884222c07 100644 --- a/vm/src/types/instance_definitions/mod.rs +++ b/vm/src/types/instance_definitions/mod.rs @@ -1,3 +1,5 @@ +use serde::Serialize; + pub mod bitwise_instance_def; pub mod builtins_instance_def; pub mod diluted_pool_instance_def; @@ -9,3 +11,25 @@ pub mod mod_instance_def; pub mod pedersen_instance_def; pub mod poseidon_instance_def; pub mod range_check_instance_def; + +#[derive(Serialize, Debug, PartialEq, Copy, Clone)] +pub struct LowRatio { + pub numerator: u32, + pub denominator: u32, +} + +impl LowRatio { + pub fn new(numerator: u32, denominator: u32) -> Self { + Self { + numerator, + denominator, + } + } + + pub fn new_int(numerator: u32) -> Self { + Self { + numerator, + denominator: 1, + } + } +} diff --git a/vm/src/types/instance_definitions/mod_instance_def.rs b/vm/src/types/instance_definitions/mod_instance_def.rs index 0b84077d33..72150b9c3b 100644 --- a/vm/src/types/instance_definitions/mod_instance_def.rs +++ b/vm/src/types/instance_definitions/mod_instance_def.rs @@ -1,12 +1,14 @@ use serde::Serialize; +use super::LowRatio; + pub(crate) const N_WORDS: usize = 4; pub(crate) const CELLS_PER_MOD: u32 = 7; #[derive(Serialize, Debug, PartialEq, Clone)] pub(crate) struct ModInstanceDef { - pub(crate) ratio: Option, + pub(crate) ratio: Option, pub(crate) word_bit_len: u32, pub(crate) batch_size: usize, } @@ -14,9 +16,28 @@ pub(crate) struct ModInstanceDef { impl ModInstanceDef { pub(crate) fn new(ratio: Option, batch_size: usize, word_bit_len: u32) -> Self { ModInstanceDef { - ratio, + ratio: ratio.map(LowRatio::new_int), word_bit_len, batch_size, } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(target_arch = "wasm32")] + use wasm_bindgen_test::*; + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn test_new() { + let builtin_instance = ModInstanceDef { + ratio: Some(LowRatio::new_int(10)), + word_bit_len: 3, + batch_size: 3, + }; + assert_eq!(ModInstanceDef::new(Some(10), 3, 3), builtin_instance); + } +} diff --git a/vm/src/types/instance_definitions/range_check_instance_def.rs b/vm/src/types/instance_definitions/range_check_instance_def.rs index 4524fca9bb..ed09d7cfb3 100644 --- a/vm/src/types/instance_definitions/range_check_instance_def.rs +++ b/vm/src/types/instance_definitions/range_check_instance_def.rs @@ -1,20 +1,26 @@ use serde::Serialize; + +use super::LowRatio; pub(crate) const CELLS_PER_RANGE_CHECK: u32 = 1; #[derive(Serialize, Debug, PartialEq)] pub(crate) struct RangeCheckInstanceDef { - pub(crate) ratio: Option, + pub(crate) ratio: Option, } impl Default for RangeCheckInstanceDef { fn default() -> Self { - RangeCheckInstanceDef { ratio: Some(8) } + RangeCheckInstanceDef { + ratio: Some(LowRatio::new(8, 1)), + } } } impl RangeCheckInstanceDef { pub(crate) fn new(ratio: Option) -> Self { - RangeCheckInstanceDef { ratio } + RangeCheckInstanceDef { + ratio: ratio.map(LowRatio::new_int), + } } } @@ -28,14 +34,18 @@ mod tests { #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_new() { - let builtin_instance = RangeCheckInstanceDef { ratio: Some(10) }; + let builtin_instance = RangeCheckInstanceDef { + ratio: Some(LowRatio::new_int(10)), + }; assert_eq!(RangeCheckInstanceDef::new(Some(10)), builtin_instance); } #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn test_default() { - let builtin_instance = RangeCheckInstanceDef { ratio: Some(8) }; + let builtin_instance = RangeCheckInstanceDef { + ratio: Some(LowRatio::new_int(8)), + }; assert_eq!(RangeCheckInstanceDef::default(), builtin_instance); } } diff --git a/vm/src/types/layout.rs b/vm/src/types/layout.rs index b1f52e7698..58ccd8ef34 100644 --- a/vm/src/types/layout.rs +++ b/vm/src/types/layout.rs @@ -8,14 +8,17 @@ use super::{ }, }; -pub(crate) const MEMORY_UNITS_PER_STEP: u32 = 8; +pub(crate) const DEFAULT_MEMORY_UNITS_PER_STEP: u32 = 8; +pub(crate) const DEFAULT_CPU_COMPONENT_STEP: u32 = 1; use serde::{Deserialize, Deserializer, Serialize}; #[derive(Serialize, Debug)] pub struct CairoLayout { pub(crate) name: LayoutName, + pub(crate) cpu_component_step: u32, pub(crate) rc_units: u32, + pub(crate) memory_units_per_step: u32, pub(crate) builtins: BuiltinsInstanceDef, pub(crate) public_memory_fraction: u32, pub(crate) diluted_pool_instance_def: Option, @@ -26,6 +29,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::plain, rc_units: 16, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::plain(), public_memory_fraction: 4, diluted_pool_instance_def: None, @@ -36,6 +41,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::small, rc_units: 16, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::small(), public_memory_fraction: 4, diluted_pool_instance_def: None, @@ -46,6 +53,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::dex, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::dex(), public_memory_fraction: 4, diluted_pool_instance_def: None, @@ -56,6 +65,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::recursive, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::recursive(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -66,6 +77,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::starknet, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::starknet(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::new(2, 4, 16)), @@ -76,6 +89,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::starknet_with_keccak, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::starknet_with_keccak(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -86,6 +101,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::recursive_large_output, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::recursive_large_output(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -95,6 +112,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::recursive_with_poseidon, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::recursive_with_poseidon(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::new(8, 4, 16)), @@ -105,6 +124,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::all_cairo, rc_units: 4, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::all_cairo(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -115,6 +136,8 @@ impl CairoLayout { CairoLayout { name: LayoutName::all_solidity, rc_units: 8, + cpu_component_step: DEFAULT_CPU_COMPONENT_STEP, + memory_units_per_step: DEFAULT_MEMORY_UNITS_PER_STEP, builtins: BuiltinsInstanceDef::all_solidity(), public_memory_fraction: 8, diluted_pool_instance_def: Some(DilutedPoolInstanceDef::default()), @@ -125,11 +148,12 @@ impl CairoLayout { CairoLayout { name: LayoutName::dynamic, rc_units: params.rc_units, + cpu_component_step: params.cpu_component_step, + memory_units_per_step: params.memory_units_per_step, public_memory_fraction: 8, - diluted_pool_instance_def: Some(DilutedPoolInstanceDef { - units_per_step: 2_u32.pow(params.log_diluted_units_per_step), - ..DilutedPoolInstanceDef::default() - }), + diluted_pool_instance_def: Some(DilutedPoolInstanceDef::from_log_units_per_step( + params.log_diluted_units_per_step, + )), builtins: BuiltinsInstanceDef::dynamic(params), } } @@ -143,7 +167,9 @@ use arbitrary::{self, Arbitrary}; #[serde(try_from = "RawCairoLayoutParams")] pub struct CairoLayoutParams { pub rc_units: u32, - pub log_diluted_units_per_step: u32, + pub cpu_component_step: u32, + pub memory_units_per_step: u32, + pub log_diluted_units_per_step: i32, pub pedersen_ratio: u32, pub range_check_ratio: u32, pub ecdsa_ratio: u32, @@ -152,14 +178,11 @@ pub struct CairoLayoutParams { pub keccak_ratio: u32, pub poseidon_ratio: u32, pub range_check96_ratio: u32, + pub range_check96_ratio_den: u32, pub add_mod_ratio: u32, + pub add_mod_ratio_den: u32, pub mul_mod_ratio: u32, - // the following are not used right now - pub cpu_component_step: u32, - pub memory_units_per_step: u32, - pub range_check96_ratio_den: u32, pub mul_mod_ratio_den: u32, - pub add_mod_ratio_den: u32, } impl CairoLayoutParams { @@ -178,7 +201,9 @@ impl CairoLayoutParams { #[derive(Deserialize, Debug, Default, Clone)] pub struct RawCairoLayoutParams { pub rc_units: u32, - pub log_diluted_units_per_step: u32, + pub cpu_component_step: u32, + pub memory_units_per_step: u32, + pub log_diluted_units_per_step: i32, #[serde(deserialize_with = "bool_from_int_or_bool")] pub uses_pedersen_builtin: bool, pub pedersen_ratio: u32, @@ -203,18 +228,15 @@ pub struct RawCairoLayoutParams { #[serde(deserialize_with = "bool_from_int_or_bool")] pub uses_range_check96_builtin: bool, pub range_check96_ratio: u32, + pub range_check96_ratio_den: u32, #[serde(deserialize_with = "bool_from_int_or_bool")] pub uses_add_mod_builtin: bool, pub add_mod_ratio: u32, + pub add_mod_ratio_den: u32, #[serde(deserialize_with = "bool_from_int_or_bool")] pub uses_mul_mod_builtin: bool, pub mul_mod_ratio: u32, - // the following are not used right now - pub cpu_component_step: u32, - pub memory_units_per_step: u32, - pub range_check96_ratio_den: u32, pub mul_mod_ratio_den: u32, - pub add_mod_ratio_den: u32, } impl TryFrom for CairoLayoutParams { @@ -317,11 +339,12 @@ mod tests { use super::*; #[cfg(feature = "mod_builtin")] use crate::types::instance_definitions::mod_instance_def::ModInstanceDef; + use crate::types::instance_definitions::{ bitwise_instance_def::BitwiseInstanceDef, ec_op_instance_def::EcOpInstanceDef, ecdsa_instance_def::EcdsaInstanceDef, keccak_instance_def::KeccakInstanceDef, pedersen_instance_def::PedersenInstanceDef, poseidon_instance_def::PoseidonInstanceDef, - range_check_instance_def::RangeCheckInstanceDef, + range_check_instance_def::RangeCheckInstanceDef, LowRatio, }; #[cfg(target_arch = "wasm32")] @@ -452,6 +475,8 @@ mod tests { // dummy cairo layout params let params = CairoLayoutParams { rc_units: 32, + cpu_component_step: 8, + memory_units_per_step: 16, log_diluted_units_per_step: 5, pedersen_ratio: 32, range_check_ratio: 32, @@ -459,19 +484,21 @@ mod tests { bitwise_ratio: 32, ec_op_ratio: 32, keccak_ratio: 32, + poseidon_ratio: 0, + range_check96_ratio: 8, + range_check96_ratio_den: 16, + add_mod_ratio: 8, + add_mod_ratio_den: 16, mul_mod_ratio: 32, - ..Default::default() // - // cpu_component_step: todo!(), - // memory_units_per_step: todo!(), - // range_check96_ratio_den: todo!(), - // add_mod_ratio_den: todo!(), - // mul_mod_ratio_den: todo!(), + mul_mod_ratio_den: 16, }; let layout = CairoLayout::dynamic_instance(params); assert_eq!(layout.name, LayoutName::dynamic); assert_eq!(layout.rc_units, 32); + assert_eq!(layout.cpu_component_step, 8); + assert_eq!(layout.memory_units_per_step, 16); assert_eq!(layout.public_memory_fraction, 8); // hardcoded assert_eq!( layout.diluted_pool_instance_def, @@ -488,7 +515,9 @@ mod tests { ); assert_eq!( layout.builtins.range_check, - Some(RangeCheckInstanceDef { ratio: Some(32) }) + Some(RangeCheckInstanceDef { + ratio: Some(LowRatio::new_int(32)) + }) ); assert_eq!( layout.builtins.ecdsa, @@ -512,14 +541,19 @@ mod tests { ); assert_eq!( layout.builtins.range_check96, - Some(RangeCheckInstanceDef { ratio: Some(0) }) + Some(RangeCheckInstanceDef { + ratio: Some(LowRatio::new(8, 16)) + }) ); #[cfg(feature = "mod_builtin")] { assert_eq!( layout.builtins.mul_mod, Some(ModInstanceDef { - ratio: Some(32), + ratio: Some(LowRatio { + numerator: 32, + denominator: 16 + }), word_bit_len: 96, // hardcoded batch_size: 1 // hardcoded }), @@ -527,7 +561,10 @@ mod tests { assert_eq!( layout.builtins.add_mod, Some(ModInstanceDef { - ratio: Some(0), + ratio: Some(LowRatio { + numerator: 8, + denominator: 16 + }), word_bit_len: 96, // hardcoded batch_size: 1 // hardcoded }) diff --git a/vm/src/vm/runners/builtin_runner/mod.rs b/vm/src/vm/runners/builtin_runner/mod.rs index 26d4a9026c..2374f43f3d 100644 --- a/vm/src/vm/runners/builtin_runner/mod.rs +++ b/vm/src/vm/runners/builtin_runner/mod.rs @@ -47,7 +47,7 @@ pub use bitwise::BitwiseBuiltinRunner; pub use ec_op::EcOpBuiltinRunner; pub use hash::HashBuiltinRunner; pub use modulo::ModBuiltinRunner; -use num_integer::div_floor; +use num_integer::{div_ceil, div_floor}; pub use output::{OutputBuiltinRunner, OutputBuiltinState}; pub use poseidon::PoseidonBuiltinRunner; pub use range_check::RangeCheckBuiltinRunner; @@ -170,6 +170,14 @@ impl BuiltinRunner { pub fn get_allocated_memory_units( &self, vm: &VirtualMachine, + ) -> Result { + Ok(self.get_allocated_instances(vm)? * self.cells_per_instance() as usize) + } + + ///Returns the builtin's allocated instances + pub fn get_allocated_instances( + &self, + vm: &VirtualMachine, ) -> Result { match *self { BuiltinRunner::Output(_) | BuiltinRunner::SegmentArena(_) => Ok(0), @@ -179,23 +187,40 @@ impl BuiltinRunner { // Dynamic layout has the exact number of instances it needs (up to a power of 2). let instances: usize = self.get_used_cells(&vm.segments)? / self.cells_per_instance() as usize; - let components = (instances / self.instances_per_component() as usize) - .next_power_of_two(); - Ok(self.cells_per_instance() as usize - * self.instances_per_component() as usize - * components) + let needed_components = instances / self.instances_per_component() as usize; + + let components = if needed_components > 0 { + needed_components.next_power_of_two() + } else { + 0 + }; + Ok(self.instances_per_component() as usize * components) } + // Dynamic layout allows for builtins with ratio 0 + Some(0) => Ok(0), Some(ratio) => { - let min_step = (ratio * self.instances_per_component()) as usize; + let min_step_num = (ratio * self.instances_per_component()) as usize; + let min_step = if let Some(ratio_den) = self.ratio_den() { + div_ceil(min_step_num, ratio_den as usize) + } else { + min_step_num + }; + if vm.current_step < min_step { return Err(InsufficientAllocatedCellsError::MinStepNotReached( Box::new((min_step, self.name())), ) .into()); }; - let value = safe_div_usize(vm.current_step, ratio as usize) - .map_err(|_| MemoryError::ErrorCalculatingMemoryUnits)?; - Ok(self.cells_per_instance() as usize * value) + + let allocated_instances = if let Some(ratio_den) = self.ratio_den() { + safe_div_usize(vm.current_step * ratio_den as usize, ratio as usize) + .map_err(|_| MemoryError::ErrorCalculatingMemoryUnits)? + } else { + safe_div_usize(vm.current_step, ratio as usize) + .map_err(|_| MemoryError::ErrorCalculatingMemoryUnits)? + }; + Ok(allocated_instances) } } } @@ -252,6 +277,15 @@ impl BuiltinRunner { } } + pub fn ratio_den(&self) -> Option { + match self { + BuiltinRunner::RangeCheck(range_check) => range_check.ratio_den(), + BuiltinRunner::RangeCheck96(range_check) => range_check.ratio_den(), + BuiltinRunner::Mod(modulo) => modulo.ratio_den(), + _ => None, + } + } + pub fn add_validation_rule(&self, memory: &mut Memory) { match *self { BuiltinRunner::RangeCheck(ref range_check) => range_check.add_validation_rule(memory), @@ -662,6 +696,8 @@ mod tests { use crate::hint_processor::builtin_hint_processor::builtin_hint_processor_definition::BuiltinHintProcessor; use crate::relocatable; use crate::types::builtin_name::BuiltinName; + use crate::types::instance_definitions::mod_instance_def::ModInstanceDef; + use crate::types::instance_definitions::LowRatio; use crate::types::program::Program; use crate::utils::test_utils::*; use crate::vm::errors::memory_errors::InsufficientAllocatedCellsError; @@ -1053,6 +1089,26 @@ mod tests { assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(256)); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_allocated_memory_units_zero_ratio() { + let builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(Some(0), true)); + let vm = vm!(); + assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(0)); + } + + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_allocated_memory_units_none_ratio() { + let mut builtin = BuiltinRunner::Keccak(KeccakBuiltinRunner::new(None, true)); + let mut vm = vm!(); + + builtin.initialize_segments(&mut vm.segments); + vm.compute_segments_effective_sizes(); + + assert_eq!(builtin.get_allocated_memory_units(&vm), Ok(0)); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn get_range_check_usage_range_check() { @@ -1543,6 +1599,30 @@ mod tests { assert_eq!(keccak_builtin.ratio(), (Some(2048)),); } + #[test] + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + fn get_ratio_den_tests() { + let rangecheck_builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new_with_low_ratio( + Some(LowRatio::new(1, 2)), + true, + ) + .into(); + assert_eq!(rangecheck_builtin.ratio_den(), (Some(2)),); + + let rangecheck96_builtin: BuiltinRunner = + RangeCheckBuiltinRunner::::new_with_low_ratio( + Some(LowRatio::new(1, 4)), + true, + ) + .into(); + assert_eq!(rangecheck96_builtin.ratio_den(), (Some(4)),); + + let mod_builtin: BuiltinRunner = + ModBuiltinRunner::new_add_mod(&ModInstanceDef::new(Some(5), 3, 3), true).into(); + assert_eq!(mod_builtin.ratio_den(), (Some(1)),); + } + #[test] #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] fn bitwise_get_used_instances_test() { diff --git a/vm/src/vm/runners/builtin_runner/modulo.rs b/vm/src/vm/runners/builtin_runner/modulo.rs index 0f5bb7719b..3339c0b562 100644 --- a/vm/src/vm/runners/builtin_runner/modulo.rs +++ b/vm/src/vm/runners/builtin_runner/modulo.rs @@ -140,7 +140,11 @@ impl ModBuiltinRunner { } pub fn ratio(&self) -> Option { - self.instance_def.ratio + self.instance_def.ratio.map(|ratio| ratio.numerator) + } + + pub fn ratio_den(&self) -> Option { + self.instance_def.ratio.map(|ratio| ratio.denominator) } pub fn batch_size(&self) -> usize { diff --git a/vm/src/vm/runners/builtin_runner/range_check.rs b/vm/src/vm/runners/builtin_runner/range_check.rs index 9c19de3309..a4800bcf78 100644 --- a/vm/src/vm/runners/builtin_runner/range_check.rs +++ b/vm/src/vm/runners/builtin_runner/range_check.rs @@ -4,7 +4,7 @@ use crate::{ cmp::{max, min}, prelude::*, }, - types::builtin_name::BuiltinName, + types::{builtin_name::BuiltinName, instance_definitions::LowRatio}, }; use crate::Felt252; @@ -35,7 +35,7 @@ lazy_static! { #[derive(Debug, Clone)] pub struct RangeCheckBuiltinRunner { - ratio: Option, + ratio: Option, base: usize, pub(crate) stop_ptr: Option, pub(crate) included: bool, @@ -43,6 +43,18 @@ pub struct RangeCheckBuiltinRunner { impl RangeCheckBuiltinRunner { pub fn new(ratio: Option, included: bool) -> RangeCheckBuiltinRunner { + RangeCheckBuiltinRunner { + ratio: ratio.map(LowRatio::new_int), + base: 0, + stop_ptr: None, + included, + } + } + + pub fn new_with_low_ratio( + ratio: Option, + included: bool, + ) -> RangeCheckBuiltinRunner { RangeCheckBuiltinRunner { ratio, base: 0, @@ -68,7 +80,11 @@ impl RangeCheckBuiltinRunner { } pub fn ratio(&self) -> Option { - self.ratio + self.ratio.map(|ratio| ratio.numerator) + } + + pub fn ratio_den(&self) -> Option { + self.ratio.map(|ratio| ratio.denominator) } pub fn name(&self) -> BuiltinName { diff --git a/vm/src/vm/runners/cairo_runner.rs b/vm/src/vm/runners/cairo_runner.rs index a283885d57..239515458d 100644 --- a/vm/src/vm/runners/cairo_runner.rs +++ b/vm/src/vm/runners/cairo_runner.rs @@ -8,11 +8,7 @@ use crate::{ ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign}, prelude::*, }, - types::{ - builtin_name::BuiltinName, - layout::{CairoLayoutParams, MEMORY_UNITS_PER_STEP}, - layout_name::LayoutName, - }, + types::{builtin_name::BuiltinName, layout::CairoLayoutParams, layout_name::LayoutName}, vm::{ runners::builtin_runner::SegmentArenaBuiltinRunner, trace::trace_entry::{relocate_trace_register, RelocatedTraceEntry}, @@ -308,7 +304,7 @@ impl CairoRunner { let included = program_builtins.remove(&BuiltinName::range_check); if included || self.is_proof_mode() { self.vm.builtin_runners.push( - RangeCheckBuiltinRunner::::new( + RangeCheckBuiltinRunner::::new_with_low_ratio( instance_def.ratio, included, ) @@ -366,8 +362,11 @@ impl CairoRunner { let included = program_builtins.remove(&BuiltinName::range_check96); if included || self.is_proof_mode() { self.vm.builtin_runners.push( - RangeCheckBuiltinRunner::::new(instance_def.ratio, included) - .into(), + RangeCheckBuiltinRunner::::new_with_low_ratio( + instance_def.ratio, + included, + ) + .into(), ); } } @@ -842,15 +841,20 @@ impl CairoRunner { diluted_pool_instance.n_bits, ); - let multiplier = safe_div_usize( - self.vm.current_step, - builtin_runner.ratio().unwrap_or(1) as usize, - )?; + let multiplier = builtin_runner.get_allocated_instances(&self.vm)?; used_units_by_builtins += used_units * multiplier; } - let diluted_units = diluted_pool_instance.units_per_step as usize * self.vm.current_step; + let diluted_units = if !diluted_pool_instance.fractional_units_per_step { + diluted_pool_instance.units_per_step as usize * self.vm.current_step + } else { + safe_div_usize( + self.vm.current_step, + diluted_pool_instance.units_per_step as usize, + )? + }; + let unused_diluted_units = diluted_units.saturating_sub(used_units_by_builtins); let diluted_usage_upper_bound = 1usize << diluted_pool_instance.n_bits; @@ -1178,7 +1182,7 @@ impl CairoRunner { // Out of the memory units available per step, a fraction is used for public memory, and // four are used for the instruction. - let total_memory_units = MEMORY_UNITS_PER_STEP * vm_current_step_u32; + let total_memory_units = instance.memory_units_per_step * vm_current_step_u32; let (public_memory_units, rem) = div_rem(total_memory_units, instance.public_memory_fraction); if rem != 0 {