Skip to content

Commit

Permalink
Rpc client optimisations (#47)
Browse files Browse the repository at this point in the history
* Created 'RpcClient' struct

* Added rpc calls optimisations.

- Added lazy_static for chain_id and latest_block_number
- Removed call to block_header

* Replaced internal call to block_header with external call

* Removed nightly from CI

* Fixed documentation

* Added type alias 'BlockWithReceipts'

* Replaced 'DatabaseError' with 'ProviderError'

* Improvements to README
  • Loading branch information
Eagle941 authored Oct 2, 2024
1 parent 0d63282 commit 441811a
Show file tree
Hide file tree
Showing 8 changed files with 505 additions and 413 deletions.
30 changes: 0 additions & 30 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,36 +31,6 @@ jobs:
- name: Run tests
run: cargo test --locked

build_nightly:
name: Test Nightly
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout source
uses: actions/checkout@v4
- name: Rust cache
uses: Swatinem/rust-cache@v2
with:
shared-key: "build-cache-nightly"
cache-all-crates: "true"
cache-directories: |
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
- name: Install rust nightly
uses: actions-rs/toolchain@v1
with:
toolchain: nightly
profile: minimal
override: true
- name: Build
run: cargo build --locked
- name: Run tests
run: cargo test --locked

clippy:
name: Lint
runs-on: ubuntu-latest
Expand Down
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# Starknet Replay

`starknet-replay` is a CLI application to replay Starknet transactions. The
state of the blockchain is queried using the Starknet RPC protocol.
It also reports the frequency with which each `libfunc` has been called when
replaying the transactions.
state of the blockchain is queried using the Starknet RPC protocol. It also
reports the frequency with which each `libfunc` has been called when replaying
the transactions.

It's possible to export the histogram of the most frequently used libfuncs
by number of calls. The data plotted in the histogram is filtered to only
include the libfuncs that amount to 80% of the total calls in the replay. This
helps readability and visual analysis.
It's possible to export the histogram of the most frequently used libfuncs by
number of calls. The data plotted in the histogram is filtered to only include
the libfuncs that amount to 80% of the total calls in the replay. This helps
readability and visual analysis.

Only `INVOKE` transactions of Sierra contracts are used for this report because
only Sierra contracts use libfuncs and only `INVOKE` transactions execute Sierra
Expand Down Expand Up @@ -44,9 +44,14 @@ histogram in the file named `"histogram.svg"`.

## Limitations

- Libfunc frequency results haven't been checked yet.
- Log of transaction traces to JSON requires a lot of memory allocation. It
still needs to be optimised.
Libfunc frequency results haven't been checked yet.

## Requirements

This crate is compatible with Rust 1.78. Both x86 and ARM are supported.

`blockifier` dependency is not compatible with Rust 1.81+. `pathfinder_simp`
dependency is not compatible with Rust 1.83+ on ARM.

## Useful links

Expand Down
5 changes: 2 additions & 3 deletions starknet-replay/src/runner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,15 @@ where
for block_number in start_block.get()..=last_block.get() {
let block_number = BlockNumber::new(block_number);

let (transactions, receipts) =
let (block_header, transactions, receipts) =
storage.get_transactions_and_receipts_for_block(block_number)?;

let transactions_to_process = transactions.len();
tracing::info!(
"{transactions_to_process} transactions to process in block {block_number:?}"
);

let header = storage.get_block_header(block_number)?;
let replay_block = ReplayBlock::new(header, transactions, receipts)?;
let replay_block = ReplayBlock::new(block_header, transactions, receipts)?;
replay_blocks.push(replay_block);
}

Expand Down
19 changes: 10 additions & 9 deletions starknet-replay/src/runner/replay_state_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ use starknet_core::types::{ContractClass as StarknetContractClass, Felt};

use crate::block_number::BlockNumber;
use crate::runner::replay_class_hash::ReplayClassHash;
use crate::storage::rpc::{contract_class, RpcStorage};
use crate::storage::rpc::contract_class;
use crate::storage::rpc::rpc_client::RpcClient;

/// This structure is used by [`blockifier`] to access blockchain data during
/// transaction replay.
pub struct ReplayStateReader<'a> {
/// The reference to [`crate::storage::rpc::RpcStorage`] to make RPC calls.
storage: &'a RpcStorage,
rpc_client: &'a RpcClient,

/// The block number used to query the state.
block_number: BlockNumber,
Expand All @@ -31,9 +32,9 @@ impl ReplayStateReader<'_> {
/// state.
/// - `block_number`: The block number at which state is read.
#[must_use]
pub fn new(storage: &RpcStorage, block_number: BlockNumber) -> ReplayStateReader<'_> {
pub fn new(rpc_client: &RpcClient, block_number: BlockNumber) -> ReplayStateReader<'_> {
ReplayStateReader {
storage,
rpc_client,
block_number,
}
}
Expand All @@ -45,7 +46,7 @@ impl StateReader for ReplayStateReader<'_> {
key: StorageKey,
) -> StateResult<Felt> {
let storage_value = self
.storage
.rpc_client
.starknet_get_storage_at(&self.block_number, &contract_address, &key)
.map_err(|err| {
StateError::StateReadError(
Expand All @@ -57,7 +58,7 @@ impl StateReader for ReplayStateReader<'_> {

fn get_nonce_at(&self, contract_address: ContractAddress) -> StateResult<Nonce> {
let nonce = self
.storage
.rpc_client
.starknet_get_nonce(&self.block_number, &contract_address)
.map_err(|err| {
StateError::StateReadError(
Expand All @@ -69,7 +70,7 @@ impl StateReader for ReplayStateReader<'_> {

fn get_class_hash_at(&self, contract_address: ContractAddress) -> StateResult<ClassHash> {
let class_hash = self
.storage
.rpc_client
.starknet_get_class_hash_at(&self.block_number, &contract_address)
.map_err(|err| {
StateError::StateReadError(
Expand All @@ -88,7 +89,7 @@ impl StateReader for ReplayStateReader<'_> {
class_hash,
};
let contract_class = self
.storage
.rpc_client
.starknet_get_class(&replay_class_hash)
.map_err(|err| {
StateError::StateReadError(
Expand Down Expand Up @@ -125,7 +126,7 @@ impl StateReader for ReplayStateReader<'_> {
class_hash,
};
let contract_class = self
.storage
.rpc_client
.starknet_get_class(&replay_class_hash)
.map_err(|err| {
StateError::StateReadError(
Expand Down
6 changes: 5 additions & 1 deletion starknet-replay/src/storage/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ use crate::{ReplayBlock, RunnerError};

pub mod rpc;

/// The type [`BlockWithReceipts`] bundles together all the block data: block
/// header, transaction data and receipt data.
pub type BlockWithReceipts = (BlockHeader, Vec<Transaction>, Vec<TransactionReceipt>);

pub trait Storage {
/// Returns the most recent block number available in the storage.
///
Expand Down Expand Up @@ -65,7 +69,7 @@ pub trait Storage {
fn get_transactions_and_receipts_for_block(
&self,
block_number: BlockNumber,
) -> Result<(Vec<Transaction>, Vec<TransactionReceipt>), DatabaseError>;
) -> Result<BlockWithReceipts, DatabaseError>;

/// Replays the list of transactions in a block and returns the list of
/// transactions traces.
Expand Down
Loading

0 comments on commit 441811a

Please sign in to comment.