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

chore: remove global state root #1039

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 1 addition & 1 deletion .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ jobs:
sudo apt-get install -y clang llvm libudev-dev protobuf-compiler
- name: Build the project
run: |
cargo build --release --workspace --features madara-state-root
cargo build --release --workspace
- name: Run benchmark
run: |
cd benchmarking
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rust-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,4 @@ jobs:

- name: Build the project
run: |
cargo build --release --workspace --features madara-state-root
cargo build --release --workspace
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
- ci: fix docker and binaries build
- ci: don't enforce changelog on PR's with label `dependencies`
- feat: rebase of core deps and 0.12.1
- chore: remove global state root

## v0.1.0

Expand Down
9 changes: 0 additions & 9 deletions crates/client/rpc-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ use serde_with::serde_as;
mod constants;
pub mod utils;

pub mod types;

use starknet_core::serde::unsigned_field_element::UfeHex;
use starknet_core::types::{
BlockHashAndNumber, BlockId, BroadcastedDeclareTransaction, BroadcastedDeployAccountTransaction,
Expand All @@ -26,8 +24,6 @@ use starknet_core::types::{
StateUpdate, SyncStatusType, Transaction,
};

use crate::types::{RpcGetProofInput, RpcGetProofOutput};

#[serde_as]
#[derive(Serialize, Deserialize)]
pub struct Felt(#[serde_as(as = "UfeHex")] pub FieldElement);
Expand Down Expand Up @@ -140,9 +136,4 @@ pub trait StarknetRpcApi {
/// Returns the receipt of a transaction by transaction hash.
#[method(name = "getTransactionReceipt")]
fn get_transaction_receipt(&self, transaction_hash: FieldElement) -> RpcResult<MaybePendingTransactionReceipt>;

/// Returns all the necessary data to trustlessly verify storage slots for a particular
/// contract.
#[method(name = "getProof")]
fn get_proof(&self, get_proof_input: RpcGetProofInput) -> RpcResult<RpcGetProofOutput>;
}
58 changes: 0 additions & 58 deletions crates/client/rpc-core/src/types.rs

This file was deleted.

2 changes: 0 additions & 2 deletions crates/client/rpc/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,3 @@
pub const MAX_EVENTS_KEYS: usize = 100;
/// Maximum number of events that can be fetched in a single chunk for the `get_events` RPC.
pub const MAX_EVENTS_CHUNK_SIZE: usize = 1000;
/// Maximum number of keys that can be used to query a storage proof using `getProof` RPC.
pub const MAX_STORAGE_PROOF_KEYS_BY_QUERY: usize = 100;
122 changes: 1 addition & 121 deletions crates/client/rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,11 @@ use std::sync::Arc;
use errors::StarknetRpcApiError;
use jsonrpsee::core::{async_trait, RpcResult};
use log::error;
use mc_rpc_core::types::{ContractData, RpcGetProofInput, RpcGetProofOutput};
pub use mc_rpc_core::utils::*;
use mc_rpc_core::Felt;
pub use mc_rpc_core::StarknetRpcApiServer;
use mc_storage::OverrideHandle;
use mc_transaction_pool::{ChainApi, Pool};
use mp_starknet::crypto::merkle_patricia_tree::merkle_tree::ProofNode;
use mp_starknet::execution::types::Felt252Wrapper;
use mp_starknet::traits::hash::HasherT;
use mp_starknet::traits::ThreadSafeCopy;
Expand All @@ -47,7 +45,7 @@ use starknet_core::types::{
Transaction, TransactionStatus,
};

use crate::constants::{MAX_EVENTS_CHUNK_SIZE, MAX_EVENTS_KEYS, MAX_STORAGE_PROOF_KEYS_BY_QUERY};
use crate::constants::{MAX_EVENTS_CHUNK_SIZE, MAX_EVENTS_KEYS};
use crate::types::RpcEventFilter;

/// A Starknet RPC server for Madara
Expand Down Expand Up @@ -870,124 +868,6 @@ where
None => Err(StarknetRpcApiError::TxnHashNotFound.into()),
}
}

/// This endpoint aims to do the same as [EIP-1186](https://eips.ethereum.org/EIPS/eip-1186)
/// It should provide all the data necessary for someone to verify some storage
/// within a starknet smart contract thanks to its merkle proof.
///
/// It takes advantages from the facts that the whole state is built as 2 tries:
/// 1. The contracts trie : stores state data of the contracts based on their address
/// 2. The classes trie : associates class hashes with classes
///
/// More information on Starknet's state [here](https://docs.starknet.io/documentation/architecture_and_concepts/State/starknet-state/)
///
/// A storage proof is *just* a merkle proof of the subtree which you can find the root within
/// the contracts trie
///
/// This implementation is highly inspired by previous work on [pathfinder](https://github.com/eqlabs/pathfinder/pull/726)
fn get_proof(&self, get_proof_input: RpcGetProofInput) -> RpcResult<RpcGetProofOutput> {
if get_proof_input.keys.len() > MAX_STORAGE_PROOF_KEYS_BY_QUERY {
error!(
"Too many keys requested! limit: {:?},
requested: {:?}",
MAX_STORAGE_PROOF_KEYS_BY_QUERY,
get_proof_input.keys.len() as u32
);
return Err(StarknetRpcApiError::ProofLimitExceeded.into());
}

let substrate_block_hash =
self.substrate_block_hash_from_starknet_block(get_proof_input.block_id).map_err(|e| {
error!("'{e}'");
StarknetRpcApiError::BlockNotFound
})?;

let block = get_block_by_block_hash(self.client.as_ref(), substrate_block_hash).unwrap_or_default();

let global_state_root = block.header().global_state_root;

let mut state_commitments = self
.overrides
.for_block_hash(self.client.as_ref(), substrate_block_hash)
.state_commitments(substrate_block_hash)
.ok_or_else(|| {
error!("Failed to retrieve state commitments");
StarknetRpcApiError::InternalServerError
})?;

let class_commitment: FieldElement = state_commitments.class_commitment.commit().into();
let storage_commitment: FieldElement = state_commitments.storage_commitment.commit().into();

let (state_commitment, class_commitment) = if class_commitment == FieldElement::ZERO {
(None, None)
} else {
(Some(global_state_root.0), Some(class_commitment))
};

// Generate a proof for this contract. If the contract does not exist, this will
// be a "non membership" proof.
let contract_proof =
state_commitments.storage_commitment.get_proof(Felt252Wrapper(get_proof_input.contract_address));

let contract_state_hash =
match state_commitments.storage_commitment.get(Felt252Wrapper(get_proof_input.contract_address)) {
Some(contract_state_hash) => contract_state_hash,
None => {
// Contract not found: return the proof of non membership that we generated earlier.
return Ok(RpcGetProofOutput {
state_commitment,
class_commitment,
contract_proof,
contract_data: None,
});
}
};

// In theory we got some state hash in the tree that means the contract's state
// has been committed already so it exists and no error should be thrown.
// If an error is still thrown it's fine and handy for debugging.

let class_hash = self
.overrides
.for_block_hash(self.client.as_ref(), substrate_block_hash)
.contract_class_hash_by_address(substrate_block_hash, get_proof_input.contract_address.into())
.ok_or_else(|| {
error!("Failed to retrieve contract class hash at '{0}'", get_proof_input.contract_address);
StarknetRpcApiError::ContractNotFound
})?;

let nonce = self
.overrides
.for_block_hash(self.client.as_ref(), substrate_block_hash)
.nonce(substrate_block_hash, get_proof_input.contract_address.into())
.ok_or_else(|| {
error!("Failed to get nonce at '{0}'", get_proof_input.contract_address);
StarknetRpcApiError::ContractNotFound
})?;

let mut contract_state_trie = self
.overrides
.for_block_hash(self.client.as_ref(), substrate_block_hash)
.contract_state_trie_by_address(substrate_block_hash, get_proof_input.contract_address.into())
.ok_or_else(|| {
error!("Failed to get contract state trie at '{0}'", get_proof_input.contract_address);
StarknetRpcApiError::ContractNotFound
})?;

let storage_proofs: Vec<Vec<ProofNode>> =
get_proof_input.keys.iter().map(|k| contract_state_trie.get_proof(Felt252Wrapper(*k))).collect();

let contract_data = ContractData {
class_hash: class_hash.into(),
nonce: nonce.into(),
root: contract_state_trie.commit().into(),
// Currently, this is defined as 0. Might change in the future
contract_state_hash_version: FieldElement::ZERO,
storage_proofs,
};

Ok(RpcGetProofOutput { state_commitment, class_commitment, contract_proof, contract_data: Some(contract_data) })
}
}

async fn submit_extrinsic<P, B>(
Expand Down
59 changes: 1 addition & 58 deletions crates/client/storage/src/overrides/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use frame_support::{Identity, StorageHasher};
use mp_starknet::execution::types::{ClassHashWrapper, ContractAddressWrapper, Felt252Wrapper};
use mp_starknet::storage::StarknetStorageSchemaVersion;
use pallet_starknet::runtime_api::StarknetRuntimeApi;
use pallet_starknet::types::{NonceWrapper, StateCommitments, StateTrie};
use pallet_starknet::types::NonceWrapper;
use sc_client_api::{Backend, HeaderBackend, StorageProvider};
use sp_api::ProvideRuntimeApi;
use sp_io::hashing::twox_128;
Expand Down Expand Up @@ -79,17 +79,6 @@ pub trait StorageOverride<B: BlockT>: Send + Sync {
) -> Option<ContractClass>;
/// Returns the nonce for a provided contract address and block hash.
fn nonce(&self, block_hash: B::Hash, address: ContractAddressWrapper) -> Option<NonceWrapper>;
/// Returns the state commitments for a provider block hash
fn state_commitments(&self, block_hash: B::Hash) -> Option<StateCommitments>;
/// Returns the state root at a provided contract address for the provided block.
fn contract_state_root_by_address(
&self,
block_hash: B::Hash,
address: ContractAddressWrapper,
) -> Option<Felt252Wrapper>;
/// Returns the contract state trie at a provided contract address for the provided block.
fn contract_state_trie_by_address(&self, block_hash: B::Hash, address: ContractAddressWrapper)
-> Option<StateTrie>;
}

/// Returns the storage prefix given the pallet module name and the storage name
Expand Down Expand Up @@ -198,50 +187,4 @@ where
fn nonce(&self, block_hash: <B as BlockT>::Hash, contract_address: ContractAddressWrapper) -> Option<NonceWrapper> {
self.client.runtime_api().nonce(block_hash, contract_address).ok()
}

/// Return the state commitments for a provided block hash
///
/// # Arguments
///
/// * `block_hash` - The block hash
///
/// # Returns
/// * `Some(commitments)` - The state commitments for the provided block hash
fn state_commitments(&self, block_hash: <B as BlockT>::Hash) -> Option<StateCommitments> {
self.client.runtime_api().get_state_commitments(block_hash).ok()
}

/// Return the contract root for a provided block hash
///
/// # Arguments
///
/// * `block_hash` - The block hash
///
/// # Returns
/// * `Some(contract_root)` - The contract root for the provided block hash
fn contract_state_root_by_address(
&self,
block_hash: <B as BlockT>::Hash,
address: ContractAddressWrapper,
) -> Option<Felt252Wrapper> {
let api = self.client.runtime_api();
api.contract_state_root_by_address(block_hash, address).ok()?
}

/// Return the contract state trie for a provided block hash
///
/// # Arguments
///
/// * `block_hash` - The block hash
///
/// # Returns
/// * `Some(state_trie)` - The contract state trie for the provided block hash
fn contract_state_trie_by_address(
&self,
block_hash: <B as BlockT>::Hash,
address: ContractAddressWrapper,
) -> Option<StateTrie> {
let api = self.client.runtime_api();
api.contract_state_trie_by_address(block_hash, address).ok()?
}
}
Loading
Loading