Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serialize and deserialize program #1458

Merged
merged 27 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
9edddbc
make `ProgramJson` serializable
kariy Jun 22, 2023
f398373
refactor: impl `From` `Program` for `ProgramJson`
kariy Jun 28, 2023
f9195e8
Restore lockfile
kariy Jun 28, 2023
73d8cd9
Merge branch 'main' into serialize-and-deserialize-program
pefontana Oct 6, 2023
4ab3344
rfc: implement From to convert HintsCollection to BTreeMap
Oppen Oct 9, 2023
c1bd539
add dummy test
pefontana Oct 9, 2023
5190028
Merge branch 'rfc/hints_collection_to_bmap' into serialize-and-deseri…
pefontana Oct 9, 2023
8d8cd6b
Fix ProgramJson::from(Program)
pefontana Oct 10, 2023
d78f9f7
Add integration test
pefontana Oct 10, 2023
39a8079
User &Program
pefontana Oct 11, 2023
c118e13
Implement ProgramDeserializer
pefontana Oct 11, 2023
eaafc56
add integration test
pefontana Oct 11, 2023
9f6f05d
Add error handling
pefontana Oct 12, 2023
fd791ed
Merge branch 'main' into serialize-and-deserialize-program
pefontana Oct 12, 2023
943c9d9
Restore Cargo.lock
pefontana Oct 12, 2023
fba991c
update CHANGELOG.md
pefontana Oct 12, 2023
0ddb38a
rename variables
pefontana Oct 12, 2023
348e3e3
Add doc
pefontana Oct 12, 2023
b1cbdba
Add doc
pefontana Oct 12, 2023
8a8c8a5
typo
pefontana Oct 12, 2023
deaee13
Merge branch 'main' into serialize-and-deserialize-program
pefontana Oct 12, 2023
9dda03f
Merge branch 'main' into serialize-and-deserialize-program
pefontana Oct 17, 2023
feeb7a5
Update CHANGELOG.md
pefontana Oct 17, 2023
7e8c529
Remove unnecesary code
pefontana Oct 17, 2023
3b7f0b6
update CHANGELOG.md
pefontana Oct 17, 2023
afa1dea
Remove ProgramJson Serialize
pefontana Oct 19, 2023
5df44fc
Merge branch 'main' into serialize-and-deserialize-program
pefontana Oct 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

#### Upcoming Changes

* feat: Implement the Serialize and Deserialize methods for the Program struct [#1458](https://github.com/lambdaclass/cairo-vm/pull/1458)

* feat: Implement a CLI to run cairo programs [#1370](https://github.com/lambdaclass/cairo-vm/pull/1370)

* fix: Fix string code of `BLAKE2S_ADD_UINT256` hint [#1454](https://github.com/lambdaclass/cairo-vm/pull/1454)
Expand Down
11 changes: 4 additions & 7 deletions vm/src/serde/deserialize_program.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ impl BuiltinName {
}

#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(Arbitrary, Clone))]
#[derive(Deserialize, Debug)]
#[derive(Serialize, Deserialize, Debug, PartialEq)]
fmoletta marked this conversation as resolved.
Show resolved Hide resolved
pub struct ProgramJson {
pub prime: String,
pub builtins: Vec<BuiltinName>,
Expand Down Expand Up @@ -195,13 +195,10 @@ fn arbitrary_parent_location(u: &mut Unstructured, depth: u8) -> arbitrary::Resu
})
}

#[cfg_attr(
all(feature = "arbitrary", feature = "std"),
derive(Arbitrary, Clone, Serialize)
)]
#[derive(Deserialize, Debug, PartialEq, Eq)]
#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(Arbitrary, Clone))]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
pub struct DebugInfo {
instruction_locations: HashMap<usize, InstructionLocation>,
pub(crate) instruction_locations: HashMap<usize, InstructionLocation>,
}

#[cfg_attr(all(feature = "arbitrary", feature = "std"), derive(Arbitrary))]
Expand Down
1 change: 1 addition & 0 deletions vm/src/serde/mod.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod deserialize_program;
mod deserialize_utils;
pub(crate) mod serialize_program;
327 changes: 327 additions & 0 deletions vm/src/serde/serialize_program.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,327 @@
use crate::stdlib::{
collections::{BTreeMap, HashMap},
prelude::*,
};

use felt::Felt252;
use serde::{Deserialize, Serialize};

use super::deserialize_program::{
ApTracking, Attribute, BuiltinName, DebugInfo, FlowTrackingData, HintParams, Identifier,
Member, ProgramJson, Reference, ReferenceManager, ValueAddress,
};
use crate::types::program::Program;
use crate::types::relocatable::MaybeRelocatable;

// This struct is used to Serialize and Deserialize a Program struct
// Their fields are equal to the ProgramJson
// but keeping the default Serialization and Deserialization traits implementation
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub(crate) struct ProgramSerializer {
pub prime: String,
pub builtins: Vec<BuiltinName>,
pub data: Vec<MaybeRelocatable>,
pub identifiers: HashMap<String, IdentifierSerializer>,
pub hints: BTreeMap<usize, Vec<HintParamsSerializer>>,
pub reference_manager: ReferenceManagerSerializer,
pub attributes: Vec<Attribute>,
pub debug_info: Option<DebugInfo>,
}

impl From<ProgramSerializer> for ProgramJson {
fn from(program_json: ProgramSerializer) -> ProgramJson {
let mut identifiers = HashMap::new();
for (key, identifier) in program_json.identifiers.clone() {
identifiers.insert(key, identifier.into());
}

let mut hints: BTreeMap<usize, Vec<HintParams>> = BTreeMap::new();
for (key, hint_params_vec) in &program_json.hints {
let mut new_hint_params_vec = Vec::new();
for hint_param in hint_params_vec {
new_hint_params_vec.push(hint_param.clone().into());
}
hints.insert(*key, new_hint_params_vec);
}

let mut reference_manager: ReferenceManager = ReferenceManager {
references: Vec::new(),
};

for reference in &program_json.reference_manager.references {
reference_manager.references.push(reference.clone().into());
}
ProgramJson {
prime: program_json.prime,
builtins: program_json.builtins,
data: program_json.data,
identifiers,
hints,
reference_manager,
attributes: program_json.attributes,
debug_info: program_json.debug_info,
}
}
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct HintParamsSerializer {
pub code: String,
pub accessible_scopes: Vec<String>,
pub flow_tracking_data: FlowTrackingDataSerializer,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct FlowTrackingDataSerializer {
pub ap_tracking: ApTracking,
pub reference_ids: HashMap<String, usize>,
}

impl From<FlowTrackingDataSerializer> for FlowTrackingData {
fn from(flow_tracking_data_serialer: FlowTrackingDataSerializer) -> FlowTrackingData {
FlowTrackingData {
ap_tracking: flow_tracking_data_serialer.ap_tracking,
reference_ids: flow_tracking_data_serialer.reference_ids,
}
}
}

impl From<FlowTrackingData> for FlowTrackingDataSerializer {
fn from(flow_tracking_data_serialer: FlowTrackingData) -> FlowTrackingDataSerializer {
FlowTrackingDataSerializer {
ap_tracking: flow_tracking_data_serialer.ap_tracking,
reference_ids: flow_tracking_data_serialer.reference_ids,
}
}
}

impl From<HintParamsSerializer> for HintParams {
fn from(hint_params_serializer: HintParamsSerializer) -> HintParams {
HintParams {
code: hint_params_serializer.code,
accessible_scopes: hint_params_serializer.accessible_scopes,
flow_tracking_data: hint_params_serializer.flow_tracking_data.into(),
}
}
}

impl From<HintParams> for HintParamsSerializer {
fn from(hint_params_serializer: HintParams) -> HintParamsSerializer {
HintParamsSerializer {
code: hint_params_serializer.code,
accessible_scopes: hint_params_serializer.accessible_scopes,
flow_tracking_data: hint_params_serializer.flow_tracking_data.into(),
}
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub(crate) struct IdentifierSerializer {
pub pc: Option<usize>,
pub type_: Option<String>,
#[serde(default)]
pub value: Option<Felt252>,

pub full_name: Option<String>,
pub members: Option<HashMap<String, Member>>,
pub cairo_type: Option<String>,
}

impl From<IdentifierSerializer> for Identifier {
fn from(identifier_serialer: IdentifierSerializer) -> Identifier {
Self {
pc: identifier_serialer.pc,
type_: identifier_serialer.type_,
value: identifier_serialer.value,
full_name: identifier_serialer.full_name,
members: identifier_serialer.members,
cairo_type: identifier_serialer.cairo_type,
}
}
}

impl From<Identifier> for IdentifierSerializer {
fn from(identifier_serialer: Identifier) -> IdentifierSerializer {
IdentifierSerializer {
pc: identifier_serialer.pc,
type_: identifier_serialer.type_,
value: identifier_serialer.value,
full_name: identifier_serialer.full_name,
members: identifier_serialer.members,
cairo_type: identifier_serialer.cairo_type,
}
}
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Default)]
pub struct ReferenceManagerSerializer {
pub references: Vec<ReferenceSerializer>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct ReferenceSerializer {
pub ap_tracking_data: ApTracking,
pub pc: Option<usize>,
pub value_address: ValueAddress,
}

impl From<Reference> for ReferenceSerializer {
fn from(reference: Reference) -> ReferenceSerializer {
ReferenceSerializer {
ap_tracking_data: reference.ap_tracking_data,
pc: reference.pc,
value_address: reference.value_address,
}
}
}

impl From<ReferenceSerializer> for Reference {
fn from(reference: ReferenceSerializer) -> Reference {
Reference {
ap_tracking_data: reference.ap_tracking_data,
pc: reference.pc,
value_address: reference.value_address,
}
}
}

impl From<&Program> for ProgramSerializer {
fn from(program: &Program) -> Self {
let references = program
.shared_program_data
.reference_manager
.clone()
.into_iter()
.map(|r| ReferenceSerializer {
value_address: ValueAddress {
offset1: r.offset1,
offset2: r.offset2,
dereference: r.dereference,
value_type: r.cairo_type.unwrap_or_default(),
},
ap_tracking_data: r.ap_tracking_data.unwrap_or_default(),
pc: None,
})
.collect::<Vec<_>>();

let mut identifiers = HashMap::new();
for (key, identifier) in program.shared_program_data.identifiers.clone() {
identifiers.insert(key, identifier.into());
}

let mut hints: BTreeMap<usize, Vec<HintParamsSerializer>> = BTreeMap::new();
for (key, hint_params_vec) in BTreeMap::from(&program.shared_program_data.hints_collection)
{
let mut new_hints_params = Vec::new();
for hint_params in hint_params_vec {
new_hints_params.push(hint_params.clone().into());
}
hints.insert(key, new_hints_params);
}

ProgramSerializer {
prime: program.prime().into(),
builtins: program.builtins.clone(),
data: program.shared_program_data.data.clone(),
identifiers,
hints,
attributes: program.shared_program_data.error_message_attributes.clone(),
debug_info: program
.shared_program_data
.instruction_locations
.clone()
.map(|instruction_locations| DebugInfo {
instruction_locations,
}),
reference_manager: ReferenceManagerSerializer { references },
}
}
}

#[cfg(test)]
mod tests {
use crate::serde::deserialize_program::parse_program_json;

use super::*;

#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::*;

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn program_json_from_program_test() {
let programs_bytes: Vec<Vec<u8>> = [
include_bytes!("../../../cairo_programs/_keccak.json").to_vec(),
include_bytes!("../../../cairo_programs/assert_nn.json").to_vec(),
include_bytes!("../../../cairo_programs/bitwise_recursion.json").to_vec(),
include_bytes!("../../../cairo_programs/blake2s_felts.json").to_vec(),
include_bytes!("../../../cairo_programs/cairo_finalize_keccak_block_size_1000.json")
.to_vec(),
include_bytes!("../../../cairo_programs/bitwise_recursion.json").to_vec(),
include_bytes!("../../../cairo_programs/_keccak.json").to_vec(),
include_bytes!("../../../cairo_programs/ec_double_slope.json").to_vec(),
include_bytes!("../../../cairo_programs/example_blake2s.json").to_vec(),
include_bytes!("../../../cairo_programs/fibonacci.json").to_vec(),
include_bytes!("../../../cairo_programs/integration.json").to_vec(),
include_bytes!("../../../cairo_programs/bitwise_recursion.json").to_vec(),
include_bytes!("../../../cairo_programs/keccak_integration_tests.json").to_vec(),
include_bytes!("../../../cairo_programs/math_integration_tests.json").to_vec(),
include_bytes!("../../../cairo_programs/pedersen_test.json").to_vec(),
include_bytes!("../../../cairo_programs/poseidon_hash.json").to_vec(),
include_bytes!("../../../cairo_programs/poseidon_multirun.json").to_vec(),
include_bytes!("../../../cairo_programs/reduce.json").to_vec(),
include_bytes!("../../../cairo_programs/secp_ec.json").to_vec(),
include_bytes!("../../../cairo_programs/sha256_test.json").to_vec(),
include_bytes!("../../../cairo_programs/uint256_integration_tests.json").to_vec(),
]
.to_vec();
for bytes in programs_bytes {
let original_program = Program::from_bytes(&bytes, Some("main")).unwrap();

let program_serializer = ProgramSerializer::from(&original_program);

let program_json = ProgramJson::from(program_serializer);

let new_program = parse_program_json(program_json, Some("main")).unwrap();

assert_eq!(&original_program, &new_program);
}
}

#[test]
#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
fn serialize_and_deserialize_programs() {
let programs_bytes: Vec<Vec<u8>> = [
include_bytes!("../../../cairo_programs/_keccak.json").to_vec(),
include_bytes!("../../../cairo_programs/assert_nn.json").to_vec(),
include_bytes!("../../../cairo_programs/bitwise_recursion.json").to_vec(),
include_bytes!("../../../cairo_programs/blake2s_felts.json").to_vec(),
include_bytes!("../../../cairo_programs/cairo_finalize_keccak_block_size_1000.json")
.to_vec(),
include_bytes!("../../../cairo_programs/bitwise_recursion.json").to_vec(),
include_bytes!("../../../cairo_programs/_keccak.json").to_vec(),
include_bytes!("../../../cairo_programs/ec_double_slope.json").to_vec(),
include_bytes!("../../../cairo_programs/example_blake2s.json").to_vec(),
include_bytes!("../../../cairo_programs/fibonacci.json").to_vec(),
include_bytes!("../../../cairo_programs/integration.json").to_vec(),
include_bytes!("../../../cairo_programs/bitwise_recursion.json").to_vec(),
include_bytes!("../../../cairo_programs/keccak_integration_tests.json").to_vec(),
include_bytes!("../../../cairo_programs/math_integration_tests.json").to_vec(),
include_bytes!("../../../cairo_programs/pedersen_test.json").to_vec(),
include_bytes!("../../../cairo_programs/poseidon_hash.json").to_vec(),
include_bytes!("../../../cairo_programs/poseidon_multirun.json").to_vec(),
include_bytes!("../../../cairo_programs/reduce.json").to_vec(),
include_bytes!("../../../cairo_programs/secp_ec.json").to_vec(),
include_bytes!("../../../cairo_programs/sha256_test.json").to_vec(),
include_bytes!("../../../cairo_programs/uint256_integration_tests.json").to_vec(),
]
.to_vec();

for bytes in programs_bytes {
let original_program = Program::from_bytes(&bytes, Some("main")).unwrap();
let program_serialized = original_program.serialize().unwrap();
let new_program = Program::deserialize(&program_serialized, Some("main")).unwrap();

assert_eq!(original_program, new_program);
}
}
}
Loading