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

Benchmark feature #31

Merged
merged 20 commits into from
Jul 16, 2024
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
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: Install LLVM
run: sudo apt-get install llvm-18 llvm-18-dev llvm-18-runtime clang-18 clang-tools-18 lld-18 libpolly-18-dev libmlir-18-dev mlir-18-tools
- name: Clippy
run: cargo clippy --all-targets -- -D warnings
run: cargo clippy --all-targets --all-features -- -D warnings

format:
name: rustfmt
Expand Down Expand Up @@ -164,4 +164,4 @@ jobs:
make runtime
echo "CAIRO_NATIVE_RUNTIME_LIBRARY=$(pwd)/libcairo_native_runtime.a" > $GITHUB_ENV
- name: Test
run: cargo test
run: cargo test --all-features
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

169 changes: 169 additions & 0 deletions replay/src/benchmark.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
use blockifier::{
context::BlockContext,
state::{cached_state::CachedState, state_api::StateReader},
};
use rpc_state_reader::{
blockifier_state_reader::{execute_tx_with_blockifier, fetch_block_context, RpcStateReader},
rpc_state::{RpcChain, RpcState},
};
use starknet_api::{
block::BlockNumber,
hash::StarkFelt,
transaction::{Transaction as SNTransaction, TransactionHash},
};
use tracing::{error, info};

pub type BlockCachedData = (
CachedState<OptionalStateReader<RpcStateReader>>,
BlockContext,
Vec<(TransactionHash, SNTransaction)>,
);

/// Fetches context data to execute the given block range
///
/// It does not actually execute the block range, so not all data required
/// by blockifier will be cached. To ensure that all rpc data is cached,
/// the block range must be executed once.
///
/// See `execute_block_range` to execute the block range
pub fn fetch_block_range_data(
block_start: BlockNumber,
block_end: BlockNumber,
chain: RpcChain,
) -> Vec<BlockCachedData> {
let mut block_caches = Vec::new();

for block_number in block_start.0..=block_end.0 {
// For each block
let block_number = BlockNumber(block_number);

let rpc_state = RpcState::new_rpc(chain, block_number.into()).unwrap();

// Fetch block context
let block_context = fetch_block_context(&rpc_state, block_number);

// Fetch transactions for the block
let transactions = rpc_state
.get_transaction_hashes()
.unwrap()
.into_iter()
.map(|transaction_hash| {
let transaction_hash = TransactionHash(
StarkFelt::try_from(transaction_hash.strip_prefix("0x").unwrap()).unwrap(),
);

// Fetch transaction
let transaction = rpc_state.get_transaction(&transaction_hash).unwrap();

(transaction_hash, transaction)
})
.collect::<Vec<_>>();

// Create cached state
let previous_rpc_state =
RpcState::new_rpc(chain, block_number.prev().unwrap().into()).unwrap();
let previous_rpc_state_reader = RpcStateReader::new(previous_rpc_state);
let cached_state = CachedState::new(OptionalStateReader::new(previous_rpc_state_reader));

block_caches.push((cached_state, block_context, transactions));
}

block_caches
}

/// Executes the given block range, discarding any state changes applied to it
///
/// Can also be used to fill up the cache
pub fn execute_block_range(block_range_data: &mut Vec<BlockCachedData>) {
for (state, block_context, transactions) in block_range_data {
// For each block

// The transactional state is used to execute a transaction while discarding state changes applied to it.
let mut transactional_state = CachedState::create_transactional(state);

for (transaction_hash, transaction) in transactions {
// Execute each transaction
let result = execute_tx_with_blockifier(
&mut transactional_state,
block_context.clone(),
transaction.to_owned(),
transaction_hash.to_owned(),
);

match result {
Ok(info) => {
info!(
transaction_hash = transaction_hash.to_string(),
succeeded = info.revert_error.is_none(),
"tx execution status"
)
}
Err(_) => error!(
transaction_hash = transaction_hash.to_string(),
"tx execution failed"
),
}
}
}
}

/// An implementation of StateReader that can be disabled, panicking if atempted to be read from
///
/// Used to ensure that no requests are made after disabling it.
pub struct OptionalStateReader<S: StateReader>(pub Option<S>);

impl<S: StateReader> OptionalStateReader<S> {
pub fn new(state_reader: S) -> Self {
Self(Some(state_reader))
}

pub fn get_inner(&self) -> &S {
self.0
.as_ref()
.expect("atempted to read from a disabled state reader")
}

pub fn disable(&mut self) {
self.0 = None;
}
}

impl<S: StateReader> StateReader for OptionalStateReader<S> {
fn get_storage_at(
&self,
contract_address: starknet_api::core::ContractAddress,
key: starknet_api::state::StorageKey,
) -> blockifier::state::state_api::StateResult<StarkFelt> {
self.get_inner().get_storage_at(contract_address, key)
}

fn get_nonce_at(
&self,
contract_address: starknet_api::core::ContractAddress,
) -> blockifier::state::state_api::StateResult<starknet_api::core::Nonce> {
self.get_inner().get_nonce_at(contract_address)
}

fn get_class_hash_at(
&self,
contract_address: starknet_api::core::ContractAddress,
) -> blockifier::state::state_api::StateResult<starknet_api::core::ClassHash> {
self.get_inner().get_class_hash_at(contract_address)
}

fn get_compiled_contract_class(
&self,
class_hash: starknet_api::core::ClassHash,
) -> blockifier::state::state_api::StateResult<
blockifier::execution::contract_class::ContractClass,
> {
self.get_inner().get_compiled_contract_class(class_hash)
}

fn get_compiled_class_hash(
&self,
class_hash: starknet_api::core::ClassHash,
) -> blockifier::state::state_api::StateResult<starknet_api::core::CompiledClassHash> {
self.get_inner().get_compiled_class_hash(class_hash)
}
}
Loading