From e642df47aa5bb824f7e740f610269537eea360ce Mon Sep 17 00:00:00 2001 From: Nimrod Weiss Date: Tue, 2 Apr 2024 08:36:24 +0300 Subject: [PATCH] chore: add deserialization types and basic casting implementation. need to add some tests --- Cargo.lock | 46 ++++--- Cargo.toml | 5 + crates/committer/Cargo.toml | 7 +- crates/committer/src/deserialization.rs | 4 + crates/committer/src/deserialization/cast.rs | 123 ++++++++++++++++++ .../committer/src/deserialization/errors.rs | 18 +++ crates/committer/src/deserialization/read.rs | 17 +++ crates/committer/src/deserialization/types.rs | 105 +++++++++++++++ crates/committer/src/lib.rs | 1 + .../src/patricia_merkle_tree/filled_node.rs | 4 + .../src/patricia_merkle_tree/types.rs | 1 + crates/committer/src/storage/storage_trait.rs | 5 +- crates/committer/src/types.rs | 5 +- 13 files changed, 319 insertions(+), 22 deletions(-) create mode 100644 crates/committer/src/deserialization.rs create mode 100644 crates/committer/src/deserialization/cast.rs create mode 100644 crates/committer/src/deserialization/errors.rs create mode 100644 crates/committer/src/deserialization/read.rs create mode 100644 crates/committer/src/deserialization/types.rs diff --git a/Cargo.lock b/Cargo.lock index e46d2d40..44c586b8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,9 +31,9 @@ checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" [[package]] name = "anyhow" -version = "1.0.81" +version = "1.0.82" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" [[package]] name = "ark-ff" @@ -176,9 +176,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.15.4" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "byte-slice-cast" @@ -367,8 +367,11 @@ name = "committer" version = "0.1.0-rc.0" dependencies = [ "pretty_assertions", + "serde", + "serde_json", "starknet-types-core", "starknet_api", + "thiserror", ] [[package]] @@ -572,9 +575,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.12" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" +checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" dependencies = [ "cfg-if", "js-sys", @@ -585,9 +588,9 @@ dependencies = [ [[package]] name = "good_lp" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa124423ded10046a849fa0ae9747c541895557f1af177e0890b09879e7e9e7d" +checksum = "3198bd13dea84c76a64621d6ee8ee26a4960a9a0d538eca95ca8f1320a469ac9" dependencies = [ "fnv", "minilp", @@ -724,6 +727,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.11" @@ -818,13 +830,12 @@ checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libredox" -version = "0.0.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ "bitflags 2.5.0", "libc", - "redox_syscall 0.4.1", ] [[package]] @@ -1177,9 +1188,9 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" +checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom", "libredox", @@ -1248,9 +1259,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47" [[package]] name = "ryu" @@ -1533,14 +1544,15 @@ dependencies = [ [[package]] name = "starknet_api" -version = "0.12.0-dev.0" +version = "0.12.0-dev.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d83d0987fc20eb9d39e39ce768534718174c30bc6532fa5c6e493a611b5f4303" +checksum = "4333a5d836f1018c87a0db36ffe673e8f79db4c40718dca65ad82430d9204b16" dependencies = [ "cairo-lang-starknet-classes", "derive_more", "hex", "indexmap 2.2.6", + "itertools 0.12.1", "once_cell", "primitive-types", "serde", diff --git a/Cargo.toml b/Cargo.toml index a01be854..5f04336a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,11 @@ license-file = "LICENSE" [workspace.dependencies] pretty_assertions = "1.2.1" +serde = { version = "1.0.197", features = ["derive"] } +serde_json = "1.0" +starknet-types-core = "0.0.11" +starknet_api = "0.12.0-dev.0" +thiserror = "1.0.24" [workspace.lints.rust] warnings = "deny" diff --git a/crates/committer/Cargo.toml b/crates/committer/Cargo.toml index ab5117a6..44526622 100644 --- a/crates/committer/Cargo.toml +++ b/crates/committer/Cargo.toml @@ -13,5 +13,8 @@ workspace = true pretty_assertions.workspace = true [dependencies] -starknet-types-core = "0.0.11" -starknet_api = "0.12.0-dev.0" +serde.workspace = true +serde_json.workspace = true +starknet-types-core.workspace = true +starknet_api.workspace = true +thiserror.workspace = true diff --git a/crates/committer/src/deserialization.rs b/crates/committer/src/deserialization.rs new file mode 100644 index 00000000..03d76f0f --- /dev/null +++ b/crates/committer/src/deserialization.rs @@ -0,0 +1,4 @@ +pub mod cast; +pub mod errors; +pub mod read; +pub mod types; diff --git a/crates/committer/src/deserialization/cast.rs b/crates/committer/src/deserialization/cast.rs new file mode 100644 index 00000000..c7a3af53 --- /dev/null +++ b/crates/committer/src/deserialization/cast.rs @@ -0,0 +1,123 @@ +use crate::deserialization::errors::DeserializationError; +use crate::deserialization::types::{ContractAddress, ContractState}; +use crate::deserialization::types::{ + Input, RawInput, StarknetStorageKey, StarknetStorageValue, StateDiff, +}; +use crate::hash::types::HashOutput; +use crate::patricia_merkle_tree::filled_node::{ClassHash, CompiledClassHash, Nonce}; +use crate::patricia_merkle_tree::types::TreeHeight; +use crate::storage::storage_trait::{StorageKey, StorageValue}; +use crate::types::Felt; + +use std::collections::HashMap; + +impl TryFrom for Input { + type Error = DeserializationError; + fn try_from(raw_input: RawInput) -> Result { + let mut storage = HashMap::new(); + for entry in raw_input.storage { + add_unique( + &mut storage, + "storage", + StorageKey(entry.key), + StorageValue(entry.value), + )?; + } + + let mut address_to_class_hash = HashMap::new(); + for entry in raw_input.state_diff.address_to_class_hash { + add_unique( + &mut address_to_class_hash, + "address to class hash", + ContractAddress(Felt::from_bytes_be_slice(&entry.key)), + ClassHash(Felt::from_bytes_be_slice(&entry.value)), + )?; + } + + let mut address_to_nonce = HashMap::new(); + for entry in raw_input.state_diff.address_to_nonce { + add_unique( + &mut address_to_nonce, + "address to nonce", + ContractAddress(Felt::from_bytes_be_slice(&entry.key)), + Nonce(Felt::from_bytes_be_slice(&entry.value)), + )?; + } + + let mut class_hash_to_compiled_class_hash = HashMap::new(); + for entry in raw_input.state_diff.class_hash_to_compiled_class_hash { + add_unique( + &mut class_hash_to_compiled_class_hash, + "class hash to compiled class hash", + ClassHash(Felt::from_bytes_be_slice(&entry.key)), + CompiledClassHash(Felt::from_bytes_be_slice(&entry.value)), + )?; + } + + let mut current_contract_state_leaves = HashMap::new(); + for entry in raw_input.state_diff.current_contract_state_leaves { + add_unique( + &mut current_contract_state_leaves, + "current contract state leaves", + ContractAddress(Felt::from_bytes_be_slice(&entry.address)), + ContractState { + nonce: Nonce(Felt::from_bytes_be_slice(&entry.nonce)), + class_hash: ClassHash(Felt::from_bytes_be_slice(&entry.class_hash)), + storage_root_hash: HashOutput(Felt::from_bytes_be_slice( + &entry.storage_root_hash, + )), + }, + )?; + } + + let mut storage_updates = HashMap::new(); + for outer_entry in raw_input.state_diff.storage_updates { + let inner_map = outer_entry + .storage_updates + .iter() + .map(|inner_entry| { + ( + StarknetStorageKey(Felt::from_bytes_be_slice(&inner_entry.key)), + StarknetStorageValue(Felt::from_bytes_be_slice(&inner_entry.value)), + ) + }) + .collect(); + add_unique( + &mut storage_updates, + "starknet storage updates", + ContractAddress(Felt::from_bytes_be_slice(&outer_entry.address)), + inner_map, + )?; + } + + Ok(Input { + storage, + state_diff: StateDiff { + address_to_class_hash, + address_to_nonce, + class_hash_to_compiled_class_hash, + current_contract_state_leaves, + storage_updates, + }, + tree_height: TreeHeight(raw_input.tree_height), + }) + } +} + +fn add_unique( + map: &mut HashMap, + map_name: &str, + key: K, + value: V, +) -> Result<(), DeserializationError> +where + K: std::cmp::Eq + std::hash::Hash + std::fmt::Debug, +{ + if map.contains_key(&key) { + return Err(DeserializationError::KeyDuplicate(format!( + "{map_name}: {key:?}" + ))); + } + map.insert(key, value); + Ok(()) +} diff --git a/crates/committer/src/deserialization/errors.rs b/crates/committer/src/deserialization/errors.rs new file mode 100644 index 00000000..09f5f722 --- /dev/null +++ b/crates/committer/src/deserialization/errors.rs @@ -0,0 +1,18 @@ +use std::fmt::Debug; + +use thiserror::Error; + +#[allow(dead_code)] +#[derive(Debug, Error)] +pub(crate) enum DeserializationError { + #[error("There is a key duplicate at {0} mapping.")] + KeyDuplicate(String), + #[error("Couldn't read and parse the given input JSON.")] + ParsingError, +} + +impl From for DeserializationError { + fn from(_: serde_json::Error) -> Self { + DeserializationError::ParsingError + } +} diff --git a/crates/committer/src/deserialization/read.rs b/crates/committer/src/deserialization/read.rs new file mode 100644 index 00000000..e980b32c --- /dev/null +++ b/crates/committer/src/deserialization/read.rs @@ -0,0 +1,17 @@ +use crate::deserialization::types::Input; +use crate::deserialization::types::RawInput; + +use crate::deserialization::errors::DeserializationError; +#[allow(dead_code)] +type DeserializationResult = Result; +#[allow(dead_code)] +pub(crate) fn parse_input(input: String) -> DeserializationResult { + let raw_input: RawInput = serde_json::from_str(&input)?; + raw_input.try_into() +} + +#[cfg(test)] +#[allow(dead_code)] +fn test_input_parsing() { + todo!() +} diff --git a/crates/committer/src/deserialization/types.rs b/crates/committer/src/deserialization/types.rs new file mode 100644 index 00000000..b6f1a231 --- /dev/null +++ b/crates/committer/src/deserialization/types.rs @@ -0,0 +1,105 @@ +use crate::hash::types::HashOutput; +use crate::patricia_merkle_tree::filled_node::{ClassHash, CompiledClassHash, Nonce}; +use crate::patricia_merkle_tree::types::TreeHeight; +use crate::storage::storage_trait::{StorageKey, StorageValue}; +use crate::types::Felt; +use serde::Deserialize; +use std::collections::HashMap; + +type RawFelt = [u8; 32]; +#[derive(PartialEq, Eq, Hash, Debug)] +// TODO(Nimrod, 1/6/2024): Swap to starknet-types-core types once implemented. +pub(crate) struct ContractAddress(pub Felt); +#[derive(PartialEq, Eq, Hash)] +// TODO(Nimrod, 1/6/2024): Swap to starknet-types-core types once implemented. +pub(crate) struct StarknetStorageKey(pub Felt); +#[allow(dead_code)] +pub(crate) struct StarknetStorageValue(pub Felt); + +#[derive(Deserialize, Debug)] +#[allow(dead_code)] +/// Input to the committer. +pub(crate) struct RawInput { + /// Storage. Will be casted to HashMap, Vec> to simulate DB access. + pub storage: Vec, + /// All relevant information for the state diff commitment. + pub state_diff: RawStateDiff, + /// The height of the patricia tree. + // TODO(Nimrod,20/4/2024): Strong assumption - all trees have same height. How can I get + // rid of it? + pub tree_height: u8, +} + +#[allow(dead_code)] +pub(crate) struct Input { + pub storage: HashMap, + pub state_diff: StateDiff, + pub tree_height: TreeHeight, +} + +#[derive(Deserialize, Debug)] +#[allow(dead_code)] +/// Fact storage entry. +pub(crate) struct RawStorageEntry { + pub key: Vec, + pub value: Vec, +} + +#[derive(Deserialize, Debug)] +#[allow(dead_code)] +pub(crate) struct RawFeltMapEntry { + pub key: RawFelt, + pub value: RawFelt, +} + +#[derive(Deserialize, Debug)] +#[allow(dead_code)] +/// Represents storage updates. Later will be casted to HashMap> entry. +pub(crate) struct RawStorageUpdates { + pub address: RawFelt, + pub storage_updates: Vec, +} + +#[derive(Deserialize, Debug)] +#[allow(dead_code)] +/// Represents current state leaf at the contract state tree. Later will be casted to +/// HashMap entry. +pub(crate) struct RawContractStateLeaf { + pub address: RawFelt, + pub nonce: RawFelt, + pub storage_root_hash: RawFelt, + pub class_hash: RawFelt, +} + +#[derive(Deserialize, Debug)] +#[allow(dead_code)] +/// Represents state diff. +pub(crate) struct RawStateDiff { + /// Will be casted to HashMap. + pub address_to_class_hash: Vec, + /// Will be casted to HashMap. + pub address_to_nonce: Vec, + /// Will be casted to HashMap. + pub class_hash_to_compiled_class_hash: Vec, + /// Will be casted to HashMap>. + pub storage_updates: Vec, + /// Will be casted to HashMap. + pub current_contract_state_leaves: Vec, +} + +#[allow(dead_code)] +pub(crate) struct StateDiff { + pub address_to_class_hash: HashMap, + pub address_to_nonce: HashMap, + pub class_hash_to_compiled_class_hash: HashMap, + pub current_contract_state_leaves: HashMap, + pub storage_updates: + HashMap>, +} + +#[allow(dead_code)] +pub(crate) struct ContractState { + pub nonce: Nonce, + pub class_hash: ClassHash, + pub storage_root_hash: HashOutput, +} diff --git a/crates/committer/src/lib.rs b/crates/committer/src/lib.rs index 10bc3e47..992e295e 100644 --- a/crates/committer/src/lib.rs +++ b/crates/committer/src/lib.rs @@ -1,3 +1,4 @@ +pub mod deserialization; pub mod hash; pub mod patricia_merkle_tree; pub mod storage; diff --git a/crates/committer/src/patricia_merkle_tree/filled_node.rs b/crates/committer/src/patricia_merkle_tree/filled_node.rs index 6cd72000..2ebbd693 100644 --- a/crates/committer/src/patricia_merkle_tree/filled_node.rs +++ b/crates/committer/src/patricia_merkle_tree/filled_node.rs @@ -2,10 +2,14 @@ use crate::patricia_merkle_tree::types::{EdgeData, LeafDataTrait}; use crate::{hash::types::HashOutput, types::Felt}; // TODO(Nimrod, 1/6/2024): Swap to starknet-types-core types once implemented. #[allow(dead_code)] +#[derive(Eq, PartialEq, Hash, Debug)] pub(crate) struct ClassHash(pub Felt); #[allow(dead_code)] pub(crate) struct Nonce(pub Felt); +#[allow(dead_code)] +pub(crate) struct CompiledClassHash(pub Felt); + #[allow(dead_code)] /// A node in a Patricia-Merkle tree which was modified during an update. pub(crate) struct FilledNode { diff --git a/crates/committer/src/patricia_merkle_tree/types.rs b/crates/committer/src/patricia_merkle_tree/types.rs index bf3ca7b2..16d04859 100644 --- a/crates/committer/src/patricia_merkle_tree/types.rs +++ b/crates/committer/src/patricia_merkle_tree/types.rs @@ -13,6 +13,7 @@ pub(crate) trait TreeHashFunction { // TODO(Amos, 01/05/2024): Implement types for NodeIndex, EdgePath, EdgePathLength #[allow(dead_code)] +#[derive(Eq, PartialEq, Hash)] pub(crate) struct NodeIndex(pub Felt); #[allow(dead_code)] diff --git a/crates/committer/src/storage/storage_trait.rs b/crates/committer/src/storage/storage_trait.rs index 788021bb..09f98f71 100644 --- a/crates/committer/src/storage/storage_trait.rs +++ b/crates/committer/src/storage/storage_trait.rs @@ -2,10 +2,11 @@ use crate::storage::errors::StorageError; use std::collections::HashMap; #[allow(dead_code)] -pub(crate) struct StorageKey(Vec); +#[derive(PartialEq, Eq, Hash, Debug)] +pub(crate) struct StorageKey(pub Vec); #[allow(dead_code)] -pub(crate) struct StorageValue(Vec); +pub(crate) struct StorageValue(pub Vec); pub(crate) trait Storage { /// Returns value from storage, if it exists. diff --git a/crates/committer/src/types.rs b/crates/committer/src/types.rs index e70ebf3b..f7ddf92d 100644 --- a/crates/committer/src/types.rs +++ b/crates/committer/src/types.rs @@ -1,6 +1,6 @@ use starknet_types_core::felt::Felt as StarknetTypesFelt; -#[derive(Eq, PartialEq)] +#[derive(Eq, PartialEq, Debug, Hash)] pub(crate) struct Felt(StarknetTypesFelt); impl From for Felt { @@ -17,4 +17,7 @@ impl From for StarknetTypesFelt { impl Felt { pub const ZERO: Felt = Felt(StarknetTypesFelt::ZERO); + pub(crate) fn from_bytes_be_slice(bytes: &[u8]) -> Self { + Self(StarknetTypesFelt::from_bytes_be_slice(bytes)) + } }