diff --git a/.github/workflows/cancel_duplicate.yml b/.github/workflows/cancel_duplicate.yml index 5b05d23f0a..0a8f1fb92f 100644 --- a/.github/workflows/cancel_duplicate.yml +++ b/.github/workflows/cancel_duplicate.yml @@ -22,4 +22,4 @@ jobs: cancelMode: allDuplicates cancelFutureDuplicates: true token: ${{ secrets.GITHUB_TOKEN }} - workflowFileName: bench_pull_request.yml + workflowFileName: bench.yml diff --git a/.github/workflows/hyperfine.yml b/.github/workflows/hyperfine.yml index 87bf26f3e3..bd2cd3a9bc 100644 --- a/.github/workflows/hyperfine.yml +++ b/.github/workflows/hyperfine.yml @@ -2,7 +2,7 @@ name: Hyperfine Benchmark on: pull_request: - branches: [ '**' ] + branches: ["**"] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -15,7 +15,7 @@ jobs: build-programs: strategy: matrix: - branch: [ base, head ] + branch: [base, head] name: Build Cairo programs for ${{ matrix.branch }} runs-on: ubuntu-22.04 outputs: @@ -39,7 +39,7 @@ jobs: if: ${{ steps.cache.outputs.cache-hit != 'true' }} uses: actions/setup-python@v4 with: - python-version: '3.9' + python-version: "3.9" - name: Install Cairo compiler if: ${{ steps.cache.outputs.cache-hit != 'true' }} @@ -54,20 +54,18 @@ jobs: - name: Export benchmark hashes id: export-hashes - run: - echo "benchmark-hashes-${{ matrix.branch }}=${{ hashFiles( 'cairo_programs/benchmarks/*.cairo' ) }}" >> "$GITHUB_OUTPUT" - + run: echo "benchmark-hashes-${{ matrix.branch }}=${{ hashFiles( 'cairo_programs/benchmarks/*.cairo' ) }}" >> "$GITHUB_OUTPUT" build-binaries: strategy: matrix: - branch: [ base, head ] + branch: [base, head] name: Build cairo-vm-cli for ${{ matrix.branch }} runs-on: ubuntu-22.04 steps: - name: Populate cache uses: actions/cache@v3 - id: cache + id: cache with: path: bin/cairo-vm-cli-${{ matrix.branch }} key: binary-${{ github.event.pull_request[matrix.branch].sha }} @@ -93,106 +91,106 @@ jobs: mkdir bin cp target/release/cairo-vm-cli bin/cairo-vm-cli-${{ matrix.branch }} - run-hyperfine: strategy: matrix: - program_state: [ modified, unmodified ] + program_state: [modified, unmodified] name: Run benchmarks for ${{ matrix.program_state }} programs - needs: [ build-programs, build-binaries ] + needs: [build-programs, build-binaries] runs-on: ubuntu-22.04 steps: - - name: Install Hyperfine - uses: taiki-e/install-action@v2 - with: - tool: hyperfine@1.16 - - - name: Fetch base binary - uses: actions/cache/restore@v3 - with: - path: bin/cairo-vm-cli-base - key: binary-${{ github.event.pull_request.base.sha }} - - - name: Fetch HEAD binary - uses: actions/cache/restore@v3 - with: - path: bin/cairo-vm-cli-head - key: binary-${{ github.event.pull_request.head.sha }} - - - name: Fetch base programs - uses: actions/cache/restore@v3 - with: - path: base_programs/*.json - key: benchmarks-base-${{ needs.build-programs.outputs.benchmark-hashes-base }} - - - name: Fetch head programs - uses: actions/cache/restore@v3 - with: - path: head_programs/*.json - key: benchmarks-head-${{ needs.build-programs.outputs.benchmark-hashes-head }} - - - name: Benchmark ${{ matrix.program_state }} programs - id: run-benchmarks - run: | - sudo swapoff -a - mkdir target_programs - if [ 'modified' = ${{ matrix.program_state }} ]; then - BINS=head - for f in head_programs/*.json; do - # Only run new or modified benchmarks - if ! cmp -s ${f/head/base} $f; then - cp $f target_programs/ - fi - done - else - BINS="base,head" - for f in base_programs/*.json; do - # Only run unmodified benchmarks - if cmp -s ${f/base/head} $f; then - cp $f target_programs/ - fi - done - fi - find target_programs -name '*.json' -exec basename -s .json '{}' '+' | \ - sort | xargs -I '{program}' \ - hyperfine -N -r 10 --export-markdown "target_programs/{program}.md" \ - -L bin "$BINS" -n "{bin} {program}" \ - -s "cat ./bin/cairo-vm-cli-{bin} target_programs/{program}.json" \ - "./bin/cairo-vm-cli-{bin} --proof_mode --layout starknet_with_keccak \ - --memory_file /dev/null --trace_file /dev/null target_programs/{program}.json" - echo "benchmark_count=$(ls target_programs/*.md | wc -l)" >> $GITHUB_OUTPUT - - - name: Print tables - if: steps.run-benchmarks.outputs.benchmark_count != 0 - run: | - { - echo "Benchmark Results for ${{ matrix.program_state }} programs :rocket:" - for f in target_programs/*.md; do - echo - cat $f - done - } | tee -a comment_body.md - - - name: Find comment - if: ${{ steps.run-benchmarks.outputs.benchmark_count != 0 }} - uses: peter-evans/find-comment@v2 - id: fc - with: - issue-number: ${{ github.event.pull_request.number }} - comment-author: 'github-actions[bot]' - body-includes: Benchmark Results for ${{ matrix.program_state }} programs - - - name: Create comment - if: steps.fc.outputs.comment-id == '' && steps.run-benchmarks.outputs.benchmark_count != 0 - uses: peter-evans/create-or-update-comment@v3 - with: - issue-number: ${{ github.event.pull_request.number }} - body-path: comment_body.md - - - name: Update comment - if: steps.fc.outputs.comment-id != '' && steps.run-benchmarks.outputs.benchmark_count != 0 - uses: peter-evans/create-or-update-comment@v3 - with: - comment-id: ${{ steps.fc.outputs.comment-id }} - body-path: comment_body.md - edit-mode: replace + - name: Install Hyperfine + uses: taiki-e/install-action@v2 + with: + tool: hyperfine@1.16 + + - name: Fetch base binary + uses: actions/cache/restore@v3 + with: + path: bin/cairo-vm-cli-base + key: binary-${{ github.event.pull_request.base.sha }} + + - name: Fetch HEAD binary + uses: actions/cache/restore@v3 + with: + path: bin/cairo-vm-cli-head + key: binary-${{ github.event.pull_request.head.sha }} + + - name: Fetch base programs + uses: actions/cache/restore@v3 + with: + path: base_programs/*.json + key: benchmarks-base-${{ needs.build-programs.outputs.benchmark-hashes-base }} + + - name: Fetch head programs + uses: actions/cache/restore@v3 + with: + path: head_programs/*.json + key: benchmarks-head-${{ needs.build-programs.outputs.benchmark-hashes-head }} + + - name: Benchmark ${{ matrix.program_state }} programs + id: run-benchmarks + run: | + sudo swapoff -a + mkdir target_programs + if [ 'modified' = ${{ matrix.program_state }} ]; then + BINS=head + for f in head_programs/*.json; do + # Only run new or modified benchmarks + if ! cmp -s ${f/head/base} $f; then + cp $f target_programs/ + fi + done + else + BINS="base,head" + for f in base_programs/*.json; do + # Only run unmodified benchmarks + if cmp -s ${f/base/head} $f; then + cp $f target_programs/ + fi + done + fi + find target_programs -name '*.json' -exec basename -s .json '{}' '+' | \ + sort | xargs -I '{program}' \ + hyperfine -N -r 10 --export-markdown "target_programs/{program}.md" \ + -L bin "$BINS" -n "{bin} {program}" \ + -s "cat ./bin/cairo-vm-cli-{bin} target_programs/{program}.json" \ + "./bin/cairo-vm-cli-{bin} --proof_mode --layout starknet_with_keccak \ + --memory_file /dev/null --trace_file /dev/null target_programs/{program}.json" + echo "benchmark_count=$(ls target_programs/*.md | wc -l)" >> $GITHUB_OUTPUT + + - name: Print tables + if: steps.run-benchmarks.outputs.benchmark_count != 0 + run: | + { + echo "Benchmark Results for ${{ matrix.program_state }} programs :rocket:" + for f in target_programs/*.md; do + echo + cat $f + done + } | tee -a comment_body.md + + - name: Find comment + if: ${{ steps.run-benchmarks.outputs.benchmark_count != 0 }} + uses: peter-evans/find-comment@v2 + id: fc + with: + issue-number: ${{ github.event.pull_request.number }} + comment-author: "github-actions[bot]" + body-includes: Benchmark Results for ${{ matrix.program_state }} programs + + - name: Create comment + if: steps.fc.outputs.comment-id == '' && steps.run-benchmarks.outputs.benchmark_count != 0 + uses: peter-evans/create-or-update-comment@v3 + with: + issue-number: ${{ github.event.pull_request.number }} + body-path: comment_body.md + permissions: write + + - name: Update comment + if: steps.fc.outputs.comment-id != '' && steps.run-benchmarks.outputs.benchmark_count != 0 + uses: peter-evans/create-or-update-comment@v3 + with: + comment-id: ${{ steps.fc.outputs.comment-id }} + body-path: comment_body.md + edit-mode: replace diff --git a/CHANGELOG.md b/CHANGELOG.md index b2a21be759..e5f28ff69e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## Cairo-VM Changelog +* feat: add Serialize and Deserialize derivation for Program. + #### Upcoming Changes * BREAKING: Partially Revert `Optimize trace relocation #906` [#1492](https://github.com/lambdaclass/cairo-vm/pull/1492) diff --git a/vm/src/hint_processor/hint_processor_definition.rs b/vm/src/hint_processor/hint_processor_definition.rs index 6e2912af3d..7d7ce0abcf 100644 --- a/vm/src/hint_processor/hint_processor_definition.rs +++ b/vm/src/hint_processor/hint_processor_definition.rs @@ -13,6 +13,7 @@ use crate::vm::vm_core::VirtualMachine; use super::builtin_hint_processor::builtin_hint_processor_definition::HintProcessorData; use felt::Felt252; +use serde::{Deserialize, Serialize}; #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; @@ -79,7 +80,7 @@ fn get_ids_data( } #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] pub struct HintReference { pub offset1: OffsetValue, pub offset2: OffsetValue, diff --git a/vm/src/serde/deserialize_program.rs b/vm/src/serde/deserialize_program.rs index 52c5e70a42..537d9ed8dc 100644 --- a/vm/src/serde/deserialize_program.rs +++ b/vm/src/serde/deserialize_program.rs @@ -126,7 +126,7 @@ impl Default for ApTracking { #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Identifier { pub pc: Option, - #[serde(rename(deserialize = "type"))] + #[serde(rename = "type")] pub type_: Option, #[serde(default)] #[serde(deserialize_with = "felt_from_number")] @@ -225,21 +225,41 @@ fn felt_from_number<'de, D>(deserializer: D) -> Result, D::Error where D: Deserializer<'de>, { - let n = Number::deserialize(deserializer)?; - match Felt252::parse_bytes(n.to_string().as_bytes(), 10) { - Some(x) => Ok(Some(x)), - None => { - // Handle de Number with scientific notation cases - // e.g.: n = Number(1e27) - let felt = deserialize_scientific_notation(n); - if felt.is_some() { - return Ok(felt); - } + // This value can be of 3 possible types + // Felt252, Number, None + #[derive(Serialize, Deserialize)] + #[serde(untagged)] + enum Tmp { + Felt252(Option), + SerializedFelt252(Felt252), + } + + let n: Tmp = Tmp::deserialize(deserializer)?; + + match n { + Tmp::Felt252(n) => { + match n { + Some(n) => { + match Felt252::parse_bytes(n.to_string().as_bytes(), 10) { + Some(x) => Ok(Some(x)), + None => { + // Handle de Number with scientific notation cases + // e.g.: n = Number(1e27) + let felt = deserialize_scientific_notation(n); + if felt.is_some() { + return Ok(felt); + } - Err(de::Error::custom(String::from( - "felt_from_number parse error", - ))) + Err(de::Error::custom(String::from( + "felt_from_number parse error", + ))) + } + } + } + None => Ok(None), + } } + Tmp::SerializedFelt252(value) => Ok(Some(value)), } } @@ -580,7 +600,7 @@ mod tests { "attributes": [], "debug_info": { "instruction_locations": {} - }, + }, "builtins": [], "data": [ "0x480680017fff8000", @@ -1107,7 +1127,7 @@ mod tests { "attributes": [], "debug_info": { "instruction_locations": {} - }, + }, "builtins": [], "data": [ ], @@ -1188,10 +1208,10 @@ mod tests { "start_pc": 402, "value": "SafeUint256: subtraction overflow" } - ], + ], "debug_info": { "instruction_locations": {} - }, + }, "builtins": [], "data": [ ], @@ -1245,7 +1265,7 @@ mod tests { let valid_json = r#" { "prime": "0x800000000000011000000000000000000000000000000000000000000000001", - "attributes": [], + "attributes": [], "debug_info": { "file_contents": {}, "instruction_locations": { @@ -1296,7 +1316,7 @@ mod tests { } } } - }, + }, "builtins": [], "data": [ ], @@ -1354,7 +1374,7 @@ mod tests { let valid_json = r#" { "prime": "0x800000000000011000000000000000000000000000000000000000000000001", - "attributes": [], + "attributes": [], "debug_info": { "file_contents": {}, "instruction_locations": { @@ -1401,7 +1421,7 @@ mod tests { } } } - }, + }, "builtins": [], "data": [ ], diff --git a/vm/src/types/program.rs b/vm/src/types/program.rs index 2ba347e6a2..c6251c1eb8 100644 --- a/vm/src/types/program.rs +++ b/vm/src/types/program.rs @@ -27,6 +27,7 @@ use crate::{ use cairo_lang_starknet::casm_contract_class::CasmContractClass; use core::num::NonZeroUsize; use felt::{Felt252, PRIME_STR}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; #[cfg(feature = "std")] use std::path::Path; @@ -55,18 +56,18 @@ use arbitrary::{Arbitrary, Unstructured}; // exceptional circumstances, such as when reconstructing a backtrace on execution // failures. // Fields in `Program` (other than `SharedProgramData` itself) are used by the main logic. -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub(crate) struct SharedProgramData { - pub(crate) data: Vec, - pub(crate) hints_collection: HintsCollection, - pub(crate) main: Option, +#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct SharedProgramData { + pub data: Vec, + pub hints_collection: HintsCollection, + pub main: Option, //start and end labels will only be used in proof-mode - pub(crate) start: Option, - pub(crate) end: Option, - pub(crate) error_message_attributes: Vec, - pub(crate) instruction_locations: Option>, - pub(crate) identifiers: HashMap, - pub(crate) reference_manager: Vec, + pub start: Option, + pub end: Option, + pub error_message_attributes: Vec, + pub instruction_locations: Option>, + pub identifiers: HashMap, + pub reference_manager: Vec, } #[cfg(all(feature = "arbitrary", feature = "std"))] @@ -102,8 +103,8 @@ impl<'a> Arbitrary<'a> for SharedProgramData { } } -#[derive(Clone, Default, Debug, PartialEq, Eq)] -pub(crate) struct HintsCollection { +#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub struct HintsCollection { hints: Vec, /// This maps a PC to the range of hints in `hints` that correspond to it. hints_ranges: Vec, @@ -176,11 +177,35 @@ impl From<&HintsCollection> for BTreeMap> { type HintRange = Option<(usize, NonZeroUsize)>; #[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(Arbitrary))] -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Program { - pub(crate) shared_program_data: Arc, - pub(crate) constants: HashMap, - pub(crate) builtins: Vec, + #[serde( + serialize_with = "serialize_shared_program_data", + deserialize_with = "deserialize_shared_program_data" + )] + pub shared_program_data: Arc, + pub constants: HashMap, + pub builtins: Vec, +} + +pub fn serialize_shared_program_data( + shared_program_data: &Arc, + serializer: S, +) -> Result +where + S: Serializer, +{ + shared_program_data.serialize(serializer) +} + +pub fn deserialize_shared_program_data<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + let shared_program_data = SharedProgramData::deserialize(deserializer)?; + Ok(Arc::new(shared_program_data)) } impl Program {