Skip to content

Commit

Permalink
feat(storage): ✨ StateDiffs are now stored and can be retrieved to …
Browse files Browse the repository at this point in the history
…reconstruct block state. (#78)
  • Loading branch information
Trantorian1 authored Apr 26, 2024
1 parent 566e7be commit d606a56
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 202 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ git # Deoxys Changelog

## Next release

- feat(storage): removed dependance on `StateUpdateWrapper`
- feat(storage): state diff are now stored for each block
- CI: fix toolchain
- CI: add `cargo test` on PR
- refactor: remove dead code on `Struct Starknet<..>`
- feat(storage): state diff are now stored for each block
- fix: verify_l2
- feat(rpc): remove duplicated code, add mod 'utils'
- feat(storage): started migrating storage to the bonsai-lib
Expand Down
50 changes: 24 additions & 26 deletions crates/client/sync/src/commitments/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use blockifier::state::cached_state::CommitmentStateDiff;
use indexmap::IndexMap;
use lazy_static::lazy_static;
use mc_db::storage_handler::{self, DeoxysStorageError, StorageView};
use mp_block::state_update::StateUpdateWrapper;
use mp_convert::field_element::FromFieldElement;
use mp_felt::Felt252Wrapper;
use mp_hashers::pedersen::PedersenHasher;
use mp_hashers::poseidon::PoseidonHasher;
Expand All @@ -12,6 +12,9 @@ use starknet_api::core::{ClassHash, CompiledClassHash, ContractAddress, Nonce};
use starknet_api::hash::StarkFelt;
use starknet_api::state::StorageKey;
use starknet_api::transaction::{Event, Transaction};
use starknet_core::types::{
ContractStorageDiffItem, DeclaredClassItem, DeployedContractItem, NonceUpdate, StateUpdate, StorageEntry,
};
use starknet_ff::FieldElement;
use starknet_types_core::felt::Felt;

Expand Down Expand Up @@ -46,54 +49,49 @@ pub fn calculate_commitments(
)
}

/// Builds a `CommitmentStateDiff` from the `StateUpdateWrapper`.
/// Aggregates all the changes from last state update in a way that is easy to access
/// when computing the state root
///
/// # Arguments
///
/// * `StateUpdateWrapper` - The last state update fetched and formated.
///
/// # Returns
///
/// The commitment state diff as a `CommitmentStateDiff`.
pub fn build_commitment_state_diff(state_update_wrapper: StateUpdateWrapper) -> CommitmentStateDiff {
/// * `state_update`: The last state update fetched from the sequencer
pub fn build_commitment_state_diff(state_update: &StateUpdate) -> CommitmentStateDiff {
let mut commitment_state_diff = CommitmentStateDiff {
address_to_class_hash: IndexMap::new(),
address_to_nonce: IndexMap::new(),
storage_updates: IndexMap::new(),
class_hash_to_compiled_class_hash: IndexMap::new(),
};

for deployed_contract in state_update_wrapper.state_diff.deployed_contracts.iter() {
let address = ContractAddress::from(deployed_contract.address);
let class_hash = if address == ContractAddress::from(Felt252Wrapper::ONE) {
for DeployedContractItem { address, class_hash } in state_update.state_diff.deployed_contracts.iter() {
let address = ContractAddress::from_field_element(address);
let class_hash = if address == ContractAddress::from_field_element(FieldElement::ZERO) {
// System contracts doesnt have class hashes
ClassHash::from(Felt252Wrapper::ZERO)
ClassHash::from_field_element(FieldElement::ZERO)
} else {
ClassHash::from(deployed_contract.class_hash)
ClassHash::from_field_element(class_hash)
};
commitment_state_diff.address_to_class_hash.insert(address, class_hash);
}

for (address, nonce) in state_update_wrapper.state_diff.nonces.iter() {
let contract_address = ContractAddress::from(*address);
let nonce_value = Nonce::from(*nonce);
for NonceUpdate { contract_address, nonce } in state_update.state_diff.nonces.iter() {
let contract_address = ContractAddress::from_field_element(contract_address);
let nonce_value = Nonce::from_field_element(nonce);
commitment_state_diff.address_to_nonce.insert(contract_address, nonce_value);
}

for (address, storage_diffs) in state_update_wrapper.state_diff.storage_diffs.iter() {
let contract_address = ContractAddress::from(*address);
for ContractStorageDiffItem { address, storage_entries } in state_update.state_diff.storage_diffs.iter() {
let contract_address = ContractAddress::from_field_element(address);
let mut storage_map = IndexMap::new();
for storage_diff in storage_diffs.iter() {
let key = StorageKey::from(storage_diff.key);
let value = StarkFelt::from(storage_diff.value);
for StorageEntry { key, value } in storage_entries.iter() {
let key = StorageKey::from_field_element(key);
let value = StarkFelt::from_field_element(value);
storage_map.insert(key, value);
}
commitment_state_diff.storage_updates.insert(contract_address, storage_map);
}

for declared_class in state_update_wrapper.state_diff.declared_classes.iter() {
let class_hash = ClassHash::from(declared_class.class_hash);
let compiled_class_hash = CompiledClassHash::from(declared_class.compiled_class_hash);
for DeclaredClassItem { class_hash, compiled_class_hash } in state_update.state_diff.declared_classes.iter() {
let class_hash = ClassHash::from_field_element(class_hash);
let compiled_class_hash = CompiledClassHash::from_field_element(compiled_class_hash);
commitment_state_diff.class_hash_to_compiled_class_hash.insert(class_hash, compiled_class_hash);
}

Expand Down
28 changes: 13 additions & 15 deletions crates/client/sync/src/fetch/fetchers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@ use mc_db::storage_handler;
use mc_db::storage_handler::StorageView;
use mp_block::DeoxysBlock;
use mp_contract::class::{ContractClassData, ContractClassWrapper};
use mp_convert::state_update::ToStateUpdateCore;
use sp_core::H160;
use starknet_api::core::ClassHash;
use starknet_api::hash::StarkFelt;
use starknet_core::types::BlockId as BlockIdCore;
use starknet_core::types::{BlockId as BlockIdCore, DeclaredClassItem, DeployedContractItem, StateUpdate};
use starknet_ff::FieldElement;
use starknet_providers::sequencer::models as p;
use starknet_providers::sequencer::models::state_update::{DeclaredContract, DeployedContract};
use starknet_providers::sequencer::models::{BlockId, StateUpdate};
use starknet_providers::sequencer::models::BlockId;
use starknet_providers::{Provider, ProviderError, SequencerGatewayProvider};
use tokio::task::JoinSet;
use url::Url;

use crate::l2::L2SyncError;
use crate::utility::block_hash_deoxys;

/// The configuration of the worker responsible for fetching new blocks and state updates from the
/// feeder.
Expand Down Expand Up @@ -104,9 +103,8 @@ async fn fetch_state_and_class_update(
) -> Result<(StateUpdate, Vec<ContractClassData>), L2SyncError> {
// Children tasks need StateUpdate as an Arc, because of task spawn 'static requirement
// We make an Arc, and then unwrap the StateUpdate out of the Arc
let state_update = Arc::new(fetch_state_update(provider, block_number).await?);
let class_update = fetch_class_update(provider, &state_update).await?;
let state_update = Arc::try_unwrap(state_update).expect("arc should not be aliased");
let state_update = fetch_state_update(provider, block_number).await?;
let class_update = fetch_class_update(provider, &state_update, block_number).await?;

Ok((state_update, class_update))
}
Expand All @@ -118,28 +116,29 @@ async fn fetch_state_update(
) -> Result<StateUpdate, L2SyncError> {
let state_update = provider.get_state_update(BlockId::Number(block_number)).await?;

Ok(state_update)
Ok(state_update.to_state_update_core())
}

/// retrieves class updates from Starknet sequencer
async fn fetch_class_update(
provider: &SequencerGatewayProvider,
state_update: &Arc<StateUpdate>,
state_update: &StateUpdate,
block_number: u64,
) -> Result<Vec<ContractClassData>, L2SyncError> {
let missing_classes: Vec<&FieldElement> = std::iter::empty()
.chain(
state_update
.state_diff
.deployed_contracts
.iter()
.map(|DeployedContract { address: _, class_hash }| class_hash),
.map(|DeployedContractItem { address: _, class_hash }| class_hash),
)
.chain(
state_update
.state_diff
.declared_classes
.iter()
.map(|DeclaredContract { class_hash, compiled_class_hash: _ }| class_hash),
.map(|DeclaredClassItem { class_hash, compiled_class_hash: _ }| class_hash),
)
.unique()
.filter(|class_hash| is_missing_class(class_hash))
Expand All @@ -149,9 +148,8 @@ async fn fetch_class_update(

let mut task_set = missing_classes.into_iter().fold(JoinSet::new(), |mut set, class_hash| {
let provider = Arc::clone(&arc_provider);
let state_update = Arc::clone(state_update);
let class_hash = *class_hash;
set.spawn(async move { fetch_class(class_hash, block_hash_deoxys(&state_update), &provider).await });
set.spawn(async move { fetch_class(class_hash, block_number, &provider).await });
set
});

Expand All @@ -168,10 +166,10 @@ async fn fetch_class_update(
/// of the current type hell this needs to be converted into a blockifier equivalent
async fn fetch_class(
class_hash: FieldElement,
block_hash: FieldElement,
block_number: u64,
provider: &SequencerGatewayProvider,
) -> Result<ContractClassData, L2SyncError> {
let core_class = provider.get_class(BlockIdCore::Hash(block_hash), class_hash).await?;
let core_class = provider.get_class(BlockIdCore::Number(block_number), class_hash).await?;

// Core classes have to be converted into Blockifier classes to gain support
// for Substrate [`Encode`] and [`Decode`] traits
Expand Down
14 changes: 5 additions & 9 deletions crates/client/sync/src/l2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,17 @@ use std::sync::{Arc, RwLock};
use futures::prelude::*;
use lazy_static::lazy_static;
use mc_db::storage_updates::{store_class_update, store_state_update};
use mp_block::state_update::StateUpdateWrapper;
use mp_block::DeoxysBlock;
use mp_contract::class::ClassUpdateWrapper;
use mp_convert::state_update::ToStateUpdateCore;
use mp_felt::Felt252Wrapper;
use mp_types::block::{DBlockT, DHashT};
use serde::Deserialize;
use sp_blockchain::HeaderBackend;
use sp_core::H256;
use starknet_api::hash::{StarkFelt, StarkHash};
use starknet_core::types::{PendingStateUpdate, StarknetError};
use starknet_core::types::{PendingStateUpdate, StarknetError, StateUpdate};
use starknet_ff::FieldElement;
use starknet_providers::sequencer::models::{BlockId, StateUpdate};
use starknet_providers::sequencer::models::BlockId;
use starknet_providers::{ProviderError, SequencerGatewayProvider};
use thiserror::Error;
use tokio::sync::mpsc;
Expand Down Expand Up @@ -233,7 +231,7 @@ pub async fn sync<C>(
// Now send state_update, which moves it. This will be received
// by QueryBlockConsensusDataProvider in deoxys/crates/node/src/service.rs
state_update_sender
.send((block_n, state_update.to_state_update_core()))
.send((block_n, state_update))
.await
.expect("state updater is not running");
},
Expand Down Expand Up @@ -308,11 +306,9 @@ pub fn update_l2(state_update: L2StateUpdate) {

/// Verify and update the L2 state according to the latest state update
pub fn verify_l2(block_number: u64, state_update: &StateUpdate) -> StarkFelt {
let state_update_wrapper = StateUpdateWrapper::from(state_update);

let csd = build_commitment_state_diff(state_update_wrapper);
let csd = build_commitment_state_diff(state_update);
let state_root = update_state_root(csd, block_number);
let block_hash = state_update.block_hash.expect("Block hash not found in state update");
let block_hash = state_update.block_hash;

update_l2(L2StateUpdate {
block_number,
Expand Down
8 changes: 6 additions & 2 deletions crates/client/sync/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ pub mod starknet_sync_worker {
use std::sync::Arc;

use mp_block::DeoxysBlock;
use mp_convert::state_update::ToStateUpdateCore;
use reqwest::Url;
use sp_blockchain::HeaderBackend;
use starknet_providers::sequencer::models::BlockId;
Expand Down Expand Up @@ -53,8 +54,11 @@ pub mod starknet_sync_worker {
);

if starting_block == 1 {
let state_update =
provider.get_state_update(BlockId::Number(0)).await.expect("getting state update for genesis block");
let state_update = provider
.get_state_update(BlockId::Number(0))
.await
.expect("getting state update for genesis block")
.to_state_update_core();
verify_l2(0, &state_update);
}

Expand Down
24 changes: 0 additions & 24 deletions crates/client/sync/src/utils/utility.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,13 @@ use std::sync::RwLock;
use std::thread::sleep;
use std::time::Duration;

use bitvec::order::Msb0;
use bitvec::view::AsBits;
use ethers::types::{I256, U256};
use lazy_static::lazy_static;
use mp_types::block::DBlockT;
use rand::seq::SliceRandom;
use rand::thread_rng;
use reqwest::header;
use serde_json::{json, Value};
use sp_blockchain::HeaderBackend;
use sp_core::H256;
use sp_runtime::traits::UniqueSaturatedInto;
use starknet_api::hash::StarkFelt;
use starknet_ff::FieldElement;
use starknet_providers::sequencer::models::StateUpdate;

use crate::fetch::fetchers::FetchConfig;
use crate::l1::{L1StateUpdate, LogStateUpdate};
Expand Down Expand Up @@ -152,19 +144,3 @@ pub fn convert_log_state_update(log_state_update: LogStateUpdate) -> Result<L1St

Ok(L1StateUpdate { block_number, global_root, block_hash })
}

/// Retrieves Deoxys block hash from state update
pub fn block_hash_deoxys(state_update: &StateUpdate) -> FieldElement {
state_update.block_hash.unwrap()
}

/// Retrieves Substrate block hash from rpc client
pub fn block_hash_substrate<C>(client: &C, block_number: u64) -> Option<H256>
where
C: HeaderBackend<DBlockT>,
{
client
.hash(UniqueSaturatedInto::unique_saturated_into(block_number))
.unwrap()
.map(|hash| H256::from_slice(hash.as_bits::<Msb0>().to_bitvec().as_raw_slice()))
}
1 change: 0 additions & 1 deletion crates/primitives/block/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use alloc::vec::Vec;

mod header;
mod ordered_events;
pub mod state_update;
pub use header::Header;
use mp_felt::Felt252Wrapper;
use mp_hashers::HasherT;
Expand Down
Loading

0 comments on commit d606a56

Please sign in to comment.