Skip to content

Commit

Permalink
Merge pull request #7 from starkware-libs/dafna/add-committer
Browse files Browse the repository at this point in the history
chore: add committer
  • Loading branch information
dafnamatsry authored Jun 6, 2024
2 parents 2f2e227 + f40f287 commit 0bd3526
Show file tree
Hide file tree
Showing 68 changed files with 6,455 additions and 0 deletions.
34 changes: 34 additions & 0 deletions crates/committer/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
[package]
name = "committer"
version.workspace = true
edition.workspace = true
repository.workspace = true
license-file.workspace = true
description = "Computes and manages Starknet state."

[lints]
workspace = true

[features]
testing = []

[dev-dependencies]
pretty_assertions.workspace = true
rand.workspace = true
rstest.workspace = true

[dependencies]
async-recursion.workspace = true
bisection.workspace = true
derive_more.workspace = true
ethnum.workspace = true
hex.workspace = true
rand.workspace = true
rstest.workspace = true
serde.workspace = true
serde_json.workspace = true
starknet-types-core.workspace = true
strum.workspace = true
strum_macros.workspace = true
thiserror.workspace = true
tokio.workspace = true
3 changes: 3 additions & 0 deletions crates/committer/src/block_committer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
pub mod commit;
pub mod errors;
pub mod input;
49 changes: 49 additions & 0 deletions crates/committer/src/block_committer/commit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
use crate::block_committer::errors::BlockCommitmentError;
use crate::block_committer::input::{Input, StateDiff};
use crate::patricia_merkle_tree::filled_tree::forest::FilledForestImpl;
use crate::patricia_merkle_tree::node_data::leaf::LeafDataImpl;
use crate::patricia_merkle_tree::original_skeleton_tree::skeleton_forest::{
OriginalSkeletonForest, OriginalSkeletonForestImpl,
};
use crate::patricia_merkle_tree::original_skeleton_tree::tree::OriginalSkeletonTreeImpl;
use crate::patricia_merkle_tree::updated_skeleton_tree::hash_function::TreeHashFunctionImpl;
use crate::patricia_merkle_tree::updated_skeleton_tree::skeleton_forest::{
UpdatedSkeletonForest, UpdatedSkeletonForestImpl,
};
use crate::patricia_merkle_tree::updated_skeleton_tree::tree::UpdatedSkeletonTreeImpl;
use crate::storage::map_storage::MapStorage;

#[allow(dead_code)]
type BlockCommitmentResult<T> = Result<T, BlockCommitmentError<LeafDataImpl>>;

#[allow(dead_code)]
pub async fn commit_block(input: Input) -> BlockCommitmentResult<FilledForestImpl> {
let mut original_forest = OriginalSkeletonForestImpl::<OriginalSkeletonTreeImpl>::create(
MapStorage::from(input.storage),
input.contracts_trie_root_hash,
input.classes_trie_root_hash,
&input.current_contracts_trie_leaves,
&input.state_diff,
)?;

let updated_forest = UpdatedSkeletonForestImpl::<UpdatedSkeletonTreeImpl>::create(
&mut original_forest,
&StateDiff::skeleton_classes_updates(&input.state_diff.class_hash_to_compiled_class_hash),
&input.state_diff.skeleton_storage_updates(),
&input.current_contracts_trie_leaves,
&input.state_diff.address_to_class_hash,
&input.state_diff.address_to_nonce,
)?;

Ok(
FilledForestImpl::create::<UpdatedSkeletonTreeImpl, TreeHashFunctionImpl>(
updated_forest,
input.state_diff.actual_storage_updates(),
StateDiff::actual_classes_updates(&input.state_diff.class_hash_to_compiled_class_hash),
&input.current_contracts_trie_leaves,
&input.state_diff.address_to_class_hash,
&input.state_diff.address_to_nonce,
)
.await?,
)
}
9 changes: 9 additions & 0 deletions crates/committer/src/block_committer/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use thiserror::Error;

use crate::{forest_errors::ForestError, patricia_merkle_tree::node_data::leaf::LeafData};

#[derive(Debug, Error)]
pub enum BlockCommitmentError<L: LeafData> {
#[error(transparent)]
ForestError(#[from] ForestError<L>),
}
125 changes: 125 additions & 0 deletions crates/committer/src/block_committer/input.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
use crate::felt::Felt;
use crate::hash::hash_trait::HashOutput;
use crate::patricia_merkle_tree::filled_tree::node::{ClassHash, CompiledClassHash, Nonce};
use crate::patricia_merkle_tree::node_data::leaf::{
ContractState, LeafDataImpl, LeafModifications, SkeletonLeaf,
};
use crate::patricia_merkle_tree::types::NodeIndex;
use crate::storage::storage_trait::{StorageKey, StorageValue};
use std::collections::{HashMap, HashSet};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
// TODO(Nimrod, 1/6/2024): Swap to starknet-types-core types once implemented.
pub struct ContractAddress(pub Felt);

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
// TODO(Nimrod, 1/6/2024): Swap to starknet-types-core types once implemented.
pub struct StarknetStorageKey(pub Felt);

#[derive(Debug, Eq, PartialEq)]
pub struct StarknetStorageValue(pub Felt);

#[derive(Debug, Default, Eq, PartialEq)]
pub struct StateDiff {
pub address_to_class_hash: HashMap<ContractAddress, ClassHash>,
pub address_to_nonce: HashMap<ContractAddress, Nonce>,
pub class_hash_to_compiled_class_hash: HashMap<ClassHash, CompiledClassHash>,
pub storage_updates:
HashMap<ContractAddress, HashMap<StarknetStorageKey, StarknetStorageValue>>,
}

#[derive(Debug, Eq, PartialEq)]
pub struct Input {
pub storage: HashMap<StorageKey, StorageValue>,
/// All relevant information for the state diff commitment.
pub state_diff: StateDiff,
pub current_contracts_trie_leaves: HashMap<ContractAddress, ContractState>,
pub contracts_trie_root_hash: HashOutput,
pub classes_trie_root_hash: HashOutput,
}

impl StateDiff {
pub(crate) fn accessed_addresses(&self) -> HashSet<&ContractAddress> {
HashSet::from_iter(
self.address_to_class_hash
.keys()
.chain(self.address_to_nonce.keys())
.chain(self.storage_updates.keys()),
)
}

/// For each modified contract calculates it's actual storage updates.
pub(crate) fn skeleton_storage_updates(
&self,
) -> HashMap<ContractAddress, LeafModifications<SkeletonLeaf>> {
self.accessed_addresses()
.iter()
.map(|address| {
let updates = match self.storage_updates.get(address) {
Some(inner_updates) => inner_updates
.iter()
.map(|(key, value)| {
(
NodeIndex::from_starknet_storage_key(key),
SkeletonLeaf::from(value.0),
)
})
.collect(),
None => HashMap::new(),
};
(**address, updates)
})
.collect()
}

pub(crate) fn skeleton_classes_updates(
class_hash_to_compiled_class_hash: &HashMap<ClassHash, CompiledClassHash>,
) -> LeafModifications<SkeletonLeaf> {
class_hash_to_compiled_class_hash
.iter()
.map(|(class_hash, compiled_class_hash)| {
(
NodeIndex::from_class_hash(class_hash),
SkeletonLeaf::from(compiled_class_hash.0),
)
})
.collect()
}

pub(crate) fn actual_storage_updates(
&self,
) -> HashMap<ContractAddress, LeafModifications<LeafDataImpl>> {
self.accessed_addresses()
.iter()
.map(|address| {
let updates = match self.storage_updates.get(address) {
Some(inner_updates) => inner_updates
.iter()
.map(|(key, value)| {
(
NodeIndex::from_starknet_storage_key(key),
LeafDataImpl::StorageValue(value.0),
)
})
.collect(),
None => HashMap::new(),
};
(**address, updates)
})
.collect()
}

pub(crate) fn actual_classes_updates(
class_hash_to_compiled_class_hash: &HashMap<ClassHash, CompiledClassHash>,
) -> LeafModifications<LeafDataImpl> {
class_hash_to_compiled_class_hash
.iter()
.map(|(class_hash, compiled_class_hash)| {
(
NodeIndex::from_class_hash(class_hash),
LeafDataImpl::CompiledClassHash(*compiled_class_hash),
)
})
.collect()
}
}
129 changes: 129 additions & 0 deletions crates/committer/src/felt.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
use crate::patricia_merkle_tree::errors::TypesError;
use ethnum::U256;
use serde::{Deserialize, Serialize};
use starknet_types_core::felt::{Felt as StarknetTypesFelt, FromStrError};

#[derive(
Eq,
PartialEq,
Clone,
Copy,
Debug,
Default,
Hash,
derive_more::Add,
derive_more::Sub,
PartialOrd,
Ord,
Serialize,
Deserialize,
)]
pub struct Felt(pub StarknetTypesFelt);

#[macro_export]
macro_rules! impl_from {
($to:ty, $from:ty, $($other_from: ty),+) => {
$crate::impl_from!($to, $from);
$crate::impl_from!($to $(, $other_from)*);
};
($to:ty, $from:ty) => {
impl From<$from> for $to {
fn from(value: $from) -> Self {
Self(value.into())
}
}
};
}
impl_from!(Felt, StarknetTypesFelt, u128, u8);

impl From<Felt> for StarknetTypesFelt {
fn from(felt: Felt) -> Self {
felt.0
}
}

impl From<&Felt> for U256 {
fn from(felt: &Felt) -> Self {
U256::from_be_bytes(felt.to_bytes_be())
}
}

#[cfg(feature = "testing")]
impl TryFrom<&U256> for Felt {
type Error = TypesError<U256>;
fn try_from(value: &U256) -> Result<Self, Self::Error> {
if *value > U256::from(&Felt::MAX) {
return Err(TypesError::ConversionError {
from: *value,
to: "Felt",
reason: "value is bigger than felt::max",
});
}
Ok(Self::from_bytes_be(&value.to_be_bytes()))
}
}

impl std::ops::Mul for Felt {
type Output = Self;

fn mul(self, rhs: Self) -> Self {
Self(self.0 * rhs.0)
}
}

impl Felt {
pub const ZERO: Felt = Felt(StarknetTypesFelt::ZERO);
#[allow(dead_code)]
pub(crate) const ONE: Felt = Felt(StarknetTypesFelt::ONE);
#[allow(dead_code)]
pub(crate) const TWO: Felt = Felt(StarknetTypesFelt::TWO);
#[allow(dead_code)]
pub(crate) const THREE: Felt = Felt(StarknetTypesFelt::THREE);
pub const MAX: Felt = Felt(StarknetTypesFelt::MAX);

pub fn from_bytes_be_slice(bytes: &[u8]) -> Self {
Self(StarknetTypesFelt::from_bytes_be_slice(bytes))
}

/// Raises `self` to the power of `exponent`.
#[allow(dead_code)]
pub(crate) fn pow(&self, exponent: impl Into<u128>) -> Self {
Self(self.0.pow(exponent.into()))
}

#[allow(dead_code)]
pub(crate) fn bits(&self) -> u8 {
self.0
.bits()
.try_into()
// Should not fail as it takes less than 252 bits to represent a felt.
.expect("Unexpected error occurred when extracting bits of a Felt.")
}

pub fn from_bytes_be(bytes: &[u8; 32]) -> Self {
StarknetTypesFelt::from_bytes_be(bytes).into()
}

pub fn to_bytes_be(self) -> [u8; 32] {
self.0.to_bytes_be()
}

/// Parse a hex-encoded number into `Felt`.
pub fn from_hex(hex_string: &str) -> Result<Self, FromStrError> {
Ok(StarknetTypesFelt::from_hex(hex_string)?.into())
}

pub fn to_hex(&self) -> String {
self.0.to_hex_string()
}

// Convert to a 64-character hexadecimal string without the "0x" prefix.
pub fn to_fixed_hex_string(&self) -> String {
// Zero-pad the remaining string
self.0
.to_fixed_hex_string()
.strip_prefix("0x")
.unwrap_or("0")
.to_string()
}
}
28 changes: 28 additions & 0 deletions crates/committer/src/forest_errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use crate::block_committer::input::ContractAddress;
use crate::patricia_merkle_tree::filled_tree::errors::FilledTreeError;
use crate::patricia_merkle_tree::node_data::leaf::{LeafData, LeafDataImpl};
use crate::patricia_merkle_tree::original_skeleton_tree::errors::OriginalSkeletonTreeError;
use crate::patricia_merkle_tree::updated_skeleton_tree::errors::UpdatedSkeletonTreeError;

use thiserror::Error;
use tokio::task::JoinError;

pub(crate) type ForestResult<T> = Result<T, ForestError<LeafDataImpl>>;

#[derive(Debug, Error)]
pub enum ForestError<L: LeafData> {
#[error(transparent)]
OriginalSkeleton(#[from] OriginalSkeletonTreeError),
#[error(transparent)]
UpdatedSkeleton(#[from] UpdatedSkeletonTreeError),
#[error(transparent)]
Filled(#[from] FilledTreeError<L>),
#[error("Missing input: Couldn't find the storage trie's current state of address {0:?}")]
MissingContractCurrentState(ContractAddress),
#[error("Can't build storage trie's updated skeleton, because there is no original skeleton at address {0:?}")]
MissingOriginalSkeleton(ContractAddress),
#[error("Can't fill storage trie, because there is no updated skeleton at address {0:?}")]
MissingUpdatedSkeleton(ContractAddress),
#[error(transparent)]
JoinError(#[from] JoinError),
}
1 change: 1 addition & 0 deletions crates/committer/src/hash.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub mod hash_trait;
10 changes: 10 additions & 0 deletions crates/committer/src/hash/hash_trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::felt::Felt;

#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub struct HashOutput(pub Felt);

impl HashOutput {
#[allow(dead_code)]
pub(crate) const ZERO: HashOutput = HashOutput(Felt::ZERO);
pub(crate) const ROOT_OF_EMPTY_TREE: HashOutput = Self::ZERO;
}
Loading

0 comments on commit 0bd3526

Please sign in to comment.